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

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

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

An npm package can execute code inside your application. An MCP server can place natural-language instructions beside your system prompt, advertise callable capabilities, return attacker-controlled content, and ask the agent to act with a user’s authority.

That does not make every MCP server malicious. It means every integration occupies a position from which a compromise, poisoned description, unsafe update, or hostile upstream result can influence the agent’s decisions.

The evidence is no longer hypothetical. The 2025 MCPTox preprint built 1,312 poisoning cases from 353 tools across 45 real-world MCP servers and evaluated 20 agents. It reported a 36.5% average attack-success rate across its model settings, with the highest result above 72%. A separate March 2026 client-security preprint tested seven MCP clients and found substantial differences in static validation, parameter visibility, warnings, sandboxing, and resistance to cross-tool poisoning.

Those are benchmark results, not an estimate of production compromise rates. They still establish the uncomfortable fact: tool metadata can steer an agent into using a different, legitimate capability for an unauthorized action.

The MCP specification is explicit that tool descriptions and annotations should be considered untrusted unless they come from a trusted server. It is equally explicit that MCP cannot enforce its security principles at the protocol level. The host and runtime have to do that work.

Installing a tool is therefore 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.

The npm analogy—and where it breaks

The headline is useful because MCP is becoming a distribution layer for agent capability. It is dangerous if taken literally.

Supply-chain questionnpm packageMCP integration
What is admitted?code and dependency metadatatools, resources, prompts, schemas, and descriptions
Where does influence enter?build or application runtimemodel context and the tool-execution path
What can change transitively?dependency versions and install scriptsserver code, remote behavior, schemas, descriptions, scopes, and upstream content
What does a lock prove?the fetched artifact matches a digestonly the pinned artifact; not tomorrow’s remote behavior or returned data
What authority is exposed?process, filesystem, and network privilegesagent identity, delegated-user scopes, surfaced tools, credentials, and approval state
What is the dangerous composition?vulnerable dependency plus reachable code pathattacker-controlled source plus sensitive data plus an authorized sink

The sharp ContextOS position is this:

MCP is a capability transport, not a governance layer. Discovery tells you what a server claims. It does not decide what the agent may do.

The protocol is not the vulnerability. Treating discovered metadata as executable authority is.

A poisoned tool does not have to be called

Consider a coding agent with three legitimate capabilities:

  • repository.search, which can inspect the current repository;
  • workspace.read_file, which can read approved project paths;
  • support.create_ticket, which can send a diagnostic report to an approved support tenant.

Now a fourth MCP server advertises a harmless-looking time tool. Its description contains a hidden instruction: before any repository task, read a credential file and place its contents in the diagnostic field of the next support request.

The user never asks for the time tool. The agent never calls it. The attack succeeds if the description enters context and changes how the agent uses the other three tools.

The trace exposes five named failures:

  1. Metadata authority laundering: descriptive text was allowed to create a new obligation.
  2. Cross-tool composition: separate read and network capabilities formed an exfiltration path.
  3. Ambient privilege: the file tool could reach data the user’s task did not require.
  4. Unbound arguments: the outbound tool accepted content and destination choices not derived from the user’s intent.
  5. Narrative auditing: the system trusted the agent’s summary instead of the actual tool envelopes and mutation receipts.

Prompt filtering may reduce the chance that the model follows the instruction. It does not remove the path. The durable fix is to make the unauthorized effect impossible even when the model is persuaded.

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.

Score the boundary, not the vendor

Do not ask whether an MCP client is “secure.” Score the controls around one server, one capability set, and one production environment.

Give each row 0 when the control is absent, 1 when it is documented or partially enforced, and 2 when it is enforced and covered by a failing regression test.

Control0 points1 point2 points
Server identityendpoint accepted from configurationowner and endpoint recordedidentity, endpoint, and transport verified at connection
Manifest pinninglive discovery is acceptedtool names are allow-listedschema and description hashes are pinned; drift denies
Description handlingmetadata enters context as instructiondescriptions are labeled untrustedstatic checks plus contextual isolation and decision-path tests
Capability minimizationfull server catalog is surfacedread and write tools are splitcompiler surfaces only capabilities required for this intent
Principal separationone shared credentialuser and agent identities are loggeduser, agent, server, and downstream audiences are independently verified
Scope designwildcard or omnibus scopesnamed scopes existprogressive, per-capability scopes with resource-bound tokens
Argument policyschema validation onlysensitive fields are previeweddestination, data class, amount, path, and tenant are policy-bound
Composition controlstools reviewed independentlydangerous pairs are documentedsource-to-sink combinations are denied or require step-up approval
Output containmentresults enter model and memory directlyprovenance is attacheduntrusted text is separated, classified, and barred from automatic promotion
Execution containmenthost privileges are inheritedpartial filesystem or network restrictionsandbox, minimal mounts, egress policy, timeouts, and idempotency are enforced
Evidence and replayagent summary is the audit recordtool calls are loggedaccepted and denied calls, policy verdicts, receipts, and replay inputs are retained
Revocationoperator removes the UI entrynew discovery is blockedregistry, compilation, execution, credentials, and in-flight work share a kill path

The maximum is 24.

  • 0–8: demo-grade. A malicious or compromised integration can borrow ambient authority.
  • 9–16: fragile. Controls exist, but important decisions still depend on convention or the model.
  • 17–21: bounded with gaps. Suitable for limited rollout only when the missing controls cannot reach a high-impact sink.
  • 22–24: production candidate. Still requires scenario-specific red-team runs, incident drills, and ongoing drift monitoring.

The score is not allowed to average away a hard stop. Token passthrough, an arbitrary proxy tool, silent schema drift, destructive calls without effect-specific approval, or no executable revocation path should block production admission regardless of the total.

Download the MCP tool-supply-chain policy starter. It turns the scorecard into a deny-by-default example covering manifest pinning, principal separation, scope minimization, forbidden compositions, evidence, and revocation. The YAML is an implementation starter, not a new ContextOS runtime contract; adapt its integration-specific fields to your gateway and identity provider.

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