DEV Community

SidClaw
SidClaw

Posted on

Governing MCP tool calls in Claude Code -- before/after in 3 minutes

30 CVEs filed against MCP servers in 60 days. 38% have no authentication. And every MCP server your Claude Code session connects to is ungoverned -- tool calls go straight from the LLM to the server with no policy check, no approval step, no audit trail.

We built a proxy that sits between Claude Code and any upstream MCP server. It evaluates every tool call against your policies before forwarding. Here's the before and after.

Before: ungoverned MCP server

Your .mcp.json points Claude Code directly at the MCP PostgreSQL server:

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres",
               "postgresql://localhost/mydb"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Claude Code asks to run DROP TABLE customers. The MCP server executes it. Done. No one reviewed it, no one approved it, no record beyond whatever postgres logs you happen to have.

After: governed with sidclaw-mcp-proxy

{
  "mcpServers": {
    "postgres-governed": {
      "command": "npx",
      "args": ["-y", "@sidclaw/sdk", "sidclaw-mcp-proxy",
               "--transport", "stdio"],
      "env": {
        "SIDCLAW_API_KEY": "ai_your_key_here",
        "SIDCLAW_AGENT_ID": "claude-code-db-agent",
        "SIDCLAW_UPSTREAM_CMD": "npx",
        "SIDCLAW_UPSTREAM_ARGS": "-y,@modelcontextprotocol/server-postgres,postgresql://localhost/mydb"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Same MCP server underneath. But now every tools/call request passes through SidClaw's policy engine first.

Three scenarios

SELECT (allowed): Claude Code runs SELECT * FROM customers. Policy matches "allow read queries." Passes through, ~50ms overhead. You don't notice.

DELETE (flagged): Claude Code runs DELETE FROM customers WHERE name = 'Eve Davis'. Policy matches "require approval for writes." The proxy returns an MCP error (-32001) with "Approval required" and a link. A reviewer gets a Slack notification (or Teams, email, dashboard -- 16 channels). They see the exact query, the agent ID, which policy triggered it. Approve or deny. Tell Claude Code to retry.

DROP (denied): Claude Code runs DROP TABLE customers. Policy matches "block destructive DDL." Denied outright. Claude Code gets an error saying the operation was blocked by policy. No retry.

How the approval flow actually works

The proxy uses error mode by default. When a tool call needs approval, it returns immediately with an MCP error. Claude Code shows you the error. You go approve (or deny) in the dashboard or Slack. Then you tell Claude Code "try again" and the second call succeeds.

We don't use block mode (which polls for 30 seconds waiting for a decision) because it'll freeze Claude Code. Error-then-retry is the right pattern here.

Claude Code
    |
    v
sidclaw-mcp-proxy  (evaluates policy, ~50ms)
    |
    v
MCP PostgreSQL Server
    |
    v
PostgreSQL
Enter fullscreen mode Exit fullscreen mode

What this wraps

Any MCP server that uses stdio transport. We've tested with:

  • @modelcontextprotocol/server-postgres (the example above)
  • @modelcontextprotocol/server-filesystem (flag writes, allow reads)
  • @modelcontextprotocol/server-github (flag PR merges, allow reads)

The tool mappings are configurable per-tool via SIDCLAW_TOOL_MAPPINGS. You can set data classification, skip governance on safe operations, and map tool names to policy operations using glob patterns (db_*, *_read).

What this doesn't do

It doesn't filter LLM outputs. It doesn't detect prompt injection. It doesn't validate that Claude Code's reasoning is correct. Those are different problems -- Pangea, Lakera, and others handle the input/output layer. SidClaw sits at the action layer: the moment the agent decides to do something in the real world.

It also doesn't help if you're using Streamable HTTP transport with Claude Code. Right now this is stdio only, which is what Claude Code uses for local MCP servers.

Try it

The examples/claude-code-governed/ directory in the repo has a working docker-compose setup. PostgreSQL with sample data, three pre-configured policies, and the .mcp.json ready to copy. docker compose up and you're testing in 2 minutes.

Top comments (0)