Migrating from stdio to Streamable HTTP
This guide walks through migrating your axm-mcp setup from the default stdio transport to the persistent Streamable HTTP server.
Why migrate?
| stdio | HTTP | |
|---|---|---|
| Processes | One per conversation | Single shared server |
| Startup time | One uvx resolve per conversation (cached, fast) |
Instant (already running) |
| AST cache | Lost between conversations | Shared across all calls |
| Protocol sessions | Per-conversation only | Persistent |
| CPU usage | Risk of zombie processes | Single managed process |
Concurrency model (what "shared" really means)
The HTTP server serves many conversations from one process, so it is explicitly designed not to let one slow call stall the others:
- Sync tools run off the event loop. In HTTP mode every tool's synchronous
body is offloaded to a worker thread (
asyncio.to_thread), so a multi-minuteverifycannot freeze/health, keep-alives, or other conversations. - Per-key serialization. Calls that mutate the same resource are
serialized on purpose:
git_*tools by (normalized) repo path,protocol_*tools bysession_id. Two conversations committing to the same repo take turns; unrelated repos run in parallel. - Lock timeout is graceful. If a lock cannot be acquired within its timeout
(30 s), the call returns a structured
{success: false, error: "… busy, retry"}rather than a raw protocol error.
This holds whether a tool is called directly or through axm_call — both go
through the same execution path.
Prerequisites
- A recent
axm-mcpwith theserve/status/stopsubcommands (runaxm-mcp --helpto confirm they are present) - macOS (launchd integration) or any OS (manual
serve)
Step 1 — Start the server
Option A: launchd service (recommended on macOS)
This generates a launchd plist at ~/Library/LaunchAgents/io.axm.mcp-server.plist, loads it via launchctl, and starts the server automatically. The service restarts on crash and starts on login.
Option B: manual
Starts the server in the foreground on 127.0.0.1:9427.
To use a different port:
Step 2 — Verify the server is running
Expected output:
You can also hit the health endpoint directly:
Step 3 — Update .mcp.json
Replace the stdio config with the HTTP config.
Before (stdio — the default setup from the Quick Start):
{
"mcpServers": {
"axm-mcp": {
"command": "uvx",
"args": ["--python", "3.12", "--from", "axm-mcp[all]@latest", "axm-mcp"]
}
}
}
After (HTTP):
This file lives at ~/.claude.json (global, applies to all projects) or
.mcp.json at a project root (project-scoped — takes precedence over the global
file when present).
Step 4 — Restart Claude Code
Restart your Claude Code session so it picks up the new .mcp.json config. The MCP client will now connect to the HTTP server instead of forking a stdio process.
Rolling back to stdio
If you need to revert:
- Restore the stdio
.mcp.jsonconfig (the "Before" block above) - Stop the HTTP server:
- If you installed the launchd service:
- Restart Claude Code
Troubleshooting
Server not running
Fix: Start the server with axm-mcp serve or reinstall the service with axm-mcp install.
If using launchd, check the logs:
Port conflict
Fix: Another process is using port 9427. Either stop that process or use a different port:
Update your .mcp.json URL to match the new port.
You can also set the port via environment variable:
Zombie processes (100% CPU)
This typically happens with leftover stdio processes from before the migration.
Fix: Stop the HTTP server and restart it cleanly:
If using launchd:
Tools not responding after migration
All path-dependent tools require explicit path arguments in HTTP mode (the server has no per-conversation working directory). If a tool returns an error about missing paths, ensure you are passing absolute paths.
PermissionError — Full Disk Access (macOS)
Found in ~/Library/Logs/axm-mcp/stderr.log.
Cause: macOS blocks launchd background services from accessing ~/Documents, ~/Desktop, and ~/Downloads without Full Disk Access granted to the binary. Because the launchd plist points to a binary inside a uv cache or project virtualenv, the OS sandbox denies access to protected directories.
Fix options (in order of preference):
- Install the binary in
~/.local/bin/(recommended) — this path is outside the protected locations and is not subject to the same FDA restrictions:
uv tool install places the binary at ~/.local/bin/axm-mcp, which axm-mcp install will detect and use automatically.
- Specify the binary path explicitly — if the binary already lives at an FDA-exempt path, pass it directly:
- Grant Full Disk Access — if you need to keep the current binary location, grant Full Disk Access to your terminal application or directly to the
axm-mcpbinary in System Settings > Privacy & Security > Full Disk Access.