Safeguard MCP Guardrail controls what your AI agents can do through MCP servers. It has two layers. Tool gating decides which Safeguard tools an assistant can see and call. Guard sits in front of your own MCP servers and checks each tool call against allow, deny or monitor rules, filters inputs and outputs, and logs every decision.
This guide is for teams whose agents already call MCP servers, or are about to. It walks through both layers in the order you would set them up, then covers the routine that keeps them useful. By the end you will have:
- Safeguard's own MCP server locked down to the tools your team needs, and a way to prove a disabled tool stays disabled.
- Guard Proxy running in front of one of your MCP servers, with an agent pointed at the proxy instead of the server.
- A first policy that starts in monitor mode, then moves to deny once the audit trail shows it is safe.
- A short daily and weekly routine for reading decisions, handling blocked calls and adding new servers.
- Approvals on the agent side as well, so a risky call needs a person even when policy allows it.
You keep the AI tools you already use while you do this. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. You can also connect Claude or ChatGPT to the Safeguard MCP server and work with Safeguard from the chat you already use.
For the design reasoning, read how MCP Guardrail works. This post is the hands-on version.
What MCP Guardrail checks
The two layers solve different problems and are configured in different places.
Tool gating applies to Safeguard's own MCP server at https://mcp.safeguard.sh/mcp. That server registers hundreds of tools, and each one has a feature flag named in the form mcp:tool:safeguard_list_projects. The enabled set is resolved per tenant, and the same set controls both discovery (tools/list) and execution (tools/call). An assistant cannot see a tool it cannot call, or call a tool it cannot see.
Guard applies to MCP traffic in general, including servers and agents you build or operate. Each JSON-RPC request is matched against the rules in your workspace's default policy. A rule can match on:
- the MCP method, such as
tools/call(exact, case insensitive) - the tool name, with glob wildcards such as
support_*(case insensitive) - the agent ID (exact)
Each rule then resolves to one of three actions. Allow lets the request through. Deny blocks it before it reaches the tool and can return a reason to the caller. Monitor lets it through but flags it for review.
On top of matching, a rule can carry:
- Input filters, run before the tool call:
block_pattern(a regular expression),max_length,encoding_checkandjailbreak_check, which catches known prompt injection phrasing such as "ignore previous instructions". - Output filters, run on the response:
pii_mask(email, SSN, credit card, phone, IP address, API keys, JWTs and bearer tokens),column_redactfor named fields,row_limitandregex_mask. - A rate limit, for example 100 requests per 60 seconds.
- Jailbreak and anomaly thresholds. The defaults are 0.75 and 0.80. A rule that leaves them out inherits the org-wide value.
Every evaluated request is written to an append-only audit trail.
| Tool gating | Guard | |
|---|---|---|
| Protects | Safeguard's own MCP server | Your MCP servers and agents |
| Decides | Which Safeguard tools exist for a tenant | Whether each call is allowed, denied or monitored |
| Configured in | Feature Flags in the admin console | The default Guard policy in your workspace |
| Scope | Per tenant, not per user | Per organization, with rules per tool and agent |
| Unknown or failed state | Falls back to the six default read-only tools | Unmatched calls are flagged for monitoring |
| Record | Tool result marked as an error | One audit event per request |
The layers are independent: Feature Flags never change a Guard policy, and Guard never changes feature flags. Guard is also separate from Safeguard's Guardrails and Enforcement, which is YAML policy as code for CI, registries and admission.
What is not there yet
The docs are clear about current limits:
- The Guard SDK, which would embed enforcement inside your own server process, is not published. There is no Guard SDK package on PyPI, npm or the Go module proxy. Treat any package claiming to be it as untrusted. Guard Proxy is the route today.
- Guard alert rules have a documented schema, but no API creates them and nothing evaluates traffic against them yet. Plan to read the audit trail on a schedule instead (shown later).
- There is no server name condition in a rule. Match on tool names or agent IDs.
Step 1: Lock down Safeguard's own MCP server
Start here even if your main concern is your own servers; it covers every assistant that talks to Safeguard.
- Check the defaults. A new workspace starts with six read-only tools on:
safeguard_list_products,safeguard_list_projects,safeguard_get_project,safeguard_list_vulnerabilities,safeguard_get_vulnerabilityandsafeguard_list_findings. Everything else ships disabled. - Check for legacy extras. Workspaces created before the default set was narrowed may still have
safeguard_add_integration,safeguard_scan_repository,safeguard_get_current_contextandsafeguard_switch_organizationswitched on. Open Feature Flags and look. - Enable only what a workflow needs. In the Safeguard admin console, open Feature Flags, search by tool name or filter by the
mcpcategory, toggle the tool on and save. Each toggle creates a tenant override. Toggling back, or using Reset to Defaults, removes it. Feature Flags is an admin screen, so ordinary members cannot widen access on their own. - Connect your assistant with OAuth. Add
https://mcp.safeguard.sh/mcpto Claude, ChatGPT, Cursor, VS Code or Gemini CLI, sign in at app.safeguard.sh and click Authorize access. The assistant can then see and do only what your Safeguard role allows, within the tools your workspace has enabled. No npm package or local bridge is needed. - Use an MCP key only for fixed header clients. If a client cannot do OAuth, create a key under Settings, Developer, choose the type MCP key, and send it as
Authorization: Bearer sg_sk_.... The key is shown once and acts as its creator across the whole workspace, so have a member with a narrow role create it. - Prove the gate holds. Ask your assistant to run a tool you have not enabled, such as a scan. It should get back a normal tool result marked as an error, with text like
Tool "safeguard_scan_manifest" is not enabled for this tenant.The connection stays open and the agent can carry on.
Changes are re-checked roughly every minute. Many clients cache the tool list at session start, so if a newly enabled tool is missing, reconnect the assistant.
This demo shows the Claude side of step 4: connecting Claude to the Safeguard MCP server with OAuth and choosing which tools Claude can use.
Click through it at your own pace, or open it full screen.
The full connection walkthroughs are in connect Claude to the Safeguard MCP server and connect ChatGPT to the Safeguard MCP server.
Step 2: Put Guard Proxy in front of your MCP servers
Guard Proxy is a self-hosted reverse proxy. It sits between your agents and an MCP server, applies your Safeguard policy to each JSON-RPC call and forwards the allowed ones upstream. You do not touch the MCP server's code, which makes it the right fit for servers you did not write.
The documented deployment is Docker Compose: proxy on port 8080, api on 8081, an audit worker, Postgres, Redis and nginx.
- Set a real secret. The Compose file defaults
JWT_SECRETto a placeholder. Set your own value before running the stack anywhere but a laptop.
cd deployments/
export JWT_SECRET="$(openssl rand -hex 32)"
docker compose up -d
- Add TLS. The shipped
nginx.confonly listens on port 80 and does not terminate TLS on 443. Put TLS in front of the stack with whatever your platform already uses. - Check it is up. The nginx
/healthpath forwards to the proxy and needs no authentication.
curl -s https://guard.example.internal/health
- Register the upstream server.
transportishttporsse.auth_typeisnone,bearer,basicorapi_key, and the matchingauth_configis attached to every upstream request.
curl -X POST https://guard.example.internal/api/v1/servers \
-H "Authorization: Bearer $GUARD_JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "billing-mcp",
"upstream_url": "https://internal-mcp.example.com",
"transport": "http",
"auth_type": "bearer",
"auth_config": {"token": "'"$BILLING_MCP_TOKEN"'"}
}'
- Get a proxy URL. Registering a server does not create a proxy path. A separate proxy URL record does, tying a slug to the server, optionally to one
agent_id(for attribution) and apolicy_id(to override the default). Ask your Guard admin to create one; here the slug isbilling-mcp. - Point the agent at the proxy. Agents send JSON-RPC to
POST /p/billing-mcp/messageand open streams onGET /p/billing-mcp/sse, with a Guard key in the header. Keys start withsg_live_in production andsg_test_in test or staging.
curl -X POST https://guard.example.internal/p/billing-mcp/message \
-H "Authorization: Bearer $SG_GUARD_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
The Guard key authenticates the agent to the proxy only. The upstream credential stays in the server record, so agents never hold it.
Most MCP clients only need a URL and header change. In the Safeguard CLI, for example, safeguard mcp add takes an HTTP URL and the name of an environment variable that holds the bearer token, and safeguard mcp get probes the tools it can reach:
export SG_GUARD_KEY="sg_live_..."
safeguard mcp add billing --url https://guard.example.internal/p/billing-mcp/message \
--bearer-token-env-var SG_GUARD_KEY
safeguard mcp get billing
If mcp get lists the tools, traffic is flowing through Guard.
One limit to know: streams on /sse get output filtering such as PII masking, but not the full request pipeline of deny rules, rate limits and jailbreak checks. Those apply to calls posted to /message, so prefer that path for anything you want to block.
Step 3: Write your first policy
Guard policies live in your Safeguard workspace, not in your repository. The proxy pulls the live default policy continuously; if it cannot reach Safeguard, it keeps enforcing the last one it fetched.
A policy belongs to one organization and holds an ordered list of rules. Exactly one policy per org should have is_default set to true, because that is the only one Guard loads. The policy's own priority only changes listing order. Rule priority is what matters: rules are checked from highest to lowest, and the first match wins.
Here is a starter policy for a billing and support agent, built from the rule fields in the Guard Policies docs (check the filter object shapes against the docs example before you save). Every risky rule starts as monitor so you can watch before you block.
{
"name": "Agents: production",
"description": "Starter policy, monitor first",
"is_default": true,
"rules": [
{
"name": "no-destructive-tools",
"priority": 300,
"action": "monitor",
"deny_reason": "Destructive tools are not available to agents. Ask a person.",
"match_methods": ["tools/call"],
"match_tools": ["*delete*", "drop_*", "refund_*"]
},
{
"name": "support-jailbreak-check",
"priority": 200,
"action": "monitor",
"deny_reason": "Request looked like prompt injection.",
"match_methods": ["tools/call"],
"match_tools": ["support_*"],
"input_filters": [
{"type": "jailbreak_check"},
{"type": "max_length", "max_length": 8000}
],
"jailbreak_threshold": 0.75
},
{
"name": "mask-exports",
"priority": 100,
"action": "allow",
"match_methods": ["tools/call"],
"match_tools": ["export_*", "query_*"],
"output_filters": [
{"type": "pii_mask"},
{"type": "column_redact", "columns": ["ssn", "internal_notes"]},
{"type": "row_limit", "limit": 500}
],
"rate_limit": {"limit": 100, "window_sec": 60}
}
]
}
Save it through the API with the bearer token from your Safeguard session:
curl -X POST https://guard.example.internal/api/v1/policies \
-H "Authorization: Bearer $GUARD_JWT" \
-H "Content-Type: application/json" \
-d @agents-production.json
Three things to know before you edit it:
- Updates replace the whole policy. A
PUTto the policy's ID takes the full object with every rule. There is no single rule endpoint, so fetch, edit and put back. - Unknown fields are silently ignored. A typo such as
match_toolhas no effect and no error. Compare against the docs example before you save. - Give every rule a distinct priority. Rules with equal priority have no guaranteed order.
A request that matches no rule is flagged for monitoring, not silently allowed or blocked, and the same goes for all traffic if there is no default policy. A new deployment is safe to observe.
Step 4: Move from monitor to deny
- Run the agent for a few days on monitor. Let it do its real work through the proxy.
- Read what matched. Audit events carry
rule_name,tool_name,agent_idand the three scores, so you can see exactly which calls each rule would have stopped.
curl -s "https://guard.example.internal/api/v1/audit?action=monitor&from=2026-10-14T00:00:00Z&page_size=200" \
-H "Authorization: Bearer $GUARD_JWT" \
| jq -r '.data[] | [.created_at, .agent_id, .tool_name, .rule_name, .jailbreak_score] | @tsv'
- Look for false positives. A tool called
soft_delete_draftmatches*delete*. Narrow the glob or add a higher priority allow rule for that case. - Flip one rule at a time. Change
actiontodenyon the rule you trust most,PUTthe policy, and watch for a day. - Confirm the deny reaches the agent. A denied call returns HTTP 403 with JSON-RPC error code
-32000and yourdeny_reason. A good agent will report the reason instead of retrying blindly.
Day to day: keep the guardrail useful
Each morning, read the denies. Each one is either the guardrail working or a rule that is too tight.
curl -s "https://guard.example.internal/api/v1/audit?action=deny&from=$(date -u -v-1d +%Y-%m-%dT%H:%M:%SZ)" \
-H "Authorization: Bearer $GUARD_JWT" | jq '.data[] | {created_at, agent_id, tool_name, rule_name, deny_reason}'
On Linux, use date -u -d '1 day ago' with the same format. Pages hold up to 200 events; follow has_more on busy days. The API cannot filter by agent_id or server_id yet, so filter those with jq.
Each week, read the trend. The timeline endpoint returns counts per hour or day, including denied requests, PII detections and jailbreak detections. Both from and to are required.
curl -s "https://guard.example.internal/api/v1/audit/timeline?period=day&from=2026-10-14T00:00:00Z&to=2026-10-21T00:00:00Z" \
-H "Authorization: Bearer $GUARD_JWT"
A rising denied count after a model or prompt change is worth a look. So is a jump in pii_detections on a tool you thought returned no personal data. The timeline counts a jailbreak detection above a fixed 0.5, separate from the thresholds in your rules.
Replace alerts with a scheduled check. Guard alert rules do not run yet, so schedule the deny query in your existing job runner and post the count and top rule names to your team channel.
When an agent misbehaves, stop it fast. Agent status (active, blocked or quarantined) is checked before policy, so a blocked agent is rejected with -32002 even if a rule would allow the call. To cut off one route instead, revoke its proxy URL slug. Slug lookups are cached for up to five minutes, so a revoked slug can still resolve briefly.
When you add an MCP server, repeat step 2, get a slug, and add monitor rules for its tool names first.
Remember retention. Audit events are kept for 90 days by default (AUDIT_RETENTION_DAYS on a self-hosted deployment). There is no export feature, so archive the JSON pages yourself if you need longer.
Pair the guardrail with approvals in the Safeguard CLI
Guard decides what a call may do once it reaches the proxy. Approvals decide whether the agent sends it at all. Use both: policy for the rules you can write down, a person for the judgment calls.
The Safeguard CLI is a single static binary for macOS, Linux and Windows. Install it with curl -fsSL https://cli.safeguard.sh/install | bash, run safeguard and sign in with /login. Its assistant has tools for your local files and your Safeguard data, and it can write files, edit code and run commands with your approval.
For MCP servers, the CLI keeps its own approval setting per server:
--approval-mode askis the default forsafeguard mcp add, so each call to that server's tools asks first.allowanddenyare the other values.safeguard mcp disable billingsets the server to deny, so its tools are not registered at all.safeguard mcp enable billingputs it back on ask.safeguard mcp list --allalso shows MCP servers from other agents' configuration that were left out, with the reason. Use it to see which servers live in other agents' configuration, such as Claude Code or Codex, and may bypass the proxy.
Inside the interactive terminal, /permissions sets the session's mode: Ask (edits and commands ask first), Accept edits, Plan (read and plan only), Auto (the default: read-only commands and what policy allows run, anything else asks) and Strict (pre-approved actions only, for CI). A --yolo flag approves everything; keep it for throwaway sandboxes, never for an agent that reaches a guarded MCP server.
A CLI session is live: it shows in the web, desktop and phone apps, so you can answer an approval prompt from your phone. This recorded session shows the phone side: the same conversations appear in the Android app, "Work on my computer" picks a computer running the CLI and a folder, the command waits for Allow once, Allow for turn or Deny on the phone, and the answer shows the tests passed.
Click through it at your own pace, or open it full screen.
The same CLI checks what agents changed: safeguard scan --dir . builds an SBOM and matches it against vulnerability databases before you merge. See review AI generated code before merge and the Safeguard CLI as an AI assistant on your machine.
Troubleshooting and common mistakes
401 on /message. Check the key prefix (sg_live_ or sg_test_) and that the key belongs to the same org as the server and slug.
404 "proxy URL not found". The slug is missing, mistyped or revoked. The response does not say which.
502 from the proxy. The upstream errored or was unreachable (code -32004). Check auth_type and auth_config on the server record first.
403 with code -32003. The jailbreak or prompt injection threshold tripped on the request or the response. Read jailbreak_score in the audit event before you lower anything.
A new event is missing from the audit API. Writes are asynchronous and batch every 100 events or every second. Wait a second or two and query again.
Common mistakes:
- Leaving
JWT_SECRETon its default. It is a placeholder. Set it before anything leaves a laptop. - Marking two policies as default. Which one loads is not guaranteed. Keep exactly one.
- Typos in rule fields. They are ignored without an error. Compare against the docs example.
- Handing out MCP keys to people. A key acts as its creator across the whole workspace. Prefer OAuth for people.
- Agents that skip the proxy. If an agent still has the upstream URL, policy never sees its calls. Remove direct credentials and check
safeguard mcp list --all.
FAQ
Is MCP Guardrail one product or two?
It is two independent layers under one name. Tool gating limits Safeguard's own MCP tools per tenant through Feature Flags. Guard enforces allow, deny and monitor policies on MCP traffic to your own servers, through Guard Proxy today.
Do I need to change my MCP server's code?
No. Guard Proxy runs in front of the server and forwards allowed calls. The in-process Guard SDK is not published yet, so the proxy is the way to deploy Guard now.
What happens to a call that matches no rule?
Guard flags it for monitoring. It goes through and appears in the audit trail with the monitor action.
Can a policy stop prompt injection?
It can catch known phrasing. The jailbreak_check input filter denies requests that look like prompt injection, such as "ignore previous instructions" or attempts to reveal the system prompt, and the threshold decides how sensitive it is. Treat it as one layer with approvals and least privilege, not the only defence.
Can I keep using Claude, ChatGPT or Codex?
Yes. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. You can also connect Claude or ChatGPT to the Safeguard MCP server with OAuth. See the CLI docs or /model for connecting a model.
Where do I see Guard decisions?
Through the Guard audit API, GET /api/v1/audit for events and GET /api/v1/audit/timeline for counts. The portal's Settings, Audit Logs page covers admin activity, not Guard decisions.
Try it yourself
To see agent side approvals in a real CLI session, click through this one. It installs the CLI with the curl command (checksum verified), asks "fix the lodash CVE in this repo", and the assistant asks before each command (press y or n) while it edits package.json to lodash 4.17.21 and runs npm install and npm test. It pushes nothing, then shows /permissions, /model and /scan ..
Click through it at your own pace, or open it full screen.
Get the CLI, desktop app and phone apps from safeguard.sh/download, and read the Safeguard CLI docs for every command.
Quick check
You turned off a Safeguard tool in Feature Flags. Does Guard now deny calls to that tool on your own MCP servers?
No. Feature Flags and Guard are independent. The flag hides the tool on Safeguard's MCP server; Guard only follows the rules in your default Guard policy.
A rule with priority 300 denies a call and a rule with priority 100 allows it. Which wins?
The priority 300 rule. Rules are checked from highest priority to lowest and the first match wins, so the call is denied.
You want to block a tool, but your agent only reaches it over the SSE stream. Will a deny rule stop it?
No. SSE streams get output filtering such as PII masking, but deny rules, rate limits and jailbreak checks apply to calls posted to the message path. Route the calls through it.
Sources
- Safeguard docs: MCP Integrations
- Safeguard docs: Tool availability and gating
- Safeguard docs: Guard Policies
- Safeguard docs: Guard Proxy
- Safeguard docs: Guard Audit Trail and Alerts
- Safeguard docs: Safeguard CLI
- Safeguard blog: How MCP Guardrail works
- Safeguard downloads
Last reviewed: 21 October 2026.