From bbc22f7ccab8b78b6869bd8edc483bc5fbc09243 Mon Sep 17 00:00:00 2001 From: os-zhuang Date: Sun, 12 Jul 2026 21:00:14 +0800 Subject: [PATCH] docs: correct agent permission-model framing to match shipped D10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent docs described MCP OAuth as "runs as you / scopes narrow tool families" — inaccurate after the ADR-0090 D10 agent work shipped (#2838/#2843/#2845). An OAuth client is now an agent acting ON BEHALF OF the user, bounded by the INTERSECTION of the consent scopes and the user's own permissions/RLS, and scopes are a real data-layer ceiling (a data:read token can never write, even where the user could) — not just a tool-family filter. - packages/mcp/src/skill.ts (SKILL.md served to every connecting agent): fix "runs as you" → agent-on-behalf + scope-ceiling intersection. - content/docs/ai/agents.mdx: same correction, user-facing. - docs/design/permission-model.md: mark the overlap rule *enforced* (#2838/#2843/#2845); honestly mark the three still-PLANNED agent guardrails — the grant-ceiling *lint* (runtime intersection already caps it; the bind-time lint is #2849), destructive-action co-sign, and the double-signature audit provenance (writer stamps only the delegator today). - docs/adr/0090: status note that D10 evaluation semantics landed. mcp suite 66 green. Co-Authored-By: Claude Opus 4.8 --- .changeset/agent-permission-doc-accuracy.md | 5 +++++ content/docs/ai/agents.mdx | 13 ++++++++---- ...permission-model-v2-concept-convergence.md | 6 ++++++ docs/design/permission-model.md | 21 +++++++++++++------ packages/mcp/src/skill.ts | 14 ++++++++----- 5 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 .changeset/agent-permission-doc-accuracy.md diff --git a/.changeset/agent-permission-doc-accuracy.md b/.changeset/agent-permission-doc-accuracy.md new file mode 100644 index 000000000..32a10192c --- /dev/null +++ b/.changeset/agent-permission-doc-accuracy.md @@ -0,0 +1,5 @@ +--- +'@objectstack/mcp': patch +--- + +Docs accuracy: correct how the MCP SKILL.md describes an agent's authority to match the shipped ADR-0090 D10 model. An OAuth-connected client is an **agent acting on behalf of** the signing-in user — every call is bounded by the **intersection** of the consent scopes and that user's own permissions/RLS (a `data:read` token can never write, even where the user could), not simply "runs as you". (Companion doc-only edits to `content/docs/ai/agents.mdx` and `docs/design/permission-model.md` correct the same framing and honestly mark the still-planned agent guardrails — the grant-ceiling lint, destructive-action co-sign, and double-signature audit provenance.) diff --git a/content/docs/ai/agents.mdx b/content/docs/ai/agents.mdx index abfc0e815..2ac6e331f 100644 --- a/content/docs/ai/agents.mdx +++ b/content/docs/ai/agents.mdx @@ -56,10 +56,15 @@ SKILL.md download (`GET /api/v1/mcp/skill`), and API-key minting. discovery metadata, clients self-register via Dynamic Client Registration (RFC 7591) and run an authorization-code + PKCE flow. Nothing is pre-registered with Anthropic or any central service, so **self-hosted and - private deployments work out of the box**. You log in through the browser - as yourself; every tool call runs under your permissions and RLS. Consent - scopes (`data:read`, `data:write`, `actions:execute`) narrow which tool - families the client gets. + private deployments work out of the box**. You authorize the client in the + browser; it then acts as an **agent on your behalf** (`principalKind: 'agent'`), + bounded by the **intersection** of the consent scopes and your own + permissions/RLS — it can never exceed either. The consent scopes + (`data:read`, `data:write`, `actions:execute`) are a real ceiling, not just a + tool-family filter: they narrow the exposed tools **and** cap what the agent + can do at the data layer — a `data:read` token can never write a record even + where you could, and `actions:execute` lets the agent invoke the business + actions you are entitled to run. - **API key (headless).** Mint a key (`POST /api/v1/keys`, shown once) and send it as `x-api-key` — for CI, scripts, and agents without a browser. diff --git a/docs/adr/0090-permission-model-v2-concept-convergence.md b/docs/adr/0090-permission-model-v2-concept-convergence.md index e052d5207..e6ced481b 100644 --- a/docs/adr/0090-permission-model-v2-concept-convergence.md +++ b/docs/adr/0090-permission-model-v2-concept-convergence.md @@ -312,6 +312,12 @@ package-owned sets: ### D10 — Principal taxonomy; agents act on intersected authority +> **Status (2026-07):** the P1 ctx *shape* shipped pre-launch as written below; the agent +> **evaluation semantics** have since landed — the intersection at every layer (#2838), the +> producer that resolves an MCP OAuth client to `principalKind:'agent'` + `onBehalfOf` with a +> scope-derived ceiling (#2843), and delegated action capabilities (#2845). Follow-ups tracked in +> #2849 (agent action-body boundary / grant-ceiling lint) and the agent audit-provenance gap. + ExecutionContext principals gain an explicit taxonomy: ``` diff --git a/docs/design/permission-model.md b/docs/design/permission-model.md index 181f85814..b0260fbda 100644 --- a/docs/design/permission-model.md +++ b/docs/design/permission-model.md @@ -270,15 +270,24 @@ Every request carries a **principal** — who (or what) is asking. Five kinds, i **both** the assistant is allowed to do **and** 张三 is allowed to do — the overlap, never the union. Why: if an assistant with big permissions simply obeyed whoever prompts it, any intern could ask it to fetch the CEO's records. The overlap rule makes that structurally impossible. +*Status: enforced.* The evaluator intersects the agent's own grants with the delegator's at every +layer — object CRUD, FLS, depth, row-level `using`/`check`, and RLS — and the producer resolves an +MCP OAuth client to `principalKind:'agent'` + `onBehalfOf` with a scope-derived ceiling +(#2838 / #2843 / #2845). An agent's own View-All is stripped unless the delegator also holds it. Three more agent guardrails: -1. **Ceiling** — sets held by agents may never contain super-user bits (View/Modify All, purge, - transfer, system permissions). Lint rejects the binding, not the request. -2. **Human co-sign for destructive actions** — an agent may *start* a deletion or mass change, - but a human must confirm it, regardless of grants. -3. **Double signature in the audit log** — every agent write records both "performed by: - assistant-X" and "on behalf of: 张三", plus the run that did it. +1. **Ceiling** — an agent can never wield super-user bits (View/Modify All, purge, transfer, system + permissions) it does not *also* hold via the delegator: the intersection above caps it at + runtime today. *Planned:* a dedicated authoring lint that rejects **binding** such a set to an + agent up front (defense-in-depth on top of the runtime cap) — see #2849. +2. **Human co-sign for destructive actions** — an agent may *start* a deletion or mass change, but a + human confirms it, regardless of grants. *Planned* (HITL signal exists on actions; the enforced + co-sign gate is not yet wired). +3. **Double signature in the audit log** — every agent write should record both "performed by: + assistant-X" and "on behalf of: 张三", plus the run that did it. *Planned:* the audit writer + currently stamps only the delegating user; agent provenance (principal kind + client) is not yet + captured. ## 10. Running a large organization diff --git a/packages/mcp/src/skill.ts b/packages/mcp/src/skill.ts index b10c72d93..3b0d9ada3 100644 --- a/packages/mcp/src/skill.ts +++ b/packages/mcp/src/skill.ts @@ -60,9 +60,11 @@ description: ${OBJECTSTACK_SKILL_DESCRIPTION} ${intro} An ObjectStack environment exposes its data **objects** (tables) and its business **actions** (registered app logic: approvals, conversions, flow -triggers) as tools over the Model Context Protocol (MCP). Every operation runs **as you** — -under your account's permissions and row-level security — so you may see a -subset of rows, or get a permission error on a write. That is expected +triggers) as tools over the Model Context Protocol (MCP). Every operation runs +under the permissions and row-level security of the identity that connected you +— and, for an OAuth connection, **within the consent scopes you were granted** +(a read-only scope can never write, even where that user could). So you may see +a subset of rows, or get a permission error on a write. That is expected governance, not a failure. ## When to use @@ -88,8 +90,10 @@ Two authentication tracks are supported: server with no credentials; the deployment is its own OAuth 2.1 authorization server, so an OAuth-capable client (claude.ai custom connectors, Claude Desktop, Claude Code) discovers it automatically, registers itself, and opens -a browser login. You sign in as yourself and every tool call runs under your -own permissions. Example (Claude Code): +a browser login. You authorize the connection in the browser; the client then +acts as an agent **on your behalf** — every tool call is bounded by the +intersection of the consent scopes you grant and your own permissions/RLS (it +can never exceed either). Example (Claude Code): \`\`\` claude mcp add --transport http objectstack ${url}