# For AI agents

Source: https://permdock.com/docs/for-ai-agents

How a coding agent should install, wire, check and audit PermDock, and how PermDock's errors and denials are written for models.

PermDock's second audience is the coding agent working in a TypeScript repository. Every page, skill, error message and CLI report is written so that an agent can wire PermDock end-to-end, verify its work and explain a denial without reading library source. This page is the entry point for that agent. The `permdock` skill and the `permdock-*` skills ship in the `permdock` package. Point an MCP client at `https://permdock.com/mcp` for search and page-fetch; [agent docs standards](/docs/standards/agent-docs-standards#docs-mcp) has install links for Cursor and VS Code and the Claude Code command.

## Start here [#start-here]

```bash
npx skills add ScaleDockHQ/PermDock
```

This installs eight Agent Skills into the folders your agent reads (`.agents/skills/`, `.claude/skills/`, `.cursor/skills/`); `--skill <name>` installs one:

* **`permdock`**: the mental model and invariants, reading a `Decision`, `explain` for an unexpected denial, the docs MCP, and which skill to load next. Start here.
* **`permdock-wire`**: detect the framework and validator, create `src/permissions.ts`, `src/policy.ts` and the factory file, add the first check and the first UI guard, run the CLI checks, add CI steps.
* **`permdock-audit`**: review an existing installation or a pull request for ungranted or unused permissions, validation and identity gaps, untested grants, and the OWASP Agentic Top 10 mapping; it runs each topic skill's Verify list.
* **`permdock-agents`**, **`permdock-approvals`**, **`permdock-tenancy`**, **`permdock-data`**, **`permdock-credentials`**: one area each, from agent delegation and human approval to named scopes, queries and RLS, and tokens, API keys and share links.

The same skills ship inside the `permdock` npm package and can be installed with `permdock skills install`, so the skill version always matches the library version. In Claude Code, `/plugin marketplace add ScaleDockHQ/permdock` adds them as the `permdock` plugin from `.claude-plugin/marketplace.json`. See [skills](/docs/cli/skills).

## What the repository gives you [#what-the-repository-gives-you]

| Artifact | Where | Use it for |
| --- | --- | --- |
| `AGENTS.md` (`CLAUDE.md` imports it) | repository root | Maintainer guide: layout, commands, the invariants and an index of the topic rules |
| `.agents/rules/*.mdc` | repository root, linked into `.cursor/rules` and `.claude/rules` | Maintainer rules attached by path: invariants, naming, docs, "when you change X also update Y", testing, prose |
| `llms.txt`, `llms-full.txt` | docs site root, and under `/docs` | Index and full text of the docs for context loading |
| `.md` per page | append `.md` to any docs URL | Read one page as Markdown without HTML |
| Docs MCP server | `POST /mcp` (`search`, `list_pages`, `get_page`) | Search and read docs from inside an MCP-capable agent |
| Docs WebMCP tools | every docs page (`search_docs`, `read_page`, `open_page`) | Search and read docs from the browser's own agent; experimental, Chrome 149+ behind a flag |
| Cloud MCP server | `https://mcp.permdock.com` | Read-only evidence, catalog and pending approvals; never resolves an approval |
| `permissions.catalog.json` and its JSON Schema | app repository, `permdock catalog --format schema` | Know which permissions exist, their arity, scopes and metadata |
| `permdock doctor --json` | app repository | Machine-readable findings with stable codes and fixes |
| Example apps | `apps/examples/<adapter>` | Working code for every adapter, each checked by `permdock doctor` |

Standards for these artifacts are on the [Agent docs standards](/docs/standards/agent-docs-standards) page.

## The shortest wiring recipe [#the-shortest-wiring-recipe]

Three files and one check. Import paths and identifiers follow the [naming convention](/docs/getting-started/naming); do not invent framework-prefixed names.

```ts
// src/permissions.ts — importable everywhere; no rules, no secrets
import { definePermissions, defineRoles, resource, crud } from "permdock";
import { Post } from "./schemas"; // any Standard Schema validator

export const permissions = definePermissions({
  post: resource(
    Post,
    crud({
      id: "id",
      actions: { publish: { title: "Publish post" } },
    }),
  ),
});

export const roles = defineRoles({ member: {} });
```

```ts
// src/policy.ts — server-only
import { definePolicy, role, allow, principal } from "permdock";
import { permissions, roles } from "./permissions";

const member = role(roles.member, [
  allow(permissions.post.read),
  allow(permissions.post.list),
  allow(permissions.post.create),
  allow(permissions.post.update, { where: { authorId: principal.id } }),
  allow(permissions.post.delete, {
    where: { authorId: principal.id },
    approval: "human",
  }),
  allow(permissions.post.publish, { approval: "human" }),
]);

export const policy = definePolicy(
  { permissions, roles },
  {
    roles: [member],
    principal: (user: User | null) =>
      user && { id: user.id, roles: user.roles },
  },
);
```

```ts
// src/permdock/server.ts — the explicit factory; same pattern for permdock/hono, permdock/mcp, permdock/ai-sdk
import { createPermDock } from "permdock/next";
import { policy } from "../policy";

export const { getPermDock, getPermission, PermDockProvider, permdockHandler } =
  createPermDock(policy, {
    subject: async () => getUser(await cookies()),
  });
```

