npx -y @modelcontextprotocol/inspector@0.22.0 --cli \
http://localhost:8000/petstore \
--transport http --method tools/list | jq -r '.tools[].name'Map a RESTful API to MCP tools
Create an AI MCP Server entity in AI Gateway, and it automatically converts your REST API endpoints into MCP tools that any MCP-compatible AI assistant can call, no custom server code required.
This tutorial shows you how to create an AI MCP Server entity using kongctl to expose a REST API as MCP tools, and how to call those tools from an MCP client.
Prerequisites
AI Gateway running
This is a Konnect tutorial and requires a Konnect personal access token.
-
Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
-
Export your token to an environment variable:
export KONNECT_TOKEN='YOUR_KONNECT_PAT' -
Run the AI Gateway quickstart script to automatically provision a control plane and data plane in Kong Konnect, and configure your environment:
curl -Ls https://get.konghq.com/ai | bash -s -- -k $KONNECT_TOKEN \ -e KONG_TRACING_INSTRUMENTATIONS \ -e KONG_TRACING_SAMPLING_RATE
This sets up a AI Gateway control plane named ai-quickstart, provisions a local data plane, and prints out the following environment variables export:
export AI_GATEWAY_ID=your-gateway-id
export KONNECT_TOKEN=$KONNECT_TOKEN
export KONNECT_CONTROL_PLANE_NAME=ai-quickstart
export KONNECT_CONTROL_PLANE_URL=https://us.api.konghq.com
export KONNECT_PROXY_URL='http://localhost:8000'Copy and paste these into your terminal to configure your session.
kongctl v1.13.0+
This tutorial uses kongctl to manage Konnect resources programmatically. We recommend keeping kongctl up to date with the latest version (1.13.0).
- Install kongctl from developer.konghq.com/kongctl.
-
Verify the installation:
kongctl version
Run a sample API to expose
Before creating an MCP Server entity, you’ll need an upstream HTTP API to expose. For this tutorial, use the Swagger Petstore sample API running in Docker. This allows you to test the entity without relying on an external service. The Petstore API comes pre-loaded with 10 pets across the available, pending, and sold statuses, which the API exposes through the /pet/findByStatus and /pet/{petId} endpoints.
docker run -d \
--name swagger-petstore \
-p 8080:8080 \
swaggerapi/petstore3:latestThe MCP Server entity will convert this API’s endpoints into MCP tool definitions.
Create an MCP Server entity
With the Petstore API running, create an MCP Server entity configured as a conversion-listener to expose the endpoints /pet/findByStatus and /pet/{petId} as MCP tools.
The following example maps the Petstore API operations to MCP tool definitions that the client can invoke.
kongctl apply -f - --auto-approve --pat "$KONNECT_TOKEN" << 'EOF'
ai_gateway_mcp_servers:
- ref: petstore-mcp
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
name: petstore-mcp
display_name: "Petstore API"
type: conversion-listener
enabled: true
policies: []
access:
acl_attribute_type: consumer
acls:
allow: []
default_tool_acls:
deny: []
config:
url: http://host.docker.internal:8080/api/v3
route:
paths:
- /petstore
logging:
payloads: false
server:
timeout: 60000
tools:
- name: get-pets-by-status
description: Find pets by status
method: GET
path: /petstore/pet/findByStatus
parameters:
- name: status
in: query
required: true
schema:
type: string
enum:
- available
- pending
- sold
description: Status value to filter pets by
- name: get-pet-by-id
description: Get a pet by ID
method: GET
path: /petstore/pet/{petId}
parameters:
- description: ID of the pet to retrieve
in: path
name: petId
required: true
schema:
type: integer
EOFTwo details of this configuration are worth calling out:
-
Route stripping:
route.paths(/petstore) is the prefix clients must hit for the AI Gateway to match this entity, so every tool’spathincludes it. Before forwarding upstream, the gateway strips that prefix and appends what’s left (for example,/pet/findByStatus) toconfig.url. -
Parameters: AI Gateway builds each tool’s input schema from its
parameterslist, prefixing every parameter name with itsinlocation:path_<name>for path parameters,query_<name>for query parameters.
Verify that the endpoints are available as tools
Use MCP Inspector CLI to verify that the MCP server exposes get-pet-by-id and get-pets-by-status as tools:
You should see the following output:
get-pet-by-id
get-pets-by-statusValidate the configuration
Let’s call the get-pets-by-status tool to see which pets are available:
npx -y @modelcontextprotocol/inspector@0.22.0 --cli \
http://localhost:8000/petstore \
--transport http --method tools/call \
--tool-name get-pets-by-status \
--tool-arg query_status=available | jq -r '.content[0].text' | jq -c '.[]'You should see the following output:
{"id":1,"category":{"id":2,"name":"Cats"},"name":"Cat 1","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}],"status":"available"}
{"id":2,"category":{"id":2,"name":"Cats"},"name":"Cat 2","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag2"},{"id":2,"name":"tag3"}],"status":"available"}
{"id":4,"category":{"id":1,"name":"Dogs"},"name":"Dog 1","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}],"status":"available"}
{"id":7,"category":{"id":4,"name":"Lions"},"name":"Lion 1","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}],"status":"available"}
{"id":8,"category":{"id":4,"name":"Lions"},"name":"Lion 2","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag2"},{"id":2,"name":"tag3"}],"status":"available"}
{"id":9,"category":{"id":4,"name":"Lions"},"name":"Lion 3","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag3"},{"id":2,"name":"tag4"}],"status":"available"}
{"id":10,"category":{"id":3,"name":"Rabbits"},"name":"Rabbit 1","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag3"},{"id":2,"name":"tag4"}],"status":"available"}Now, we can check the details of Lion 1 - id:7 - by calling the get-pet-by-id tool:
npx -y @modelcontextprotocol/inspector@0.22.0 --cli \
http://localhost:8000/petstore \
--transport http --method tools/call \
--tool-name get-pet-by-id \
--tool-arg path_petId=7 | jq -r '.content[0].text' | jq -c '.'You should see the following response:
{"id":7,"category":{"id":4,"name":"Lions"},"name":"Lion 1","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}],"status":"available"}You can validate this result against the Swagger Petstore API source.