Model Context Protocol

Give your AI agent a phone.

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.

Overview

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.

Connect a client

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.

Claude Desktop

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"
      ]
    }
  }
}

Claude.ai (web) — custom connector

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/mcp

Cursor

Add 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" }
    }
  }
}

VS Code (Copilot agent mode)

Add to .vscode/mcp.json:

{
  "servers": {
    "dialfetch": {
      "type": "http",
      "url": "https://app.dialfetch.com/api/mcp",
      "headers": { "Authorization": "Bearer df_YOUR_KEY" }
    }
  }
}

Windsurf / Cline

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" }
    }
  }
}

Claude Agent SDK / API

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"
}

Any stdio-only client

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"

Authentication

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.

How calls work (async)

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.

Tools reference

ToolInputsWhat it does
place_callphone, 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_callcall_idCurrent status, outcome, structured answers, notes, transcript, recording URL, duration, cost.
wait_for_callcall_id, timeout_seconds?Block (with progress) until the call is terminal or the timeout elapses. Returns the call + timed_out.
cancel_callcall_idCancel a queued or in-progress call.
list_callsstatus?, limit?, cursor?List your recent calls, newest first, with cursor pagination.
get_call_recordingcall_idA short-lived (10 min) presigned URL to the call audio.
list_phone_numbersThe caller-ID numbers you own (use an id as from_number_id).
get_pricingCurrent per-call and per-number pricing, in cents.
get_credit_balanceYour 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.

Resources & prompts

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.

Cookbook

Retail / office hours

“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_time

License / insurance verification

Confirm 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" }
  ]
})

Order / claim status

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" }
  ]
})

Safety & limits

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.

Prefer HTTP? See the REST API reference.