← Back
Home › Systems › AI and agent security

mcp-supply-chain

Vets, pins, signs and monitors the MCP servers and tool definitions that agents load, so a changed or malicious server cannot silently redirect an agent.

mcp-scan, registry verification

Open the mcp-supply-chain pagemcp-supply-chain Model context protocolagentic-ai: JSON-RPC protocol exposing tools, resources and prompts from servers to LLM clients, with capability negotiationPrompt injection defenseagentic-ai: Treat every untrusted string as data, track where values came from, and make tool calls and rendered output depend on provenance, not on what the model says.›Dataflow analysiscompilers: Compute a fact per program point (live variables, reaching definitions, available expressions) by iterating transfer functions over the CFG to a fixed point.›Dependency version resolutioncompilers: Pick package versions that satisfy semver constraints (PubGrub, SAT) with readable conflict errorsSemver range matchingcompilers: Semantic version parsing, ordering and range matchingHdr histogramdata-structures: Log-linear bucketed histograms with bounded relative error for latency percentilesDeterministic simulationtestingdistributed: Run the whole system in one thread under a seeded scheduler with simulated network, disk and clock, so every failure replays from its seed.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Process supervisionhardware-os: A parent process starts services, reaps their exits and restarts them under a delay and burst-limit policy, then stops them with SIGTERM before SIGKILL.›Text classificationmachine-learning: Assign one or more labels to a text; climb a ladder from regexes to linear models on n-grams, embeddings, fine-tuned encoders and LLM prompts, only as far as the data and budget demand.›Json rpcnetworking: JSON-RPC 2.0 requests, notifications and batchingFuzzy string matchingsearch-sort: Edit distance, Jaro-Winkler and trigram indexes for approximate name and address matchingCode signingsecurity: Signing binaries/apps with certificate chains, entitlements, timestamping and notarizationCoverage-guided fuzzingsecurity: Mutate inputs, run an instrumented target, and keep any input that reaches a new code edge; feedback turns blind guessing into incremental search.›Cryptographic hashes (SHA-2,BLAKE3)security: Collision-resistant fingerprints of data; SHA-256 for interoperability, BLAKE3 for speed, HMAC/HKDF for keyed uses.›JSON Schema validationsecurity: Compile a JSON Schema once, then check every untrusted document against it, within hard size and depth limits, and report failures as JSON Pointers.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›Policy engines (PDP/PEP, Cedar,Rego, ReBAC)security: Decide allow/deny for (principal, action, resource, context) in a separate engine with default deny, forbid-wins and an auditable list of determining policies.›Process sandboxingsecurity: Run risky work in a separate, deliberately powerless process and let a small privileged broker do the few things it needs, over a narrow pipe.›Reproducible buildssecurity: Bit-identical outputs: fixed timestamps, ordering, paths and toolchainsSecret scanningsecurity: Detect committed credentials with provider-specific patterns, entropy checks and live verification, then revokeSigned attestationssecurity: DSSE/in-toto signed provenance statements about artifactsSoftware bill of materialssecurity: SBOM generation (SPDX/CycloneDX) and component matchingSoftware supply chain securitysecurity: Control what code enters your build and prove what comes out: lockfiles, vetted updates, hardened CI, signed artifacts with provenance.›Taint trackingsecurity: Track untrusted sources to sensitive sinks through dataflowTransparency logsecurity: A public append-only Merkle tree whose signed heads let anyone verify an entry is present and history was only extended, with log-size proofs.›Unicode confusablessecurity: UTS #39 maps every string to a "skeleton" so look-alikes collide, and restricts script mixing so most spoofs never get registered.›Vulnerability advisory matchingsecurity: Matching package versions against advisory databases (OSV, NVD, distro feeds) with ecosystem-specific rulesPoint-in-time recoverystorage: Periodic base backups plus a continuously archived change log let you rebuild the state as of any instant, then branch from there.›Graceful drainingsystems: On a termination signal, stop taking new work, finish in-flight work until a deadline, tell clients to reconnect elsewhere, then exit; clients resume from a token.›Server-sent eventsnetworking: One long-lived HTTP response carries text events one way; each event may carry an id, so a reconnecting client names its last one and the server resumes.›OAuth 2 and OpenID Connectsecurity: Delegated authorization (OAuth 2) and login on top of it (OIDC); use authorization code + PKCE for every user-facing client.›Html sanitizationsecurity: Allowlist-based HTML sanitizing of untrusted markupControl-flow graphcompilers: Split a linear instruction list into basic blocks and connect them by jumps; the graph every optimizer, SSA builder and dataflow pass walks.›Topological sortgraphs: Order the vertices of a DAG so every edge points forward; the core of build systems, schedulers and dependency resolution, with cycle detection for free.›CDCL SAT solvingoptimization: Search for a satisfying assignment, learn a new constraint from every dead end, and jump back past irrelevant choices; handles industrial formulas with millions of clauses.›Cooperative schedulingconcurrency: Green threads/coroutines yielding at await points on a user-space schedulerFault injectiondistributed: Inject latency, errors, kills and partitions via proxies, netem or hooksRecord replaydistributed: Record every nondeterministic input (time, randomness, I/O, model and tool outputs, per-tick player input) and re-execute deterministically for debugging, replays and state rebuildLeasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›Byte-pair encoding (BPE)tokenizationmachine-learning: Learn a subword vocabulary by repeatedly merging the most frequent adjacent pair; byte-level BPE encodes any input losslessly with 32k-200k tokens.›+ 35 moreCsprngsecurity: OS-backed cryptographically secure random numbers (getrandom), and when a fast PRNG is not enoughJSON Web Token (JWT)security: A signed, URL-safe token carrying claims; the verifier's job is the checklist (pinned algorithm, key by kid, exp, aud, iss), not the format.›BFS and DFSgraphs: The two basic graph traversals; BFS gives hop distances and levels, DFS gives discovery/finish order for cycles, components and topological order.›d-ary heapdata-structures: Array heap with d children per node (d = 4 typical): half the binary heap's depth, siblings in one cache line, faster priority queues.›Drat proof certificatesoptimization: DRAT and LRAT proofs emitted by SAT solvers and checked independentlyTwo watched literalsoptimization: Unit propagation for SAT that watches just two literals per clause, so backtracking costs nothing and long clauses are rarely touched.›Arena allocatorsystems: Allocate by bumping a pointer through big chunks and free everything at once: a few instructions per allocation for objects sharing a lifetime.›Transparent interceptionnetworking: Redirect traffic to a local proxy (iptables TPROXY/REDIRECT, eBPF) without app changesSyscall interceptionsecurity: Intercept syscalls via ptrace, seccomp user notify or a user-space kernelFencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›+ 21 moreSparse matrices (CSR)numerics: Compressed sparse row stores only nonzeros as row pointers, column indices and values; SpMV streams them once, so it is memory-bandwidth bound.›Ebpfhardware-os: Verified in-kernel programs on hooks (kprobes, XDP, tc) with mapsPtrace process controlhardware-os: Ptrace: attach, read and write registers and memory, single-step, intercept signals+ 5 more

How it is built

Architecture overview

An agent that speaks the Model Context Protocol loads servers, and each server offers tools whose names and descriptions the model reads as instructions. That makes two supply chains. One is ordinary software: the server is a package or container with dependencies, a build and a publisher. The other is new: the tool descriptions, schemas and results are text the model trusts, and they can change after a human approved them. A server that was reviewed on Monday can ship a new description on Friday; a name that looks like a trusted server can belong to someone else.

This page describes the defensive architecture: how an organisation decides which servers may run, proves what it is running, notices change, and contains the damage if one turns bad. It contains no attack payloads. The one idea that makes it work is to treat a server as a pinned, signed, reviewed artifact plus a pinned, reviewed description, and re-check both at every load, with the runtime confined so that a bad server can do little even when it gets through. The MCP specification itself says clients must consider tool annotations untrusted unless they come from trusted servers, and that a human should be able to deny tool invocations (source: MCP tools specification). Tools in this area include mcp-scan (now Snyk Agent Scan) and registry verification.

Parts:

 publisher ─► build (CI) ─► package + provenance attestation ─► registry ─► internal catalogue
                                  │ signed, logged in transparency log          │ allowlist + pins
                                  ▼                                              ▼
            admission gate: verify signature/provenance ─► resolve deps ─► advisory match
                         │   scan description + code (classifier, dataflow) ─► human review
                         ▼
        approved record: {package digest, description hash, tool list hash, permissions}
                         │
 agent host ── at every load: fetch, recompute hashes, compare ─► mismatch = block + alert
                         │ ok
                         ▼ sandboxed server process (no secrets, egress allowlist, mTLS to host)
                         tool call ─► policy engine (per tool, per argument) ─► result ─► injection filter ─► model

