MCP Phone Tool: Give Claude a Phone Line

MCP Phone Tool: Give Claude a Phone Line

4 min read
Yanis Mellata
Integrations

What You're Building

An MCP server that gives any MCP-compatible client — Claude Code, Cursor, Windsurf, or your own agent — the ability to place real phone calls.

The server exposes one tool: place_phone_call. Claude says "call the restaurant and book a table," the tool dials the number, handles the conversation, and returns structured results.

Prerequisites:

  • Node.js 18+
  • npm install @modelcontextprotocol/sdk zod
  • An AgentPhone API key (get one free — 5 calls, no credit card)

The MCP Server

Here's the complete server. Save this as index.ts:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const API_KEY = process.env.AGENTPHONE_API_KEY!;
const BASE = "https://agentphone.app/api/v1";
const HEADERS = {
  "x-api-key": API_KEY,
  "Content-Type": "application/json",
};

const server = new McpServer({
  name: "agentphone",
  version: "1.0.0",
});

server.tool(
  "place_phone_call",
  "Place a real phone call to a business or person. The call is handled by " +
    "a voice AI that navigates IVR menus, waits on hold, handles transfers, " +
    "and has a natural conversation. Returns the outcome (achieved / " +
    "not_achieved / partial), a summary, and the full transcript. " +
    "Use when a task requires calling someone by phone — booking appointments, " +
    "verifying information, following up on orders, getting quotes.",
  {
    to_phone_number: z
      .string()
      .describe("Phone number in E.164 format (e.g. +14155551234)"),
    objective: z
      .string()
      .describe(
        "What the call should accomplish. Be specific — include names, " +
          "dates, reference numbers, relevant context."
      ),
    business_name: z
      .string()
      .optional()
      .describe("Name of the business being called"),
    website: z
      .string()
      .optional()
      .describe("Business website URL (provides additional context)"),
  },
  async ({ to_phone_number, objective, business_name, website }) => {
    // Create the call
    const createRes = await fetch(`${BASE}/calls`, {
      method: "POST",
      headers: HEADERS,
      body: JSON.stringify({
        to_phone_number,
        objective,
        business_name: business_name || "",
        website: website || "",
      }),
    });

    if (!createRes.ok) {
      const err = await createRes.json();
      return {
        content: [
          { type: "text", text: `Failed to create call: ${JSON.stringify(err)}` },
        ],
      };
    }

    const { data } = await createRes.json();
    const callId = data.call_id;

    // Poll until done
    let result: any;
    while (true) {
      await new Promise((r) => setTimeout(r, 4000));
      const pollRes = await fetch(`${BASE}/calls/${callId}`, {
        headers: HEADERS,
      });
      result = (await pollRes.json()).data;
      if (["completed", "failed", "canceled"].includes(result.status)) break;
    }

    const output = [
      `Call Status: ${result.status}`,
      `Outcome: ${result.outcome || "unknown"}`,
      `Details: ${result.outcome_details || ""}`,
      `Summary: ${result.summary || ""}`,
      `Duration: ${result.duration_seconds || 0}s`,
      ``,
      `Transcript:`,
      result.transcript || "No transcript available",
    ].join("\n");

    return { content: [{ type: "text", text: output }] };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

That's the entire server — 80 lines of TypeScript. One tool, clear description, straightforward implementation.

Setup

Install Dependencies

mkdir agentphone-mcp && cd agentphone-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod tsx

Configure Claude Code

Add to your MCP config. For Claude Code, edit ~/.claude.json or run:

claude mcp add agentphone -- npx tsx /path/to/agentphone-mcp/index.ts

Or manually add to your config file:

{
  "mcpServers": {
    "agentphone": {
      "command": "npx",
      "args": ["tsx", "/path/to/agentphone-mcp/index.ts"],
      "env": {
        "AGENTPHONE_API_KEY": "your_api_key_here"
      }
    }
  }
}

Configure Cursor

Cursor uses .cursor/mcp.json in your project root — same config as above.

Restart your MCP client after adding the config.

Using It

Once configured, ask your MCP client to call any number with an objective — it uses the tool, waits for results, and reports outcome, summary, and transcript.

What You Can Do With It

  • "Call the hosting provider and ask about upgrading our plan"
  • "Check if the API vendor has 24/7 support, their number is +18005551234"
  • "Call the office supply store and place a reorder for toner cartridges"
  • "I'm getting errors with their API. Call their developer support line and describe the issue"
  • "Call the client and confirm the deployment window for Saturday"
  • Build a scheduling agent that calls businesses to book on behalf of users
  • Build a procurement agent that calls vendors for quotes
  • Build a research agent that calls businesses to verify information

Other frameworks (no MCP needed): OpenAI Agents SDK | LangChain | CrewAI

Ready to give your agent a phone?

Get Your API Key →

Written by Yanis Mellata, Founder & CEO