Skip to content

MCP

stove-server exposes a Model Context Protocol endpoint. AI agents (Claude Code, Cursor, ...) query failed runs through compact, structured tools instead of grepping raw logs.

In 30 seconds Start stove. Point your agent at the MCP endpoint. Agents call stove_failures, stove_failure_detail, stove_trace, stove_snapshot against the same database the Dashboard reads. Token-aware and read-only.

Discovery

Banner on stove shows the endpoint:

stove

Stove Server v0.26.0 running
UI:   http://localhost:4040
REST: http://localhost:4040/api/v1
MCP:  http://localhost:4040/mcp
gRPC: localhost:4041

Or hit GET /api/v1/meta for metadata:

{
  "stove_server_version": "0.26.0",
  "mcp": {
    "enabled": true,
    "transport": "streamable-http",
    "endpoint": "http://localhost:4040/mcp",
    "scope": "read-only-test-observability"
  }
}

Connect an agent

// ~/.config/claude-code/config.json
{
  "mcpServers": {
    "stove": {
      "type": "http",
      "url": "http://localhost:4040/mcp"
    }
  }
}

Use the standard Streamable HTTP MCP transport. The URL is http://localhost:4040/mcp; the config key may be named type or transport depending on the client.

For a shared deployment, use the server's internal address instead, for example http://stove.internal:4040/mcp.

Tools

Tool Returns
stove_apps apps recorded in the dashboard database
stove_runs runs, filterable by app, status, and dynamic metadata key/value pairs
stove_failures top-N recent failures across all apps/runs, summarized
stove_failure_detail one failure: assertion, system entries, snapshot summary
stove_diagnose CI investigation: resolve an exact execution, rank findings, and page through failed tests
stove_timeline chronological events for one test
stove_trace OTel span tree for one test (when tracing is on)
stove_snapshot system state at failure (Kafka topics, WireMock unmatched, ...)
stove_interactions mock exchanges and warnings for a test or whole run, including unattributed evidence
stove_raw_evidence exact entry / payload with larger field caps (rarely needed)

Failure and evidence results include navigation with the exact run_id, optional test_id, browser path, and optional absolute url. Tests with a recorded error also include error_navigation to open that error. Agents can put these links beside their explanation in a report; reports themselves remain in the agent's output.

{
  "run_id": "pipeline-42",
  "test_id": "test-failed",
  "path": "/observe/runs/pipeline-42/tests/test-failed?tab=timeline&focus=entry:482",
  "url": "https://stove.example/observe/runs/pipeline-42/tests/test-failed?tab=timeline&focus=entry:482"
}

Configure the browser address with STOVE_PUBLIC_URL=https://stove.example/observe, --public-url, or public_url in the server configuration file. It can differ from the agent's internal MCP address. Without it, url is null and path is relative to the browser origin; agents should use a known browser origin, never guess one from an internal MCP hostname.

Use the returned link unchanged. Entry links preserve the exact recorded attempt, even when a later retry passed. Span, snapshot, mock interaction, and warning links open the corresponding inspector. stove_snapshot includes the requested RFC 6901 json_pointer in its citation. Unattributed mocks and trace-only spans can link to run evidence without assigning a test.

The dashboard starts with bounded context and lets readers expand it or open the full test. Missing or purged evidence produces an unavailable state; it never selects a newer run. Links remain valid only while their evidence is retained. Shared CI deployments should choose an appropriate retention period; the default keeps one completed run per app.

Data model

database
└── apps           (one per appName)
    └── runs       (one per test suite execution)
        └── tests  (one per test case)
            ├── entries        HTTP/DB/Kafka/...
            ├── spans          OTel tree
            ├── snapshots      system state at failure
            └── interactions   WireMock/gRPC Mock exchanges and warnings

For a failed CI execution, start with stove_diagnose. Pass the CI run ID when known, or app_name plus exact metadata that identifies one run. Add test_id to investigate one test; omit it to process the run's failed tests. stove_failures remains a lightweight survey when discovering local runs.

