feat: MCP collector — handshake supply-chain defenses (phase 11a) - #17
Merged
Merged
Conversation
Transparent stdio proxy (agentfw mcp -- <server>) that tees the MCP handshake to the daemon, which pins each server's tool manifest and flags three supply-chain attacks at handshake, deterministically: rug-pull (manifest drift), tool-name shadowing (cross-server/builtin collisions), and description poisoning (reuses the existing injection-over-tool_description rule). Daemon-connected (central cross-server view), server identity via --id, ships in shadow mode (enforce opt-in; non-interactive ask fails closed), fail-open on any proxy/daemon error. v1 defers per-call inspection and HTTP/SSE transport. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 TDD tasks: ToolDecl schema + McpHandshake event, description-facet projection, drift/shadow policy signals, inspect_mcp_handshake, JSON-RPC manifest recognition, stable manifest hash + diff, persistent pin store + tool registry, shipped drift/shadow rules, the /mcp endpoint, the stdio relay, the agentfw mcp subcommand, end-to-end tests, and README + PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase 08 already defined ManifestSeen as the MCP-handshake event and projects its tool descriptions onto ToolDescription; the plan's McpHandshake was a duplicate. Task 2 (facet projection) is therefore already satisfied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Runs the injection detector over tool descriptions (reusing the shipped ask-injection-in-tool-description rule) and evaluates policy with the daemon- computed manifest_changed and tool_shadow facts. Pure; no manifest state in the agent crate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… proxy - jsonrpc: tolerant recognition of the tools/list manifest over stdio - manifest: stable SHA-256 pin (reorder-invariant, content-sensitive) + diff - store: persistent per-server pins (0600) + cross-server ToolRegistry - proxy: transparent bidirectional stdio relay + handshake tee + fail-open enforce Adds sha2 dep and pub mod mcp. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Computes manifest drift (vs the persistent pin) and tool-name shadowing (vs the cross-server registry), runs detection + policy via inspect_mcp_handshake, pins the new manifest, audits an mcp_handshake line, and returns the verdict. Fails open on unparsable input. AppState gains manifests + tools; wired into main and the two existing test harnesses. 4 endpoint tests: first-sight allow, drift ask, poisoned-description ask, builtin-shadow ask. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wraps a real MCP server (agentfw mcp --id <name> -- <cmd> <args>), resolves the daemon /mcp URL from config, loads the bearer token, and runs the stdio relay. Server id defaults to a 12-char hash of the command when --id is omitted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds an 'MCP supply-chain defenses' subsection (agentfw mcp wrapping, the three handshake checks, shadow-default + fail-closed-on-enforce + fail-open-on-error), updates the test badge (336->405) and per-crate/module tables, and records phase 11a in the project history and roadmap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 11a — the MCP supply-chain collector
MCP servers hand the agent a list of tools at startup. Three attacks live at that boundary and none are visible to the hook collector: rug-pull (a trusted server quietly changes a tool later), tool-name shadowing (two servers, or a server and a builtin, claim the same name), and description poisoning (injection text in a tool description). This adds a transparent
agentfw mcpstdio proxy and a/mcpdaemon endpoint that catch all three at handshake, deterministically — no model required.How it works
agentfw mcp --id <name> -- <real server cmd>wraps the server in the client's.mcp.json. The proxy relays JSON-RPC byte-for-byte and tees only the handshake to/mcp. The daemon pins each server's tool manifest (stable SHA-256 over sorted names + descriptions + input schemas, at~/.agentfw/manifests/) and checks:ask(ask-manifest-drift) when the hash differs from the pinask(ask-tool-shadow) against a cross-server + builtin registryask-injection-in-tool-descriptionrule over each descriptionSafety posture
enforce: true.allowverdict returns a JSON-RPC error in place of the manifest (the server starts with no tools that session).Architecture
The
agentcrate stays I/O-free:inspect_mcp_handshake(server, tools, manifest_changed, tool_shadow)runs the injection detector over descriptions and evaluates policy. The daemon owns all I/O — a persistentManifestStoreand an in-memoryToolRegistryinAppState. Reuses the existingEventKind::ManifestSeen(defined in phase 08) rather than a new variant.Tests
405 workspace tests (from 384), 0 failing; clippy
-D warningsclean; fmt clean. New: manifest hash/diff (5), pin store + shadowing (3), JSON-RPC manifest recognition (2), relay enforcement (1+1),inspect_mcp_handshake+ policy signals (agent), and 4/mcpendpoint tests (first-sight pins, drift→ask, poisoned→ask, builtin-shadow→ask).Documented v1 deferrals
Per-call argument/result inspection and HTTP/SSE MCP transport are out of scope (stdio only). The drift audit reason is a summary, not a per-tool diff, because the pin stores the hash, not the old manifest. Cross-server shadowing seeds names lazily per daemon run (clients re-handshake every server at startup).
Design:
docs/superpowers/specs/2026-07-30-agent-firewall-11-mcp-collector-design.md. Plan:docs/superpowers/plans/2026-07-30-agent-firewall-11-mcp-collector.md.🤖 Generated with Claude Code