A read-only Model Context Protocol server for the CallRail v3 REST API. Lets Claude Code (or any MCP client) list and inspect calls, companies, trackers, form submissions, text conversations, users, tags, notifications, and integrations.
Read-only by design — no outbound calls, SMS sends, or updates are exposed.
The package is plain Python. With uv:
uv tool install --from . callrail-mcpOr with pip:
pip install .| Variable | Required | Notes |
|---|---|---|
CALLRAIL_API_KEY |
yes | Found at Account Settings → Integrations → API Access in CallRail. |
CALLRAIL_BASE_URL |
no | Defaults to https://api.callrail.com/v3. |
CALLRAIL_DEFAULT_ACCOUNT_ID |
no | If set, account_id is optional on each tool call. Otherwise, use list_accounts first. For agency users, use the agency account_id; sub-companies are listed via list_companies and scoped per-tool with company_id. |
CALLRAIL_MCP_TRANSPORT |
no | stdio (default) for local Claude Desktop / Claude Code, or http for a remote server reachable from mobile / claude.ai. |
CALLRAIL_MCP_BEARER_TOKEN |
http only | Required when CALLRAIL_MCP_TRANSPORT=http. Clients must send Authorization: Bearer <token>. |
CALLRAIL_MCP_HOST |
no | Default 0.0.0.0. HTTP transport only. |
CALLRAIL_MCP_PORT |
no | Default 8000. HTTP transport only. |
A starter .env.example is included.
claude mcp add callrail \
--env CALLRAIL_API_KEY=your_key \
-- uvx --from . callrail-mcpFor Claude Desktop, add to claude_desktop_config.json:
{
"mcpServers": {
"callrail": {
"command": "uvx",
"args": ["--from", "/path/to/callrail-mcp", "callrail-mcp"],
"env": { "CALLRAIL_API_KEY": "your_key" }
}
}
}Mobile, claude.ai web, and cloud Claude Code (the GitHub-connected web agent) all need a hosted server — they can't run a local stdio process. Run with:
export CALLRAIL_API_KEY=your_key
export CALLRAIL_MCP_TRANSPORT=http
export CALLRAIL_MCP_BEARER_TOKEN=$(python -c 'import secrets; print(secrets.token_urlsafe(32))')
callrail-mcpA Dockerfile and fly.toml are committed. From the repo root:
# 1. Install + auth (one-time)
brew install flyctl # or: curl -L https://fly.io/install.sh | sh
fly auth login
# 2. Create the app, keeping our committed fly.toml
fly launch --copy-config --no-deploy --name <unique-app-name>
# 3. Set secrets
fly secrets set \
CALLRAIL_API_KEY=your_callrail_key \
CALLRAIL_MCP_BEARER_TOKEN=$(python -c 'import secrets; print(secrets.token_urlsafe(32))')
# 4. Ship it
fly deploy
# 5. Grab the public hostname + token for the connector step below
fly status
fly secrets list # token value is hidden; you saved it from step 3, right?The default fly.toml provisions a 256 MB shared-cpu machine with auto-stop —
fine for occasional use. For always-warm response, set
min_machines_running = 1 in fly.toml and redeploy.
Once deployed, in claude.ai → Settings → Connectors → Add custom connector:
URL: https://<your-app-name>.fly.dev/mcp
Header: Authorization: Bearer <CALLRAIL_MCP_BEARER_TOKEN>
The same connector is then available across:
- claude.ai web chat
- Claude on iOS / Android
- Cloud Claude Code (every GitHub-connected repo, current and future)
- Local Claude Code / Claude Desktop (alternative to the stdio config above)
If you'd rather scope the connector to specific repos instead of account-wide,
commit a .mcp.json in each repo root:
{
"mcpServers": {
"callrail": {
"type": "http",
"url": "https://<your-app-name>.fly.dev/mcp",
"headers": { "Authorization": "Bearer ${CALLRAIL_MCP_BEARER_TOKEN}" }
}
}
}Then set CALLRAIL_MCP_BEARER_TOKEN as a Claude Code secret for that repo.
Custom MCP connectors require a Pro/Team/Enterprise plan in claude.ai.
| Tool | Endpoint |
|---|---|
list_accounts, get_account |
/a.json, /a/{id}.json |
list_companies, get_company |
/a/{id}/companies(.json) |
list_calls, get_call |
/a/{id}/calls(.json) |
get_call_summary, get_call_timeseries |
/a/{id}/calls/summary.json, /calls/timeseries.json |
get_call_recording |
/a/{id}/calls/{call_id}/recording.json |
list_trackers, get_tracker |
/a/{id}/trackers(.json) |
list_form_submissions |
/a/{id}/form_submissions.json |
list_text_conversations, get_text_conversation |
/a/{id}/text-messages(.json) |
list_users, get_user |
/a/{id}/users(.json) |
list_tags, list_notifications, list_integrations |
/a/{id}/tags.json, /notifications.json, /integrations.json |
Pagination is not auto-followed: each list tool returns the raw
{page, per_page, total_pages, total_records, …} envelope so the client can
decide whether to fetch the next page (page=N).
uv sync
uv run callrail-mcp # stdio server
npx @modelcontextprotocol/inspector uv run callrail-mcp # interactive UIMIT.