wrap-mcp
wrap-mcp is a proxy that sits between Claude Code (or any MCP client) and an MCP server. It intercepts JSON-RPC tool calls on stdin/stdout and verifies delegation tokens before forwarding.
Quick Start
kanoniv-auth wrap-mcp --mode strict -- npx my-mcp-serverHow It Works
Claude Code --> wrap-mcp (verify) --> MCP Server
|
~/.kanoniv/session-tokenwrap-mcp spawns the MCP server as a child process. All JSON-RPC messages on stdin are intercepted. For each tools/call request, it extracts the tool name as the required scope and verifies authorization. Non-JSON and non-tool messages are forwarded unchanged.
The child process's stderr is inherited directly - wrap-mcp only interposes on stdin/stdout.
Modes
| Mode | No valid proof or token | Use case |
|---|---|---|
strict | Reject with JSON-RPC error (-32600) | Production |
warn | Log warning to stderr, forward anyway | Rollout / testing |
audit | Log everything, verify nothing | Observability only |
The default mode is warn.
Verification Priority
Two verification paths, tried in order:
_prooffield - If the tool call arguments contain a_prooffield, wrap-mcp verifies it as a cryptographic MCP proof (requires--root-key). The_proofis stripped from arguments before forwarding to the MCP server.Session token - If no
_proofor_proofverification fails, wrap-mcp reads the token file (default:~/.kanoniv/session-token). The tool name is the required scope.
If both fail, the mode determines behavior (reject / warn / audit).
INFO
When no --root-key is specified, _proof verification is disabled and wrap-mcp falls back to the session token path. A note is printed to stderr on startup.
Options
| Option | Default | Description |
|---|---|---|
--mode, -m | warn | Enforcement mode: strict, warn, audit |
--root-key, -k | none | Root key file for _proof verification |
--token-file | ~/.kanoniv/session-token | Delegation token file |
INFO
The token file is re-read on every tool call. You can re-delegate mid-session (via /delegate, /scope, or /ttl) without restarting the MCP server.
Scope Matching
The tool name IS the required scope. Matching is hierarchical and case-sensitive:
- Tool
resolverequires scoperesolve- exact match - Tool
resolve.entityis covered by scoperesolve- prefix match (the scope plus.is a prefix of the tool name) - Scope
*matches any tool
If the tool name does not match any scope in the token, the call is denied (in strict mode) or warned (in warn mode).
Usage with Claude Code
In your Claude Code MCP config (.mcp.json or settings.json):
{
"mcpServers": {
"my-server": {
"command": "kanoniv-auth",
"args": ["wrap-mcp", "--mode", "strict", "--", "npx", "my-mcp-server"]
}
}
}With a root key for _proof verification:
{
"mcpServers": {
"my-server": {
"command": "kanoniv-auth",
"args": [
"wrap-mcp",
"--mode", "strict",
"--root-key", "~/.kanoniv/root.key",
"--", "npx", "my-mcp-server"
]
}
}
}Combining with /delegate
When Claude Code runs /delegate, it saves the token to ~/.kanoniv/session-token. wrap-mcp reads this file on every tool call. The two work together:
- User runs
/delegate --scope resolve,search --ttl 2h - Token saved to
~/.kanoniv/session-token - wrap-mcp reads token, allows
resolveandsearchtool calls - User runs
/scope resolve(dropssearch) - wrap-mcp picks up new token on next call, blocks
search
No restart needed at any step.
Error Response (strict mode)
When a tool call is rejected in strict mode, wrap-mcp returns a JSON-RPC error to the client and does not forward the call to the MCP server:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "DENIED: tool 'merge' — tool 'merge' not in token scopes [\"resolve\", \"search\"]"
}
}The error code is always -32600 (Invalid Request). The message includes the tool name and the reason for denial.
WARNING
In strict mode, every tool call without a valid proof or session token returns a JSON-RPC error. Make sure the agent has an active delegation before starting work.
Token Verification Details
When verifying a session token, wrap-mcp performs these checks in order:
- Read and decode - Reads the token file, decodes from base64url
- Expiry check - If the token has an
expires_atfield, checks it against the current time. Expired tokens are rejected without revealing scope information. - Chain verification - If a
--root-keyis provided, verifies the delegation chain signatures back to the root identity - Scope check - Verifies the tool name matches at least one scope in the token (exact, prefix, or wildcard)
Stderr Logging
wrap-mcp logs all verification decisions to stderr. The format depends on the mode:
[kanoniv-auth] VERIFIED: resolve — token:ci-agent
[kanoniv-auth] DENIED: merge — tool 'merge' not in token scopes ["resolve"]
[kanoniv-auth] WARNING: merge — token file expired (forwarding anyway)
[kanoniv-auth] AUDIT: resolve — proof=no, token:ci-agentThese logs are visible in the MCP client's server output panel (in Claude Code, check the MCP server logs).
Next Steps
- Claude Code Skills - Client-side enforcement via PreToolUse hooks
- MCP Proof Format - Cryptographic proof specification for the
_prooffield - Auth Modes - Detailed comparison of authentication modes
- Delegation Chains - How delegation chains and caveats work
- Overview - Back to Kanoniv Auth overview