How the parts fit together

Catalogue and identity: which server is this, really

Start with a closed list. The official MCP Registry gives clients a list of servers like an app store for them, and verifies two things at publication: that the publisher controls the namespace in the server's name (for example a GitHub-based prefix), and that the underlying package matches its metadata; it does not verify that the remote URL is controlled by the publisher or what the server does (source: MCP Registry documentation). That is useful identity, not a safety review. An organisation therefore keeps its own catalogue of approved entries and treats the public registry as a source of candidates.

Look-alike names are the entry attack. Unicode confusables maps each name to a skeleton so that names which differ only by look-alike characters collide, and restricts script mixing; fuzzy-string-matching (no page yet) flags names within a small edit distance of an approved one. Run both at admission and again whenever a name is shown to a human for approval.

Provenance and signing: prove origin, not benevolence

A signature says who published and that bytes are unchanged; it does not say the code is good. The useful layers are: signed artifacts (code-signing (no page yet)); attestations, signed statements about how an artifact was built (signed-attestations (no page yet); the in-toto statement format and the DSSE envelope are the common shape, sources below); and, where achievable, builds that give identical output from identical input (reproducible-builds (no page yet)) so a third party can rebuild and compare. Sigstore ties signing to short-lived certificates issued against a CI identity and records signatures in a public log; npm's provenance feature uses this so users can see where a package was built, signed by Sigstore public-good servers and logged in a transparency ledger (source: npm provenance docs). The log is a Transparency log: an append-only Merkle tree whose signed heads let anyone verify an entry exists and that history was not rewritten; Rekor is the Sigstore instance (source: Sigstore logging overview). SLSA names the levels of build assurance so a policy can say "accept only builds at level N from these builders" (source: SLSA levels). The general control set (lockfiles, pinned versions, verified builds) is in Supply-chain security.

Dependencies and advisories

A server is mostly dependencies. dependency-version-resolution (no page yet) produces the exact set the lockfile picks, so analysis runs on what will actually load. vulnerability-advisory-matching (no page yet) matches package versions against advisory data; OSV is an open database and schema for this (sources: OSV documentation, OSV schema). secret-scanning (no page yet) checks the package contents for embedded credentials. The result is a risk record per version, recomputed when new advisories land, not just at admission. Compare Vulnerability scanner for the general system.

semver-range-matching (no page yet) is what makes the resolved set predictable: a package declares ranges such as "compatible with 2.x", and the matcher decides which published versions satisfy them, which is also how an advisory's affected range ("below 2.4.1") is tested against a pinned version. software-bill-of-materials (no page yet) records the result: the exact packages, versions and hashes inside a server release, produced at build time and stored with the approval. When a new advisory appears, the gate searches the stored bills instead of re-resolving every server, and lists which approved servers contain the affected component.

Description and behaviour scanning

Tool descriptions are model-visible instructions, so a description is code in effect. Scanners treat it that way. Text classification flags descriptions containing instruction-like content aimed at the model (directions to ignore earlier rules, to read unrelated files, to send data elsewhere) rather than documenting what the tool does; the classifier needs examples drawn from your own reviewed catalogue, and it should output a score for a person to review, not a final verdict. For server code, Dataflow analysis and taint-tracking (no page yet) trace untrusted input (arguments, fetched content) to sensitive sinks (file writes, network calls, shell). Agent Scan, the successor to mcp-scan, reads tool descriptions from configured servers and checks them for threats such as prompt injections; its documentation warns that scanning can launch servers or contact URLs, so scans must themselves run in a sandbox (source: Agent Scan README). At runtime the model-facing side is covered by Prompt injection defense: results are data, not instructions.

