Dunetrace MCP server - expose agent signals to Claude Code, Cursor, and Codex
Project description
Dunetrace MCP Server
Query agent signals, run details, and health scores directly from Claude Code, Cursor, Codex, or any MCP-compatible client — without leaving your editor. Read-only for signals and runs; nine tools write — the policy and custom-detector lifecycle operations, plus resolve_issue and trigger_explain. See the ✎ markers in docs/mcp-server.md for which ones, and what each affects.
Setup (install, client config for Claude Code / Cursor / Codex, environment variables) is covered in docs/mcp-server.md — the quick version:
pip install dunetrace-mcp
{
"mcpServers": {
"dunetrace": {
"command": "dunetrace-mcp",
"env": { "DUNETRACE_API_URL": "http://localhost:8002", "DUNETRACE_API_KEY": "dt_dev_test" }
}
}
}
Tools
list_agents
List all monitored agents with their run counts, signal counts, and failure type breakdown.
No arguments.
Example output:
AGENT RUNS SIGS CRIT HIGH LAST SEEN
───────────────────────────────────────────────────────────────────────────────
research-agent 129 55 0 46 6h ago
TOOL_LOOP×46, STEP_COUNT_INFLATION×8
billing-agent 36 34 0 33 10h ago
TOOL_LOOP×33
get_agent_signals
Get recent failure signals for a specific agent, with titles, explanations, and top fix suggestion.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID (from list_agents) |
limit |
int | 20 | Max signals to return (max 100) |
severity |
string | — | Filter: CRITICAL, HIGH, MEDIUM, or LOW |
Example:
🟠 [HIGH] TOOL_LOOP conf=90% step=7 6h ago
Tool loop detected: `web_search` called 6× in steps 2–7
What: The agent called web_search 6 times with identical args.
Fix: Deduplicate `web_search` calls — identical arguments seen 6×
get_signal_detail
Full detail for a specific signal: complete evidence dict, impact statement, and all suggested fixes with code snippets.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
signal_id |
int | required | Integer signal ID (visible in search_signals output) |
agent_id |
string | — | Agent ID (optional — omit to search all agents) |
Example output:
🟠 Signal #495
Type: TOOL_LOOP
Severity: HIGH confidence=90%
Agent: research-agent vabcd1234
Run: 019e217d-bd24-…
Step: 7
Detected: 2026-05-13 13:19 UTC (6h ago)
What happened:
The agent called `web_search` 6 times in steps 2–7 with identical
arguments every time. It is not tracking which queries it has tried.
Why it matters:
Looping agents burn tokens without producing value. A 5-step loop at
typical gpt-4o pricing costs $0.15–$0.30 with nothing to show for it.
Evidence:
tool: web_search
count: 6
args_identical: True
args: ['{"query": "LLM benchmarks"}', '{"query": "LLM benchmarks"}', …+4 more]
Suggested fixes (2):
1. Deduplicate `web_search` calls — identical arguments seen 6×
```python
seen = set()
if args not in seen:
seen.add(args)
call_tool(args)
```
2. Set a hard step limit as a circuit breaker
get_agent_health
Health score (0–100) and per-component breakdown for an agent.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
Scoring components:
| Component | Max points | Measures |
|---|---|---|
failure_rate |
40 | % of runs that triggered any signal |
loop_avoidance |
25 | % of runs without a tool loop |
token_efficiency |
20 | Avg prompt tokens vs. per-agent baseline |
latency |
15 | Avg LLM latency vs. per-agent baseline |
Requires ≥3 runs for a score. Token/latency components return neutral (half points) until ≥30 runs accumulate a baseline.
Example output:
🔴 Health score for research-agent: 41/100
Sample runs: 24
Baseline ready: no (need ≥30 runs for token/latency)
Component breakdown:
failure_rate 7/40 (current: 83.3 % runs with failures)
loop_avoidance 4/25 (current: 83.3 % runs with loops)
token_efficiency 15/20
latency 15/15 (current: 3005.0 avg LLM latency ms)
get_run_detail
Full detail for a specific run: metadata, detected signals with fixes, and a step-by-step event timeline.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
run_id |
string | required | Run UUID |
agent_id |
string | — | Optional — not used for the lookup, reserved for future use |
Example output:
Run: 019e217d-bd24-7d72-a8be-4715c2dcf385
Agent: research-agent vabcd1234
Started: 2026-05-13 13:19 UTC (6h ago)
Duration: 5.6s
Steps: 8
Exit: run.completed
Signals (1):
🟠 TOOL_LOOP [HIGH] conf=90% step=7
Tool loop detected: `web_search` called 6× in steps 2–7
Fix: Deduplicate `web_search` calls — identical arguments seen 6×
Event timeline (18 events):
[ 0] +0.0s run.started
[ 1] +0.0s llm.called model=gpt-4o-mini p=512 c=98 800ms
[ 2] +2.8s tool.called tool=web_search ok=True 200ms
[ 3] +2.8s tool.called tool=web_search ok=True 200ms
…
[ 8] +3.1s run.completed final_answer
Event timeline is capped at 40 entries; longer runs show a count of remaining events.
search_signals
Search signals across all agents with combined filters. Useful for cross-agent audits or time-bounded investigations.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
severity |
string | — | Filter: CRITICAL, HIGH, MEDIUM, or LOW |
failure_type |
string | — | Detector name e.g. TOOL_LOOP, COST_SPIKE, CONTEXT_BLOAT |
since_hours |
int | — | Only signals from the last N hours |
agent_id |
string | — | Restrict to one agent; searches all agents if omitted |
limit |
int | 30 | Max signals to return (max 200) |
Example:
# All CRITICAL signals in the past 24 hours
search_signals(severity="CRITICAL", since_hours=24)
# All TOOL_LOOP signals for one agent
search_signals(failure_type="TOOL_LOOP", agent_id="research-agent")
Example output:
Signals (3 shown, 6 matched):
🟠 6h ago [HIGH ] TOOL_LOOP agent=research-agent
id=495 run=019e217d-bd2… conf=90%
Tool loop detected: `web_search` called 6× in steps 2–7
get_agent_patterns
Analyze failure patterns for an agent: systemic vs. one-off classification, daily signal trend, failure rates by type, and input hashes that consistently trigger failures.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
Systemic classification: a failure is marked SYSTEMIC when it has appeared in a high proportion of runs over an extended window. A ⚠ Occasional label means isolated incidents.
Input patterns: when the same input hash (a structural fingerprint of the user query) reliably triggers a specific failure type, it appears in the "Input patterns" section. Only patterns with a hit rate ≥50% are shown — lower rates are noise.
Example output:
Failure patterns for: research-agent
Systemic patterns:
🚨 SYSTEMIC TOOL_LOOP 12/12 runs (100%)
first seen 5d ago last seen 6h ago
Daily signal counts (last 7 days):
FAILURE TYPE 05-07 05-08 05-09 05-12 05-13
─────────────────────────────────────────────────────────────────
TOOL_LOOP 1 2 1 5 5
Failure rate by type:
TOOL_LOOP ████████████████████ 100% (5/5 runs on 2026-05-13)
Input patterns that reliably trigger failures (rate ≥ 50%):
hash=e47617d3 TOOL_LOOP 38/39 runs (97%)
→ This input hash consistently causes this failure.
summarize_agent
One-shot diagnosis of an agent. Combines health score, failure breakdown, recent signals with their fixes, and health component bars. Start here before diving deeper.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
Example output:
═══ Agent summary: research-agent ═══
Health score: 🔴 41/100
Total runs: 129
Total signals: 55
Last seen: 6h ago
Failure breakdown:
TOOL_LOOP 46 signals (36% of runs)
STEP_COUNT_INFLATION 8 signals (6% of runs)
Most recent signals:
🟠 TOOL_LOOP conf=90% 6h ago run=019e217d…
The agent called `web_search` 6 times with identical args.
Impact: Looping agents burn tokens without producing value.
Fix: Deduplicate `web_search` calls — identical arguments seen 6×
Health components:
failure_rate ███░░░░░░░░░░░░░░░░░ 7/40
loop_avoidance ███░░░░░░░░░░░░░░░░░ 4/25
token_efficiency ███████████████░░░░░ 15/20
latency ████████████████████ 15/15
get_agent_runs
List recent runs for an agent with durations and signal status.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
limit |
int | 20 | Max runs to return (max 100) |
Example output:
Recent runs for: research-agent
RUN ID STARTED DUR STEPS SIGS STATUS
──────────────────────────────────────────────────────────────────────
019e217d-bd2 6h ago 5.6s 8 🔴 1
019e2163-a89 6h ago 4.7s 8 🔴 1
019e2163-66f 6h ago 4.8s 4 ✅ 0
list_voice_calls
List recent voice calls with call-level metrics: duration, how the call ended, silence percentage, voice signal count, and cost. A "call" is a voice agent's conversation (the runs that share one conversation id).
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | "" | Filter to one voice agent (optional) |
completion_status |
string | "" | natural | dropped | escalated (optional) |
cost_bucket |
string | "" | low (<$0.10) | medium ($0.10–$1) | high (>$1) (optional) |
limit |
int | 20 | Max calls to return (max 100) |
Example output:
Voice calls
CALL AGENT WHEN DUR SILENCE SIGS COST STATUS
────────────────────────────────────────────────────────────────────────────────────
101 voice-support 5m ago 92s 12% 0 $ 0.0423 ✅ natural
102 voice-support 15m ago 45s 55% 🔴 2 $ 0.5000 🔴 dropped
get_call_detail
One voice call's full picture: call-level metrics, per-stage cost breakdown (STT / LLM / TTS / telephony), the voice failure signals detected, and links to the call audio when recorded.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
conversation_id |
int | required | Call id (from list_voice_calls) |
Example output:
Call #102 (voice-support)
Ended: 🔴 dropped
Duration: 45s over 2 run(s)
Silence: 55%
Talk ratio: agent 62% / caller 38%
Cost: $0.5000
stt=$0.1000 llm=$0.3000 tts=$0.1000
Voice signals (2):
🔴 VOICE_DEAD_AIR
🔴 VOICE_TRUNCATED_RESPONSE
Recordings (1):
🎧 https://audio.example/conv-def.wav (45s)
get_agent_token_stats
Per-window token usage and waste breakdown for an agent (1d / 7d / 30d). Shows total tokens consumed, wasted tokens (on runs with at least one live failure signal), and estimated API cost for each window. The 30-day view adds a Waste by failure type breakdown so you can see which failure types cost the most to leave unfixed.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID (from list_agents) |
Example output:
═══ Token stats: research-agent ═══
── Last 24 h ──
Runs: 10 (3 with failures)
Total tokens: 50.0k
Wasted tokens: 15.0k (30% of total)
Total cost: $0.0500
Wasted cost: $0.0150 (30% of total)
── Last 7 days ──
Runs: 50 (12 with failures)
Total tokens: 250.0k
Wasted tokens: 60.0k (24% of total)
Total cost: $0.2500
Wasted cost: $0.0600 (24% of total)
Waste by failure type (30 days):
TOOL_LOOP 150.0k tok $0.15 (30 runs)
COST_SPIKE 75.0k tok $0.08 (15 runs)
get_instrumentation_guide
Get a quick-start code snippet for instrumenting an agent with Dunetrace. Works for Python, LangChain/LangGraph, TypeScript, Haystack, voice agents, plain tool-call tracking, and OpenTelemetry.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
framework |
string | required | Framework name: python, langchain, langgraph, typescript, haystack, tools, voice, or otel |
Aliases accepted: lc, lc-graph, lc_graph, langgraph, ts, js, javascript, node, tracking, tool_calls, voice-agent, stt, tts, speech, otlp, opentelemetry (and more).
get_fix_status
Check whether a fix applied for a signal reduced recurrence.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
signal_id |
int | required | The signal ID to check |
agent_id |
string | — | Optional agent ID |
Verdict values: verified, likely_fixed, still_occurring, insufficient_data.
list_agent_fixes
List all fixes that have been applied for an agent's signals, with type, delivery method, and verdict.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
trigger_explain
Trigger root-cause analysis for a signal, built natively from the run's own stored events — no external tracing system required. Returns a fix_category: dunetrace_native (a runtime policy Dunetrace can apply directly) or customer_code (a prompt/code diff you apply yourself, or via a GitHub PR for code_change fixes).
Note: Makes an LLM call — may take 5–15 seconds.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
signal_id |
int | required | Signal ID to analyze |
agent_id |
string | — | Optional agent ID |
list_policies
List runtime policies configured for agents. Policies trigger actions (stop, switch_model, inject_prompt, log) when a metric threshold is crossed during a live run.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | — | Filter to one agent (optional) |
create_policy
Create a runtime policy.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
name |
string | required | Policy label |
agent_id |
string | required | Target agent (* for all) |
condition |
string | required | JSON: {"metric": "cost_usd", "operator": "gt", "threshold": 5.0} |
action |
string | required | JSON: {"type": "stop"} or {"type": "switch_model", "model": "gpt-4o-mini"} |
toggle_policy
Enable or disable a runtime policy.
Arguments: policy_id (string), enabled (bool)
delete_policy
Delete a runtime policy.
Arguments: policy_id (string)
list_custom_detectors
List custom detectors with status, fire rate, and run counts.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | — | Filter to one agent (optional) |
create_custom_detector
Create a custom detector from a plain-English description. Translates the description to a structured config via LLM, then creates the detector in shadow mode.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
description |
string | required | Plain-English description, e.g. "Alert when tool calls exceed 20 in a run" |
agent_id |
string | * |
Target agent |
activate_custom_detector
Activate a custom detector so it fires live alerts.
Arguments: detector_id (string)
pause_custom_detector
Pause a custom detector so it stops evaluating.
Arguments: detector_id (string)
delete_custom_detector
Delete a custom detector permanently (also deletes historical results).
Arguments: detector_id (string)
list_agent_issues
List open or resolved issues for an agent. Issues are aggregated across runs — the same failure type appearing in multiple runs is tracked as a single issue, and auto-resolves after 5 consecutive clean runs.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
status |
string | open |
open, resolved, or all |
search_issues
Search issues across all agents in your org — fleet-wide triage, where list_agent_issues is scoped to one agent.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
query |
string | "" |
Substring match against agent_id / failure_type / resolution_notes. Empty matches everything |
status |
string | "" |
Filter: open, resolved, or reopened. Empty = all |
agent_id |
string | "" |
Restrict to one agent. Empty = all agents |
failure_type |
string | "" |
Restrict to one detector, e.g. TOOL_LOOP |
limit |
int | 20 | Max issues to return |
query is a plain substring match, not full-text search or relevance ranking — issues have no free-text title beyond resolution_notes, which is only populated once an issue has been manually resolved.
get_issue
Full context for a single issue — metadata, affected runs, root cause, and a suggested fix. The deep-dive tool for one issue found via search_issues or list_agent_issues.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
issue_id |
int | required | Integer issue ID |
Note: may trigger an LLM call to generate the root cause/fix (the same native analysis trigger_explain uses) and can take 5–15 seconds. With no LLM key configured on the backend, root_cause/suggested_fix are omitted and the rest of the report still returns.
code_references is always empty for now — Dunetrace has no source-mapping capability yet.
resolve_issue ✎
Manually mark an issue resolved with notes on what fixed it — for when you've made a code/prompt change and want to close the loop, distinct from Dunetrace's automatic resolve-after-5-clean-runs detection.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
issue_id |
int | required | Integer issue ID to resolve |
resolution_notes |
string | required | What fixed it, e.g. "Added a per-tool call limit of 3 in the agent's retry loop." |
If the failure recurs later the issue reopens automatically, whether it was auto- or manually-resolved — resolution_notes is kept as a historical record either way.
get_failure_pattern_detail
Deep dive into a specific failure type: evidence aggregates, a 14-day trend with ASCII bar chart, co-occurring signals, and top example runs.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
agent_id |
string | required | Agent ID |
failure_type |
string | required | Detector name, e.g. TOOL_LOOP |
compare_runs
Compare two runs side by side — duration, step count, token usage, signals detected, and exit reason. Useful for spotting regressions between a good run and a bad one.
Arguments:
| Argument | Type | Default | Description |
|---|---|---|---|
run_id_1 |
string | required | First run UUID |
run_id_2 |
string | required | Second run UUID |
agent_id |
string | — | Optional agent ID |
Example output:
Run comparison
RUN 1 RUN 2
──────────────────────────────────────────────────────────────────────────
Run ID run-aaa-111 run-bbb-222
Agent my-agent v1.0 my-agent v1.1
Duration 10.0s 20.0s !!
Steps 5 12 !!
Tokens 1,000 8,000 !!
Exit reason final_answer error !!
Signals none TOOL_LOOP [HIGH] !!
!! = differs between runs
Typical workflows
Triage an alert
You: I got a Slack alert for TOOL_LOOP on research-agent. What's happening?
Agent: [calls summarize_agent("research-agent")]
Health is 41/100. TOOL_LOOP is systemic — 46 signals across 36% of
runs. The fix is to deduplicate web_search calls (identical arguments
seen 6× per run). Signal #495 is the most recent. Want the code?
You: Yes, show me signal #495.
Agent: [calls get_signal_detail(495, "research-agent")]
Here's the evidence and fix code…
Investigate a specific run from Slack
The Slack alert includes a "View Run" link: http://localhost:3000/runs/<run_id>. You can also pass the run ID directly:
You: Check run 019e217d-bd24-7d72-a8be-4715c2dcf385
Agent: [calls get_run_detail("019e217d-…")]
Duration 5.6s, 8 steps. TOOL_LOOP at step 7 — web_search called
6× with identical args. Fix: add a dedup set.
Cross-agent audit
You: Are there any CRITICAL signals in the last 24 hours?
Agent: [calls search_signals(severity="CRITICAL", since_hours=24)]
2 CRITICAL signals: PROMPT_INJECTION_SIGNAL on billing-agent (2h ago)
and COST_SPIKE on data-agent (5h ago). Want details on either?
Before a deploy
You: Is research-agent stable enough to deploy to production?
Agent: [calls get_agent_patterns("research-agent")]
TOOL_LOOP is systemic — 100% of runs in the last 7 days.
Recommending you fix the dedup issue before deploying.
Data Handling
All data served by the MCP tools comes from the Dunetrace Customer API:
- Tool arguments, LLM prompts and outputs → stored and returned as-is (shown as
args,output, etc. in evidence) - Token counts, latency, step counts → stored as plain numbers
- Run and signal metadata → stored as plain text
The evidence dict in signal responses contains the actual content the detector used, not a hash of it.
Tests
cd packages/mcp-server
python -m pytest tests/ -v
188 tests, all offline — no running stack required.
Source
packages/mcp-server/
dunetrace_mcp/
__init__.py
client.py # thin httpx wrapper around the Customer API
server.py # FastMCP server with 31 tools + 8 doc resources
tests/
test_tools.py # 188 unit tests (all offline)
pyproject.toml
README.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dunetrace_mcp-0.4.2.tar.gz.
File metadata
- Download URL: dunetrace_mcp-0.4.2.tar.gz
- Upload date:
- Size: 107.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe2f2aa7d2fa626c0abc7edc14ae0d24e4bd578ea94d9bf0948f24c3fa74a77e
|
|
| MD5 |
335aabf31f36815c22ba23eab2f84a51
|
|
| BLAKE2b-256 |
b27bd9bbbfccf04c2b4051d5b573c4963fc5d0b4dabc067bc29d87ba47de3141
|
File details
Details for the file dunetrace_mcp-0.4.2-py3-none-any.whl.
File metadata
- Download URL: dunetrace_mcp-0.4.2-py3-none-any.whl
- Upload date:
- Size: 91.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac5469123a12c89b503dd3cd33f85ec428a5270d71f47c4dbee216199f1a69f4
|
|
| MD5 |
a5c64c002250c3d4ae282de51d00dd14
|
|
| BLAKE2b-256 |
27e88571541bcaf6c0135839514a5214db8c032c55ccd69902cb6a3c0bcc5e83
|