Spoor
Provides tools for investigating service incidents across the VictoriaMetrics observability stack (VictoriaMetrics for metrics via MetricsQL, VictoriaLogs and VictoriaTraces for logs and traces via LogsQL). Enables an agent to ask what changed in a time window and get back a short, ranked digest of changes across metrics, logs and traces — including request/error rates, latency, CPU, memory, consumer lag, new or spiking error-log patterns, and per-span errors — with cause-vs-echo attribution that identifies the service where a failure started and labels affected callers as echoes.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Spoorwhat changed in the last 30 minutes and where did the failure start?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Spoor
Spoor tells an AI agent what changed in your services, and where it started, in a few hundred tokens.
Spoor is an open-source MCP server and CLI for the VictoriaMetrics observability stack (VictoriaMetrics, VictoriaLogs, VictoriaTraces). When something breaks, an agent asks Spoor "what changed in the last 30 minutes?" and gets back a short, ranked answer across metrics, logs and traces, with the service where the failure started at the top and its echoes in other services labelled as such.
Spoor (n.): the track or trail left by an animal. Spoor follows the trail a failure leaves in your telemetry back to where it started.
$ spoor what-changed --since 30m{
"summary": "11 change(s) in 3 services. Strongest: payment — Failures start here, in \"POST /charge\" (100% of 100 failed traces). Also: payment — Span \"POST /charge\" errors 0.2% → 43.7% (229 spans). … Errors at their usual rate (not new) in: product-catalog, recommendation, ad.",
"findings": [
{"service": "payment", "signal": "traces", "kind": "error_origin", "score": 0.99, "title": "Failures start here, in \"POST /charge\" (100% of 100 failed traces)"},
{"service": "payment", "signal": "logs", "kind": "new_log_pattern", "score": 0.94, "title": "New error log pattern: \"charge failed: card processor returned <N> for order <N>\""},
{"service": "payment", "signal": "metrics", "kind": "error_rate_up", "score": 0.92, "title": "Error rate up: 0.2% → 40.5%", "onset": "2026-10-16T09:50:00Z"},
{"service": "checkout", "signal": "logs", "kind": "spiking_log_pattern", "score": 0.74, "title": "Error log pattern spiking: \"call to payment.Charge failed with status <N>\" — originates in payment"}
],
"coverage": {"metrics": "ok", "logs": "ok", "traces": "ok"}
}Findings carry evidence links to the query behind them (when the token budget allows), so a human can check the agent's work in one click.
Why use it
In a microservice failure, the broken service and every caller of it all light up. An agent with only raw query tools has to decide what to query, compare against "normal" itself, page through results and work out which service is the cause and which are echoes. It burns tool calls and tokens, and often blames the loudest symptom or an error that was always there.
Spoor does that work in plain, deterministic Go and hands the agent the conclusion's evidence:
Cross-signal: request rate, error rate, latency, CPU, memory and consumer lag; new and spiking error-log patterns; per-span errors and latency, all for the same window against the same baseline.
Cause vs. echo: trace trees show where failures start and where latency is added. Callers that merely suffer are demoted and labelled
— originates in payment.Steady errors called out: errors that were already there are listed as
steady_errors, "not a new problem", so agents stop blaming them.Bounded output: every tool takes
budget_tokens(default 800).Honest about gaps:
coveragesays when a backend failed or had no data, or the baseline was empty. "No change" never stands in for "no data".No LLM inside, read-only: same input, same output; only query endpoints are ever called.
Measured
The benchmark runs Claude Code headless on six incidents (five faults and a no-fault control), five times each, once with the official VictoriaMetrics MCP servers alone and once with Spoor added. On Claude Haiku, 60 isolated sessions:
Official Victoria MCP servers | + Spoor | |
Correct root cause, fault scenarios | 19/25 | 25/25 |
Correct answer, all scenarios incl. "nothing is wrong" | 23/30 | 30/30 |
Median tool calls per investigation | 24 | 10 |
Median time | 55 s | 23 s |
Total cost | $0.60 | $0.19 (−68%) |
Synthetic incidents and one small model, so treat it as a first data point. Method, per-scenario results and caveats: docs/benchmark.md.
Related MCP server: Cairn
Quickstart
Requires Go 1.25+, Docker and jq. About five minutes.
git clone https://github.com/asinha07/spoor && cd spoor
make it-up # empty VictoriaMetrics, VictoriaLogs, VictoriaTraces in Docker
make demo-data # six synthetic incidents; ground truth in scenarios/out/
make build
T=scenarios/out/payment-failure.truth.json
./bin/spoor what-changed --since $(jq -r .since $T) --until $(jq -r .until $T) --prettyConnect it to Claude Code:
claude mcp add spoor \
-e SPOOR_METRICS_URL=http://localhost:8428 \
-e SPOOR_LOGS_URL=http://localhost:9428 \
-e SPOOR_TRACES_URL=http://localhost:10428 \
-- "$PWD/bin/spoor" serveRun it from the spoor directory (or add -s user to register it everywhere), then ask Claude: "Something is
failing in the shop. What changed, and where did it start?"
Other clients, your own backends and config: Getting started.
Tools
Tool | Returns |
| Ranked changes across all signals, with cause/echo attribution, for one service or all. Start here. |
| Error log patterns that are new or spiking, with counts, first-seen time and a redacted example. |
| Span names that got slower or started failing, with an example trace ID. |
| Services in the window and which signals have data for each. |
Inputs: service, since, until, baseline (previous, 1d, 7d), budget_tokens.
Full reference: docs/tools.md.
How it works
flowchart LR
Agent[AI agent / CLI] -->|MCP| S[Spoor]
S -->|MetricsQL| VM[(VictoriaMetrics)]
S -->|LogsQL| VL[(VictoriaLogs)]
S -->|LogsQL| VT[(VictoriaTraces)]
S -->|"ranked digest ≤ budget"| AgentFor a window and a baseline (by default, the six windows just before it), Spoor scores every metric with a robust z-score, compares error-log patterns with a Poisson test, compares span error rates and p95s, walks failed traces to the span where each failure started, boosts changes confirmed by more than one signal, and trims the result to the token budget. Details: architecture · scoring and attribution.
Run it for a team
spoor serve --http :8080 --auth-token … serves MCP over streamable HTTP with bearer-token auth, /healthz and
Prometheus /metrics. Ready-made, hardened manifests:
Kubernetes (Kustomize):
deploy/kubernetes/Docker Compose:
deploy/docker/systemd:
deploy/systemd/
Security, sizing, monitoring and upgrades: Production guide.
Verified on the OpenTelemetry Demo
On the OpenTelemetry Demo v3.1.0 (about 20 real
services), a 50% payment failure came back as "payment: failures start here, in charge", with checkout and
frontend errors labelled "originates in payment" (732 tokens); the adHighCpu fault came back as the single
finding "ad: CPU up 0.07 → 3.97 cores" (192 tokens). Reproduce it: deploy/demo/.
Documentation
Install, try, connect an agent | |
Kubernetes, Docker, systemd, security, monitoring | |
Every option and recipes for common setups | |
Inputs and response format | |
Design and scoring | |
Why use it, and what else exists | |
When something looks wrong |
Status and limitations
Early (pre-1.0). Tool output fields may still change between minor versions; see the changelog.
VictoriaMetrics stack only for now. Prometheus, Loki and Tempo adapters are next.
Cause/echo attribution needs traces; without them Spoor still ranks metric and log changes.
Attribution reads at most 50,000 span rows per read; on very busy systems a trace can be cut.
The benchmark uses synthetic incidents written by the author and one model; treat it as a first data point.
More in how-it-works.md.
Roadmap
Prometheus, Loki and Tempo adapters
"Since the last deploy" windows from Kubernetes rollouts
HolmesGPT toolset
Kafka consumer-group digest
Benchmark on the OpenTelemetry Demo and larger models
Contributing
Issues and pull requests are welcome. make test needs nothing but Go; see CONTRIBUTING.md.
Security issues: SECURITY.md.
License
Apache-2.0. See LICENSE.
Available Tools
4 toolslist_servicesList servicesBRead-onlyIdempotent
Services seen in the window and which signals (metrics, logs, traces) have data for each.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | Window start: RFC 3339 time or a duration ago. Default 30m. | |
| until | No | Window end. Default now. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds the meaningful behavioral detail that the result is window-scoped and reports which signals have data per service, but says nothing about pagination, empty results, or service identity semantics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence with no filler, and the scope qualifier ("in the window") is front-loaded. It is slightly terse to the point of being a fragment, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-optional-parameter read tool with no output schema, the description usefully characterizes the return value (services plus their per-signal data coverage), which is the main thing the agent needs to know. Only minor gaps remain around result size and service naming conventions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and both since/until are documented with formats and defaults in the schema itself. The description only echoes the window concept without adding format or interaction detail, so the schema does the heavy lifting and baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource (services) and scopes it to a time window, plus discloses the per-service signal breakdown (metrics, logs, traces). It reads as a noun phrase rather than an explicit verb, but the listing intent is unambiguous and clearly distinct from siblings like new_errors or trace_diff.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to reach for this tool versus the siblings (new_errors, trace_diff, what_changed), nor any prerequisite or exclusion. The agent must infer that this is the inventory/discovery entry point rather than anything the description states.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
new_errorsNew errorsARead-onlyIdempotent
Error logs compared with a baseline: errors that are new or spiking (messages that differ only by IDs count as one error), services whose error volume rose beyond their traffic, each with counts, first-seen time and one redacted example line, plus services whose errors are steady.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | Window start: RFC 3339 time or a duration ago such as 30m or 2h. Default 30m. | |
| until | No | Window end: RFC 3339 time or a duration ago. Default now. | |
| service | No | Service name. Omit to scan every service seen in the window. | |
| baseline | No | What to compare against: previous (the windows just before; default), 1d or 7d (same window that many days earlier). | |
| budget_tokens | No | Upper bound on the response size in tokens, 200 to 4000. Default 800. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and closed-world, so safety is covered. The description adds real behavioral context beyond that: the dedup rule that messages differing only by IDs count as one error, that each entry carries counts, first-seen time and a redacted example line, and that steady services are also reported. Auth, rate limits and ordering are not disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence, but front-loaded with the core comparison and then the result contents in priority order; every clause (dedup rule, counts, first-seen, redaction, steady services) carries information. It reads as a run-on, which costs a point, but nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must carry the return contract — and it does, enumerating the three result groups and the per-entry fields. What is missing is edge behavior: what happens when budget_tokens truncates results, ordering, and the exact shape of 'steady' services versus the rising ones.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and all five parameters (since, until, service, baseline, budget_tokens) are fully documented in the schema with defaults and formats. The description reinforces the baseline-comparison concept but adds no syntax, units or format detail beyond what the schema already states, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the specific analysis performed (error logs compared against a baseline) and the three result categories: new/spiking errors, services with error volume rising beyond traffic, and steady-error services. It is clearly distinguishable from list_services and trace_diff, though it never names a sibling explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the output framing — an agent can infer this is the tool for spotting newly introduced or regressing errors versus a baseline. However, there is no explicit when-to-use guidance, no statement of when NOT to use it, and no routing against the sibling what_changed, which could plausibly overlap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
trace_diffTrace diffARead-onlyIdempotent
Span names that got slower or started failing compared with a baseline: p95 and error rate before/after, each span's share of the added latency, and an example trace ID.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | Window start: RFC 3339 time or a duration ago such as 30m or 2h. Default 30m. | |
| until | No | Window end: RFC 3339 time or a duration ago. Default now. | |
| service | No | Service name. Omit to scan every service seen in the window. | |
| baseline | No | What to compare against: previous (default), 1d or 7d. | |
| operation | No | Limit to one span name, e.g. 'POST /checkout'. | |
| budget_tokens | No | Upper bound on the response size in tokens, 200 to 4000. Default 800. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safe read-only, idempotent, non-destructive profile, so the bar is low. The description goes beyond them by disclosing the exact result shape (p95 and error rate before/after, per-span latency share, an example trace ID), which is valuable since no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One dense sentence with a front-loaded subject and a colon-delimited list of returned fields; nothing is wasted. It is a noun phrase rather than a verb-first statement, which slightly weakens immediate readability but not materially.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and does so well, and all six parameters are fully documented in the schema with annotations covering the safety profile. The only real gap is the absence of routing guidance among sibling tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter is already documented with formats, defaults, and examples. The description only restates the baseline concept and adds no syntax or constraints beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific analysis: span names that regressed in latency or error rate versus a baseline, plus the returned metrics. It clearly identifies the resource (spans) and operation (diff against a baseline), though it never names the siblings it should be preferred over.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the phrase 'compared with a baseline' and the regression framing, which tells an agent this is for finding performance/error regressions. However, it offers no explicit when-to-use rules and does not distinguish itself from what_changed or new_errors, which an agent must infer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
what_changedWhat changedARead-onlyIdempotent
Ranked list of what changed in a service (or all services) across metrics, logs and traces, compared with a baseline. Returns a one-paragraph summary, findings with before/after values, onset times and evidence links, and per-signal coverage. Start here.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | Window start: RFC 3339 time or a duration ago such as 30m or 2h. Default 30m. | |
| until | No | Window end: RFC 3339 time or a duration ago. Default now. | |
| service | No | Service name. Omit to scan every service seen in the window. | |
| baseline | No | What to compare against: previous (the windows just before; default), 1d or 7d (same window that many days earlier). | |
| budget_tokens | No | Upper bound on the response size in tokens, 200 to 4000. Default 800. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the bar is lower. The description adds real value by disclosing the return structure: a summary paragraph, ranked findings with before/after values and onset times, evidence links, and per-signal coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the core purpose before the return-value detail. The trailing imperative 'Start here.' is a useful orientation cue rather than waste, though it sits at the end rather than the front.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the burden of describing returns and does so thoroughly (summary, findings, coverage). Combined with fully documented parameters and annotations covering safety, an agent has nearly everything needed, though explicit routing to sibling tools is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter (since, until, service, baseline, budget_tokens) is already documented in the schema. The description's mention of 'baseline' and '(or all services)' overlaps with what the schema already states, adding no syntax or format detail beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('what changed in a service... across metrics, logs and traces') and scopes it precisely with the baseline comparison. The 'Start here' framing positions it as the entry-point tool, distinguishing it from the drill-down siblings new_errors and trace_diff.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Start here' gives clear context that this is the workflow entry point, and 'Omit to scan every service' clarifies scope behavior. It stops short of naming when NOT to use it or routing to new_errors/trace_diff explicitly, so it lacks the full when-not/alternatives treatment of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.2.1- First observed
list_services - First observed
new_errors - First observed
trace_diff - First observed
what_changed
TDQS
Scored across 4 tools
Each tool maps to a distinct signal scope: list_services inventories services, new_errors is log-specific, trace_diff is trace-specific, and what_changed is the cross-signal ranked entry point. The only soft overlap is that what_changed subsumes findings from new_errors and trace_diff, but its 'start here' framing and ranked-summary output keep the boundary workable.
All names use snake_case casing, but the conventions are mixed: list_services is verb_noun, new_errors is adjective_noun, trace_diff is noun_noun, and what_changed is a question-phrase. Readable and memorable, but there is no predictable verb_noun pattern an agent can rely on.
Four tools for a baseline-diffing observability server is well-scoped: one inventory, one per signal (logs, traces), and one aggregator. Each tool clearly earns its place with no gratuitous or redundant entries.
The surface covers service inventory, log errors, trace regressions, and a unified change ranking, which handles the core diagnostic workflow. Gaps are minor: no metric-specific diff tool (metrics only surface inside what_changed), no raw log/span search, and no way to configure the comparison window or baseline.
Maintenance
Related MCP Connectors
- causelyOAuthapp.causely
Causal reasoning for reliability: root cause, blast radius, and service health for agents.
- SuperlogOAuthsh.superlog
Open-source agent that observes and fixes your application. Query logs, traces, metrics, incidents.
Triage failing GitHub Actions jobs and see what self-heal repaired, in natural language.
AI agent observability for production traces, natural-language insights, and improvement loops.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables autonomous SRE incident investigation by allowing users to describe incidents in natural language. The agent follows a governed state machine to gather read-only evidence and produce grounded conclusions.MIT
- FlicenseNot gradedqualityAmaintenanceEnables users to investigate infrastructure incidents in plain English, correlate observability and deploy data with runbooks, and get evidence-backed root-cause proposals with approval-gated remediation.5-
- AlicenseNot gradedqualityCmaintenanceEnables a local AI agent to investigate service incidents by querying read-only telemetry such as service health, metrics, logs, and traces, then produce verifiable assessments with evidence links and uncertainty.Apache 2.0

Mirador MCPofficial
AlicenseNot gradedqualityBmaintenanceEnables AI agents to perform observability analysis across Prometheus-compatible metrics and logs without prior vendor knowledge, including signal discovery, entity topology resolution, root-cause analysis, correlation, and capacity forecasting through natural-language anchors.Apache 2.0