Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions app/_config/releases/ai-gateway/v1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ app/_how-tos/ai-gateway/v1/mcp/map-API-to-mcp-tools.md:
app/_how-tos/ai-gateway/v1/mcp/map-weather-api-to-mcp-tools.md:
canonical_url: /ai-gateway/get-started-with-mcp-server/
app/_how-tos/ai-gateway/v1/mcp/observe-autogenerated-mcp-tools-for-weather-api.md:
status: pending
canonical_url: /ai-gateway/mcp/
canonical_url: /ai-gateway/monitor-mcp-traffic-with-http-log/
app/_how-tos/ai-gateway/v1/mcp/observe-mcp-traffic-with-acls.md:
status: pending
canonical_url: /ai-gateway/mcp/
Expand Down
373 changes: 373 additions & 0 deletions app/_how-tos/ai-gateway/monitor-mcp-traffic-with-http-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,373 @@
---
title: Monitor MCP traffic with the HTTP Log plugin
content_type: how_to
related_resources:
- text: "{{site.ai_gateway}}"
url: /ai-gateway/
- text: AI MCP Server entity
url: /ai-gateway/entities/ai-mcp-server/
- text: HTTP Log
url: /ai-gateway/policies/http-log/

description: |
Create an AI MCP from a mock API using `conversion-listener` mode, then use the HTTP Log Policy to record and inspect the payloads and responses from the example MCP.

products:
- ai-gateway

permalink: /ai-gateway/monitor-mcp-traffic-with-http-log/

series:
id: mcp-conversion-2-0
position: 2

works_on:
- konnect

min_version:
ai-gateway: '2.0'

tags:
- ai
- mcp

tldr:
q: How do I observe and validate MCP traffic from autogenerated tools?
a: |
Use an AI MCP entity to expose a mock API as an MCP tool, then use the HTTP Log Policy to capture tool calls and validate their payloads and responses.

tools:
- kongctl

cleanup:
inline:
- title: Destroy the {{site.base_gateway}} container
include_content: cleanup/products/gateway
icon_url: /assets/icons/gateway.svg
- title: Clean up {{site.konnect_short_name}} environment
include_content: cleanup/platform/konnect
icon_url: /assets/icons/gateway.svg

automated_tests: false

---

## Reconfigure the AI MCP Proxy plugin

To observe traffic for MCP tools, you first must **enable logging and statistics** on the AI MCP. Apply the below configuration to reconfigure the AI MCP to log full payloads:

```sh
kongctl apply -f - --auto-approve --pat "$KONNECT_TOKEN" <<EOF
ai_gateway_mcp_servers:
- ref: marketplace-mcp
ai_gateway: !lookup {id: $AI_GATEWAY_ID}
name: marketplace-mcp
display_name: "Marketplace API"
type: conversion-listener
enabled: true
policies: []
access:
acl_attribute_type: consumer
acls:
allow: []
default_tool_acls:
deny: []
config:
url: http://host.docker.internal:3000
route:
paths:
- /marketplace
logging:
payloads: true
statistics: true
server:
timeout: 60000
tools:
- name: get-users
description: Get users
method: GET
path: /marketplace/users
parameters:
- name: id
in: query
required: false
schema:
type: string
description: Optional user ID
- name: get-orders-for-user
description: Get orders for a user
method: GET
path: /marketplace/orders
parameters:
- description: User ID to filter orders
in: query
name: userid
required: true
schema:
type: string
EOF
```


## Log MCP traffic

Before we send tool calls, we need to set up the a HTTP Logs Policy to check how many tokens we've managed to save by using our configuration.

Check failure on line 114 in app/_how-tos/ai-gateway/monitor-mcp-traffic-with-http-log.md

View workflow job for this annotation

GitHub Actions / Vale

[vale] reported by reviewdog 🐶 [base.Terms] Use 'an HTTP' instead of 'a HTTP'. Raw Output: {"message": "[base.Terms] Use 'an HTTP' instead of 'a HTTP'.", "location": {"path": "app/_how-tos/ai-gateway/monitor-mcp-traffic-with-http-log.md", "range": {"start": {"line": 114, "column": 50}}}, "severity": "ERROR"}

