MCP made it trivial to plug tools into an agent. It didn't make it safe.
Model Context Protocol solved a real problem: every agent framework used to need its own bespoke integration for every tool. MCP standardized that into one protocol, and the ecosystem responded. Registries now list well over 5,500 public MCP servers, and remote MCP deployments are up roughly 4x since May 2025 [1]. The protocol has also moved from "Anthropic's thing" to infrastructure serious enough that Anthropic handed its stewardship to the Linux Foundation's Agentic AI Foundation [2].
That speed is also the problem. MCP standardized how an agent calls a tool. It didn't standardize whether that call should be allowed to happen. Those are different questions, and 2026 has been the year the gap between them started showing up as CVEs.
What the research actually found
In April 2026, OX Security disclosed a systemic flaw in how MCP's STDIO transport, the default path for local tool execution, handles input: it executes operating system commands without sanitization or validation. The estimated blast radius was roughly 200,000 vulnerable instances across a supply chain of more than 150 million package downloads [3].
That wasn't an isolated finding. An internet-wide scan in mid-2025 found 1,862 MCP servers publicly exposed and responding to unauthenticated tool-listing requests: no login, no token, just answer the request [3]. By May 2026, researchers had tracked at least seven confirmed high- or critical-severity CVEs across major MCP-integrated platforms [3]. Invariant Labs, which first disclosed the technique in April 2025, calls this class of attack "tool poisoning": malicious instructions hidden in a tool's own metadata. They're invisible to the human who approved the integration, but fully readable by the model calling it [4].
None of this means MCP is a bad protocol. It means MCP is a transport. It moves a tool call from an agent to an endpoint efficiently. It was never designed to answer the harder question: should this specific agent, right now, be allowed to run this specific action against this specific system, and who's accountable if it shouldn't have been?
The part MCP leaves for you to build
Plug an MCP server directly into an agent framework and you get connectivity, not governance. Three gaps show up almost immediately once you're past the demo:
No risk tiering. MCP treats "list my calendar" and "delete this repository" as the same kind of request: a tool call. Nothing in the protocol distinguishes a read from an irreversible write, so nothing stops an agent from executing either one unattended.
No approval step. If a tool poisoning attack or a bad prompt gets an agent to call a destructive action, MCP has no native pause. The call either executes or it doesn't: there's no built-in point where a human sees it first.
No shared audit trail. When five different MCP servers are wired into five different agents, "what did any of this actually do last Tuesday" becomes a log-correlation project, not a query.
Every one of the CVEs above is really a version of the same root cause: a protocol built for connectivity got treated as if it were also a policy layer.
How Matimo governs the same tool calls
We didn't build Matimo OSS as an alternative to MCP: we built it as the governance layer MCP doesn't include. Matimo OSS connects to any MCP server over HTTP, HTTPS, or stdio, the same transports the protocol defines. The difference is what happens between "the agent wants to call this tool" and "the tool executes":
Risk classification lives in the tool definition, not in a wiki page someone forgets to update. Every Matimo tool ships as a YAML file with an explicit risk tier. Here's the actual, unedited definition for mailchimp-send-campaign, a real tool in the @matimo/mailchimp package, chosen because "send this campaign" is exactly the kind of irreversible, no-undo action MCP's protocol treats identically to a harmless read:
name: mailchimp-send-campaign
description: >
Send a Mailchimp campaign. This triggers the campaign to be delivered
to all recipients in the audience. The campaign must have content set
and all required settings configured before sending.
⚠️ This action requires approval as it triggers mass email delivery.
requires_approval: true
(full definition, including params and execution config: definition.yaml on GitHub)
requires_approval: true isn't decorative. The MCP server enforces it as a protocol-level gate: the first call returns an error explaining approval is required; only a second call, carrying _matimo_approved: true, is allowed to execute. An agent, or an attacker who's poisoned its instructions, can't talk its way past that in a single turn, because the gate is in the transport layer, not in a system prompt asking nicely.
Credential injection, not credential exposure is the other half of it. Recall the STDIO flaw above: it mattered specifically because any credential reachable by the process was reachable by the exploit. Matimo's MCP server resolves secrets through a configurable chain instead of handing raw keys to the agent process:
const server = new MCPServer({
transport: 'http',
tools: ['slack_send_channel_message'],
secretResolver: {
resolvers: [
{ type: 'env' },
{ type: 'vault', secretPath: 'secret/data/myapp' },
],
},
});
(from docs/MCP.md § Programmatic Usage)
Tie those two primitives together, approval gating enforced at the protocol level, and secrets resolved through a chain instead of held by the process, and you get Matimo OSS's actual guarantee. Any tool call it executes directly, whether through its own SDK, through LangChain, or through its own MCP server, carries the same risk tier, approval flow, and audit log. Matimo Governance layers PII and secret detection on top, on execution requests and responses, aimed at the exact leakage pattern the research above keeps finding in ungoverned MCP traffic.
None of this is theoretical: it's a working example you can run. The examples/mcp directory in the Matimo repo is a real LangChain ReAct agent that connects to matimo mcp over stdio and drives 12 live Slack tools end-to-end:
const client = new MultiServerMCPClient({
mcpServers: {
matimo: {
transport: 'stdio',
command: 'npx',
args: ['matimo', 'mcp'], // spawned as a subprocess, no server to run
},
},
});
const tools = await client.getTools(); // auto-discovered from installed @matimo/* packages
const agent = createReactAgent({ llm, tools }); // same LangChain agent, now governed
(trimmed from agent-stdio.ts; there's an equivalent agent-http.ts for the Streamable HTTP transport)
That's the "framework-agnostic" claim made concrete: it's the same createReactAgent call any LangChain user already writes, pointed at Matimo's MCP server instead of a hand-rolled tool list. Swap the transport and the identical agent runs against a remote, authenticated MCP server instead of a local subprocess: the governance underneath doesn't change either way.
The Python SDK ships the identical pattern, not a second-class port of it: same 12 Slack tools, same LangChain ReAct agent, same stdio transport, just langchain-mcp-adapters and langgraph.prebuilt on the Python side:
async with MultiServerMCPClient(
{
"matimo": {
"command": "python",
"args": [server_script], # server_stdio.py, spawned as a subprocess
"transport": "stdio",
"env": {**os.environ},
}
}
) as client:
tools = client.get_tools()
agent = create_react_agent(llm, tools)
(trimmed from agent_stdio.py, the full python/examples/mcp/ directory also has an HTTP variant and a standalone MCP server example)
For teams already standardized on MCP, this isn't a rip-and-replace. Any MCP server you've already built or connected keeps working: Matimo sits in front of the call, not instead of it.
The question worth asking before you wire up the next MCP server
Not "does this MCP server work," most of them do. The question is: if this tool call went wrong at 2am, would anything have stopped it, and would you be able to show, after the fact, exactly what it did and why it was allowed to? MCP doesn't answer that. It was never supposed to.
Matimo OSS is free, MIT-licensed, and installs with npm install matimo or pip install matimo: it's built to sit in front of the MCP servers you're already running, not to compete with them. If you're evaluating how to govern tool calls across an existing MCP setup, get in touch. If you want to see the platform, Matimo Workbench and Matimo Governance are both generally available at matimo.ai, and extend the same governance to the rest of your agent stack, including the MCP node inside Matimo Studio's graph-native workflow canvas.
PS: Vulnerability counts, CVE figures, and adoption statistics cited above are drawn from third-party security research and adoption reports published at the time of writing. This is a fast-moving area: treat the figures as directional, and check the primary sources for anything you're relying on.