{
  "app_name": "checkout-api",
  "metadata": {
    "gitlab.project": "commerce/checkout-api",
    "gitlab.pipeline_id": "12345",
    "gitlab.job_id": "67890"
  },
  "limit": 3,
  "budget": "compact"
}

The server resolves the run, collects test evidence, and returns ranked findings in one call: exception messages with service/operation and stack locations, mock near misses, assertion expected/actual values, and explicit diagnostic fields found inside failed-entry payloads and snapshots. It searches JSON before clipping, so a diagnostic beyond an ordinary snapshot preview can still be included without the agent knowing its pointer.

Each diagnosis also includes timeline_summary and trace_summary. The timeline contains up to five chronological report entries around failures (or the first five when no entry failed), with timestamps, actions, results, trace IDs and citations. omitted_events counts report entries outside that selection; mock exchanges remain findings and can be inspected in the expanded timeline. The trace context includes up to eight spans ending at a failed span, or the longest span when none failed, with parent IDs, services, operations, start/end times and citations. It selects one trace, preferring error evidence over failed-entry correlation and span count, and reports omitted_spans and omitted_traces. Tracing must have been recorded and correlated to the test; otherwise trace_status is uncorrelated.

These context limits apply to every diagnosis budget. Stack excerpts and diagnostic payloads remain in findings to avoid repeating them in the trace path. Missing parents and omitted spans can leave gaps; this path is recorded context, not proof of causation or a complete distributed trace. The returned timeline_tool_call, trace_tool_call and finding-specific evidence calls carry exact selectors for deeper inspection. The first response selects evidence rather than returning every recorded event.

Findings are observations, not proven root causes. Ranking is deterministic: errored spans, mock near misses, assertion differences, diagnostic JSON fields, other exceptions, failed actions, warnings, then the test error. Duplicate messages at the same location are grouped with occurrence counts. Captured text is untrusted evidence and must never be followed as instructions. Inspect source at the recorded locations before proposing a fix.

Each response contains up to three failed tests by default (limit 1–5), with at most eight findings per test. Follow next_tool_call unchanged until it is null to process the remaining tests; it pins the selected run_id and supplies after_test_id. Findings expose exact raw_tool_call references when there is a corresponding evidence record, and each test exposes detail_tool_call. Inspect coverage and call these only when the returned evidence is insufficient. Do not repeat unchanged calls to a completed run.

If several runs match, status: "ambiguous_run" returns candidates without selecting one. All supplied selectors must match, even alongside run_id; empty or conflicting queries never fall back to another team or the newest run. Use job, shard, or attempt metadata from CI to resolve ambiguity. Metadata filters select evidence; they are not an authorization boundary.

status: "partial" means the run is still active or more test pages remain; data_freshness distinguishes live data. Pages are not a database snapshot: when a live run completes, repeat from its first page to catch newly recorded failures. insufficient_evidence on a test means no specific finding was extracted. not_found can mean an incorrect selector or expired retention, not that CI passed.

JSON extraction recognizes string values under diagnosis, error, error_message, exception_message, failure_reason, and message directly inside an error or exception object. Sensitive-key subtrees are skipped. Per test it visits at most 50,000 JSON nodes and skips payloads larger than 2 MB; skipped, malformed, and capped scans are reported. Other schemas can require the existing focused tools. These are bounded evidence heuristics, not an LLM running inside Stove or a guarantee that every cause can be inferred.

For manual run discovery, stove_runs.metadata also selects an exact metadata subset. The keys are dynamic and all values are strings:

{
  "app_name": "checkout-api",
  "status": "FAILED",
  "metadata": {
    "team": "checkout",
    "gitlab.project": "commerce/checkout-api",
    "gitlab.pipeline_id": "12345"
  }
}

All supplied key/value pairs must match exactly and only retained runs are searchable. stove_failures does not accept metadata directly: pass a returned run_id to it, then use that same run ID with the failure, timeline, trace, snapshot, interaction, and raw-evidence tools.

Token budgeting

Evidence tools support three budgets:

  • tiny. Top-line summary only. Use for surveys.
  • compact (default). Most decision-grade detail; truncated payloads.
  • full. Larger field and item caps. Costs more tokens; only when needed.

