Skip to content

Use the MCP server

Metergraph exposes a stdio MCP server and a stateless remote HTTP MCP endpoint. For deployment choices, credentials, client configuration, privacy and troubleshooting, start with Connect a coding agent.

This page is the tool reference. It keeps the tool names, arguments, response bounds, and content behavior in one place.

A zero-dependency bridge speaks newline-delimited JSON-RPC over stdio and forwards to the Agent Access HTTP API. The remote endpoint accepts the same JSON-RPC messages at /v1/agent/mcp, one message per request, so clients that support remote MCP do not need to run a local process. Both target MCP protocol revision 2025-11-25 and also accept 2025-03-26 and 2025-06-18. The bridge refuses redirects, so the bearer token cannot be walked to another origin, and both cap protocol messages at 1 MiB and API responses at 5 MiB.

METERGRAPH_URL must be an absolute HTTP or HTTPS URL with only a scheme, host and path. A query string, a fragment or embedded credentials are rejected at startup with exit code 2. The token is read from the process environment, never from a protocol message, and is never written to stdout or stderr.

Use metergraph_get_capabilities to discover what your server supports. The table below is a reference for server editions. Stdio requires a separate metergraph-mcp installation on the client machine; a Docker bundle does not install it there.

Local Staging Hosted BYOC core Self-hosted OSS
Transport stdio and remote HTTP remote HTTP remote HTTP stdio and remote HTTP stdio and remote HTTP
Credential Keys page coding-agent key, or the operator CLI for a local development tenant Keys page coding-agent key Keys page coding-agent key Keys page coding-agent key, or the operator CLI Static MG_AGENT_TOKENS bearer tokens set by the operator
Workspace context Yes Yes Yes Yes Yes
Capability discovery Yes Yes Yes Yes Yes
Routes Yes Yes Yes Yes Yes
Usage Yes Yes Yes Yes Yes
Trace metadata Yes Yes Yes Yes Yes
Ingestion health Yes Yes Yes Yes No
Incidents Yes Yes Yes Yes No
Reports Yes Yes Yes Yes No
Trace content Yes Yes Yes No No
Replay Yes Yes Yes No No

BYOC core includes the Agent Access API but withholds trace content and replay, because that profile does not run the debugging and provider paths. The self-hosted open-source server is content-blind by construction and has no ingest batches, detectors, or imported reports, so it advertises ingestion health, incidents, reports, trace content, and replay as unavailable. In every case, calling an unavailable tool returns the stable unsupported_capability error rather than an ambiguous failure.

These tools are the default content-free read surface:

  • metergraph_get_workspace_context: workspace identity, retention, scopes, and content policy.
  • metergraph_get_capabilities: deployment profile, available capabilities, tool availability, and response bounds.
  • metergraph_list_routes: route governance, constraints, and call counts.
  • metergraph_get_usage: bounded route/day usage with cost, tokens, latency, and error counts, optionally filtered by exact metadata.
  • metergraph_get_usage_aggregates: usage totals grouped by provider, model, route, UTC day or one metadata key, with whole-window totals.
  • metergraph_get_ingestion_health: pending, processed, failed, and classified ingestion batches with the latest receive/process timestamps.
  • metergraph_list_incidents: bounded tenant detector incidents.
  • metergraph_query_traces: bounded trace metadata, including models, providers, tokens, cost, latency, status, and environment provenance, newest first or as a spread sample across the window.

Every investigation response declares content_included: false where content could otherwise be ambiguous. Time windows are capped at 90 days and list responses at 200 rows. A truncated field tells the client when it hit a row bound.

The existing Dropzone debugging tools remain available for clients whose key is authorized for that work:

  • metergraph_get_trace returns the bounded trace debugging contract, including retained content when it exists, and requires the Debug role (agent:read).
  • metergraph_replay_trace runs one bounded, non-persistent replay and requires the Replay role (agent:read and agent:replay).

Metadata-role keys (agent:metadata) do not see these tools or metergraph_get_report_evidence in tools/list, and the server rejects calls to them.

Use capability discovery before calling either tool. The investigation surface is content-free by default; debugging content and replay are separate choices. Capability discovery reports each tool’s privacy_class and required_scope, and tool errors carry a stable error.code that clients can handle without matching message text.