Apply the below config to create an HTTP logs Policy and attach it to the AI MCP entity:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbaross - can we just use the file log policy instead of this script? It makes this flow much simpler

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be an entirely separate how-to, using the http-log is the point of this one


```sh
kongctl apply -f - --auto-approve --pat "$KONNECT_TOKEN" <<EOF
ai_gateway_policies:
- ref: my-http-log-policy
name: my-http-log-policy
ai_gateway: !lookup {id: $AI_GATEWAY_ID}
type: http-log
enabled: true
global: false
config:
http_endpoint: http://host.docker.internal:9999/
headers:
Authorization: Bearer some-token
method: POST
timeout: 3000

ai_gateway_mcp_servers:
- ref: marketplace-mcp
ai_gateway: !lookup {id: $AI_GATEWAY_ID}
name: marketplace-mcp
display_name: "Marketplace API"
type: conversion-listener
enabled: true
policies: [ !ref my-http-log-policy#name ]
access:
acl_attribute_type: consumer
acls:
allow: []
default_tool_acls:
deny: []
config:
url: http://host.docker.internal:3000
route:
paths:
- /marketplace
logging:
payloads: true
statistics: true
server:
timeout: 60000
tools:
- name: get-users
description: Get users
method: GET
path: /marketplace/users
parameters:
- name: id
in: query
required: false
schema:
type: string
description: Optional user ID
- name: get-orders-for-user
description: Get orders for a user
method: GET
path: /marketplace/orders
parameters:
- description: User ID to filter orders
in: query
name: userid
required: true
schema:
type: string
EOF
```

Let's run a simple log collector script which collect logs at `9999` port. Copy and run this snippet in your terminal:

```
cat <<EOF > log_server.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import datetime

LOG_FILE = "kong_logs.txt"

class LogHandler(BaseHTTPRequestHandler):
def do_POST(self):
timestamp = datetime.datetime.now().isoformat()

content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length).decode('utf-8')

log_entry = f"{timestamp} - {post_data}\n"
with open(LOG_FILE, "a") as f:
f.write(log_entry)

print("="*60)
print(f"Received POST request at {timestamp}")
print(f"Path: {self.path}")
print("Headers:")
for header, value in self.headers.items():
print(f" {header}: {value}")
print("Body:")
print(post_data)
print("="*60)

# Send OK response
self.send_response(200)
self.end_headers()
self.wfile.write(b"OK")

if __name__ == '__main__':
server_address = ('', 9999)
httpd = HTTPServer(server_address, LogHandler)
print("Starting log server on http://0.0.0.0:9999")
httpd.serve_forever()
EOF
```

Now, run this script with Python and UV:

```sh
uv run log_server.py
```

If the script is successful, you'll receive the following prompt in your terminal:

```sh
Starting log server on http://0.0.0.0:9999
```

## Validate MCP tools configuration

You can validate that the HTTP Logs plugin is collecting metrics by generating MCP traffic to the mock API server. Enter the following question in Chatwise:

```text
what has bob ordered ?
```

Once Chatwise has finished reasoning, you will see MCP audit log entries logged by the HTTP plugin in your terminal similar to the following:


```sh
Received POST request at 2026-07-31T15:56:28.232507
Path: /
Headers:
Content-Length: 5145
Authorization: Bearer some-token
Content-Type: application/json
Host: host.docker.internal:9999
User-Agent: lua-resty-http/0.17.2 (Lua) ngx_lua/10028
Body:
{
"upstream_uri": "/",
"latencies": {
"redis": 0,
"socket": 0,
"request": 15,
"kong": 14,
"proxy": -1,
"third_party": 5.936128,
"client": 0.7616,
"receive": 0,
"http_client": 5.936128,
"kong_internal": 8.50176,
"dns": 0
},
"request": {
"headers": {
"mcp-protocol-version": "2025-11-25",
"content-length": "126",
"accept": "application/json, text/event-stream",
"connection": "keep-alive",
"accept-language": "en-GB",
"mcp-session-id": "8b2d1534-3ef9-4708-9baf-5cf36b4f5b1f",
"content-type": "application/json",
"host": "localhost:8000",
"user-agent": "ChatWise/26.7.4"
},
"size": 483,
"method": "POST",
"url": "http://localhost:8000/marketplace",
"id": "cc054f73a6b04f3573cdf02883d92f53",
"uri": "/marketplace",
"querystring": {}
},
"route": {
"service": {
"id": "c16f08e6-4cfe-5c39-8627-f744f091e592"
},
"regex_priority": 0,
"preserve_host": false,
"protocols": [
"http",
"https"
],
"path_handling": "v0",
"name": "marketplace-mcp-route",
"id": "a878697a-8bbc-591b-8640-e1bd5aba261c",
"ws_id": "17dadd2f-94c5-4ba4-86bb-8d2d4e6e22cd",
"paths": [
"/marketplace"
],
"updated_at": 1785509686,
"strip_path": true,
"https_redirect_status_code": 426,
"response_buffering": true,
"request_buffering": true,
"created_at": 1785509686
},
"started_at": 1785509788214,
"client_ip": "192.168.165.1",
"workspace": "17dadd2f-94c5-4ba4-86bb-8d2d4e6e22cd",
"workspace_name": "default",
"ai": {
"mcp": {
"mcp_session_id": "8b2d1534-3ef9-4708-9baf-5cf36b4f5b1f",
"rpc": [
{
"id": "2",
"method": "tools/call",
"response_body_size": 2175,
"tool_name": "get-orders-for-user",
"latency": 15,
"payload": {
"request": "{\"id\":2,\"params\":{\"name\":\"get-orders-for-user\",\"arguments\":{\"query_userid\":\"e5f6g7h8\"}},\"jsonrpc\":\"2.0\",\"method\":\"tools/call\"}",
"response": "{\"id\":2,\"jsonrpc\":\"2.0\",\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"[{\\\"id\\\":\\\"ord001\\\",\\\"name\\\":\\\"Sugar (50kg)\\\",\\\"userId\\\":\\\"a1b2c3d4\\\"},{\\\"id\\\":\\\"ord002\\\",\\\"name\\\":\\\"Cleaning Supplies Pack\\\",\\\"userId\\\":\\\"a1b2c3d4\\\"},{\\\"id\\\":\\\"ord003\\\",\\\"name\\\":\\\"Canned Tomatoes (100 cans)\\\",\\\"userId\\\":\\\"a1b2c3d4\\\"},{\\\"id\\\":\\\"ord004\\\",\\\"name\\\":\\\"Flour (100kg)\\\",\\\"userId\\\":\\\"e5f6g7h8\\\"},{\\\"id\\\":\\\"ord005\\\",\\\"name\\\":\\\"Dish Soap (10 bottles)\\\",\\\"userId\\\":\\\"e5f6g7h8\\\"},{\\\"id\\\":\\\"ord006\\\",\\\"name\\\":\\\"Salt (25kg)\\\",\\\"userId\\\":\\\"e5f6g7h8\\\"},{\\\"id\\\":\\\"ord007\\\",\\\"name\\\":\\\"Olive Oil (20L)\\\",\\\"userId\\\":\\\"i9j0k1l2\\\"},{\\\"id\\\":\\\"ord008\\\",\\\"name\\\":\\\"Baking Powder (10kg)\\\",\\\"userId\\\":\\\"i9j0k1l2\\\"},{\\\"id\\\":\\\"ord009\\\",\\\"name\\\":\\\"Rice (200kg)\\\",\\\"userId\\\":\\\"m3n4o5p6\\\"},{\\\"id\\\":\\\"ord010\\\",\\\"name\\\":\\\"Vegetable Oil (15L)\\\",\\\"userId\\\":\\\"m3n4o5p6\\\"},{\\\"id\\\":\\\"ord011\\\",\\\"name\\\":\\\"Pasta (80kg)\\\",\\\"userId\\\":\\\"m3n4o5p6\\\"},{\\\"id\\\":\\\"ord012\\\",\\\"name\\\":\\\"Canned Beans (50 cans)\\\",\\\"userId\\\":\\\"m3n4o5p6\\\"},{\\\"id\\\":\\\"ord013\\\",\\\"name\\\":\\\"Toilet Paper (Case of 48)\\\",\\\"userId\\\":\\\"q7r8s9t0\\\"},{\\\"id\\\":\\\"ord014\\\",\\\"name\\\":\\\"Hand Sanitizer (20 bottles)\\\",\\\"userId\\\":\\\"q7r8s9t0\\\"},{\\\"id\\\":\\\"ord015\\\",\\\"name\\\":\\\"Laundry Detergent (10L)\\\",\\\"userId\\\":\\\"u1v2w3x4\\\"},{\\\"id\\\":\\\"ord016\\\",\\\"name\\\":\\\"Trash Bags (100 ct)\\\",\\\"userId\\\":\\\"u1v2w3x4\\\"},{\\\"id\\\":\\\"ord017\\\",\\\"name\\\":\\\"Disinfectant Spray (5 bottles)\\\",\\\"userId\\\":\\\"u1v2w3x4\\\"},{\\\"id\\\":\\\"ord018\\\",\\\"name\\\":\\\"Coffee Beans (30kg)\\\",\\\"userId\\\":\\\"k7l8m9n0\\\"},{\\\"id\\\":\\\"ord019\\\",\\\"name\\\":\\\"Tea Bags (500ct)\\\",\\\"userId\\\":\\\"k7l8m9n0\\\"},{\\\"id\\\":\\\"ord020\\\",\\\"name\\\":\\\"Condensed Milk (40 cans)\\\",\\\"userId\\\":\\\"k7l8m9n0\\\"},{\\\"id\\\":\\\"ord021\\\",\\\"name\\\":\\\"Paper Towels (24 rolls)\\\",\\\"userId\\\":\\\"g3h4i5j6\\\"},{\\\"id\\\":\\\"ord022\\\",\\\"name\\\":\\\"Broom & Mop Set\\\",\\\"userId\\\":\\\"g3h4i5j6\\\"},{\\\"id\\\":\\\"ord023\\\",\\\"name\\\":\\\"Cereal (20 boxes)\\\",\\\"userId\\\":\\\"c9d0e1f2\\\"},{\\\"id\\\":\\\"ord024\\\",\\\"name\\\":\\\"Powdered Milk (10kg)\\\",\\\"userId\\\":\\\"c9d0e1f2\\\"},{\\\"id\\\":\\\"ord025\\\",\\\"name\\\":\\\"Snacks Variety Pack\\\",\\\"userId\\\":\\\"c9d0e1f2\\\"},{\\\"id\\\":\\\"ord026\\\",\\\"name\\\":\\\"Cooking Gas Cylinder\\\",\\\"userId\\\":\\\"y5z6a7b8\\\"},{\\\"id\\\":\\\"ord027\\\",\\\"name\\\":\\\"Napkins (1000ct)\\\",\\\"userId\\\":\\\"y5z6a7b8\\\"}]\"}],\"isError\":false}}"
}
}
]
}
},
"tries": [],
"response": {
"status": 200,
"headers": {
"connection": "close",
"cache-control": "no-store, no-transform",
"transfer-encoding": "chunked",
"x-kong-request-id": "cc054f73a6b04f3573cdf02883d92f53",
"server": "kong/2.0.1-ai-gateway",
"content-type": "text/event-stream",
"x-kong-response-latency": "14"
},
"size": 2503
},
"service": {
"name": "marketplace-mcp",
"ws_id": "17dadd2f-94c5-4ba4-86bb-8d2d4e6e22cd",
"host": "host.docker.internal",
"write_timeout": 60000,
"connect_timeout": 60000,
"updated_at": 1785509686,
"read_timeout": 60000,
"retries": 5,
"id": "c16f08e6-4cfe-5c39-8627-f744f091e592",
"created_at": 1785509686,
"port": 3000,
"enabled": true,
"protocol": "http"
},
"source": "kong",
"upstream_status": ""
}

```
Loading