When AI Subagents Call MCP Tools, Who Owns the Permission Decision?

- Share:





2938 Members
Over the last day, teams debugging Claude Code subagent behavior hit the same uncomfortable question: if the parent can call an MCP tool, who actually owns the permission decision when a child process makes the call? The real-world reports in issue #46228 and issue #61315 show why this is not an edge case. Once work moves into background subagents, token context, approval surfaces, and runtime authority can drift apart fast.
That is why subagents should be treated as delegated actors, not permission clones. Parent access to Gmail, GitHub, or a database over MCP is not a blanket grant for every spawned worker. Permission ownership has to stay explicit at runtime: who asked, for which tool, with what scope, under whose consent, and for how long.
A parent agent usually operates inside a parent session that already has user context, OAuth token state, and a configured list of authorized MCP servers. A subagent, however, is a new execution principal with different instructions, narrower task context, and often different risk profile. Treating it as identical to the parent collapses your trust boundary.
A more effective model is runtime authorization rather than static inheritance. Runtime authorization asks: this specific child agent, for this exact tool call, now, should it be allowed? That decision must incorporate user consent, policy, and risk signals, then produce an auditable decision artifact.
The minimum control surface has these actors:
mcpServers allowlist: configuration boundary, not full authorization logic.Permission enforcement should be influenced by the permission gate policy decision, ensuring explicit runtime authorization in conjunction with other factors such as policy, context, and user roles.
When teams report "it worked in parent, failed in subagent" or "subagent hung waiting," they are usually seeing boundary confusion, not random bugs. The behavior discussed in this issue, this issue, and the official subagent model in the subagents documentation point to three recurring failure modes.

Concrete example: the parent agent has an OAuth token for a cloud MCP server with write scope. A reporting subagent is spawned to read status, but because token inheritance is implicit, it can also perform write operations it was never intended to run. The child did not request delegated access and the user did not re-consent for that child context. This is privilege carryover, not least privilege.
Why it happens: teams conflate "same workflow" with "same principal."
Fix pattern: no inheritance by default, child-specific token binding, and short TTL per delegated decision.
Concrete example: mcpServers includes payments-prod, so the subagent can invoke refund.create even though its task prompt was "draft customer response." The server was allowlisted, but tool intent, argument shape, and scope were never checked at runtime. The result is technically authorized by config, but operationally unauthorized by intent.
Why it happens: mcpServers allowlist is treated as full authorization.
Fix pattern: split tool scoping from runtime authorization. Allowlist defines possible connectivity; permission gate decides each call.
Concrete example: a subagent requests a sensitive tool that requires user consent. The platform does not route an explicit escalation event and the child waits indefinitely, producing no clear deny or retry signal. Parent orchestration appears stuck, operators cannot tell if this was denied or never reviewed, and users lose trust.
Why it happens: no anti-silent-stall behavior and weak timeout semantics.
Fix pattern: explicit escalation channel, timeout with fail-closed, deterministic denial reason, and parent-visible recovery path.
Security architects should make these distinctions explicit in design docs and code:
Parent vs subagent
Parent is orchestrator identity. Subagent is delegated identity. They may share user context, but they do not share authority by default.
Token inheritance vs token broker
Inheritance passes existing OAuth token material downward. A token broker mints child-bound, scope-minimized, time-bounded credentials tied to a delegated approval event.
Tool scoping vs runtime authorization
Tool scoping answers "what could ever be called." Runtime authorization answers "what is allowed right now for this child, this action, these args, this consent state."

Use policy patterns that assume delegation is risky unless proven safe:
An effective process involves: child proposes tool call, permission gate evaluates policy plus consent state, broker issues bound token if approved, execution occurs with bounded credential, and both decision and outcome are logged.
At this point, teams need an implementation layer that can evaluate policy consistently across parent and child calls. This is where a control plane such as Permit.io can enforce delegated rules, attach environment and identity context, and return explicit allow or deny decisions that your runtime can execute without ambiguity. The important architecture point is not vendor lock-in, but central decisioning plus local enforcement hooks at every MCP call boundary.
To keep decisions portable and auditable, define a delegated approval event with fixed fields:
{
"parent_session_id": "ps_123",
"child_agent_id": "ca_987",
"child_agent_type": "research_subagent",
"user_id": "u_456",
"mcp_server": "payments-prod",
"tool_name": "refund.create",
"normalized_args_hash": "sha256:...",
"requested_scope": "refund:write",
"decision": "approve",
"decision_reason": "user-confirmed-ticket-ownership",
"approval_channel": "in_app_prompt",
"approver_id": "u_456",
"token_binding_id": "tb_001",
"effective_ttl": "300s",
"policy_version": "2026-06-24.1",
"outcome": "executed_success"
}
This schema forces three properties that matter in incident response: who approved, what exactly was approved, and what actually happened.
For audit completeness, store two record classes:
Both are required. Decision logs without execution logs cannot prove impact. Execution logs without decision logs cannot prove legitimacy.
Start with a brokered authorization path and keep inheritance disabled:
mcpServers allowlist narrow and environment-specific.normalized_args_hash before policy evaluation.effective_ttl.
Default answer is no. Direct inheritance should be treated as an exception path for low-risk, read-only operations with strict ceilings and very short-lived credentials. Even then, log it as an explicit delegated approval, not an implicit shortcut.
No. An allowlist only narrows reachable servers. It does not validate runtime intent, argument risk, consent freshness, or child role appropriateness. You still need runtime authorization per call.
Normal reuse assumes one principal continuing work. Delegated access introduces a second principal, the subagent, so token rights must be rebound to that child identity and scoped to the approved action window.
Use timeout plus fail-closed behavior. Return a deterministic denial or escalation-required outcome to the parent agent. Do not let the child hang silently and do not auto-upgrade privileges.
Set maximum scopes per child_agent_type, independent of parent privileges. For example, a summarizer child can read ticket metadata but can never execute financial write tools, even if the parent can.
At minimum: parent_session_id, child_agent_id, user_id, mcp_server, tool_name, requested_scope, decision, decision_reason, approver_id, token_binding_id, effective_ttl, policy_version, and final outcome. Without these, root cause and blast radius analysis will be slow or incomplete.
Introduce the permission gate in shadow mode first, compare inherited vs brokered decisions, then enforce on high-risk MCP servers before broad rollout. Keep explicit escalation paths and operator visibility from day one so teams can resolve denies quickly without bypassing controls.

Co-Founder / CEO at Permit.io