Skip to main content

Architecture

┌──────────────────────┐  MCP JSON-RPC   ┌────────────────────┐  evaluate-action  ┌────────────────────┐
│  Claude Code /       │ ──────────────▶ │  Sentrail local    │ ────────────────▶ │  Sentrail cloud    │
│  Cursor / Codex      │                 │  (localhost:3773)  │                   │  policy engine     │
│                      │ ◀────────────── │                    │ ◀──────────────── │                    │
└──────────────────────┘  allow/block/   └────────────────────┘  decision +       └────────────────────┘
                          approval-msg            │              approval status
                                                  │ if allowed / approved

                                        ┌────────────────────┐
                                        │  Upstream MCP      │
                                        │  server            │
                                        └────────────────────┘
The local proxy speaks the MCP JSON-RPC protocol. Your agent connects to http://localhost:3773 instead of the upstream server. The proxy intercepts every tools/call, classifies the command, and asks the Sentrail cloud policy engine for a decision before forwarding.

What gets intercepted

Shell commands

The proxy classifies bash, shell, terminal, execute_command, run_command, and command tool names as shell invocations. The command string is extracted from the tool arguments and matched against a risk classifier:
Command patternActionRisk
rm -rf / rm -r / rm --recursivefile.delete_recursivecritical
git push --force / git push -fgit.force_pushcritical
DROP TABLE / DROP DATABASE / TRUNCATEsql.destructivecritical
curl … | bash / wget … | shshell.remote_execcritical
chmod 777file.permission_changehigh
sudo …shell.sudohigh
docker rm / docker rmicontainer.deletehigh
kubectl deletek8s.deletehigh
git reset --hardgit.reset_hardhigh
git push (no force)git.pushmedium
git branch -Dgit.branch_deletemedium
ls, cat, grep, find, echo, …shell.readlow
Everything elseunknown_writemedium

MCP methods

Only the following JSON-RPC methods pass through to the upstream server:
  • initialize — handled locally; proxied to upstream if connected
  • tools/list — forwarded to upstream, results cached 60 seconds
  • tools/callintercepted and policy-evaluated
  • ping — answered locally
All other methods (resources/*, prompts/*, custom methods) are denied with error code -32601. This prevents agents from routing side-effecting work around the policy engine.

Decision flow

allow

The call is forwarded to the upstream MCP server. The result is returned to the agent with no modification.
✅ ALLOW   bash · Read-only shell command

block

The upstream server is never contacted. The agent receives a JSON-RPC error:
{
  "error": {
    "code": -32000,
    "message": "🛑 Blocked by Sentrail: Destructive file deletion (rm -rf) blocked by Sentrail policy. Policy: Block destructive deletes"
  }
}
🛑 BLOCK   bash · Destructive file deletion (rm -rf) blocked by Sentrail policy

require_approval

The proxy holds the call and waits for a human decision:
  1. Prints the pending notice to the terminal with the action details.
  2. Sends an approval request to the Sentrail cloud (and notifies configured reviewers via Slack/email).
  3. Polls the cloud for the decision every 3 seconds, up to 120 seconds (configurable).
  4. On approve: forwards the call to upstream and returns the result.
  5. On deny or timeout: returns a block error to the agent.
⏳ APPROVAL REQUIRED   bash · Force push to a protected branch (main/master/prod)

Fail-closed behavior

If the Sentrail cloud is unreachable (network error, timeout, 5xx), the proxy fails safe:
  • Write actions (medium/high/critical risk) → blocked
  • Read-only actions (low risk) → allowed through
This means a cloud outage does not allow destructive commands to slip through unchecked.

Running without an upstream server

The proxy starts and accepts connections even if no upstream MCP URL is configured. Allowed calls will return an error when forwarding is attempted. This is useful for testing policy behavior in isolation — blocks and approvals work correctly without an upstream.