MCP Governance
Control Model Context Protocol (MCP) client connections and tool calls through governance policies, edge authentication, and session management.
Model Context Protocol (MCP) governance enables Faramesh to intercept and control tool access from external MCP clients, preventing unauthorized tool calls and enforcing policy across MCP HTTP gateways and stdio wrappers.
Why MCP Governance
Section titled “Why MCP Governance”MCP servers expose tool interfaces over HTTP or stdio. Without governance:
# Uncontrolled MCP client could call any toolcurl -X POST http://mcp-server:8080/tools/call \ -H "Content-Type: application/json" \ -d '{"name": "shell/rm", "arguments": {"path": "/data/*"}}' # ← No policy check# Result: arbitrary command executionWith Faramesh MCP governance:
# Policy evaluates all MCP tools/calls before forwardingcurl -X POST http://faramesh-gateway:19092/tools/call \ -H "Content-Type: application/json" \ -d '{"name": "shell/rm", "arguments": {"path": "/data/*"}}'# Faramesh evaluates: deny shell/*# Response: JSON-RPC error -32003 (permission denied)1. Architecture
Section titled “1. Architecture”Deployment Modes
Section titled “Deployment Modes”Faramesh intercepts MCP through two modes:
HTTP Gateway:
MCP Client ↓ HTTPS/HTTPFaramesh Gateway (port 19092) ├─ Validates JSON-RPC format ├─ Enforces policy on tools/call ├─ Manages sessions and auth ↓ HTTPUpstream MCP Server (port 8080)Stdio Wrapper:
MCP Client ↓ stdin/stdout (JSON-RPC)faramesh mcp wrap -- mcp-server-cmd ├─ Intercepts tools/call in JSON-RPC stream ├─ Evaluates policy ├─ Enforces decision ↓ stdin/stdoutWrapped MCP Server ProcessMessage Flow
Section titled “Message Flow”For a tools/call request:
1. Client sends: {"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "stripe/refund", "arguments": {...}}}
2. Gateway validates: ✓ Valid JSON-RPC 2.0 format ✓ Required fields present ✓ No required fields missing
3. Policy evaluation: ✓ Agent has permission for stripe/refund ✓ Arguments match policy expressions ✓ Decision: PERMIT
4. Forward to upstream: POST /tools/call {"name": "stripe/refund", ...}
5. Post-scan tool output: Response from upstream → Scan for secrets → Return to client2. Setting Up MCP Gateway
Section titled “2. Setting Up MCP Gateway”Basic Configuration
Section titled “Basic Configuration”faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080Parameters:
--mcp-proxy-port— Gateway listens on this port (default: 19092)--mcp-target— Upstream MCP server address
With Origin Controls
Section titled “With Origin Controls”Restrict which clients can connect:
faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-allowed-origins https://app.example.com,https://admin.example.comBehavior:
Originheader from requests must match allowlist- If no
Originheader → allowed - Loopback origins (
localhost,127.0.0.1) → always allowed
3. Edge Authentication
Section titled “3. Edge Authentication”Gate MCP client access with cryptographic credentials:
Bearer Token
Section titled “Bearer Token”Require Authorization: Bearer <token> header:
faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-edge-auth-mode bearer \ --mcp-edge-auth-bearer-token "sk_test_abc123xyz"Client request:
curl -X POST http://localhost:19092/tools/call \ -H "Authorization: Bearer sk_test_abc123xyz" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "tools/call", "params": {...}}'From environment:
export FARAMESH_MCP_EDGE_AUTH_BEARER_TOKEN="sk_test_abc123xyz"faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-edge-auth-mode bearerMutual TLS (mTLS)
Section titled “Mutual TLS (mTLS)”Require client certificate:
faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-edge-auth-mode mtls \ --tls-cert /etc/faramesh/tls.crt \ --tls-key /etc/faramesh/tls.key \ --tls-client-ca /etc/faramesh/client-ca.crtClient request (with certificate):
curl -X POST https://localhost:19092/tools/call \ --cert /path/to/client.crt \ --key /path/to/client.key \ --cacert /path/to/ca.crt \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "tools/call", "params": {...}}'Combined Bearer or mTLS
Section titled “Combined Bearer or mTLS”Allow either authentication method:
faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-edge-auth-mode bearer_or_mtls \ --mcp-edge-auth-bearer-token "sk_test_abc123xyz"4. Policy Governance
Section titled “4. Policy Governance”Controlling Tool Access
Section titled “Controlling Tool Access”Write FPL policies that evaluate tools/call:
agent llm-client { default deny
rules { # Allow read operations permit tools/call when \ tool matches "read_*" && \ args.permission == "read-only"
# Allow stripe operations (verified principal only) permit tools/call when \ tool matches "stripe/*" && \ principal.verified == true && \ args.amount <= 1000
# Deny admin operations deny tools/call when tool matches "admin_*"
# Defer high-value operations defer tools/call when tool matches "stripe/*" && args.amount > 1000 \ notify: "finance" }}Expression Context
Section titled “Expression Context”In policy expressions, access MCP request details:
# tool nametool matches "stripe/*"
# arguments (tool-specific)args.amount <= 1000args.recipient matches "*.com"
# JSON-RPC metadatamethod == "tools/call"
# Standard context (principal, delegation, etc.)principal.verified == true5. Session Management
Section titled “5. Session Management”Session Tracking
Section titled “Session Tracking”Track individual client sessions:
faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-session-ttl 30m \ --mcp-session-idle-timeout 10mParameters:
--mcp-session-ttl— Max session lifetime (e.g., 30m, 1h)--mcp-session-idle-timeout— Inactivity timeout (e.g., 10m)
Session Headers
Section titled “Session Headers”Client provides session ID in requests:
curl -X POST http://localhost:19092/tools/call \ -H "Mcp-Session-Id: session_abc123" \ -H "Authorization: Bearer token" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "tools/call", "params": {...}}'Behavior:
- Session TTL starts from first request
- Idle timeout resets on each new request
- When TTL or idle timeout exceeded → request denied
DELETErequest terminates session
6. Protocol Version Enforcement
Section titled “6. Protocol Version Enforcement”Enforce strict protocol version compatibility:
Strict Mode
Section titled “Strict Mode”faramesh serve \ --policy policy.fpl \ --mcp-proxy-port 19092 \ --mcp-target http://127.0.0.1:8080 \ --mcp-protocol-version-mode strict \ --mcp-protocol-version 2025-06-18Validation:
- Client must send
MCP-Protocol-Version: 2025-06-18header - Upstream must respond with matching header
- Mismatch →
400(bad request) or502(bad gateway)
Client Request
Section titled “Client Request”curl -X POST http://localhost:19092/tools/call \ -H "MCP-Protocol-Version: 2025-06-18" \ -H "Authorization: Bearer token" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "tools/call", "params": {...}}'7. Stdio Wrapper
Section titled “7. Stdio Wrapper”Wrapping MCP Servers
Section titled “Wrapping MCP Servers”Wrap existing MCP server commands:
# Without Farameshmcp-server-binary --port 8080
# With Faramesh governancefaramesh mcp wrap -- mcp-server-binary --port 8080How it works:
- Faramesh intercepts stdin/stdout JSON-RPC stream
- Evaluates
tools/callagainst policy - Forwards allowed calls to actual server
- Returns policy decisions for denied calls
CLI Configuration
Section titled “CLI Configuration”faramesh mcp wrap \ --policy /etc/faramesh/policy.fpl \ --data-dir /var/lib/faramesh \ -- /usr/bin/mcp-server --port 8080Parameters:
--policy— Policy file path--data-dir— Faramesh data directory--— Separator before wrapped command
8. Event Notifications and Responses
Section titled “8. Event Notifications and Responses”One-Way Forwarding
Section titled “One-Way Forwarding”MCP allows notifications and responses (one-way messages):
{"jsonrpc": "2.0", "method": "notifications/progress", "params": {...}}Behavior:
- One-way messages are forwarded upstream
- Gateway returns
202 Accepted(HTTP) - No response correlation required
Batch Operations
Section titled “Batch Operations”Process multiple requests/responses:
[ {"jsonrpc": "2.0", "method": "tools/call", "params": {...}, "id": 1}, {"jsonrpc": "2.0", "method": "notifications/progress", "params": {...}}, {"jsonrpc": "2.0", "result": "ok", "id": 2}]Processing:
- Each request is evaluated and forwarded
- Notifications/responses are one-way forwarded
- Output array includes only responses (notifications omitted)
9. Production Configuration
Section titled “9. Production Configuration”Comprehensive production setup:
faramesh serve \ --policy /etc/faramesh/policy.fpl \ --data-dir /var/lib/faramesh \ --mcp-proxy-port 19092 \ --mcp-target http://mcp-server.internal:8080 \ --mcp-allowed-origins https://app.example.com,https://admin.example.com \ --mcp-edge-auth-mode bearer \ --mcp-edge-auth-bearer-token "$FARAMESH_MCP_AUTH_TOKEN" \ --mcp-protocol-version-mode strict \ --mcp-protocol-version 2025-06-18 \ --mcp-session-ttl 30m \ --mcp-session-idle-timeout 10m \ --mcp-sse-replay-enabled \ --mcp-sse-replay-max-events 256 \ --mcp-sse-replay-max-age 10m \ --tls-cert /etc/faramesh/tls.crt \ --tls-key /etc/faramesh/tls.key10. Troubleshooting
Section titled “10. Troubleshooting”Client Gets “Unauthorized” on Bearer Token
Section titled “Client Gets “Unauthorized” on Bearer Token”Check:
# Verify token matchesecho $FARAMESH_MCP_EDGE_AUTH_BEARER_TOKEN
# Check header in requestcurl -v -X POST ... \ -H "Authorization: Bearer $FARAMESH_MCP_EDGE_AUTH_TOKEN"Fix: Ensure exact token match
mTLS Connection Fails
Section titled “mTLS Connection Fails”Check:
# Verify certificatesopenssl x509 -in /etc/faramesh/tls.crt -text -nooutopenssl x509 -in /etc/faramesh/client-ca.crt -text -noout
# Test connectioncurl --cert client.crt --key client.key --cacert ca.crt \ https://localhost:19092/tools/callPolicy Denies Valid Calls
Section titled “Policy Denies Valid Calls”Check:
# Review policy evaluationfaramesh policy validate policy.fpl
# Test specific tool in audit modefaramesh policy eval --tool "stripe/charge" \ --args '{"amount": 500}'Fix: Adjust policy rules or arguments
11. Production Checklist
Section titled “11. Production Checklist”- Bearer token or mTLS is configured
- Policy file validates without errors
- Tool governance rules are tested
- Origin allowlist is configured
- Session TTL and timeouts are set appropriately
- Protocol version mode is strict
- SSE replay is configured (if using SSE)
- Upstream MCP server is healthy
- TLS certificates are valid and not expiring
- Audit logging is enabled for MCP calls