Repository navigation
verify: the MCP access-profile denial shape #303
Description
What needs verifying
"Least-Privileged MCP Access Profiles" (Nov 2026) gives "each consumer a scoped subset
of tools instead of all-or-nothing MCP access".
Three things need confirming:
- Whether tool discovery returns only the caller's in-profile tools, or the full
server tool list with enforcement deferred to call time. - What the gateway returns when an out-of-profile tool is called — status, body,
and any distinguishing header. - Whether 1 and 2 are identical for both server origins: a Mule-hosted MCP
server (MCP Bridge, a Mule app) and a framework-built one (e.g. FastMCP over
streamable HTTP) behind the same gateway. The SDK's consumer path is meant to
be origin-agnostic (tools: donkey.tools.discover() returns governed MCP tools as native framework tools #221, tools: a typed refusal for a tool outside the consumer's MCP access profile #312); this confirms it rather than assumes it.
Why it blocks work
BG §2.7 already states that discovery filters to "only the tools it is allowed to
call", but donkey.tools.discover is _verify.blocked today (#221) and the
enforcement semantics behind that sentence are unconfirmed.
Without the denial shape, an out-of-profile tool call classifies through the generic
4xx branch in classify() and the agent cannot distinguish "this tool is not
provisioned for you" — a configuration problem for a human to fix — from "this
call broke a content policy" — a runtime refusal the agent might handle by
rephrasing. Those need different reactions, and BG §1.2's whole premise is that the
refusal type drives the reaction.
The discovery question matters independently: if discovery is not profile-scoped,
the SDK must filter client-side after resolution, which changes where
tools/filter.py sits in the flow.
Done when
- Confirmed whether discovery is profile-scoped server-side.
- The out-of-profile denial shape captured as a fixture.
- Confirmed whether the denial names the tool or the profile.
- Questions 1 and 2 answered for both a Mule-hosted and a framework-built
server, with a fixture for each, or the difference recorded. -
docs/verified-apis.mdrow added.
Cross-surface impact
- tools:
session.pyandfilter.py— where filtering happens depends on the answer. - core:
errors.py— a new typed refusal if the denial is distinguishable. - registry: the discovery query, if profile scoping is a query parameter.
- llm / integrations: not affected.
- TypeScript parity (planned): flag.
- Provisioning CLI: not affected.
- Nextra docs site: the tool-access section.
- docs/verified-apis.md: new row.
Related
- tools: donkey.tools.discover() returns governed MCP tools as native framework tools #221 —
donkey.tools.discover(), blocked on verification. - tools: allow/deny filtering and descriptor token accounting #222 — allow/deny filtering and descriptor token accounting.
Activity