Imported pipeline reports are available through four tools. The pipeline is never called live: the tools read the versioned report artifact that was imported into the workspace.

  • metergraph_list_reports: bounded report metadata, newest first, with the pipeline schema version, provenance, outcome, workload counts (including failed and ineligible workloads), opportunity count, evidence coverage, and the artifact’s SHA-256 and size.
  • metergraph_get_report: one report’s metadata plus safe per-workload projections (state, warnings, numeric opportunity summaries, evidence coverage). Model-authored text is never included. On a workload an evaluation suite gated, each opportunity’s evaluation_gate names the suite revision, the reasons with the checks they cite, and the candidate’s case counts.
  • metergraph_get_suite_results: one workload’s suite results, as the app’s Suite results page shows them: for each candidate, its outcome and reasons, and for each check its kind, pass rate or mean score, scored and eligible cases, counts per outcome, and representative case ids. It is metadata-class and returns no case text; each case id can be read with metergraph_get_report_evidence. A workload no suite gated returns available: false with unavailable_reason: "no_suite_gate".
  • metergraph_get_report_evidence: a separate content-class tool that pages the replay evidence for one workload. Each text field is bounded to 16 KiB. On a suite-gated workload, each case’s suite_examples lists the checks whose representative cases cite it.

HTTP clients can download the stored artifact from GET /v1/agent/reports/{analysis_id}/artifact with a Debug-role key. No MCP tool serves it. The download is capped at 32 MiB, sets Cache-Control: no-store, and returns the SHA-256 of the served body as the ETag. Larger artifacts return response_too_large.

Analysis-run and report responses carry links that open the same thing in the Metergraph app. Give these to a person as returned; do not build a link from an ID.

Tool Field Opens
metergraph_get_analysis_run, metergraph_list_analysis_runs, metergraph_start_analysis, metergraph_start_classification run.app_url The Analysis page, where the run’s progress and the latest report appear. The app has no page for a single run.
same run.report.app_url The run’s report. null until report.available is true.
metergraph_list_reports, metergraph_get_report app_url The Analysis page, which shows the workspace’s most recent report. For an older report, link its workloads and opportunities, which always open that report’s evidence.
metergraph_get_report workloads[].app_url The workload’s model comparison when the report holds one, otherwise the workload’s page.
metergraph_get_report workloads[].opportunities[].app_url The opportunity’s evidence. A switch withheld by the workload’s acceptance policy opens the workload’s suite results, where the app explains it.
metergraph_get_report_evidence metergraph_links.workload The workload’s page for that report.
metergraph_get_suite_results app_url The workload’s suite results in that report. null when no suite gated it.

Each link is built from the deployment’s configured public address, so it is correct on hosted, customer-local and customer-cloud installs alike. It also names its workspace: opened while signed in to a different workspace, the app says the link belongs to another workspace instead of showing that workspace’s data. A field is null when the deployment has no usable public address.

No arguments. Returns every route in the workspace with its description, constraints, evaluation contract, last update, total call count, and how many of those calls are eligible for replay.

The usage summary accepts days from 1 to 90, default 7, and limit from 1 to 200, default 100. It returns route/day aggregates only.

Argument Type Range
days integer 1 to 90, default 7
limit integer 1 to 200, default 100
metadata object of string to string 1 to 5 keys; see Metadata filters

items holds at most limit route/day rows. totals covers every matching call in the window, whatever the limit. Before presenting a figure as the whole answer, check two fields:

  • coverage.completeness is complete, partial or unknown, and coverage.reasons says why (for example row_limit, retention_clipped or ingestion_pending). coverage.requested_window and coverage.effective_window show what was asked and what was read.
  • pricing.cost is exact only when every call has a known price. zero_cost_calls are calls priced at zero; unpriced_calls have no price and unknown_calls cannot be told apart, so the cost is a lower bound.

Groups usage server-side by any of provider, model, route and day (UTC). Each group and the window totals carry cost in USD, calls, errors, input, output, cache and reasoning tokens, cost per call, and latency; each group also carries its pricing. Cost is attributed per call, so a trace that used two providers splits its cost between them. totals cover every matching call whatever limit is. As with metergraph_get_usage, check coverage.completeness and pricing.cost before presenting a figure as the whole answer; coverage.rows.matched reports how many groups matched. The latency p95 is estimated from a histogram of the group’s own calls, never a maximum of daily values. Calls without a provider or model appear as (unknown), and calls without a route as (unrouted).

Argument Type Range
group_by array distinct provider, model, route, day and at most one metadata.<key>; default provider, model
days integer 1 to 90, default 7: today and the previous whole UTC days
since, until ISO 8601 string an exact window of at most 90 days; not with days
route, provider, model array of strings 1 to 20 exact values
environment array of strings 1 to 20 environments
metadata object of string to string 1 to 5 keys; see Metadata filters
limit integer 1 to 200 groups, default 100

