Skip to main content
Version: 3.9.x

Expose REST APIs as MCP Tools for AI Agents

This guide explains how to use the openapi-to-mcp plugin to expose existing REST APIs as MCP tools. The plugin forwards MCP traffic to the OpenAPI-to-MCP service deployed alongside the gateway. The service reads your OpenAPI definition, exposes each operation as a tool, and sends tool invocations to the upstream REST API.

Overview

MCP (Model Context Protocol) is an open protocol for connecting AI agents to external tools. Without MCP, agent integrations with REST APIs are usually custom and fragile. The openapi-to-mcp plugin and its companion service convert OpenAPI 3.x operations into MCP tool definitions.

With this approach:

  • Existing REST APIs stay unchanged.
  • AI agents discover tools dynamically through MCP.
  • The OpenAPI-to-MCP service (MCP sidecar) translates tool invocations into upstream HTTP requests.

How MCP Gateway Works

The request path is:

AI Agent -> MCP Client -> API7 Gateway -> OpenAPI-to-MCP Service -> Upstream REST APIs

The plugin does not start an MCP server. It proxies requests to the separately deployed OpenAPI-to-MCP service at 127.0.0.1:3000 by default. The service must share the gateway's network namespace and exposes two transport modes:

  • sse (default):
    • GET /.api7_mcp/sse for the event stream
    • POST messages on the MCP message endpoint
  • streamable_http:
    • Stateless POST /.api7_mcp/mcp_stateless

For the SSE connection request and for Streamable HTTP requests, the plugin automatically injects x-openapi2mcp-base-url and x-openapi2mcp-openapi-spec headers from the plugin configuration before forwarding the request to the MCP sidecar. You do not need to set these headers manually.

Prerequisites

Before you begin, make sure you have:

  • API7 Enterprise Gateway running (openapi-to-mcp is Enterprise-only).

  • The OpenAPI-to-MCP service deployed in the same network namespace as the gateway. For Helm deployments, set openapiToMcp.enabled: true. For Docker or bare-metal deployments, follow the deployment and compatibility guidance.

  • Create a token from the Dashboard and save it to an environment variable:

    export API_KEY=your-dashboard-token # replace with your Dashboard token
  • Replace {gateway_group_id} with your gateway group ID. Use default if you are following the quickstart.

  • If you are following the Admin API examples, create or reuse a service in API7 Gateway. If you do not have one yet, follow Create or Reuse a Service, then save its ID to an environment variable:

    export SERVICE_ID=your-service-id # replace with your service ID
  • An upstream service that exposes a valid OpenAPI 3.x specification.

  • An MCP client (for example, Claude Desktop or another MCP-compatible client) for validation.

Configure the MCP Gateway

Configure a route with the openapi-to-mcp plugin. The following example uses the public Petstore service:

{
"openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"base_url": "https://petstore3.swagger.io/api/v3",
"transport": "sse",
"headers": {},
"flatten_parameters": false
}
curl -k "https://localhost:7443/apisix/admin/routes?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
--data-binary @- <<EOF
{
"id": "openapi-to-mcp-route",
"service_id": "$SERVICE_ID",
"paths": ["/.api7_mcp/*"],
"plugins": {
"openapi-to-mcp": {
"openapi_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"base_url": "https://petstore3.swagger.io/api/v3",
"transport": "sse",
"headers": {},
"flatten_parameters": false
}
}
}
EOF

openapi_url points to your OpenAPI 3.x document. The OpenAPI-to-MCP service parses this spec and builds MCP tools from its operations.

base_url is the upstream REST API base address used when a tool is invoked.

transport selects the MCP transport. Use sse (default) or streamable_http.

flatten_parameters controls whether nested parameters are flattened when generating tool input schemas.

Validate with an MCP Client

Step 1 — Connect and Discover Tools

SSE Mode

Connect the MCP client to the SSE endpoint:

curl -N "http://127.0.0.1:9080/.api7_mcp/sse"

A successful connection returns an event stream with a session-specific endpoint:

event: endpoint
data: /.api7_mcp/sse?sessionId=abc123xyz

The sessionId value identifies the SSE session created by the MCP sidecar. Use the session-specific endpoint returned by the event stream instead of the route pattern configured on the gateway.

Streamable HTTP Mode

The two transports are mutually exclusive for a route. Before using the Streamable HTTP examples, change transport from sse to streamable_http in the route configuration shown earlier, then reapply that configuration with the Admin API or adc sync.

Send a tools/list request to discover available tools:

curl -s "http://127.0.0.1:9080/.api7_mcp/mcp_stateless" \
-X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'

Response (HTTP 200) — 19 tools discovered:

event: message
data: {
"result": {
"tools": [
{"name": "updatePet", "description": "Update an existing pet by Id"},
{"name": "addPet", "description": "Add a new pet to the store"},
{"name": "findPetsByStatus", "description": "Multiple status values can be provided with comma separated strings"},
{"name": "findPetsByTags", "description": "Multiple tags can be provided with comma separated strings"},
{"name": "getPetById", "description": "Returns a single pet"},
{"name": "updatePetWithForm", "description": "Updates a pet in the store with form data"},
{"name": "deletePet", "description": "Deletes a pet"},
{"name": "uploadFile", "description": "Uploads a file"},
{"name": "getInventory", "description": "Returns a map of status codes to quantities"},
{"name": "placeOrder", "description": "Place a new order in the store"},
{"name": "getOrderById", "description": "For valid response try integer IDs with value <= 5 or > 10"},
{"name": "deleteOrder", "description": "For valid response try integer IDs with positive integer value"},
{"name": "createUser", "description": "This can only be done by the logged in user"},
{"name": "createUsersWithListInput", "description": "Creates list of users with given input array"},
{"name": "loginUser", "description": "Logs user into the system"},
{"name": "logoutUser", "description": "Logs out current logged in user session"},
{"name": "getUserByName", "description": "Get user by user name"},
{"name": "updateUser", "description": "This can only be done by the logged in user"},
{"name": "deleteUser", "description": "This can only be done by the logged in user"}
]
},
"jsonrpc": "2.0",
"id": 2
}

Step 2 — Invoke a Tool

With the route configured for streamable_http, call a tool to verify end-to-end REST forwarding. The following example calls getPetById, which requires a path parameter petId. Because flatten_parameters is false, provide the parameter under pathParameters.

curl -s "http://127.0.0.1:9080/.api7_mcp/mcp_stateless" \
-X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "getPetById",
"arguments": {
"pathParameters": {
"petId": 1
}
}
}
}'

A successful call returns HTTP 200 and an MCP result containing the Petstore resource with ID 1. The other resource fields can vary because the public Petstore data is mutable. Depending on the deployed OpenAPI-to-MCP service version, the resource can appear in a JSON-encoded upstream response envelope or directly in the result, including as structuredContent.

Security Considerations

When exposing APIs as MCP tools, apply the same controls as production API traffic:

  • Authentication: enforce gateway auth (API key, JWT, mTLS) before MCP tool calls reach the OpenAPI-to-MCP service.
  • Authorization: restrict which routes and operations an agent identity can access.
  • Rate limiting: apply request or token-based limits to prevent abusive or runaway agent traffic.
  • Sidecar egress: restrict network egress from the OpenAPI-to-MCP service because it fetches the OpenAPI document and sends upstream requests directly. Configure allowed_hosts when base_url contains variables.

Next Steps