Skip to content
🎯 New workshop: Govern AI Costs in Real Time — Hands-On with agentgateway agentgateway has joined the Agentic AI FoundationLearn more

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

Page as Markdown

Set up MCP guardrails

Gate and mutate MCP method calls with an external ExtMCP policy server.

Gate and mutate Model Context Protocol (MCP) method calls with an external policy server. For more information about how MCP guardrails work, see About MCP guardrails.

In this guide, you route tools/call and tools/list requests through a sample ExtMCP server that denies any tool whose name contains forbidden and annotates each tool description in tools/list responses.

Before you begin

Install the agentgateway binary.

Set up MCP guardrails

  1. Start a sample ExtMCP policy server. This example uses a prebuilt gRPC server that denies tools/call when the tool name contains forbidden, and appends [extmcp] to every tool description in tools/list responses. The server listens on port 9001.

    docker run --rm -p 9001:9001 ghcr.io/agentgateway/testbox:0.0.1

    Build your own ExtMCP server: The sample server is for demonstration only. To build your own, implement the ExtMcp gRPC service from the ExtMCP protocol definition. The service has two methods:

    • CheckRequest: Called in the request phase, before the call reaches the MCP backend. Return the request unchanged, return mutated params, or return an AuthorizationError to deny the call.
    • CheckResponse: Called in the response phase, after the MCP backend returns a result. Return the response unchanged, return a mutated result, or return an AuthorizationError to deny the call.

    Generate gRPC bindings from the proto file in your language, implement the two methods, and serve them over cleartext HTTP/2 (h2c) on the port that agentgateway connects to. For more information about the request and response messages, outcomes, and error codes, see About MCP guardrails.

  2. In another terminal, create a config.yaml file. The MCP backend exposes a local stdio MCP server, and the mcpGuardrails policy on the target routes selected MCP methods through the ExtMCP server.

    # yaml-language-server: $schema=https://agentgateway.dev/schema/config
    binds:
    - port: 3000
      listeners:
      - routes:
        - policies:
            cors:
              allowOrigins:
              - "*"
              allowHeaders:
              - mcp-protocol-version
              - content-type
              - mcp-session-id
              exposeHeaders:
              - "Mcp-Session-Id"
          backends:
          - mcp:
              targets:
              - name: everything
                stdio:
                  cmd: npx
                  args: ["@modelcontextprotocol/server-everything"]
                policies:
                  mcpGuardrails:
                    processors:
                    - kind: remote
                      host: "localhost:9001"
                      failureMode: failClosed
                      methods:
                        tools/call: request
                        tools/list: response

    Review the following table to understand the mcpGuardrails policy.

    SettingDescription
    kind: remoteUse a remote gRPC ExtMCP server to enforce this processor.
    hostThe address of the ExtMCP policy server. This example points to the sample server from the previous step.
    failureMode: failClosedDeny requests if the policy server is unreachable or returns an error. To allow requests instead, set failOpen.
    methodsThe MCP methods to route through the policy server, and the phase for each. tools/call: request sends each tool call to the server before it reaches the MCP backend, so the server can allow, mutate, or deny the call. tools/list: response sends the tool listing to the server after the backend returns it, so the server can filter or annotate the list. For the full list of phases and method matching, see About MCP guardrails.
  3. Run agentgateway with the configuration file.

    agentgateway -f config.yaml

Verify the guardrails

Verify that the policy server gates tools/call and mutates tools/list responses.

  1. Initialize an MCP session and save the session ID.

    export MCP_SESSION_ID=$(curl -s -D - http://localhost:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -H "MCP-Protocol-Version: 2025-03-26" \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}' \
      | grep -i "mcp-session-id:" | sed 's/.*: //' | tr -d '\r')
    echo $MCP_SESSION_ID
  2. Send the notifications/initialized notification to complete the MCP handshake. The MCP server does not answer other requests, such as tools/list, until initialization is complete.

    curl -s http://localhost:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -H "MCP-Protocol-Version: 2025-03-26" \
      -H "mcp-session-id: $MCP_SESSION_ID" \
      -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
  3. List the available tools. Verify that each tool description ends with [extmcp], which the policy server added in the response phase.

    curl -s http://localhost:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -H "MCP-Protocol-Version: 2025-03-26" \
      -H "mcp-session-id: $MCP_SESSION_ID" \
      -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

    Example output:

    data: {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"echo","description":"Echoes back the input [extmcp]",...}]}}
    
  4. Call a tool whose name contains forbidden. Verify that the policy server denies the call with a JSON-RPC error.

    curl -s http://localhost:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -H "MCP-Protocol-Version: 2025-03-26" \
      -H "mcp-session-id: $MCP_SESSION_ID" \
      -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"forbidden-tool","arguments":{}}}'

    Example output:

    {"jsonrpc":"2.0","id":3,"error":{"code":-32001,"message":"tool forbidden-tool is not allowed"}}
    
  5. Call an allowed tool, such as echo. Verify that the call succeeds.

    curl -s http://localhost:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -H "MCP-Protocol-Version: 2025-03-26" \
      -H "mcp-session-id: $MCP_SESSION_ID" \
      -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"echo","arguments":{"message":"hello"}}}'

Cleanup

You can remove the resources that you created in this guide.
  1. Stop the agentgateway process with Ctrl+C.
  2. Stop the sample ExtMCP server with Ctrl+C in its terminal.
  3. Delete the configuration file.
    rm config.yaml
Was this page helpful?
Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.