Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/agent-permission-doc-accuracy.md
Original file line number Diff line number Diff line change
@@ -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.)
13 changes: 9 additions & 4 deletions content/docs/ai/agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
6 changes: 6 additions & 0 deletions docs/adr/0090-permission-model-v2-concept-convergence.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand Down
21 changes: 15 additions & 6 deletions docs/design/permission-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 9 additions & 5 deletions packages/mcp/src/skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}
Expand Down