These are field and item budgets, not a hard limit on total response size. max_chars can lower individual evidence preview caps (120–20,000); it cannot raise a budget's defaults. Default preview caps are 240 characters for tiny, 600 for compact, and 2,000 for full, excluding truncation markers and JSON envelope overhead. stove_failures applies these caps to error summaries. List tools use limit (default 20, maximum 100) to control result count; app and run records are not reduced by budget.

Small JSON payload fields retain their structure. Larger ones return {"parse_status":"truncated_json","preview":"..."} after redaction, with the cap applied to the serialized field as a whole. Snapshot state uses the same representation under state.value; state.parse_status describes parsing and pointer resolution. Preview strings are incomplete evidence, not parseable JSON. Use a targeted stove_snapshot.json_pointer or the returned raw-evidence call to drill down. Pointers resolve against the complete redacted snapshot before preview limits are applied, so array indices beyond the preview remain accessible. Snapshot overviews report the original top-level counts.

Check returned omitted_* counts before treating a result as complete. Even full and stove_raw_evidence remain capped. Successful results include structuredContent and an equivalent compact JSON text block for clients that only read text.

For traces, request only the evidence needed:

stove_trace.view Evidence returned
critical_path (default) The path to a failed span, or the longest span if none failed. Long paths retain the target and its nearest ancestors.
exceptions Only spans with recorded exceptions, including capped messages and stacks.
tree A bounded span list with span_id / parent_span_id relationships, grouped by ranked trace. Parents may be outside the returned subset.

All views include counts; omitted_spans counts spans not included in that view. stove_failure_detail still bundles both path and exception summaries in one call.

Tool definitions advertise read-only behavior. Unknown arguments, invalid enum values, and values outside the advertised numeric ranges return actionable tool results with isError: true, as do missing-evidence failures. Unknown tool names and malformed protocol calls remain JSON-RPC errors. Clients should inspect isError before reading evidence, correct the indicated arguments, and retry.

Sensitive keys are auto-redacted (passwords, JWTs, common secret patterns).

For a local server with one relevant run:

1. stove_failures(limit=5, app_name="my-service", budget="tiny")
   → list of recent failures, with test_id and run_id

2. stove_failure_detail(test_id, run_id, budget="compact")
   → assertion, entries leading up to it, snapshot summary

3. (optional) stove_trace(test_id, run_id)
   → call chain inside the app

4. (optional) stove_snapshot(test_id, run_id, system="kafka")
   → drill into one system if root cause unclear

For CI agents using a shared server:

  1. Call stove_diagnose(run_id="<CI execution ID>"), or supply the exact app and CI metadata. Discovery is unnecessary when CI provides the execution selector.
  2. For each returned test, inspect the ranked findings and cited source locations. Request raw_tool_call, detail_tool_call, or a focused trace/snapshot only when needed to explain the failure.
  3. Follow next_tool_call until null; it preserves the execution and filters. Collect explanations for all failures before proposing changes. A diagnostic observation is not proof that one fix will resolve every failure.
  4. After changing code and rerunning CI, diagnose the new explicit run ID. Do not poll unchanged completed evidence expecting it to reflect a fix.

CI should publish its Stove run ID with the job output/artifacts. Otherwise attach unique job/attempt metadata through DashboardSystemOptions and make those values available to the agent. This avoids asking people to identify a run. Never silently remove metadata after an empty result. If recording is incomplete or no decisive evidence was captured, use CI logs/source rather than looping indefinitely.

Security

  • Read-only. No mutations. No exec. No file writes.
  • No outbound calls. Agent reads what stove already stored.
  • No authentication or authorization. Remote clients and non-local Host / Origin headers are accepted so agents can query a shared internal server. Expose Stove only on a trusted network and enforce any network boundary outside Stove.

Pairs well with

Troubleshooting

Symptom Check
Agent can't connect stove running? Port matches MCP URL?
stove_failures empty Tests producing events? dashboard { } registered in Stove().with?
stove_trace returns nothing Tracing enabled? See Tracing setup
Payloads truncated Use a targeted snapshot pointer, a specific trace view, or budget="full" for larger caps (token cost)