The scanner gets descriptions the same way a client does: it starts the server in the sandbox and sends a tools/list request, a json-rpc (no page yet) call with a method, parameters and an id. Each response carries tool names, descriptions and input schemas, which become the text that is classified and hashed. The scanner also notes whether a server sends list-changed notifications, since a server that can change its tools after approval needs the re-check described below.

Pinning and change detection: the "rug pull" control

Approval must attach to content, not to a name. The approved record stores the package digest, a hash of each tool's name, description and input schema, and a hash over the whole tool list. Hashing a set of objects that each have their own hash is a content-addressed structure (merkle-dag (no page yet)), which lets the gate report which tool changed. At each load the host fetches the live list, recomputes, compares. MCP servers can announce list changes with a listChanged notification (source: MCP tools specification); a host should treat that as a signal to re-verify, not as permission. Store the approved records with atomic-file-replacement (no page yet) so a crash never leaves a half-written pin. A change does not always mean attack, so the gate's outcome is "block until re-reviewed" with a diff shown to the reviewer.

The digests in the record come from SHA-256 or BLAKE3: a package digest, a hash of each tool's name, description and input schema (serialised in one canonical form so equal content gives equal bytes), and a hash over the sorted list of those. Collision resistance makes a matching digest a proof of identical content, and BLAKE3's speed lets the gate re-hash the live tool list on every session start, not only at install.

Runtime confinement and policy

Assume a server will eventually turn bad and limit the blast radius. Process sandboxing runs the server in a powerless process with only the files, network destinations and environment variables it needs; secrets are not placed in its environment (see Agent credentials and secret brokering). Between host and server, Mutual TLS or short-lived signed tokens (JWT) authenticate remote servers both ways. A Policy engine sits at the host and decides per tool call: this agent, this tool, these argument values, this session context; default deny; high-risk tools require human confirmation. The MCP security best practices page lists the protocol-level traps this must respect: confused deputy through OAuth proxies, token passthrough, session hijacking, local server compromise and over-broad scopes (source: MCP security best practices). See also MCP server, MCP host and Agent sandbox.

The gate also validates each tool call's arguments against the tool's pinned input schema with JSON Schema validation before it reaches the server. Because the schema is part of the approved record, a call with extra fields, wrong types or out-of-range values is rejected at the gate with a typed error, and a server cannot widen what it accepts without changing the hash that triggers re-approval.

State and testing

Approvals, pins, attestation results and logs are state: Primary-backup replication and Point-in-time recovery keep the gate available and recoverable; after a restore, re-verify pins against the transparency log rather than trusting the restored file. Coverage-guided fuzzing hardens the parsers that read untrusted manifests and tool lists, and Deterministic simulation testing exercises the gate under reordering and partial failure.

Operations

Gate latency. hdr-histogram (no page yet) records the time the gate adds to a tool call: allowlist lookup, pin comparison, argument validation and policy decision. A log-linear histogram keeps bounded relative error from microseconds to seconds, so p99 per server shows whether the pin check or a scan result lookup slows every call. The same histogram type tracks scan duration per package, which sets how long a new version waits in quarantine before it can be approved.

Staying in the path. The gate and the scanners are long-running processes. Process supervision restarts a crashed gate under a delay and burst limit. A gate that is down must fail closed for servers that need approval, so supervision keeps the restart fast and bounded and reports a crash loop instead of masking it. Sandboxed MCP server processes are also children of a supervisor, which reaps them and applies the same restart policy.

Rolling the gate. On a deploy or a policy reload graceful draining lets in-flight tool calls and open sessions finish until a deadline while new sessions go to the new instance, and it tells clients to reconnect and resume. A session is never split across two policy versions, which keeps the audit record of what was allowed consistent.

Data and control flow

Admitting a new server:

  1. A team requests a server by registry name. The gate checks the name against the catalogue for look-alikes.
  2. It fetches the package, verifies the signature and the build attestation against the transparency log, and checks the builder against policy.
  3. It resolves the dependency tree from the lockfile and matches versions against advisories; high-severity results stop the flow.
  4. In a sandbox it starts the server, lists its tools, and records descriptions and schemas. The classifier and static analysis score them.
  5. A reviewer sees the score, the diff against nothing, the permissions asked, and the data flows found; approves or rejects.
  6. The approved record (digests, hashes, permissions, reviewer) is written to the catalogue and replicated.