Then, in a Server Component or Server Action:

```ts
const permdock = await getPermDock();
permdock.assert(permissions.post.update, post);
```

And in a client component:

```tsx
import { usePermission } from "permdock/react";
const { allowed, status } = usePermission(permissions.post.update, post);
```

Finish with `pnpm exec permdock doctor` and add `permdock collect --check` and `permdock usage --strict` to CI. Full versions: [quick start](/docs/getting-started/quick-start), [Next.js adapter](/docs/adapters/next), [MCP adapter](/docs/adapters/mcp), [AI SDK adapter](/docs/adapters/ai-sdk).

### The shortest OpenAPI recipe [#the-shortest-openapi-recipe]

If the project already generates an OpenAPI description, do not write `security` by hand and do not add a PermDock wrapper around the generator. Emit PermDock's Overlay and let the existing pipeline apply it:

```bash
permdock openapi emit --doc public/openapi.json --format overlay --out permdock.overlay.json
```

For Next.js and the other frameworks next-openapi-gen scans, add `overlay: { apply: ['./permdock.overlay.json'] }` to `openapi-gen.config.ts`; for Redocly pipelines, `redocly join openapi.json --overlay permdock.overlay.json -o dist/openapi.json`. Hey API, Orval, Scalar and any OpenAPI-to-MCP bridge then read the applied description with no PermDock code. Rules: every operation needs an `operationId`; do not also declare security through the producer (`@auth`, `authPresets`) on operations PermDock covers; add `permdock openapi emit --format overlay --check` to CI. Details: [OpenAPI adapter](/docs/adapters/openapi), [adapters](/docs/adapters).

## Invariants an agent must keep [#invariants-an-agent-must-keep]

Use permission references, never strings; keep the policy and every `createPermDock` call out of client files (`permdock doctor` reports it as `PD001`); take the subject only from the verified session or token, never from a model argument; prefer portable `where` conditions to closures; and put `approval: 'human'` on destructive actions an agent can reach. Everything else fails closed: a `Decision` is only ever `granted`, `denied` or `approval-required`. The full list, with the reason for each rule, is the [threat model](/docs/security/threat-model).

## How errors and denials are written for models [#how-errors-and-denials-are-written-for-models]

PermDock assumes the reader of a denial may be a language model deciding what to do next, so denials carry structure rather than prose:

* `decide()` returns `{ outcome: 'denied', denials: [{ role, reason }], alternatives: [...] }`. `reason` is the policy author's string; `alternatives` lists permissions on the same resource this subject does hold, so the model can pick a permitted action instead of retrying the same one.
* `approval-required` carries the grant, a reason and a `token`. Adapters turn it into AI SDK `user-approval`, `WorkflowAgent` `needsApproval`, MCP `input_required` or an HTTP 403 with the `.../approval-required` problem type; the token must be echoed back when the approval resumes.
* MCP refusals return `isError: true` with the `Decision` in `structuredContent`, and missing delegation produces a `scopeChallenge` naming the exact scope (`post:delete`) to request.
* HTTP adapters emit RFC 9457 `application/problem+json` with `type`, `title`, `permission`, `denials` and `alternatives`; see [Errors](/docs/concepts/errors) and [Problem Details](/docs/standards/problem-details).
* `PermDockValidationError` names the resource and the schema issues; `PermDockDeniedError` and `PermDockApprovalRequiredError` carry the `Decision`.
* CLI reports use stable codes (`PD001`...) with a one-line fix each, and `--json` output carries a `$schema`.
* `simulate([[permission, data], ...])` lets an agent pre-flight a whole plan and read every decision before acting; it is the in-process form of AuthZEN `evaluations`. `simulate({ arazzo, openapi })` does the same from an Arazzo workflow; a `provisional: true` step is not a final grant.

## Reading the docs [#reading-the-docs]

* Concepts first: [Permissions](/docs/concepts/permissions), [Policies](/docs/concepts/policies), [Decisions](/docs/concepts/decisions), [Subject](/docs/concepts/subject), [Authentication](/docs/concepts/authentication).
* One adapter page per framework, each with the same sections (purpose, API, lifecycle, validation, denials, example app, standards): [Adapters](/docs/adapters).
* Design rationale lives in the Why sections of the owning pages; cite them instead of relitigating a constraint: [permissions](/docs/concepts/permissions), [Next.js plugin](/docs/cli/next-plugin), [decisions](/docs/concepts/decisions).
* A `Status: planned` or `Status: tracking` line under a page's frontmatter means the thing does not exist yet; a page without one describes code in the package. Undecided design questions are listed on the [roadmap](/docs/roadmap).

## Security framing for agent code [#security-framing-for-agent-code]

PermDock's agent features map onto the [OWASP Top 10 for Agentic Applications](/docs/security/owasp-agentic): per-tool permissions with `approval: 'human'` gates and boundary validation address ASI02 Tool Misuse; the two-principal subject and delegation intersection address ASI03 Identity and Privilege Abuse. When writing tool handlers, wire the permission on the tool (`permission` in `registerTool` or the `tools` map) rather than checking inside the handler, so `list_tools` and capability middleware can hide what the caller may not do.
