Connect a guarded MCP server

Your MCP server keeps working exactly as it does today. Point your AI gateway at a Shield route instead of at the server directly, and every tool call is screened on the way in and redacted on the way out.

Five fields. No SDK, no OpenAPI spec, no code changes on either side.

Table of contents
  1. What you are connecting to
  2. Add Server: five fields
  3. Confirm it works
  4. What to expect
  5. If something looks wrong
    1. Use API Token, not OAuth
  6. Common questions
  7. Reference: Shield-side setup
  8. Related

What you are connecting to

Your AI gateway
      |
      v
https://api.guardrails.votal.ai/gateway/{route}/mcp    <- Shield enforces here
      |
      v
your MCP server                                        <- unchanged

Shield speaks MCP to your server as an ordinary client, using the official SDK. Your server never knows it is proxied.

Add Server: five fields

This is the entire integration surface. Only two rows carry real values.

Field Value
Name Anything you like, for example Bank Core (guarded)
Prefix Required by the dialog, but cosmetic. It does not change the tool names Shield sees.
URL https://api.guardrails.votal.ai/gateway/{route}/mcp
MCP authentication method API Token, set to the tenant key we provide
Client ID / Secret / Scopes Leave empty. See Use API Token, not OAuth.

Confirm it works

Tool discovery happens at runtime over JSON-RPC. Ask the gateway what it offers and you get the live, permission-filtered list back.

curl -s -X POST "https://api.guardrails.votal.ai/gateway/{route}/mcp" \
  -H "X-API-Key: <tenant key>" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

A JSON-RPC result listing tools means you are connected. That list is already filtered by role, so a tool this connection may not use is not merely blocked, it is never offered.

What to expect

Normal calls pass. A well-formed request for one specific record goes straight through. Latency is a single added hop.

Sensitive fields come back masked. A record may return with a card as **** **** **** 1111 and the CVV removed. That is policy working, not an error. Do not retry.

Unsafe calls are refused. Bulk pulls, injection, and requests for unauthorized fields return a JSON-RPC error beginning Blocked by Shield: followed by the reason. The call never reaches the MCP server. Surface the reason to the user rather than retrying, because a retry of the same argument will be refused again.

If something looks wrong

Symptom Cause Fix
Tool list is empty The agent was granted prefixed tool names, or was not granted any Grant the unprefixed names. The Prefix field is cosmetic.
Tools list, but every call is denied Connected over OAuth, or the agent is not registered Use API Token auth. Confirm the agent is registered on the Shield side.
Session terminated Driving MCP with plain curl without a session Expected. Use an MCP client, or the single tools/list call above.

Use API Token, not OAuth

Shield is a full OAuth 2.1 authorization server and the token exchange itself succeeds, but the identity it derives cannot currently be governed: the subject contains colons, which the agent registry rejects. The result is a connection where tools appear in the list and every call is denied. API Token is the supported path today.

Common questions

Do you need an OpenAPI spec? No. MCP is JSON-RPC, not REST. Tools are discovered at runtime through tools/list, so there is no spec to import, host, or keep in sync.

Do you change the MCP server? No. No SDK, no headers, no awareness of the proxy. Shield connects to it with the standard MCP client, exactly as any other consumer would.

What gets logged? Every allow, redact, and block is recorded in the tenant telemetry, with the tool name, the route, and the decision.


Reference: Shield-side setup

Included so the whole picture is visible. These steps are run once by the Shield operator before the connection is handed over. The gateway team does not run them.

export SHIELD=https://api.guardrails.votal.ai
export KEY=<tenant API key>
export ROUTE=<route name>
export UPSTREAM=https://<your-app>/mcp

1. Register the MCP server behind a named route

curl -s -X PUT "$SHIELD/v1/tenant/me/mcp-gateway/upstreams/$ROUTE" \
  -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d "{\"transport\":\"http\",\"url\":\"$UPSTREAM\",
       \"enforcement_backend\":\"inprocess\",\"isolation_ack\":true}"

2. Register the agent and grant it tools, using unprefixed names

curl -s -X POST "$SHIELD/v1/agents/registry" \
  -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  -d '{"agent_id":"mcp-agent",
       "tools":["account_balance_get","customer_profile_get","card_details_get"],
       "role_permissions":{"":["account_balance_get","customer_profile_get"]}}'

The empty role "" is what a connection with no user role resolves to. Give it only the safe subset: anything left out is both invisible and unusable.

3. Apply the data policy

curl -s -X POST "$SHIELD/v1/data-policies/global/policy" \
  -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
  --data @saas/examples/mcp_input_protection_policy.json

Input rules refuse the call. Output rules redact the result.

4. Verify, then hand over the URL and key

curl -s "$SHIELD/v1/tenant/me/telemetry?limit=10" -H "X-API-Key: $KEY"

Treat the tenant key as a credential: it authenticates the connection and scopes it to one tenant.