How to Secure MCP (Model Context Protocol) Servers
SafeClaw by Authensor enforces deny-by-default policies on every tool call, resource access, and prompt invocation in MCP (Model Context Protocol) servers. MCP defines a standard interface for AI agents to interact with external capabilities — SafeClaw sits between the MCP client (the AI agent) and the MCP server's tool handlers, evaluating each request against your YAML policy before execution.
How MCP Tool Execution Works
The Model Context Protocol defines three primary capabilities: tools (functions the AI can call), resources (data the AI can read), and prompts (templates the AI can use). When an MCP client sends a tools/call request, the MCP server routes it to the appropriate handler. The protocol itself has no built-in authorization or safety layer — any connected client can call any exposed tool with any arguments. This is by design (MCP focuses on interoperability), but it means the security responsibility falls entirely on the server implementation.
MCP Client → tools/call request → [SafeClaw Policy Check] → Tool handler or Deny
Quick Start
npx @authensor/safeclaw
Creates a safeclaw.yaml in your project. SafeClaw provides MCP middleware that wraps your server's tool handlers.
Step 1: Define MCP Server Policies
# safeclaw.yaml
version: 1
default: deny
policies:
- name: "mcp-tool-access"
description: "Control MCP tool invocations"
actions:
- tool: "query_database"
effect: allow
constraints:
operation: "SELECT"
- tool: "query_database"
effect: deny
constraints:
operation: "DROP|DELETE|TRUNCATE|ALTER"
- tool: "read_file"
effect: allow
constraints:
path_pattern: "data/|docs/"
- tool: "write_file"
effect: allow
constraints:
path_pattern: "output/**"
- tool: "execute_command"
effect: deny
- name: "mcp-resource-access"
description: "Control MCP resource reads"
actions:
- tool: "resource:config"
effect: allow
- tool: "resource:logs"
effect: allow
constraints:
date_range_max_days: 7
- tool: "resource:secrets"
effect: deny
- tool: "resource:credentials"
effect: deny
- name: "mcp-prompt-access"
description: "Control MCP prompt invocations"
actions:
- tool: "prompt:summarize"
effect: allow
- tool: "prompt:generate_code"
effect: allow
- tool: "prompt:system_admin"
effect: deny
Step 2: Integrate with the MCP TypeScript SDK
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { SafeClaw } from "@authensor/safeclaw";
const safeclaw = new SafeClaw("./safeclaw.yaml");
const server = new Server(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: { tools: {}, resources: {} } }
);
// Gate tool calls with SafeClaw
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
const decision = safeclaw.evaluate(name, args ?? {});
if (!decision.allowed) {
return {
content: [
{
type: "text",
text: Action denied by SafeClaw: ${decision.reason},
},
],
isError: true,
};
}
// Execute the tool
const result = await executeToolHandler(name, args);
return {
content: [{ type: "text", text: JSON.stringify(result) }],
};
});
// Gate resource reads
server.setRequestHandler("resources/read", async (request) => {
const { uri } = request.params;
const resourceName = resource:${extractResourceType(uri)};
const decision = safeclaw.evaluate(resourceName, { uri });
if (!decision.allowed) {
throw new Error(Resource access denied: ${decision.reason});
}
return await readResource(uri);
});
const transport = new StdioServerTransport();
await server.connect(transport);
Step 3: Build a SafeClaw MCP Proxy
For existing MCP servers you don't control, deploy SafeClaw as a proxy:
import { SafeClaw } from "@authensor/safeclaw";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
const safeclaw = new SafeClaw("./safeclaw.yaml");
// SafeClaw proxy: sits between MCP client and upstream MCP server
const proxyServer = new Server(
{ name: "safeclaw-proxy", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
const upstreamClient = new Client(
{ name: "safeclaw-proxy-client", version: "1.0.0" },
{}
);
// Proxy tool/list — pass through
proxyServer.setRequestHandler("tools/list", async () => {
return await upstreamClient.request({ method: "tools/list" }, {});
});
// Proxy tools/call — gate with SafeClaw
proxyServer.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
const decision = safeclaw.evaluate(name, args ?? {});
if (!decision.allowed) {
return {
content: [{ type: "text", text: Denied: ${decision.reason} }],
isError: true,
};
}
return await upstreamClient.request(
{ method: "tools/call", params: { name, arguments: args } },
{}
);
});
This proxy pattern lets you add SafeClaw safety to any MCP server without modifying its code.
Step 4: Multi-Client Access Control
MCP servers can be accessed by multiple clients (Claude Desktop, Cursor, VS Code Copilot). Define per-client policies:
policies:
- name: "claude-desktop-access"
client: "claude-desktop"
actions:
- tool: "query_database"
effect: allow
- tool: "write_file"
effect: allow
constraints:
path_pattern: "output/**"
- name: "cursor-access"
client: "cursor"
actions:
- tool: "query_database"
effect: allow
- tool: "write_file"
effect: deny
Step 5: Audit MCP Server Activity
npx @authensor/safeclaw audit --last 100 --filter protocol=mcp
Every MCP request — tool calls, resource reads, prompt invocations — is recorded in the hash-chained audit log with the client identity, tool name, arguments, and decision.
Why SafeClaw
- 446 tests covering policy evaluation, edge cases, and audit integrity
- Deny-by-default — no MCP tool call executes without an explicit allow rule
- Sub-millisecond evaluation — no perceptible latency added to MCP request/response
- Hash-chained audit log — tamper-evident record of all MCP server activity
- Works with Claude AND OpenAI — secures MCP servers regardless of which AI client connects
Related Pages
- How to Use SafeClaw with the Claude Agent SDK
- How to Add Action Gating to GitHub Copilot Workflows
- How to Secure AI Agents in Cursor IDE
- How to Add Safety Controls to Windsurf AI Agents
- How to Secure Your Claude Agent with SafeClaw
Try SafeClaw
Action-level gating for AI agents. Set it up in your browser in 60 seconds.
$ npx @authensor/safeclaw