PingGateway

MCP security gateway

Model Context Protocol (MCP) offers an open standard to connect artificial intelligence (AI) agents with AI servers. By exposing services over MCP, you make them usable by AI agents.

The challenge, however, consists in implementing an appropriate, consistent, documented, and adaptable security model across the service assets you expose over MCP. PingGateway helps you meet this challenge as an MCP gateway, protecting MCP servers to:

  • Allow only valid MCP requests.

  • Audit MCP requests and actors.

  • Throttle request rates.

  • Enforce coarse-grained OAuth 2.0 security controls.

  • Enforce fine-grained access control policies with PingOne Authorize, PingOne Protect, and Advanced Identity Cloud.

  • Perform token transformation mapped to your security models.

PingGateway in MCP security architecure

Business teams can focus on accelerating AI adoption in the business while identity and access management and security teams address security.

This feature has Evolving interface stability. It’s subject to change without notice, even in a minor or maintenance release.

This page shows how to protect a sample MCP server using Advanced Identity Cloud as the OAuth 2.0 authorization server (AS). Follow similar steps when using PingOne or PingAM as the AS and to protect your own MCP servers.

Goals

When you complete this tutorial, you will know:

  • How to use PingGateway to protect an MCP server.

  • How to create a route to audit, protect, and validate MCP requests.

  • How PingGateway acts as an MCP gateway and resource server in an MCP security architecture.

What you’ll do

In this tutorial, you will start by preparing the sample MCP software with the server to protect. Once the MCP software works on your computer, you will use PingGateway to protect the MCP server.

The tutorial assumes you already have PingGateway and the AS set up, and only need to configure them for MCP.

The full example has two parts with multiple tasks each:

  1. Prepare the sample MCP software:

    By completing these tasks, you show the sample MCP software works on your computer without PingGateway or Advanced Identity Cloud.

  2. Protect the sample MCP server with PingGateway:

    By completing these tasks, you show how to use PingGateway in an MCP security architecture.

Preparation

Before trying the tutorial, prepare the sample MCP software to make sure it works on your computer.

Preparation task 1: Install software for this tutorial

  1. Install the prerequisite software on your computer:

    • Docker to run the sample MCP server.

    • Ollama for the sample MCP agent model.

    • Python 3.11 or later to run the sample MCP agent.

  2. Download the sample MCP software from Ping Identity Download Center and unpack it on your computer.

Preparation task 2: Run the sample MCP server

The sample MCP server runs as a Docker container.

  1. Pull the sample MCP server Docker image:

    $ docker pull gcr.io/forgerock-io/ig-sample-mcp-server/docker-build:2025.11.1
  2. Run the sample MCP server:

    $ docker run --rm -it -p 8000:8000 --name sample-mcp-server \
    gcr.io/forgerock-io/ig-sample-mcp-server/docker-build:2025.11.1

You have successfully started the sample MCP server.

Preparation task 3: Run the MCP agent

