Monitor MCP traffic with OpenTelemetry

TL;DR

To monitor MCP tool traffic, attach an OpenTelemetry Policy to an AI MCP Server entity. AI Gateway automatically sends metrics like tool call counts, response sizes, and request durations to your observability backend, with no code changes required.

This tutorial shows you how to attach the Policy using kongctl, generate some MCP traffic, and see the resulting metrics in a local OpenTelemetry Collector.

Prerequisites

This page is part of the Map a RESTful API to MCP tools and observe MCP traffic series.

Complete the previous page, Map a RESTful API to MCP tools before completing this page.

This tutorial uses kongctl to manage Konnect resources programmatically. We recommend keeping kongctl up to date with the latest version (1.13.0).

  1. Install kongctl from developer.konghq.com/kongctl.
  2. Verify the installation:

    kongctl version

Launch a local OpenTelemetry Collector in the background, listening on port 4318:

docker run -d \
  --name otel-collector \
  -p 127.0.0.1:4318:4318 \
  otel/opentelemetry-collector:0.141.0

Attach an OpenTelemetry Policy to the MCP Server entity

By default, an AI Policy applies to every resource on your AI Gateway. Setting global to false changes that: the otel-mcp Policy only takes effect on entities that explicitly list it, instead of applying to all entities.

The petstore-mcp entity does this by referencing otel-mcp in its policies list. As a result, every request that goes through petstore-mcp is measured and exported as metrics to the collector you started earlier. The service.name value under resource_attributes is a label attached to that exported data, so if you’re running multiple AI Gateways or services into the same collector, you can tell which one a given metric came from.

kongctl apply -f - --auto-approve --pat "$KONNECT_TOKEN" << 'EOF'
ai_gateway_policies:
  - ref: otel-mcp
    ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
    name: otel-mcp
    display_name: "otel-mcp"
    type: opentelemetry
    enabled: true
    global: false
    config:
      traces_endpoint: http://host.docker.internal:4318/v1/traces
      metrics:
        endpoint: http://host.docker.internal:4318/v1/metrics
        enable_ai_metrics: true
      resource_attributes:
        service.name: kong-mcp

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:
      - !ref otel-mcp#name
    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
EOF

Generate MCP traffic

Now, we can check the details of Dog 1 (id:4) 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=4 | jq -r '.content[0].text' | jq -c '.'

You should see the following response:

{"id":4,"category":{"id":1,"name":"Dogs"},"name":"Dog 1","photoUrls":["url1","url2"],"tags":[{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}],"status":"available"}

Validate metrics

Check the collector’s logs for kong.gen_ai.mcp to find the emitted metrics:

Allow a few seconds for the collector to export metrics after traffic generation.

docker logs otel-collector 2>&1 | grep -A 15 kong.gen_ai.mcp

You should see data like the following:

Metric #8
Descriptor:
     -> Name: kong.gen_ai.mcp.response.size
     -> Description: Size of AI MCP response body
     -> Unit: By
     -> DataType: Histogram
     -> AggregationTemporality: Cumulative
HistogramDataPoints #0
Data point attributes:
     -> kong.workspace.name: Str(default)
     -> kong.route.name: Str(petstore-mcp-route)
     -> mcp.method.name: Str(tools/call)
     -> gen_ai.tool.name: Str(get-pet-by-id)
     -> kong.service.name: Str(petstore-mcp)
Count: 1
Sum: 2175.000000

Metric #10
Descriptor:
     -> Name: mcp.server.operation.duration
     -> Description: MCP request/notification duration as observed on the receiver
     -> Unit: s
     -> DataType: Histogram
     -> AggregationTemporality: Cumulative
HistogramDataPoints #3
Data point attributes:
     -> gen_ai.operation.name: Str(execute_tool)
     -> kong.workspace.name: Str(default)
     -> kong.route.name: Str(petstore-mcp-route)
     -> mcp.method.name: Str(tools/call)
     -> gen_ai.tool.name: Str(get-pet-by-id)
     -> kong.service.name: Str(petstore-mcp)
Count: 1
Sum: 0.037000

kong.route.name carries a -route suffix because AI Gateway auto-generates a Route for the MCP Server entity.

See MCP metrics for the full metric reference.

MCP Metrics in Konnect

You can also view MCP traffic metrics without setting up a collector, using Konnect Analytics:

  1. Go to Observability > Dashboards.
  2. Click Create dashboard > Create from template.
  3. Select the Agentic analytics dashboard. This dashboard highlights which tools are called most frequently, breaks down tool usage by consumer, and tracks average latency per tool over time, helping teams operating MCP-enabled services understand usage patterns and identify performance bottlenecks.
  4. Click Use template to see MCP tool usage, total MCP requests, total MCP errors, and other statistics.

Cleanup

docker rm -f otel-collector
docker rm -f swagger-petstore

To clean up all AI Gateway resources created in this guide, run:

curl -Ls https://get.konghq.com/ai | bash -s -- -d

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!