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. API7 Gateway reads your OpenAPI definition, starts a local MCP server, and maps each API operation to a tool that AI agents can discover and invoke.
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 solves this by converting OpenAPI 3.x operations into MCP tool definitions at the gateway layer.
With this approach:
- Existing REST APIs stay unchanged.
- AI agents discover tools dynamically through MCP.
- Tool invocations are translated into normal upstream HTTP requests through API7 Gateway.
How MCP Gateway Works
The request path is:
AI Agent → MCP Client → API7 Gateway (MCP Server) → Upstream REST APIs
At runtime, the plugin starts a local MCP server and exposes two transport modes:
sse(default):GET /.api7_mcp/ssefor the event streamPOSTmessages on the MCP message endpoint
streamable_http:- Stateless
POST /.api7_mcp/mcp_stateless
- Stateless
In streamable_http (stateless) mode, the openapi_url and base_url from the plugin config are not automatically passed as context per request. Each MCP request must include these headers:
| Header | Purpose | Example |
|---|---|---|
x-openapi2mcp-base-url | Base URL for REST calls | https://petstore3.swagger.io |
x-openapi2mcp-openapi-spec | URL of the OpenAPI spec | https://petstore3.swagger.io/api/v3/openapi.json |
Accept | Must include SSE support | application/json, text/event-stream |
Prerequisites
Before you begin, make sure you have:
- API7 Enterprise Gateway running (
openapi-to-mcpis Enterprise-only). - 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 required baseline configuration:
{
"openapi_url": "http://upstream/openapi.json",
"base_url": "http://upstream:8080",
"transport_mode": "sse",
"headers": {},
"flatten_parameters": false
}
- Admin API
- ADC
curl "http://127.0.0.1:7080/apisix/admin/routes?gateway_group_id=default" -X PUT \
-H "X-API-KEY: $ADMIN_API_KEY" \
-d '{
"id": "openapi-to-mcp-route",
"service_id": "$SERVICE_ID",
"paths": ["/.api7_mcp/*"],
"plugins": {
"openapi-to-mcp": {
"openapi_url": "http://upstream/openapi.json",
"base_url": "http://upstream:8080",
"transport_mode": "sse",
"headers": {
"Authorization": "Bearer $ctx.var.upstream_token"
},
"flatten_parameters": false
}
}
}'
❶ openapi_url points to your OpenAPI 3.x document. The plugin 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_mode selects the MCP transport. Use sse (default) or streamable_http.
❹ headers adds upstream request headers for tool invocations. Header values support ctx.var variable resolution.
❺ flatten_parameters controls whether nested parameters are flattened when generating tool input schemas.
services:
- name: MCP Gateway
routes:
- uris:
- /.api7_mcp/*
name: openapi-to-mcp-route
plugins:
openapi-to-mcp:
openapi_url: http://upstream/openapi.json
base_url: http://upstream:8080
transport_mode: sse
headers:
Authorization: "Bearer $ctx.var.upstream_token"
flatten_parameters: false
❶ openapi_url points to your OpenAPI 3.x document. The plugin 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_mode selects the MCP transport. Use sse (default) or streamable_http.
❹ headers adds upstream request headers for tool invocations. Header values support ctx.var variable resolution.
❺ flatten_parameters controls whether nested parameters are flattened when generating tool input schemas.
Synchronize the configuration to API7 Gateway:
adc sync -f adc.yaml
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 the session endpoint:
event: endpoint
data: /.api7_mcp/*?sessionId=abc123xyz
Streamable HTTP mode
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" \
-H "x-openapi2mcp-base-url: https://petstore3.swagger.io" \
-H "x-openapi2mcp-openapi-spec: https://petstore3.swagger.io/api/v3/openapi.json" \
-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
Call a tool to verify end-to-end REST forwarding. The following example calls getPetById, which requires a path parameter petId. This verifies the gateway correctly interpolates MCP arguments into the REST URL path.
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" \
-H "x-openapi2mcp-base-url: https://petstore3.swagger.io" \
-H "x-openapi2mcp-openapi-spec: https://petstore3.swagger.io/api/v3/openapi.json" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "getPetById",
"arguments": {"petId": 1}
}
}'
Response (HTTP 200):
event: message
data: {
"result": {
"content": [
{
"type": "text",
"text": "{\"id\":1,\"name\":\"Cat 1\",\"photoUrls\":[],\"tags\":[],\"status\":\"available\"}"
}
]
},
"jsonrpc": "2.0",
"id": 4
}
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 upstream services.
- 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.
Next Steps
- Token-Based Rate Limiting and Quota Management — Control agent traffic and cost.
- AI Observability and Cost Tracking — Monitor tool usage and latency.
- For the full configuration reference, see
openapi-to-mcp.