Loading an approved server in an agent session:

  1. The host reads the pin, fetches the package digest and compares it. Mismatch: refuse and alert.
  2. It starts the server in the sandbox with the permissions in the record and connects over an authenticated channel.
  3. It lists tools and compares hashes to the pin. A new or changed tool is held back and reported; unchanged tools proceed.
  4. During the session each call goes through the policy engine; results pass the injection filter before reaching the model.
  5. A listChanged notification triggers step 3 again.

Scaling

In the order you hit them:

  1. Review capacity. People are the bottleneck for new servers. Levers: a small curated catalogue, risk tiers (read-only local tools versus tools with network and write access), automation that rejects clear failures before a person looks.
  2. Re-verification cost at load. Hashing and comparing is cheap; fetching live lists adds latency. Levers: cache verified results for the session, verify lists lazily on first use, batch checks.
  3. Advisory churn. New advisories arrive daily. Levers: reindex by package-version with an inverted index, re-evaluate only affected records, route by severity.
  4. Fleet size. Many hosts load the same servers. Levers: central gate with signed approval bundles distributed to hosts, caching of verified digests.
  5. Scanner quality. False positives erode trust. Levers: calibrate the classifier on your catalogue, require two independent signals before blocking.
  6. Transparency-log dependence. Verification needs log access. Levers: bundle inclusion proofs with artifacts, mirror the log, define offline behaviour.

Failure modes

FailureSymptomCauseFix
Description changed after approvalagent starts calling tools in new waysapproval tied to a name, not to a hashpin description and schema hashes; block on diff; re-review
Look-alike server namewrong server installedname approved by eyeskeleton comparison and edit-distance check at admission and approval
Trusted publisher, bad updatemalicious code in a new versioncompromised maintainer or CIpin digests, require provenance from the expected builder, delay adoption of new versions
Signature without reviewsigned but harmful server runssignature mistaken for safetypolicy requires review and behaviour scan beyond signature
Scanner executes a bad serverscan host is compromisedscanner starts untrusted commandsrun scans in a sandbox with no secrets and limited network
Token passthroughdownstream API cannot tell callers apartserver forwards client tokensaccept only tokens issued for the server; exchange for downstream tokens
Over-broad permissionstool reads or writes beyond its jobpermissions granted per server, not per toolper-tool policy; least-privilege sandbox profile
Stale advisory dataknown-bad version runsscan only at admissioncontinuous re-matching; alert and quarantine on new findings
Gate unavailableagents blocked, or bypass is usedsingle gate with no replicationreplicated gate, last-known-good bundles, break-glass procedure that logs
Restore loses revocationsrevoked server loads againpins restored from old backupre-verify against the log after restore; replay the revocation list

Real implementations

namewhat it isrepolicencenotes
Snyk Agent Scan (formerly mcp-scan)scanner for agent components: MCP servers, skills, tool descriptionsinvariantlabs-ai/mcp-scanApache-2.0needs a Snyk token to run; output format marked experimental; run in a sandbox
MCP Registrypublic catalogue of MCP servers with namespace and package verificationmodelcontextprotocol/registryMIT, moving to Apache-2.0 (per LICENSE)does not review server behaviour
Sigstore cosignsigning and verification of containers and other artifactssigstore/cosignApache-2.0short-lived certificates and a public log
Sigstore Rekortransparency log for signed software metadatasigstore/rekorApache-2.0public instance and self-hosted option
in-totoframework for verifying each step of a software supply chainin-toto/in-totoApache-2.0layouts, steps and signed link metadata
SLSAspecification of build-integrity levelsslsa-framework/slsaCommunity Specification License 1.0a spec, not software
OSV-Scannervulnerability scanner using OSV datagoogle/osv-scannerApache-2.0open advisory database and schema

The alternative: run only servers you wrote or vendor, and pin them like any dependency. For a handful of internal tools this is cheaper than a gate. A gate pays off once teams adopt third-party servers faster than a person can review them.

Sources

Similar systems

Share the most building blocks with this one.