The same read is available over HTTP at GET /v1/agent/usage/aggregates.

group_by may include one metadata.<key> entry, for example metadata.prompt_id. Each group then carries that key’s exact string value under the same name. Everything after metadata. is the key, so a key may contain dots or commas. Over HTTP, send a metadata entry as its own group_by parameter, since only the built-in dimensions are split on commas.

Calls without an eligible value form the group whose value is null: the key is missing, its value is not a string, or the value is longer than 1,000 characters. That group carries their calls, cost and pricing like any other, so the groups always add up to totals. coverage.reasons adds metadata_key_missing and metadata_value_ineligible when such calls exist. These reasons explain the null group and never lower completeness.

When calls without a usable value hold at least half of the window’s calls, or at least half of its cost, the response also carries a metadata_key_mostly_missing warning. When no call carries the key, the warning says so outright: usually the key is misspelled or the SDK does not set it. completeness still says complete in that case, because every call was read and counted in the null group, so totals are exact. The warning is about how much of the window the per-value rows can explain, not about what was read.

metadata_groups.unlisted says where every call that is not in a listed row with a value went, from the same statement as the groups and totals. It has four buckets, and they never overlap:

  • other_values: groups past limit that do have a value, with a groups count.
  • missing_key: calls without the key.
  • ineligible_value: calls with a non-string or oversize value.

The listed rows with a value, plus these three buckets, add up to totals.calls. Truncation works as for the other dimensions. coverage.rows.matched counts the distinct values, including the null group. When groups were cut, coverage.completeness is partial with reason row_limit, and the truncated and coverage_incomplete warnings are set. Metadata reads always scan the raw calls, because the hourly rollups do not keep metadata.

A route and a metadata value are different things. One route usually carries several prompt IDs, so grouping by route and prompt shows how a route splits:

{"name": "metergraph_get_usage_aggregates",
"arguments": {"days": 7, "group_by": ["route", "metadata.prompt_id"]}}
{"group_by": ["route", "metadata.prompt_id"],
"items": [
{"route": "support-reply", "metadata.prompt_id": "reply-v3", "calls": 812, "cost_usd": 4.1},
{"route": "support-reply", "metadata.prompt_id": "reply-v2", "calls": 97, "cost_usd": 0.6},
{"route": "support-reply", "metadata.prompt_id": null, "calls": 40, "cost_usd": 0.2}
],
"coverage": {"rows": {"shape": "top_n", "returned": 3, "limit": 100,
"truncated": false, "matched": 3}, "totals_scope": "population",
"completeness": "complete", "reasons": ["metadata_key_missing"]},
"metadata_groups": {"key": "prompt_id", "unlisted": {
"other_values": {"groups": 0, "calls": 0, "cost_usd": 0},
"missing_key": {"calls": 40, "cost_usd": 0.2},
"ineligible_value": {"calls": 0, "cost_usd": 0}}}}

Returns bounded ingestion batch counts and the latest receive and process timestamps for the selected window.

Argument Type Range
days integer 1 to 90, default 7

Returns bounded detector incidents for the workspace.

Argument Type Range
limit integer 1 to 200, default 50

Recent traces, grouped by trace id, newest first. The optional workload filter matches a route or a saved analysis workload. The optional environment array selects one or more environments.

Argument Type Range
route string 1 to 512 characters
status string 1 to 128 characters
workload string 1 to 200 characters
environment string array 1 to 20 unique values, each 1 to 128 characters
metadata object of string to string 1 to 5 keys; see Metadata filters
cursor string opaque continuation cursor, up to 512 characters
days integer 1 to 90, default 7
limit integer 1 to 200, default 50
sample string spread

Anything else is rejected as an unknown argument. Each trace comes back with its name, first and last span times, input/output and cache token totals, cost, a success or error status, and the distinct routes, providers and models it touched.

A trace’s cost_usd is the total for the whole trace. It is null when no call in the trace has a price, and mixed_providers marks a trace that used more than one provider. A trace total cannot be split between providers, so use metergraph_get_usage for per-provider or per-route cost. coverage reports the unread next_cursor and any other reason a page is incomplete; a final page without a cursor can still be partial.

A route, status or request_id filter selects calls, not whole traces. Each returned trace then covers only its matching calls, and attribution.cost_basis is matching_calls instead of trace_total.

sample: "spread" returns traces drawn across the window instead of the newest. The window is split into limit equal time slices, at most 10, and each slice contributes the matching call nearest its middle, one trace per call. The same window and filters return the same traces. A sample takes no cursor, trace_id or request_id, and a limit above 10 is reduced to 10. coverage.rows.shape is sample, coverage.reasons includes sampled, and sample reports how many slices had a matching call.

