For most of the short history of LLM applications, model risk was text risk. A model could output bad advice, leak something from its prompt, or produce a convincing phishing draft — but it couldn't touch your machine. The worst case ended at the screen.
Wire a few tools into that model — a shell, an HTTP fetcher, a filesystem client, a cloud SDK — and the equation changes. With the Model Context Protocol, agents don't just suggest operations anymore; they perform them, with your credentials, on your infrastructure. The attack surface moves from "text the model wrote" to "actions the model took." A prompt that used to produce a paragraph can now produce a process.
If you're shipping agents with tool access, configuration hygiene is necessary but not sufficient. Here's why, and what runtime verification adds.
The abuse patterns below are well-known categories — described generically, without reference to any specific project's incidents.
1. Code execution tools → remote code execution. Shell, exec
, and interpreter tools exist because agents genuinely need them: running tests, scaffolding projects, transforming data. But any string that reaches a shell is a command. Content fetched from a web page, a filename, an error message, or a dependency's metadata can carry shell metacharacters. The model doesn't need to be "hacked" in the classic sense — it just needs to faithfully pass attacker-influenced text into an execution tool.
2. Fetch / HTTP tools → SSRF. Agents love to fetch URLs: docs, APIs, "read this link the user pasted." A URL is also a network destination. Point a fetcher at a cloud metadata endpoint (169.254.169.254
), a private RFC1918 address, or localhost:port
and the agent becomes an SSRF primitive — reading internal services from inside your network perimeter and helpfully summarizing what it found.
3. Credential and environment-variable exfiltration. Agent processes inherit environment: AWS_*
, GITHUB_TOKEN
, database URLs, API keys. Tools often accept arbitrary key/value or arguments. A two-step chain — read a sensitive file or env var, then POST it somewhere via the HTTP tool — turns a "helpful agent" into an exfiltration channel. Neither step looks dramatic on its own.
4. Prompt injection → unauthorized tool calls. Untrusted content in the model's context (a web page, an email, a file, a tool result) can contain instructions aimed at the model. The model is the one holding the tool handles, and it can't always tell your instructions apart from instructions embedded in data. The outcome is a tool call you never authorized, performed with your authority.
Scanning your MCP configuration is the right first move. A static scanner catches real, fixable problems: plain-HTTP transports, disabled TLS verification, credentials pasted directly into config JSON, missing timeouts, over-broad permissions, unpinned server versions. The open-source correctover-scan runs
.cursor/mcp.json
, claude_desktop_config.json
, .claude/mcp.json
, mcp.json
, and a few more.But a config file is static, and the dangerous part is dynamic. Your mcp.json
will never contain the argument the model constructs at 3 a.m. — the URL it decided to fetch, the command string it assembled from a tool result, the env var name it placed into an HTTP body. Static analysis answers "is this setup reasonable?"; it cannot answer "is this specific call safe?"
The tempting shortcut is a keyword blacklist: block calls containing exec
, block URLs containing 169.254
, block arguments containing AWS_SECRET
. It doesn't work, because safety is contextual:
exec()
as its normal, declared function is exec()
string passed to a shell is Same tokens, different verdicts — because the tool, the caller, the arguments, and the chain of preceding calls differ. That judgment has to happen at call time, with the actual arguments in hand.
A runtime verifier sits in front of tool execution and evaluates every call before it runs. For security, five layers matter most:
payments.send
call with amount
as a string fails before it reaches the API.Two non-negotiable properties:
Step 1 — scan your configs locally. Zero dependencies, no network needed:
npx correctover-scan
npx correctover-scan mcp.json -f sarif > report.sarif
npx correctover-scan -d ./my-project
Step 2 — add the runtime verifier as an MCP server. ccs-mcp-server is a zero-dependency stdio MCP server. Drop this into your client config (Claude Desktop, Cursor, or any other stdio-compatible MCP client):
{
"mcpServers": {
"ccs-runtime-evidence": {
"command": "npx",
"args": ["-y", "ccs-mcp-server"]
}
}
}
It exposes verify_tool_call
(the checks above, blocking unsafe calls by default), issue_evidence
(Ed25519-signed receipts for every decision, allow and deny), and config-audit and receipt-verification tools. The signing keypair is generated automatically on first run; set the CCS_KEY_DIR
environment variable only if you want to control where it persists.
Step 3 — wrap an existing server (optional). If you'd rather verify calls transparently around a server you already run, the compatibility package forwards to the verification gateway:
npx -y correctover-mcp-server --stdio -- npx -y <your-existing-mcp-server>
Both servers are published in the official MCP Registry as io.github.Correctover/ccs
and io.github.Correctover/mcp
.
The evidence model behind these tools is documented in the CCS protocol specification, draft-correctover-ccs, which defines the receipt schema, cryptographic bindings (request, parameters, runtime context, issuer, audience, freshness), fail-closed transport requirements, and conformance levels for evidence propagation across agent chains. This is an individual Internet-Draft, not an RFC or IETF endorsement.
Everything lives at github.com/Correctover. If you're building agents with tool access:
npx correctover-scan
in your repo and in CI — it takes seconds and needs no credentials.Agents that can act are agents that can err at machine speed. Verify the call before it becomes the action.
Alternative titles: