TrafficMorph MCP Server
████████╗██████╗ █████╗ ███████╗███████╗██╗ ██████╗
╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██╔════╝██║██╔════╝
██║ ██████╔╝███████║█████╗ █████╗ ██║██║
██║ ██╔══██╗██╔══██║██╔══╝ ██╔══╝ ██║██║
██║ ██║ ██║██║ ██║██║ ██║ ██║╚██████╗
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝
███╗ ███╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗
████╗ ████║██╔═══██╗██╔══██╗██╔══██╗██║ ██║
██╔████╔██║██║ ██║██████╔╝██████╔╝███████║
██║╚██╔╝██║██║ ██║██╔══██╗██╔═══╝ ██╔══██║
██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ██║ ██║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
Drive TrafficMorph from Claude Desktop, Claude Code, Cursor, or anyother host that speaks the Model Context Protocol.
The flagship use case is CI-failure triage:
"My TrafficMorph CI step just failed on run 1234 — whatregressed vs the baseline?"
→ Claude calls the right tools, fetches the relevant runs, computesthe per-metric delta, and produces a human-readable triage reportin seconds.
Current release: 1.2.0 — 25 tools + 4 prompts + 5 resources(34 catalog entries total). See CHANGELOG.md forthe per-release history and STABILITY.md for what1.x commits to keeping stable. See MCP-USAGE.md forworked example conversations.
Quick start
Four steps from zero to "Claude is driving my TrafficMorphaccount":
1. Get an API key
Open the TrafficMorph app → Settings → API Keys → clickGenerate. Copy the tm_… value.
2. Install (or skip — uvx runs without install)
# Permanent install:
pip install tm-mcp
# OR — no install at all. The MCP host config below uses `uvx`,
# which downloads + caches the package on first run. Confirm
# uvx itself is on your PATH:
uvx --version
Note:
tm-mcpis a long-running MCP server, not a CLI witha--helpflag. Invoking it directly without env vars exits 2with a configuration error. The host (Claude Code / Desktop)subprocesses it and pipes JSON-RPC over stdio — you don't runit yourself unless you're debugging.
3. Register with your MCP host
Both Claude Code and Claude Desktop use the same env-var protocol:TM_API_KEY for the API key, TM_BASE_URL for the base URL. Theserver reads both at startup and fails fast with a clean"couldn't start" message if either is missing — your host's logshows that instead of opaque "tool call failed" errors later.
In all snippets below, replace two placeholders with your own values:
TM_API_KEY=tm_xxxxxxxxxxxxxxxx→ the API key from Step 1.TM_BASE_URL=https://YOUR-TRAFFICMORPH-HOST→ the URL of yourTrafficMorph server. Common values:- Local dev:
http://localhost:8080 - Self-hosted prod:
https://trafficmorph.your-company.com(or whatever URL your install lives at) - Cloud SaaS: the URL shown in your TrafficMorph app'sbrowser address bar
- Do not copy the literal
YOUR-TRAFFICMORPH-HOSTplaceholder— it won't resolve and every tool call will fail.
- Local dev:
Claude Code — one command:
claude mcp add trafficmorph \
-e TM_API_KEY=tm_xxxxxxxxxxxxxxxx \
-e TM_BASE_URL=https://YOUR-TRAFFICMORPH-HOST \
-- uvx tm-mcp
Project-scope alternative — drop this at the repo root:
// .mcp.json
{
"mcpServers": {
"trafficmorph": {
"command": "uvx",
"args": ["tm-mcp"],
"env": {
"TM_API_KEY": "tm_xxxxxxxxxxxxxxxx",
"TM_BASE_URL": "https://YOUR-TRAFFICMORPH-HOST"
}
}
}
}
Claude Desktop — add to ~/Library/Application Support/Claude/claude_desktop_config.json(macOS) or the equivalent on your OS, then restart Claude Desktop:
{
"mcpServers": {
"trafficmorph": {
"command": "uvx",
"args": ["tm-mcp"],
"env": {
"TM_API_KEY": "tm_xxxxxxxxxxxxxxxx",
"TM_BASE_URL": "https://YOUR-TRAFFICMORPH-HOST"
}
}
}
}
4. Verify
In Claude Code:
> /mcp
You should see trafficmorph listed with 25 tools, 4 prompts,and 5 resources. If you see red / error, check:
claude mcp list # is `trafficmorph` registered?
claude mcp get trafficmorph # what env vars + command?
The most common gotcha: forgetting TM_BASE_URL. The serverrefuses to start without it and surfaces a clear "$TM_BASE_URLis not set" error in your MCP host's log. Set it in the hostconfig (see step 3).
What you can do
Conversational examples — try any of these once the server is wired up:
- "List my TrafficMorph profiles."
- "Show me the last 5 runs for profile 42."
- "Start a run on profile 42 and wait for the verdict."
- "Create a profile 'smoke-test' hitting https://api.example.com/health at 50 RPS for 60s."
- "What domains am I cleared to load-test against?"
- "Add api.example.com as a new domain and walk me through verification."
- "Compare run 1234 against run 1198."
Or invoke a slash-command prompt for a guided workflow:
/tm_triage 42— find the most recent failed run for profile 42, diff against the latest PASS baseline, narrate the regression./tm_setup_loadtest https://api.example.com 100 60— handle domain verification + profile creation + optional immediate run./tm_compare_baseline 42— quick regression check vs the last green./tm_import_capture_guided ~/.trafficmorph/captures/my.jsonl— analyse → preview → import workflow.
Or @-mention a resource to pull pre-baked context into the chat:
@tm://profiles— your full profile list as session-start context.@tm://history/recent— the last 20 runs across all profiles.@tm://domains— verified domain list.@tm://profiles/42or@tm://history/1234— one specific entity by id.
See MCP-USAGE.md for end-to-end worked conversationsincluding failure triage, new-test setup, and capture-driven profileimport.
Full catalog
Tools (25) — AI-invoked actions
| Tool | Action |
|---|---|
| Read | |
tm_list_profiles |
List all profiles owned by the authenticated user |
tm_get_profile |
Full config + run status for one profile |
tm_list_history |
Paginated past runs with filters (auto_verdict=FAIL is the CI-triage filter) |
tm_get_run |
Full metric set + verdict for one run |
tm_list_domains |
All registered domains + verification status |
tm_compare_runs |
Side-by-side metric diff of two runs (synthetic) |
tm_analyse_capture |
Per-endpoint analysis of a JSONL capture file |
| Run control | |
tm_start_run |
Start a run; with wait=True + fail_on_verdict=["FAIL","WARN"] mirrors tm runs start --wait in the CLI |
tm_stop_run |
Stop the in-flight run for a profile (idempotent) |
tm_pause_run |
Pause without losing position (idempotent) |
tm_resume_run |
Resume a paused run from where it left off |
| Profile lifecycle + capture import | |
tm_create_profile |
Create a new profile (fails fast on name collision to prevent silent upsert wipe) |
tm_update_profile |
Partial update by id (read-modify-write internally — only pass the fields you want to change) |
tm_delete_profile |
Remove a profile |
tm_import_capture |
Persist analysed-capture groups as profiles |
| Domain management | |
tm_add_domain |
Register a domain for verification (idempotent) |
tm_verify_domain_dns |
Check the TXT challenge record |
tm_verify_domain_http |
Check the /.well-known/trafficmorph-verify.txt file |
tm_delete_domain |
Remove a domain |
| Variables-set lifecycle | |
tm_list_variables_sets |
List all variables sets owned by the user |
tm_get_variables_set |
Single set's metadata (id, name, mode, columns, row count) |
tm_create_variables_set |
Upload a CSV-style set (inline csv_content string, mode is one of "ROW" / "COLUMN" / "SEQUENTIAL") |
tm_rename_variables_set |
Rename without touching content (idempotent) |
tm_change_variables_set_mode |
Switch between ROW / COLUMN / SEQUENTIAL without re-uploading |
tm_delete_variables_set |
Remove a set; 400s if still attached to any profile (detach via tm_update_profile first) |
Prompts (4) — user-invoked slash commands
| Slash command | Workflow |
|---|---|
/tm_triage <profile_id> |
Find the most recent FAIL → diff vs latest PASS → narrate the regression |
/tm_setup_loadtest <url> <rps> <duration_seconds> |
Domain verification (if needed) + profile creation + optional run |
/tm_compare_baseline <profile_id> |
Quick regression check: latest run vs latest PASS |
/tm_import_capture_guided <path> |
Analyse → present groups → user picks → import |
Prompts return a templated user message that steers the AI througha specific tool sequence. They're how you kick off a known workflowwithout typing the full natural-language description every time.
Resources (5) — @-mention URIs
| URI | What it returns |
|---|---|
tm://profiles |
All your profiles, JSON |
tm://profiles/{id} |
One profile's full config |
tm://history/recent |
Last 20 runs across all profiles |
tm://history/{run_id} |
One run's full metrics |
tm://domains |
All registered domains + verification status |
Resources are read-only data the host pulls into context — usuallyat session start via @-mention. They wrap the corresponding readtools 1:1; the difference is who decides when to read (AI fortools, host for resources).
Configuration
| Env var | Required | Notes |
|---|---|---|
TM_API_KEY |
yes | Full tm_… value provisioned from in-app Settings → API keys |
TM_BASE_URL |
yes | URL of your TrafficMorph install (http://localhost:8080 for local dev, your hosted URL otherwise). No built-in default — the server refuses to start without it |
TM_MCP_CAPTURE_ROOT |
no | Defaults to ~/.trafficmorph/captures/. Allow-listed root for tm_analyse_capture + tm_import_capture file paths |
Capture-file path validation
tm_analyse_capture and tm_import_capture accept a server-sidefile path. The MCP server validates each path before passing itthrough:
| Rule | Why |
|---|---|
Must resolve inside $TM_MCP_CAPTURE_ROOT (default ~/.trafficmorph/captures/) |
Prevents AI invocation from probing ~/.ssh/, ~/.aws/, ~/Documents/… |
| Symlinks resolving outside the root are rejected | Classic symlink-escape defense |
.. segments rejected at parse time |
Path-traversal defense |
Only .jsonl extension |
The capture parser reads plain JSONL (no gzip wrapping) |
Override the root via TM_MCP_CAPTURE_ROOT in your MCP host'sserver config:
"env": {
"TM_API_KEY": "...",
"TM_BASE_URL": "...",
"TM_MCP_CAPTURE_ROOT": "/path/to/your/captures"
}
Troubleshooting
Server fails to start with $TM_API_KEY is not set or$TM_BASE_URL is not set. One of the required env vars wasn'tdelivered to the subprocess by your MCP host. The error names themissing variable; add it to the env block in your host config(see Step 3).
Server starts, but every tool call hits a network / DNS error.TM_BASE_URL is set to something that doesn't resolve — typicallya placeholder like https://YOUR-TRAFFICMORPH-HOST that wasn'tedited, a typo in the hostname, or an internal URL not reachablefrom where the MCP host runs. Read the URL back fromclaude mcp get trafficmorph and confirm it resolves withcurl -I "$TM_BASE_URL/api/v1/profiles".
Tools work, but specific ones return 404. Your TrafficMorphserver is running an older build that doesn't expose thoseendpoints yet. Upgrade the server.
PLAN_UPGRADE_REQUIRED on every call. Your TrafficMorphaccount doesn't have API access enabled. Check your accountsettings, or point the MCP server at a deployment where youraccount has API access.
tm_create_profile refuses with "A profile named X alreadyexists". The server's POST endpoint is upsert-by-name and wouldsilently replace the existing profile (including scripts /callbacks / alerts the MCP tool surface doesn't expose). Usetm_update_profile(profile_id=<id from error>) instead, or picka unique name.
tm_update_profile refuses to rename. A rename would collidewith another profile under your account. The error names bothids; either pick a unique new name OR call tm_update_profileagainst the OTHER profile if you actually meant to edit that one.
Domain verify returns 400 immediately. That's the fail-fastcontract — verification is NOT polling-style. The 400 messageincludes the expected TXT record / URL + token; read it back tothe user, wait for them to install the record / file, then retry.
Versioning
| Source | Notes | |
|---|---|---|
| MCP server release | tm-mcp on PyPI |
Pin via pip install 'tm-mcp==X.Y.Z'. See CHANGELOG.md. |
| API version | tm_mcp.__version__ matches the PyPI version |
Use to identify what's installed in support tickets |
The MCP server is a thin layer over the trafficmorph Python SDK.The dependency pin in pyproject.toml constrains the SDK rangethis release is tested against; updating the SDK ships as a newtm-mcp release.
See also
- MCP-USAGE.md — comprehensive user guide with worked example conversations
- CHANGELOG.md — per-release history
- STABILITY.md — v1.0 stability promise (what tools / prompts / resources stay stable across 1.x)
- examples/ — full conversation transcripts (triage, setup, capture import)
- TrafficMorph Python SDK — the HTTP layer this MCP server uses