TrafficMorph MCP server — drive load tests and triage regressions from Claude / Cursor / Continue.dev.
Project description
TrafficMorph MCP Server
████████╗██████╗ █████╗ ███████╗███████╗██╗ ██████╗
╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██╔════╝██║██╔════╝
██║ ██████╔╝███████║█████╗ █████╗ ██║██║
██║ ██╔══██╗██╔══██║██╔══╝ ██╔══╝ ██║██║
██║ ██║ ██║██║ ██║██║ ██║ ██║╚██████╗
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝
███╗ ███╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗
████╗ ████║██╔═══██╗██╔══██╗██╔══██╗██║ ██║
██╔████╔██║██║ ██║██████╔╝██████╔╝███████║
██║╚██╔╝██║██║ ██║██╔══██╗██╔═══╝ ██╔══██║
██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ██║ ██║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
Drive TrafficMorph from Claude Desktop, Claude Code, Cursor, or any other host that speaks the Model Context Protocol.
The flagship use case is CI-failure triage:
"My TrafficMorph CI step just failed on run 1234 — what regressed vs the baseline?"
→ Claude calls the right tools, fetches the relevant runs, computes the per-metric delta, and produces a human-readable triage report in seconds.
Current release: 1.2.0 — 25 tools + 4 prompts + 5 resources (34 catalog entries total). See CHANGELOG.md for the per-release history and STABILITY.md for what 1.x commits to keeping stable. See MCP-USAGE.md for worked example conversations.
Quick start
Four steps from zero to "Claude is driving my TrafficMorph account":
1. Get an API key
Open the TrafficMorph app → Settings → API Keys → click
Generate. 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 with a--helpflag. Invoking it directly without env vars exits 2 with a configuration error. The host (Claude Code / Desktop) subprocesses it and pipes JSON-RPC over stdio — you don't run it 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. The
server reads both at startup and fails fast with a clean
"couldn't start" message if either is missing — your host's log
shows 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 your TrafficMorph 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's browser 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 server
refuses to start without it and surfaces a clear "$TM_BASE_URL
is not set" error in your MCP host's log. Set it in the host
config (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 conversations including failure triage, new-test setup, and capture-driven profile import.
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 through a specific tool sequence. They're how you kick off a known workflow without 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 — usually at session start via @-mention. They wrap the corresponding read tools 1:1; the difference is who decides when to read (AI for tools, 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-side
file path. The MCP server validates each path before passing it
through:
| 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's
server 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't
delivered to the subprocess by your MCP host. The error names the
missing 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 — typically
a placeholder like https://YOUR-TRAFFICMORPH-HOST that wasn't
edited, a typo in the hostname, or an internal URL not reachable
from where the MCP host runs. Read the URL back from
claude mcp get trafficmorph and confirm it resolves with
curl -I "$TM_BASE_URL/api/v1/profiles".
Tools work, but specific ones return 404. Your TrafficMorph server is running an older build that doesn't expose those endpoints yet. Upgrade the server.
PLAN_UPGRADE_REQUIRED on every call. Your TrafficMorph
account doesn't have API access enabled. Check your account
settings, or point the MCP server at a deployment where your
account has API access.
tm_create_profile refuses with "A profile named X already
exists". The server's POST endpoint is upsert-by-name and would
silently replace the existing profile (including scripts /
callbacks / alerts the MCP tool surface doesn't expose). Use
tm_update_profile(profile_id=<id from error>) instead, or pick
a unique name.
tm_update_profile refuses to rename. A rename would collide
with another profile under your account. The error names both
ids; either pick a unique new name OR call tm_update_profile
against the OTHER profile if you actually meant to edit that one.
Domain verify returns 400 immediately. That's the fail-fast contract — verification is NOT polling-style. The 400 message includes the expected TXT record / URL + token; read it back to the 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 range
this release is tested against; updating the SDK ships as a new
tm-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
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 tm_mcp-1.2.1.tar.gz.
File metadata
- Download URL: tm_mcp-1.2.1.tar.gz
- Upload date:
- Size: 79.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdef679a2654918862ed540ffeb3c815537e0feaf2090f66bfdfae4cd177d7a7
|
|
| MD5 |
48f705075c2cede2047a3b2740365f93
|
|
| BLAKE2b-256 |
2395ddea50628bb850c993d736839062183b5b8756fddd57541bb1b02a438a6a
|
Provenance
The following attestation bundles were made for tm_mcp-1.2.1.tar.gz:
Publisher:
publish-tm-mcp.yml on trafficmorph-gif/tm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tm_mcp-1.2.1.tar.gz -
Subject digest:
bdef679a2654918862ed540ffeb3c815537e0feaf2090f66bfdfae4cd177d7a7 - Sigstore transparency entry: 1573577265
- Sigstore integration time:
-
Permalink:
trafficmorph-gif/tm@d712946e7ed723c21fbc877ed20d3e2d5bfc9ce3 -
Branch / Tag:
refs/tags/tm-mcp-v1.2.1 - Owner: https://github.com/trafficmorph-gif
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-tm-mcp.yml@d712946e7ed723c21fbc877ed20d3e2d5bfc9ce3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tm_mcp-1.2.1-py3-none-any.whl.
File metadata
- Download URL: tm_mcp-1.2.1-py3-none-any.whl
- Upload date:
- Size: 65.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e3266bf120cc85432126f8de1e574e33bdaa91d65f55eccf305394f181e58c3
|
|
| MD5 |
92884ceef3f8ca169df635b412772d95
|
|
| BLAKE2b-256 |
1cb8bc1c8994aca0c4bdc7ea0fd59de2ee117ff58b103a984421c11ae329d027
|
Provenance
The following attestation bundles were made for tm_mcp-1.2.1-py3-none-any.whl:
Publisher:
publish-tm-mcp.yml on trafficmorph-gif/tm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tm_mcp-1.2.1-py3-none-any.whl -
Subject digest:
8e3266bf120cc85432126f8de1e574e33bdaa91d65f55eccf305394f181e58c3 - Sigstore transparency entry: 1573577304
- Sigstore integration time:
-
Permalink:
trafficmorph-gif/tm@d712946e7ed723c21fbc877ed20d3e2d5bfc9ce3 -
Branch / Tag:
refs/tags/tm-mcp-v1.2.1 - Owner: https://github.com/trafficmorph-gif
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-tm-mcp.yml@d712946e7ed723c21fbc877ed20d3e2d5bfc9ce3 -
Trigger Event:
push
-
Statement type: