Model Context Protocol
Connect Dialfetch over MCP and your agent can place real phone calls — navigating IVR menus, waiting on hold, and talking to humans — then hand back structured answers, a transcript, and a recording. Works in Claude, Cursor, VS Code, Windsurf, Cline, the Claude Agent SDK, and any MCP client. Endpoint https://app.dialfetch.com/api/mcp.
The Dialfetch MCP server exposes the same goal-call engine as the REST API, as a set of MCP tools. You give it a phone number and a plain-language goal; it makes the call and returns the result. It is industry-agnostic — retail hours, flight status, order and claim status, license and insurance verification, government and utility lines.
Calls are asynchronous (typically 2–10 minutes, longer with holds or transfers). place_call returns a call_id right away; you then poll get_call or use wait_for_call until the call is done.
The endpoint speaks Streamable HTTP (with SSE fallback). Authenticate with a Dialfetch API key from your dashboard (starts with df_), or use one-click OAuth on clients that support custom connectors.
Add to claude_desktop_config.json (Settings → Developer → Edit config):
{
"mcpServers": {
"dialfetch": {
"command": "npx",
"args": [
"-y", "mcp-remote", "https://app.dialfetch.com/api/mcp",
"--header", "Authorization: Bearer df_YOUR_KEY"
]
}
}
}Settings → Connectors → Add custom connector. Paste the URL below and finish the one-click OAuth sign-in — no API key to paste.
https://app.dialfetch.com/api/mcpAdd to ~/.cursor/mcp.json (global) or .cursor/mcp.json (project):
{
"mcpServers": {
"dialfetch": {
"url": "https://app.dialfetch.com/api/mcp",
"headers": { "Authorization": "Bearer df_YOUR_KEY" }
}
}
}Add to .vscode/mcp.json:
{
"servers": {
"dialfetch": {
"type": "http",
"url": "https://app.dialfetch.com/api/mcp",
"headers": { "Authorization": "Bearer df_YOUR_KEY" }
}
}
}Both take a remote server URL plus an Authorization header:
{
"mcpServers": {
"dialfetch": {
"serverUrl": "https://app.dialfetch.com/api/mcp",
"headers": { "Authorization": "Bearer df_YOUR_KEY" }
}
}
}Attach it as an MCP server on your agent run:
{
"type": "url",
"url": "https://app.dialfetch.com/api/mcp",
"name": "dialfetch",
"authorization_token": "df_YOUR_KEY"
}Bridge the remote server to stdio with mcp-remote:
npx -y mcp-remote https://app.dialfetch.com/api/mcp --header "Authorization: Bearer df_YOUR_KEY"API key (all clients). Send an Authorization: Bearer df_… header, exactly like the REST API. Create and revoke keys on the keys page.
OAuth 2.1 (custom connectors). Clients like Claude.ai discover our authorization server automatically (Dynamic Client Registration + PKCE), send you through a Dialfetch sign-in and consent screen, and receive a scoped token. Nothing to copy or paste. Discovery lives at /.well-known/oauth-authorization-server.
The reliable pattern is place → poll:
1. place_call({ phone: "+18002758777", goal: "What time does the retail counter close today?",
extract: [{ name: "closing_time", type: "string" }] })
→ { call_id: "…", status: "queued" }
2. get_call({ call_id }) // repeat every ~15-30s
→ status: queued → dialing → ivr → in_conversation → … → completed
3. when status is completed / failed / canceled, read:
{ outcome, answers: { closing_time: { value, quote } }, transcript, recording_url, ... }For short calls you can let the server wait for you with wait_for_call({ call_id, timeout_seconds }) — it blocks (emitting progress) up to 5 minutes, then returns the call with timed_out so you can keep polling. Prefer polling for calls that may involve long holds or transfers.
| Tool | Inputs | What it does |
|---|---|---|
| place_call | phone, goal, extract?, context?, disclose_ai?, from_number_id?, max_duration_minutes?, webhook_url? | Start an outbound goal-call. Returns { call_id, status } immediately (async). |
| get_call | call_id | Current status, outcome, structured answers, notes, transcript, recording URL, duration, cost. |
| wait_for_call | call_id, timeout_seconds? | Block (with progress) until the call is terminal or the timeout elapses. Returns the call + timed_out. |
| cancel_call | call_id | Cancel a queued or in-progress call. |
| list_calls | status?, limit?, cursor? | List your recent calls, newest first, with cursor pagination. |
| get_call_recording | call_id | A short-lived (10 min) presigned URL to the call audio. |
| list_phone_numbers | — | The caller-ID numbers you own (use an id as from_number_id). |
| get_pricing | — | Current per-call and per-number pricing, in cents. |
| get_credit_balance | — | Your current prepaid credit balance, in cents. |
extract is an array of { name, type, description? } where type is one of string, number, boolean, date. Answers come back keyed by field name as { value, quote } — the value plus the verbatim line that supports it.
Resource dialfetch://calls/{id} — any call and its full result as an addressable JSON resource.
Prompts — ready-made starting points your client can surface: check_store_hours, verify_license, and get_order_status. Each expands into a fully-formed instruction that already wires up place_call with the right extract schema.
“Call the Walgreens at +1 305-555-0148 and tell me what time the pharmacy closes today.”
place_call({
phone: "+13055550148",
goal: "What time does the pharmacy counter close today?",
extract: [{ name: "pharmacy_closing_time", type: "string" }]
}) → poll get_call → answers.pharmacy_closing_timeConfirm a contractor’s license is active before funding — without sending any SSN.
place_call({
phone: "+18005551234",
goal: "Verify that contractor license CGC1523456 for Acme Builders is active.",
context: "License #CGC1523456, holder: Acme Builders LLC",
extract: [
{ name: "is_active", type: "boolean" },
{ name: "expires", type: "date" },
{ name: "status_detail", type: "string" }
]
})Let a support agent (human or AI) chase a status without sitting on hold.
place_call({
phone: "+18665557000",
goal: "Get the current status of order A-88213 and the expected ship date.",
context: "Order reference A-88213",
extract: [
{ name: "status", type: "string" },
{ name: "expected_date", type: "date" }
]
})Every call goes through the same guardrails as the REST API: goal-screening (which fails closed), a per-call credit hold, and per-number / per-user rate limits. Calls disclose that they are an AI by default (disclose_ai). The agent will refuse to read out or collect sensitive personal data — SSNs, passwords, full card numbers — on a call. An MCP client cannot bypass any of these.