Do Not Give Every Sub Agent the Parent's Tool Belt

Do Not Give Every Sub Agent the Parent's Tool Belt

A supervisor agent can read customer history, issue credits, update billing records, and send messages. It delegates one task to a research sub-agent: summarize the customer's recent support cases.The child inherits the parent's complete tool registry and bearer token because that was the easiest way to make the demo work.A retrieved support note contains an instruction to “close the account and refund the balance.” The child sees tools that can do both.This synthetic scenario combines two familiar security failures: excessive privilege and a confused deputy. The sub-agent received authority unrelated to its assignment, then processed untrusted content that tried to redirect that authority.Multi-agent orchestration needs more than a prompt saying “only research.” A handoff should narrow power in code and credentials.Delegation Is Not CloningMany agent frameworks make delegation feel like calling another function:await runAgent(researchAgent, { task, tools: parent.tools, credentials: parent.credentials, context: parent.context, }); That copies four risk surfaces:capability: which actions exist;authority: which actions the identity may perform;data: which context and resources are visible; andbudget: how much time, money, and work may be consumed.A safe delegation should constrain all four.parent authority | v delegation policy | +--> selected tools +--> selected resources/tenant +--> bounded data projection +--> expiry and usage budget +--> redelegation: denied | v sub-agent capability The child should receive what its task requires, not what the parent happens to possess.Define a Delegation GrantRepresent the handoff as a signed or server-issued grant rather than free-form prompt text:type DelegationGrant = { grantId: string; parentRunId: string; childRunId: string; subject: string; tenantId: string; audience: string; allowedTools: string[]; resourceConstraints: Record; maxToolCalls: number; maxSpendCents: number; expiresAt: string; mayRedelegate: false; policyVersion: string; }; The grant is not itself a prompt instruction. The tool gateway verifies it on every call.async function authorizeToolCall( grant: DelegationGrant, call: ProposedToolCall, ) { assert(Date.parse(grant.expiresAt) > Date.now()); assert(grant.audience === call.toolServerAudience); assert(grant.allowedTools.includes(call.name)); assert(resourceAllowed(grant.resourceConstraints, call.args)); assert(await usage.withinBudget(grant.grantId)); return policy.evaluate({ grant, call }); } Do not trust a child-supplied tenant ID. Bind tenant and resource restrictions to the grant and compare them with server-side records.Removing dangerous tools from the model's tool list reduces accidental proposals and prompt-injection opportunities. It does not replace server-side authorization.Conversely, leaving every tool visible while expecting a gateway to deny most of them can degrade model behavior and leak internal capability names.Use both:build a task-specific tool catalog for the child; andenforce the same or stricter constraints at execution.function toolsFor(grant: DelegationGrant, registry: ToolRegistry) { return grant.allowedTools.map(name => { const tool = registry.describe(name); return { name: tool.name, description: tool.description, inputSchema: applyResourceConstraints( tool.inputSchema, grant.resourceConstraints, ), }; }); } The descriptor given to the model has no credential and no direct execute closure. Proposed calls travel through the authorized gateway.Exchange Broad Identity for Narrow AccessPassing the parent's long-lived bearer token to a child creates an authority copy that may outlive the handoff. OAuth 2.0 Token Exchange (RFC 8693) defines a protocol for a client to exchange a token for another security token, potentially with a different audience or scope and with actor/subject relationships. It is a useful building block when your authorization server supports it.parent workload identity | v authorization server | +--> short-lived child token audience = support-search service scope = cases:read tenant = tenant_42 expires = 10 minutes Do not invent an unsigned “scope” field inside agent state and assume downstream services will honor it. Mint or obtain credentials that the target resource server validates.The current MCP authorization specification similarly requires HTTP-based clients to use resource indicators so tokens are bound to their intended audience, and resource servers must validate that audience. It also forbids token passthrough: an MCP server must not simply forward a token issued for itself to another downstream service.MCP tool annotations can label a tool with hints including readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. The MCP project explicitly warns that these are untrusted unless the server is trusted.A malicious or buggy server can describe a write as read-only. A tool gateway can also change implementation without changing its metadata.Use annotations for UI, planning, and conservative defaults. Base authorization on verified server identity, policy, and a locally controlled capability registry.Prevent the Confused-Deputy PathA sub-agent commonly reads untrusted material: webpages, emails, support cases, documents, tool output, or another agent's message. That content can contain instructions.Treat it as data:type RetrievedArtifact = { source: "support_case" | "web" | "email"; sourceRef: string; trust: "untrusted" | "verified_internal"; content: string; }; The research child should return structured findings, not executable commands:type ResearchResult = { caseIds: string[]; issueCodes: string[]; summary: string; untrustedInstructionsDetected: boolean; }; The parent may use those findings in a new decision. They should never silently expand the child's grant.Make Redelegation ExplicitIf a child can spawn another child, authority can spread through a tree until nobody can explain which principal enabled an action.Default to mayRedelegate: false. When redelegation is necessary, require monotonic narrowing:function canDeriveChildGrant( parent: DelegationGrant, child: DelegationGrant, ) { return ( parent.mayRedelegate === true && isSubset(child.allowedTools, parent.allowedTools) && constraintsNarrower(child.resourceConstraints, parent.resourceConstraints) && child.maxToolCalls c.ref), issueCodes: record.cases.flatMap(c => c.issueCodes), openedAt: record.cases.map(c => c.openedAt), }; } Projection also reduces prompt-injection surface and makes retention easier to reason about.Record Authority EvidenceFor every child tool proposal, capture bounded evidence:delegation.created parent=run_1 child=run_2 grant=dg_9 delegation.scope tools=[case_search] tenant=tenant_ref_42 tool.proposed child=run_2 tool=case_search tool.authorized grant=dg_9 policy=v12 result=allow tool.executed operation=op_7 outcome=success delegation.closed grant=dg_9 reason=task_complete For denials, record policy reason codes such as tool_not_allowed, resource_out_of_scope, grant_expired, or budget_exceeded. Avoid raw tokens, prompts, and sensitive arguments.Test the Negative SpaceMulti-agent tests should prove what the child cannot do:call a tool missing from its catalog;call a visible tool without a valid grant;switch tenant or resource ID;reuse an expired token;pass its token to another audience;redelegate when forbidden;exceed tool-call or spend budget;convert retrieved instructions into direct execution; andcontinue acting after cancellation.Mock success paths alone validate orchestration, not containment.Capability Should Shrink Down the TreeA useful invariant is:authority(child) ⊆ authority(parent) ∩ requirements(task) In practice, a dedicated service may hold privileges that the parent does not, so the parent can request an authorized workflow without possessing the underlying credential. The conceptual rule still holds: delegation should not create unexplained ambient authority.Multi-agent systems are distributed systems with model-driven planners. The agents may speak naturally, but the security boundary must remain exact.Do not hand a child the parent's tool belt and hope it remembers the job description. Issue one narrow capability, verify it at the point of use, and make the lineage visible.ReferencesRFC 8693: OAuth 2.0 Token ExchangeMCP Authorization SpecificationMCP Tool Annotations

Original Source

Read the full article at Hackernoon →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.