To compare groups of traffic, draw one sample per group with that group’s filters, for example one metadata value or route each, then read the sampled traces’ prompts and responses with metergraph_get_trace (Debug role).

When a coding-agent key has an environment allowlist, broad queries are automatically limited to that allowlist. Requesting any other environment returns forbidden. The same boundary applies to metergraph_get_trace and workload detail lookups, so a known identifier does not bypass the filter.

An environment that is NULL, empty, or whitespace-only in stored telemetry is untagged. Restricted keys exclude untagged telemetry. Unrestricted keys keep their existing visibility, and responses represent an untagged, mixed, or unavailable environment as null. A filtered trace is returned only when all of its spans belong to the selected environment set.

Metadata is the string dictionary your code attaches with the SDK’s tags(), context(tags=...), withTags() or setTags(). It is stored as sent and read back as opaque strings. Metergraph does not split prefixes (a queue: value is just a string), fold case, trim whitespace, or infer a workflow or activity from a route name. metergraph_get_usage, metergraph_get_usage_aggregates and metergraph_query_traces accept metadata.

  • A filter is an exact, case-sensitive match of a key to a string value. p-1 does not match p-10, P-1 or p-1 .
  • Several keys are ANDed. A call must carry every pair.
  • A call without the key never matches. An empty string "" matches only a key that is present with an empty value.
  • A stored value that is not a string (for example a number sent through the raw ingest API) never matches a filter.
  • On traces, metadata selects spans the way route and status do, so a returned trace’s totals cover its matching spans.
  • Every response echoes the applied pairs, sorted by key, in coverage.filters.metadata. The list is empty when no filter was sent.
  • Filters compose with environment scope. A key restricted to some environments sees only matching calls inside them.

Keys and values travel as JSON strings, so =, :, /, quotes, commas and non-ASCII text need no escaping. Over REST, send metadata as one URL-encoded JSON object: GET /v1/agent/traces?metadata=%7B%22prompt_id%22%3A%22p-1%22%7D. A malformed object, a duplicate key or a non-string value returns 422. The bounds apply to the decoded keys and values, so escaped (\u4e2d) and unescaped spellings of the same object are treated alike. Very long values can still exceed a proxy’s URL length limit over HTTP; MCP has no such limit.

Bound Limit
Keys per request 5
Key length 1 to 200 characters
Value length 0 to 1,000 characters
Metadata group dimensions 1 per aggregate read
Groups listed limit, at most 200, with totals covering the rest

Metadata keys and values are metadata-class fields: any key with agent:metadata can filter and group by them, inside its environment scope. Keys that look like credentials are not eligible for filtering or grouping, because an exact match would let a caller test guesses and grouping would print the values. A key is refused when, lowercased with everything except letters and digits removed, it ends in accesskey, apikey, auth, authorization, bearer, clientsecret, cookie, credential, credentials, encryptionkey, jwt, passwd, password, privatekey, secret, secretkey, sessionkey, signingkey or token. Error messages never repeat a key or value you sent. Do not put secrets in tags.

Only native SDK capture fills metadata today. OTLP spans and provider imports do not map their own metadata dictionaries into it yet.

Argument Type Range
limit integer 1 to 200, default 50
Argument Type Range
analysis_id string 1 to 200 characters, required
Argument Type Range
analysis_id string 1 to 200 characters, required
workload_id string 1 to 200 characters, required
Argument Type Range
analysis_id string 1 to 200 characters, required
workload_id string 1 to 200 characters, required
cursor string opaque continuation cursor, up to 512 characters
limit integer 1 to 50, default 50

The response payload carries content_included: false, and that is not a promise, it is a description of the query: the endpoints behind these tools never select prompt text, response text, tool arguments or tool results. The debugging endpoint is intentionally separate from the investigation surface. Capability discovery tells the client whether retained content or replay is available for its current key and deployment.

So the investigation tools can reason about cost, latency, failure and coverage without reading your users’ data. Debugging content is a separate, explicit capability.

The Agent Access API is available in managed, local, and byoc-core profiles. The self-hosted open-source server exposes the same tool names with static MG_AGENT_TOKENS bearer tokens and advertises trace content, replay, incidents, ingestion health, and pipeline reports as unavailable, because it is content-blind by construction and has no detectors, ingest batches, or imported reports. Capability discovery is authoritative for a particular deployment. See Connect a coding agent for the full matrix.