The sample MCP agent uses Meta’s Llama 3.2 model.

  1. Download, install, and run Ollama if you haven’t already done so.

  2. Install the Ollama model for the sample MCP server locally:

    $ ollama pull llama3.2:1b
  3. Run Ollama.

  4. In the directory where you unpacked the sample MCP agent, add the Python requirements.

    Install the requirements based on the requirements.txt file provided with the sample MCP agent:

    $ pip install -r requirements.txt
  5. In the directory where you unpacked the sample MCP agent, run the sample MCP agent script:

    $ python3 sample-mcp-agent.py --mcp-server-url http://localhost:8000

    If necessary, learn about additional options in the script help: python3 sample-mcp-agent.py --help

  6. In the console where the sample MCP agent runs, notice the available commands:

    [INFO] Discovered tools [http://localhost:8000]:
    [INFO] - geocode: Returns a list of objects containing city name, latitude, longitude, country, admin1 (region), and timezone for each matching city
    [INFO] - forecast_daily: Returns a multi-day weather forecast for a given location
    [INFO] - forecast_periods: Returns weather forecasts for each representative period of the current day
    [INFO] - forecast_hourly: Returns an hourly weather forecast for the current day
    [INFO] - weather_at_time: Returns the forecasted weather for a specific time at a given location
    
    Enter your message (or 'exit|quit|q'):

    This shows the sample MCP agent can connect to the sample MCP server.

  7. Enter a prompt and get a response from the MCP server, then exit the agent.

    The following example uses the forecast_daily tool to get the daily forecast for Tokyo:

    Enter your message (or 'exit|quit|q'): What is the daily forecast for Tokyo?
    Agent: The daily forecast for Tokyo is:
    
    <MCP server response with forecast details>
    
    Enter your message (or 'exit|quit|q'): exit
    User requested exit. Goodbye!

You have successfully run the sample MCP agent with the sample MCP server.

Tutorial

Now you know the sample MCP software works on your computer. Show you can protect the sample MCP server with PingGateway using Advanced Identity Cloud as the AS.

Tutorial task 1: Before you begin

  • Prepare the sample MCP software as described in Preparation.

  • Make sure you can access the PingOne Advanced Identity Cloud tenant as an administrator.

  • Choose the realm to use in the PingOne Advanced Identity Cloud tenant.

    The following tasks use the alpha realm.

  • Prepare PingGateway as described in PingGateway and PingOne Advanced Identity Cloud.

    After you follow the instructions to register the PingGateway agent in Advanced Identity Cloud, update the agent profile to set the token introspection scope:

    1. Open the AM admin UI.

    2. Go to Applications > Agents > Identity Gateway > agent ID.

    3. Set Token Introspection to Realm Only.

    4. Click Save Changes.

Tutorial task 2: Prepare Advanced Identity Cloud as the AS

  1. Sign on to the Advanced Identity Cloud admin UI as an administrator.

  2. Add a script to support RFC 8707, Resource Indicators for OAuth 2.0 as required by MCP.

    Go to code Scripts > Auth Scripts > New Script > OAuth2 / OIDC > OAuth2 Access Token Modification, click Next, add the following script, and click Save and Close:

    • Name: MCP aud script

    • JavaScript:

      // Make sure the `audience` claim matches the resource identifier
      (function () {
      	  accessToken.setField("audience", requestProperties.get("requestParams").get("resource").get(0));
      	  // No return value is expected. Leave it undefined.
      	  }());
  3. Update the OAuth2 Provider service settings for the realm.

    In the AM admin UI at open_in_new Native Consoles > Access Management, go to Services and change the following settings for the OAuth2 Provider service:

    • Advanced > Client Registration Scope Allowlist: Add test.

    • Client Dynamic Registration > Allow Open Dynamic Client Registration: Enable and click Save Changes.

    • Plugins > Access Token Modification Script: Set to MCP aud script and click Save Changes.

You have successfully prepared Advanced Identity Cloud to act as the AS.

Tutorial task 3: Configure PingGateway

Previously, you prepared PingGateway for HTTPS as described in Tutorial task 1: Before you begin. Now, configure PingGateway to protect the sample MCP server:

  1. In the admin.json file for PingGateway, enable streaming:

    "streamingEnabled": true

    PingGateway requires this setting for server-side events (SSE), part of MCP.

  2. Restart PingGateway to apply the change.

  3. Add the following route to PingGateway updating "properties" as needed for your deployment:

    Linux

    $HOME/.openig/config/routes/mcp.json

    Windows

    %appdata%\OpenIG\config\routes\mcp.json

    {
      "name": "mcp",
      "condition": "${find(request.uri.path, '^/mcp')}",
      "properties": {
        "amUrl": "https://myTenant.forgeblocks.com/am/",
        "amRealm": "/alpha",
        "gatewayUrl": "https://ig.example.com:8443",
        "mcpServerUrl": "http://localhost:8000"
      },
      "baseURI": "&{mcpServerUrl}",
      "heap": [
        {
          "name": "AuditService",
          "type": "AuditService",
          "config": {
            "eventHandlers": [
              {
                "class": "org.forgerock.audit.handlers.json.JsonAuditEventHandler",
                "config": {
                  "name": "json",
                  "logDirectory": "&{ig.instance.dir}/audit",
                  "topics": [
                    "access",
                    "mcp"
                  ]
                }
              }
            ]
          }
        },
        {
          "name": "SecretsPasswords",
          "type": "Base64EncodedSecretStore",
          "config": {
            "secrets": {
              "agent.secret.id": "cGFzc3dvcmQ="
            }
          }
        },
        {
          "name": "AmService",
          "type": "AmService",
          "config": {
            "url": "&{amUrl}",
            "realm": "&{amRealm}",
            "agent": {
              "username": "ig_agent",
              "passwordSecretId": "agent.secret.id"
            },
            "secretsProvider": "SecretsPasswords"
          },
          "sessionCache": {
            "enabled": true
          }
        },
        {
          "name": "rsFilter",
          "type": "OAuth2ResourceServerFilter",
          "config": {
            "scopes": [
              "test"
            ],
            "accessTokenResolver": {
              "type": "TokenIntrospectionAccessTokenResolver",
              "config": {
                "amService": "AmService",
                "providerHandler": {
                  "type": "Chain",
                  "config": {
                    "filters": [
                      {
                        "type": "HttpBasicAuthenticationClientFilter",
                        "config": {
                          "username": "ig_agent",
                          "passwordSecretId": "agent.secret.id",
                          "secretsProvider": "SecretsPasswords"
                        }
                      }
                    ],
                    "handler": "ClientHandler"
                  }
                }
              }
            }
          }
        }
      ],
      "handler": {
        "type": "Chain",
        "capture": "all",
        "config": {
          "filters": [
            {
              "type": "McpAuditFilter",
              "config": {
                "auditService": "AuditService"
              }
            },
            {
              "type": "UriPathRewriteFilter",
              "config": {
                "mappings": {
                  "/mcp": "/"
                }
              }
            },
            {
              "type": "McpProtectionFilter",
              "config": {
                "resourceId": "&{gatewayUrl}/mcp",
                "authorizationServerUri": "&{amUrl}oauth2/realms/root/realms&{amRealm}",
                "resourceServerFilter": "rsFilter",
                "supportedScopes": [
                  "test"
                ],
                "resourceIdPointer": "/audience"
              }
            },
            {
              "type": "McpValidationFilter",
              "config": {
                "acceptedOrigins": ".*"
              }
            }
          ],
          "handler": {
            "type": "ReverseProxyHandler",
            "config": {
              "soTimeout": "20 seconds"
            }
          }
        }
      }
    }

    Source: mcp.json

    Notice the following features of the route:

    • The sample route uses a base-64 encoded secret to connect to the AS. In production, don’t include secrets in route files.

    • PingGateway acts as an OAuth 2.0 resource server (RS) when protecting the sample MCP server.

    • The McpAuditFilter audits MCP requests. PingGateway records MCP audit events in an audit/mcp.audit.json file.

    • The UriPathRewriteFilter sends the request to the root resource of the MCP server. The MCP server expects requests at /.

    • The McpProtectionFilter uses the RS configuration, extending it for MCP.

    • PingGateway validates MCP requests with an McpValidationFilter.

    • The ReverseProxyHandler uses a long "soTimeout" setting to accommodate an MCP agent receiving few or infrequent SSE updates.

    This simple route doesn’t show throttling or fine-grained access control. Add those features as needed to meet your security requirements.

  4. Check the PingGateway log to verify the route loads successfully.

You have successfully configured PingGateway to protect the sample MCP server.

Tutorial task 4: Start the MCP agent

In the directory where you unpacked the sample MCP agent, start the sample MCP agent again. This time, point it to the PingGateway route for MCP requests:

$ python3 sample-mcp-agent.py --mcp-server-url https://ig.example.com:8443/mcp

You have successfully started the sample MCP agent.

Validation

With PingGateway protecting the MCP server, the sample MCP agent directs you to the AS to sign on as an end user and authorize access to make MCP requests.

  1. Copy the authorization URL the sample MCP agent now displays in your terminal window.

    The URL looks similar to https://myTenant.forgeblocks.com/am/oauth2/realms/root/realms/alpha/authorize?response_type=code&client_id=…​

  2. In your browser’s privacy or incognito mode, go to the authorization URL you copied.

  3. Sign on with the end-user credentials (username: demo password: Ch4ng3!t) and consent to the requested test scope before closing the browser tab as prompted.

    You set these credentials when you set up a demo user in PingOne Advanced Identity Cloud.

  4. In the terminal where the sample MCP agent runs, notice the available commands:

    [INFO] Discovered tools [https://ig.example.com:8443/mcp]:
    [INFO] - geocode: Returns a list of objects containing city name, latitude, longitude, country, admin1 (region), and timezone for each matching city
    [INFO] - forecast_daily: Returns a multi-day weather forecast for a given location
    [INFO] - forecast_periods: Returns weather forecasts for each representative period of the current day
    [INFO] - forecast_hourly: Returns an hourly weather forecast for the current day
    [INFO] - weather_at_time: Returns the forecasted weather for a specific time at a given location
    
    Enter your message (or 'exit|quit|q'):
  5. Enter a prompt and get a response from the MCP server through PingGateway, then exit the agent:

    The following example uses the forecast_daily tool to get the daily forecast for Tokyo:

    Enter your message (or 'exit|quit|q'): What is the daily forecast for Tokyo?
    Agent: The daily forecast for Tokyo is:
    
    <MCP server response with forecast details>
    
    Enter your message (or 'exit|quit|q'): exit
    User requested exit. Goodbye!

    Find additional details about the MCP request in the PingGateway log.

You have successfully validated PingGateway can protect the MCP server.