Find the latency in your AI agent, before your users do.
Relay is an open-source MCP server + CLI that audits AI agent and voice-agent codebases for latency bottlenecks. It reads your real source with an AST engine, ranks issues by impact, runs a verification pass to drop false positives, and can apply fixes directly from Claude Code.
No account. No API key. No backend. It runs entirely on your machine.
Quickstart · MCP tools · CLI · What it detects · Contributing
Why Relay
Agent latency is rarely one slow model call. It is a streaming response that was never
streamed, three awaits that should have been one asyncio.gather, an HTTP client
rebuilt on every request, a requests.post sitting inside an async def. These are
structural, they are invisible in a profiler flame graph averaged over a request, and
they are obvious in the AST.
Relay finds them, then argues with itself about each one so you don't get a wall of false positives.
Quickstart
In Claude Code (MCP)
claude mcp add relay -- uvx relay-mcp
Or add it to your MCP config (~/.claude/claude_code_config.json) by hand:
{
"mcpServers": {
"relay": {
"command": "uvx",
"args": ["relay-mcp"]
}
}
}
Then, in Claude Code, just say:
Audit this codebase for latency issues
That's it - there is no key to configure.
As a CLI
pip install relay-mcp # or: uv tool install relay-mcp
relay scan . # structural pass - candidate latency patterns
relay verify . # semantic pass - only the confirmed gaps
relay report . # write relay-report.json
Requires Python 3.11+.
Features
- AST-based detection. Parses Python with the real grammar, not regexes, so it
understands
async/await, call targets, keyword arguments, and**kwargsforwarding. - Six latency detectors. Streaming gaps, sequential tool calls, cache misses, blocking I/O in async code, per-request client construction, and prefetch opportunities - see What it detects.
- Verification pass. Every candidate is re-read in context and either confirmed as a real gap or dismissed as a false positive, each with a one-line reason.
- Impact ranking. Findings are scored and sorted so the highest-impact fixes surface first.
- Apply fixes from Claude Code.
apply_fixrewrites the flagged site (dry-run by default) so you review before anything is written. - MCP and CLI share one engine. Agent output and terminal output never diverge.
- CI gate.
relay verify --fail-on highexits non-zero when a confirmed high-impact gap remains, so it can fail a pipeline. - Dashboard reports. Each audit writes a
relay-report.jsonyou can drop into the Relay dashboard for a visual view. - Essentially no dependencies. Just the
mcppackage - no network calls, no telemetry, nothing leaves your machine. - Persistent state. The last audit is cached at
~/.relay/last-audit.json, soget_findings,verify_audit, andapply_fixkeep working across MCP restarts.
MCP tools
| Tool | Description |
|---|---|
audit_codebase(directory, write_report=true) |
Reads Python files in a directory, returns ranked findings (each with a verification verdict) and writes relay-report.json. |
get_findings(severity?, finding_type?, status?) |
Filters findings from the last audit. |
verify_audit() |
Shows which findings the semantic pass confirmed vs dismissed, with reasons. |
apply_fix(finding_rank, file_path?, dry_run=true) |
Applies a fix. Dry run by default; file path defaults to the finding's file. |
The last audit is cached at ~/.relay/last-audit.json, so the read-only tools keep
working after an MCP server restart.
CLI
relay scan <dir> # structural pass - flag candidate latency patterns
relay verify <dir> # semantic pass - keep only confirmed gaps
relay report <dir> [-o f] # write relay-report.json for the dashboard
relay mcp # run the MCP server (same as relay-mcp)
| Flag | Applies to | Meaning |
|---|---|---|
--json |
scan, verify |
Print the full report as JSON (for CI, jq). |
--fail-on {none,high,any} |
verify |
Exit non-zero when confirmed gaps remain. Default none. |
-o, --output |
report |
Output path. Default <dir>/relay-report.json. |
Use in CI
- run: pip install relay-mcp
- run: relay verify . --fail-on high
--fail-on high fails the job only when a confirmed high-impact gap remains, so a
noisy structural match will not break your build.
What it detects
| Detector | What it flags |
|---|---|
| SyncOutput | LLM calls missing streaming (stream=True), including calls that forward **kwargs. |
| SequentialTools | Consecutive data-independent awaits that should run under asyncio.gather(). |
| CacheMiss | The same fully-static LLM call repeated at multiple call sites with no caching. |
| BlockingIO | Sync requests / DB calls, plus time.sleep and blocking subprocess calls inside async functions. |
| ColdStart | API clients (OpenAI, httpx.Client, aiohttp.ClientSession, …) built per request instead of at startup. |
| Prefetch | Predictable tool-call pairs that could be started speculatively. |
The verification pass
Every finding is re-read in context and either confirmed as a real latency gap or
dismissed as a false positive, with a one-line reason either way - for example
"one branch (lookup) is in-memory - nothing to parallelize" or
"structured/JSON output - not streamable". The summary counts
(flagged, confirmed, dismissed, confirmed_high_impact) drive both the CLI output
and the --fail-on CI gate.
This is the difference between a linter you mute after a week and one you keep in CI.
The dashboard
Relay itself is fully local. If you want a visual view of a report, the
Relay dashboard lets you drag in any
relay-report.json and browse findings, severity breakdown, and time-lost aggregates.
The report never leaves your browser - it is parsed client-side.
Configuration
None. Relay needs no environment variables, no config file, and no API key. It reads your source, writes a report, and does nothing else.
Repository structure
relay-mcp/
├── mcp_server/ # MCP server + CLI (ships as relay-mcp on PyPI)
│ ├── server.py # entry point, exposes the MCP tools
│ ├── cli.py # `relay` command (scan / verify / report / mcp)
│ └── engine/ # audit engine (detectors, scorer, verify, code_parser)
├── tests/ # end-to-end engine tests
├── assets/ # logo and screenshots
├── run.sh / run.ps1 # dev runners (install / register / test / serve)
└── pyproject.toml # package config for uvx / pip install
Development
pip install -e ".[dev]" # install relay + relay-mcp in editable mode
pytest # run the test suite
ruff check . # lint
relay scan . # dogfood the engine on itself
See RUNNING.md for the full dev workflow and CONTRIBUTING.md for how to add a detector or open a PR.
Project
- Contributing - CONTRIBUTING.md
- Code of conduct - CODE_OF_CONDUCT.md
- Security policy - SECURITY.md
- Changelog - CHANGELOG.md
Relay is a product of the Arclat studio. Learn more at relay.arclat.com.
License
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 relay_arclat-0.3.0.tar.gz.
File metadata
- Download URL: relay_arclat-0.3.0.tar.gz
- Upload date:
- Size: 28.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d12641a8c16148869942666384d248593a7cdb4e5abb53715c7d9dfc738e5fe0
|
|
| MD5 |
580b669b29b12e1d34d8a97162060190
|
|
| BLAKE2b-256 |
52043354a205be331be547988a8289309ac5c215d0b5c87666efac6fbd4f1144
|
Provenance
The following attestation bundles were made for relay_arclat-0.3.0.tar.gz:
Publisher:
release.yml on arclat-ai/relay-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
relay_arclat-0.3.0.tar.gz -
Subject digest:
d12641a8c16148869942666384d248593a7cdb4e5abb53715c7d9dfc738e5fe0 - Sigstore transparency entry: 2275235031
- Sigstore integration time:
-
Permalink:
arclat-ai/relay-mcp@0e543a3314dc02e949fbc4c4366d246bde2258b7 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/arclat-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0e543a3314dc02e949fbc4c4366d246bde2258b7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file relay_arclat-0.3.0-py3-none-any.whl.
File metadata
- Download URL: relay_arclat-0.3.0-py3-none-any.whl
- Upload date:
- Size: 31.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e711b59744b227f60f309d509756431473b83270470944d411121b317b9bbad
|
|
| MD5 |
f13b1388b3049120c4c0c54be188de0f
|
|
| BLAKE2b-256 |
aa0b872682fb7b66425b0cf1ffc39dac79f33d749cc454d9c205e0713e9356a0
|
Provenance
The following attestation bundles were made for relay_arclat-0.3.0-py3-none-any.whl:
Publisher:
release.yml on arclat-ai/relay-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
relay_arclat-0.3.0-py3-none-any.whl -
Subject digest:
6e711b59744b227f60f309d509756431473b83270470944d411121b317b9bbad - Sigstore transparency entry: 2275235101
- Sigstore integration time:
-
Permalink:
arclat-ai/relay-mcp@0e543a3314dc02e949fbc4c4366d246bde2258b7 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/arclat-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0e543a3314dc02e949fbc4c4366d246bde2258b7 -
Trigger Event:
push
-
Statement type: