Skip to content
Back to Blog
Agent security engineering series
July 12, 2026
·by ·8 min read

Secure the MCP and Tool Supply Chain: Trust Must Be Continuous

Share:XBSMRedditHNEmail

Installing a tool is not one trust decision. It is the beginning of a stream of trust decisions.

The code can change. The server can change. Its owner can change. The authorization scopes can expand. A schema can drift. A resource can contain an injection. A safe read can be composed with a dangerous write. A remote tool can remain perfectly honest while returning attacker-controlled data.

That is why agent supply-chain security is larger than package scanning and larger than MCP authorization. A production system has to validate the tool before admission, constrain it during every run, distrust the content it returns, and retain a fast path to revoke it.

This guide is part of the Agent security engineering series. It focuses on the boundary between agents and MCP servers, connectors, plugins, skills, APIs, and remote tools.

Four supply chains overlap

An agent tool has at least four supply chains.

Supply chainWhat can changeFailure mode
Codepackage, container, local binary, skill, transitive dependencymalicious update, RCE, secret access
Serviceremote MCP server, API, DNS, certificate, operatorbehavior changes after approval
Contenttool descriptions, resources, search results, tool outputsprompt injection, poisoned evidence, memory poisoning
AuthorityOAuth client, scopes, tokens, user consent, agent identityprivilege abuse, confused deputy, token theft

Traditional dependency controls help with the first row: pin versions, verify signatures, scan dependencies, and review source. They do not prove that tomorrow’s remote response is safe to place beside a write-capable tool.

Anthropic’s containment guidance makes the distinction cleanly: an audited connector is not the same thing as audited data, and a remote tool can change after the original approval. Its recommendation to separate model, environment, and external-content defenses is the right starting point.

Treat discovery as a claim, not a grant

MCP discovery tells the client what a server claims to expose. Production admission decides what the runtime is willing to use.

The admitted manifest should be narrower than discovery:

import type { AdapterDefinition } from "@/lib/contextos/types"
 
export const paymentsMcp: AdapterDefinition = {
  adapter_id: "adp_payments_mcp",
  type: "MCP",
  endpoint_ref: "mcp://registry/payments-prod@sha256:9c2d...",
  capabilities: [
    "payments.lookup_transaction",
    "payments.issue_refund",
  ],
  approval_mode: "destructive",
  capability_risks: {
    "payments.lookup_transaction": {
      effect: "none",
      authority: "service",
      reversibility: "read_only",
      interaction: "api",
      data_scope: "CONFIDENTIAL",
    },
    "payments.issue_refund": {
      effect: "external_state",
      authority: "human_approved",
      reversibility: "compensatable",
      interaction: "api",
      data_scope: "CONFIDENTIAL",
      decision_ttl_seconds: 300,
    },
  },
}

The manifest is not generated and accepted in one step. A reviewer or admission policy should bind it to:

  • owner and escalation contact;
  • server identity and allowed endpoint;
  • protocol and schema versions;
  • content or image digest where pinning is possible;
  • exact capability names;
  • input, output, error, and evidence schemas;
  • maximum ApprovalMode and native ActionRisk per capability;
  • required scopes and credential issuer;
  • allowed destinations and data classifications;
  • idempotency, retry, timeout, and compensation behavior;
  • review date and revocation state.

The model may read a tool description. The description must never be the source of authority.

Authorization has two audiences

Agent-tool authorization is easy to blur because the agent often acts for a user while also being its own workload.

Keep the principals separate:

delegated user -> agent identity -> tool gateway -> MCP server -> upstream API

Each arrow is an authorization boundary. A credential accepted on one arrow should not automatically cross the next.

The official MCP Security Best Practices explicitly forbids token passthrough. Passing an inbound bearer token to a downstream API can bypass audience checks, weaken audit, and turn an intermediary into a confused deputy.

The safer sequence is:

  1. verify the agent’s short-lived identity claim and workload proof;
  2. verify the delegated user and requested scopes when user authority is required;
  3. intersect user scopes, agent scopes, manifest permissions, policy, and approval mode;
  4. request a new credential for the exact downstream resource and reduced scope set;
  5. keep the raw credential out of model context, tool arguments, and normal traces;
  6. record claim hashes, audiences, scopes, and policy decisions instead.

Google Cloud’s 2026 Agent Identity and Agent Gateway model reflects the same direction: first-class agent principals, attested identity, centralized agent-to-tool policy, access boundaries, and explicit human approval for sensitive actions.

The runtime validation order

Every invocation should pass through the same order. Order matters because policy cannot authorize an ambiguous principal or an unvalidated capability.

At minimum, validate:

  • server identity and endpoint against the admitted registry;
  • token issuer, signature, expiry, audience, and binding;
  • agent lifecycle and tenant;
  • capability presence in the compiled manifest;
  • argument schema and policy constraints;
  • destination, redirect behavior, and egress class;
  • data classification against the sink;
  • approval for the exact effect, not the general tool;
  • idempotency before writes;
  • result schema before the result reaches the model or memory.

If the server’s schema or identity differs from the admitted version, fail visibly. Do not silently re-discover and continue.

Tool output is untrusted content

A correctly authenticated MCP server can return malicious content for ordinary reasons:

  • it retrieved a poisoned README;
  • a customer inserted instructions into a support ticket;
  • a webpage contains hidden text;
  • a compromised upstream API returned an adversarial string;
  • another tenant’s data crossed an isolation boundary;
  • a tool description changed and now steers tool selection.

Treat all returned natural language as data with provenance. It may inform the model. It may not grant authority.

Useful controls include:

  • separate structured fields from display text;
  • label source, tenant, classification, and retrieval time;
  • bind evidence to stable references and hashes where possible;
  • prevent tool results from directly changing the surfaced tool set;
  • require a new policy decision before output becomes a tool argument;
  • prevent raw external content from becoming durable memory automatically;
  • redact credentials and restricted data before context assembly;
  • restrict which sinks can coexist with untrusted sources.

This is why “sanitize the prompt” is not a complete control. The runtime must assume some hostile content will be admitted and contain what it can influence.

Local and remote tools fail differently

Local tools offer inspectability but increase host risk. Remote tools reduce local code execution but add mutable-service and network trust.

Tool shapePrimary riskStrong control
Local binaryhost files, environment, credentials, process executionsandbox, minimal mounts, no ambient credentials, pinned digest
Local MCP serverlocalhost trust, startup config, dependency chainexplicit admission, port ownership, process isolation, signed package
Remote MCP servermutable behavior, auth, DNS/TLS, service compromiseregistry identity, mTLS/TLS, audience-bound tokens, egress policy
Cloud connectorbroad user delegation and changing external dataleast scopes, per-user consent, result provenance, revocation
Skill or project instructioncode-like behavior hidden in natural languagereview before activation, version pin, capability ceiling, test corpus

“Local” is not synonymous with trusted. Project-open hooks, configuration files, skills, and localhost listeners are inbound content until admitted. “Remote” is not synonymous with unsafe. A remote service with strong identity, narrow scopes, a stable manifest, isolated data, and immediate revocation may have a smaller blast radius than a local process with host credentials.

Make composition risk visible

Many agent attacks need two individually legitimate capabilities:

read secret + communicate externally = exfiltration
read untrusted content + execute code = RCE path
retrieve customer record + update CRM = integrity path
read approval message + issue payment = transaction path
recall memory + write memory = persistence path

Review capability combinations, not only individual tools. The Context Pack Compiler can surface the smallest set for the current intent. The Policy Engine can deny dangerous co-presence or require step-up approval when a source and sink meet in one run.

A useful manifest check is a forbidden composition list:

forbidden_compositions:
  - when:
      sources: [external_web, inbound_email]
      sinks: [secrets.read, network.post]
    unless:
      controls: [destination_bound, payload_previewed, human_approved]

The syntax is deployment-specific. The principle is stable: risk emerges from paths.

The admission and release checklist

Before admitting an MCP server, connector, tool, or skill:

  • Verify ownership, maintainer history, signing, dependencies, and update process.
  • Pin what can be pinned: version, digest, schema, endpoint, protocol.
  • Split read and write capabilities.
  • Reject generic proxy tools that accept arbitrary methods, paths, or commands.
  • Declare ActionRisk, maximum ApprovalMode, scopes, and data ceiling per capability.
  • Test wrong audience, expired token, revoked agent, cross-tenant access, and token passthrough.
  • Test malicious descriptions, resources, and tool results.
  • Test redirects, DNS changes, egress escapes, oversized payloads, and schema drift.
  • Test duplicate calls, timeout after mutation, retry, and compensation.
  • Record a named owner, review date, kill switch, and incident contact.

At runtime:

  • resolve only admitted versions;
  • deny drift rather than auto-accepting it;
  • issue short-lived resource-bound credentials;
  • enforce destination and data policy outside the model;
  • emit accepted and denied ToolCallEnvelope / ToolResultEnvelope records;
  • monitor for new capabilities, scope growth, unusual destinations, and error-rate changes;
  • make revocation effective at discovery, compilation, credential exchange, and execution.

Incident response needs a kill path

When a tool is compromised, removing it from a UI is not enough.

Revocation should:

  1. mark the adapter or capability revoked in the registry;
  2. prevent it from entering new compiled contexts;
  3. reject in-flight execution at the Gateway;
  4. revoke or expire credentials and consent grants;
  5. cancel or quarantine long-running tasks;
  6. identify runs, tenants, users, data, and mutations touched by the tool;
  7. preserve manifests, schemas, claims, and transcripts for replay;
  8. require re-admission under a new version before restoration.

The inventory and DecisionRecords that seemed operationally boring become the incident scope.

Research base

Found this useful? Share it.

Share:XBSMRedditHNEmail

Continue through the same topic without returning to the index.

View the series