Dialpad MCP Server
by cprfirstaid
README.md
# Dialpad MCP Server
An [MCP](https://modelcontextprotocol.io) server that exposes **Dialpad calls and
transcripts** as tools, for use from Claude Desktop and Claude Code.
## Tools
| Tool | What it does |
| --- | --- |
| `get_call(call_id)` | Full details for a concluded call: direction, duration, participants, recording URLs, CSAT, routing, voicemail, transcription text. |
| `initiate_call(user_id, phone_number, ...)` | Place an outbound click-to-call from a Dialpad user to a number (E.164). |
| `get_call_transcript(call_id)` | Raw Dialpad Ai transcript (lines + moments). |
| `get_call_transcript_text(call_id)` | Transcript flattened into a readable `Speaker: text` block. |
| `get_call_transcript_url(call_id)` | A downloadable URL for the transcript. |
| `list_calls(days_ago, target_id, target_type, office_id, timezone, limit)` | Historical call records via the Stats export API (kicks off an export, polls, downloads + parses the CSV). Takes ~15–60s. |
Base API: `https://dialpad.com/api/v2` (override with `DIALPAD_BASE_URL` for sandbox).
## Setup
```bash
cd ~/projects/dialpad-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
# Add your key (kept out of git via .gitignore)
cp .env.example .env
# then edit .env and set DIALPAD_API_KEY=...
```
The server auto-loads `.env`, so your API key never has to live in client config JSON.
### Get a Dialpad API key
Dialpad Admin → **Company Settings → Authentication → API keys** (you need a Company
Admin role). Paste the key into `.env` as `DIALPAD_API_KEY`.
## Connect to Claude Code
From this project directory it's picked up automatically via `.mcp.json`. To register
it for **all** projects (user scope):
```bash
claude mcp add dialpad -s user \
-- /home/fredericktejada/projects/dialpad-mcp/.venv/bin/python -m dialpad_mcp
```
Then run `claude mcp list` to confirm it shows up, and `/mcp` inside a session.
## Connect to Claude Desktop
Edit the config file:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
Add:
```json
{
"mcpServers": {
"dialpad": {
"command": "/home/fredericktejada/projects/dialpad-mcp/.venv/bin/python",
"args": ["-m", "dialpad_mcp"],
"cwd": "/home/fredericktejada/projects/dialpad-mcp"
}
}
}
```
> On Windows + WSL, the desktop app can't call the Linux venv directly. Either run
> Claude Desktop's command through `wsl.exe` (`"command": "wsl.exe", "args": ["-e",
> "/home/fredericktejada/projects/dialpad-mcp/.venv/bin/python", "-m", "dialpad_mcp"]`)
> or run the server inside a native Windows Python install.
Restart Claude Desktop after editing.
## Export transcripts to a Google Sheet
`export_transcripts.py` pulls call transcripts into a Google Sheet, one row per call.
```bash
# New sheet, last 7 days, recorded calls only
.venv/bin/python export_transcripts.py --days 7 --recorded-only
# Full rebuild of a tab (clears + rewrites)
.venv/bin/python export_transcripts.py --days 14 --recorded-only --sheet-id <ID> --tab Transcripts
# Append only NEW calls (dedup by Call ID; skips transcript fetch for known calls)
.venv/bin/python export_transcripts.py --append --days 2 --recorded-only --sheet-id <ID> --tab Transcripts
```
### Daily scheduled append
`run_daily.sh` appends new calls to the live sheet
(`1Jlea4o_0g0h9HygILZV4XbN305E_fW9DWgD-qDh3q08`). Installed in cron at 07:45 Manila:
```cron
45 7 * * * /home/fredericktejada/projects/dialpad-mcp/run_daily.sh >> .../cron.log 2>&1
```
`--append` reads existing Call IDs first and skips them, so the daily run only fetches
transcripts for genuinely new calls (fast, idempotent — safe to overlap windows).
Columns: Call ID, Date started, Direction, Category, External/Internal number, Agent,
Email, Talk (s), Total (s), Recorded, Moments, Transcript.
Google auth reuses the shared OAuth client at `~/.api-credentials/google/oauth-client.json`.
The account token lives in `secrets/google-sheets-token.json` (gitignored). To (re)connect
a different Google account on WSL, use the manual paste flow:
```bash
.venv/bin/python g_auth.py --url # open the printed URL, sign in, click Allow
# copy the localhost redirect URL into code.txt
.venv/bin/python g_auth.py --finish # saves the token
```
Key options: `--days`, `--limit`, `--recorded-only`, `--target-id`/`--target-type`,
`--office-id`, `--timezone` (default Australia/Melbourne), `--sheet-id`, `--title`, `--tab`.
## Analytics tabs (Overview / Call Reasons / Customer Type)
`build_analytics.py` adds three analysis tabs to the spreadsheet:
```bash
.venv/bin/python build_analytics.py --days 14 --limit 8000 \
--sample-reasons 800 --sample-customers 800 \
--sheet-id 1Jlea4o_0g0h9HygILZV4XbN305E_fW9DWgD-qDh3q08
```
- **Overview** — per agent/line: total calls, inbound/outbound, answered, avg talk, avg
handle time, total talk hours. Built on the full call set (lines vs agents split by name).
- **Call Reasons** — recorded inbound transcripts classified (keyword rules + custom
moments) into Booking / Voicemail / Theory / Certificate / Cancellation / Payment /
Reschedule-transfer / Location / Group / Course-info / Other, with avg handle time per
concern. Runs on a random sample (`--sample-reasons`), extrapolated to the population.
- **AHT by Agent x Concern** — matrix of each agent's average handle time per concern
(cells show `m:ss (n)`), built from the same reason sample. Agents need ≥8 sampled calls.
- **Customer Type** — unique inbound numbers matched against **aXcelerate**
(`/contacts/search?q=<local#>`) and **HubSpot** (`crm/v3/objects/contacts/search`,
`CONTAINS_TOKEN *<9 digits>*` on phone+mobilephone). Returning = found in either.
Runs on a random sample (`--sample-customers`).
Lookups are cached in `.cache_lookups.json` (gitignored) so re-runs are cheap. aXcelerate
tokens come from `~/.api-credentials/.env`; the HubSpot token from
`~/projects/cprfa-tickets/secrets/.env` (needs `crm.objects.contacts.read` scope). Volume
note: ~8k call legs / 14 days, so reasons + customer type are sampled by design.
## Daily CALLS + AHT tracker (build_daily_report.py)
Row-per-day tabs inspired by the ops "2026 REPORTS" sheet:
- **Daily AHT** — `Day | AHT (all) | <each agent's AHT>` (one row per day)
- **Daily CALLS** — `Day | volume metrics (Admin/Sales inbound, outbound, missed,
abandoned, voicemail) | Top Call Drivers (reason categories)`
```bash
# Full 30-day (re)build — pulls in 5-day chunks (single 30d export won't generate)
.venv/bin/python build_daily_report.py --days 30
# Daily append of newly-completed days (run by run_daily.sh / cron)
.venv/bin/python build_daily_report.py --append
```
Dialpad can't generate a 30-day export in one shot, so `fetch_chunked` **primes all
day-range chunks at once** (concurrent server-side generation) then collects each as it
finishes (`days_ago_start`/`days_ago_end`). Today is excluded (partial); the daily cron
adds each day once complete. Drivers reuse the transcript cache.
`run_daily.sh` (cron, 07:45 Manila) now appends **transcripts + Daily AHT + Daily CALLS**.
## Notes
- `list_calls` uses the Stats **export** API, which is asynchronous — the tool blocks
while polling (up to ~90s) then parses the returned CSV.
- Rate limits vary per endpoint (`get_call` is only ~10/min; transcripts ~1200/min).
- Secrets live in `.env` only — never commit it.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues