Skip to content

Bound client-controlled tool/prompt name cardinality on per-request metrics #6169

Description

@glageju

mcp.server.operation.duration sets gen_ai.tool.name and gen_ai.prompt.name from parsedMCP.ResourceID, which is params.name verbatim from the client's JSON-RPC body (pkg/telemetry/middleware.go, recordOperationDuration). Neither is validated against the server's resolved tool or prompt set, so a client calling arbitrary names grows this metric's series count without bound.

The same pattern exists in vMCP's optimizer: stacklok.vmcp.optimizer.call_tool.{requests,duration} carry tool_name from input.ToolName (pkg/vmcp/server/sessionmanager/factory.go). The not_found outcome is direct evidence that unresolvable names reach that path and are still recorded.

mcp.method.name and http.request.method were bounded to the semconv _OTHER sentinel in #5956, so this is the remaining unbounded surface on per-request metrics. It matters more after #5956 because mcp.server.operation.duration becomes the sole per-method metric once the legacy twins are retired — there is no unaffected alternative to fall back on.

RFC §3.3 cardinality policy: never use an unbounded value as a label on a per-request metric.

Why it wasn't fixed in #5956

Bounding these requires the resolved tool/prompt set, which the telemetry middleware has no access to — it sees a parsed request, not the backend's capability list. Fixing it means either threading capability state into telemetry or resolving the name before the metric is recorded. That's a design change, not a one-line guard.

Interim mitigation (documented)

Drop the attributes with a Prometheus metric_relabel_config if untrusted clients can reach the proxy. See docs/observability.md → Cardinality warning.

Acceptance

  • No per-request metric carries a client-controlled tool/prompt name unbounded.
  • Unresolvable names record a bounded sentinel, with the raw value available on spans.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue needs initial triage by a maintainer

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions