# MCP Server The MCP (Model Context Protocol) server lets any MCP-compatible AI assistant control the Hold Slayer gateway. Built with [FastMCP](https://github.com/jlowin/fastmcp), it is mounted on the FastAPI app at **`/mcp/`** (trailing slash) over **streamable HTTP** and authenticates with an owner-minted Personal Access Token (`hs_pat_…`) — the same owner-only auth as the REST API and WebSocket. Auth is enforced by an ASGI guard (`_owner_only_mcp` in `main.py`) that resolves the bearer to the owner; a Casdoor JWT also works, but MCP clients can't refresh one, so a PAT is the intended credential. ## Overview An AI assistant connects to the MCP endpoint and gains access to 15 tools and 3 resources for placing calls, checking status, sending DTMF, getting transcripts, and managing call flows. The assistant can orchestrate an entire call through natural language. `make_call` places a **real PSTN call** that may incur charges; emergency numbers are always refused, and the concurrent-call cap applies. ## Tools ### make_call Place an outbound call through the SIP trunk. | Param | Type | Required | Description | |-------|------|----------|-------------| | `number` | string | Yes | Phone number to call (E.164 format) | | `mode` | string | No | `direct`, `hold_slayer`, or `ai_assisted` (default: `direct`) | | `intent` | string | No | What you want to accomplish on the call | | `call_flow_id` | string | No | ID of a stored call flow to follow | | `device` | string | No | Device to transfer to when a human is detected | Returns: call ID and initial status. ### get_call_status Check the current state of a call — status, duration, hold time, current audio classification, recent transcript. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to check | ### transfer_call Transfer an active call to a registered device. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to transfer | | `device` | string | Yes | Device ID or type to ring | ### hangup Hang up an active call. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to hang up | ### list_active_calls List all calls currently in progress. No parameters. ### send_dtmf Send touch-tone digits on an active call (manual IVR navigation). | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to send digits on | | `digits` | string | Yes | DTMF digits (e.g., `"1"`, `"123#"`) | ### get_call_transcript Get the full transcript of an active call. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to get the transcript for | ### get_call_recording Get recording metadata (path, duration) for a persisted call. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to look up | ### get_call_summary Stored summary, action items, and sentiment for a persisted call. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | The call to look up | ### search_call_history Search past call records. | Param | Type | Required | Description | |-------|------|----------|-------------| | `phone_number` | string | No | Filter by phone number (partial match) | | `intent` | string | No | Filter by intent text (partial match) | | `limit` | int | No | Max results (default: 10) | ### get_call_flow Look up the stored IVR call flow for a phone number. | Param | Type | Required | Description | |-------|------|----------|-------------| | `phone_number` | string | Yes | Number to look up (E.164) | ### create_call_flow Store a new IVR call flow by hand. | Param | Type | Required | Description | |-------|------|----------|-------------| | `name` | string | Yes | Human-readable name | | `phone_number` | string | Yes | Phone number (E.164) | | `steps_json` | string | Yes | JSON array of call flow steps | | `notes` | string | No | General notes | ### learn_call_flow Build (or refine) a reusable IVR call flow from a completed hold-slayer exploration call. Exploration calls record every IVR prompt heard and DTMF sent; this turns those discoveries into a stored flow so the next call navigates directly. | Param | Type | Required | Description | |-------|------|----------|-------------| | `call_id` | string | Yes | A completed call that ran in exploration mode | | `company_name` | string | No | Company name for labeling a new flow | ### list_devices List registered devices and their online/offline status. No parameters. ### gateway_status Trunk registration, device count, active calls, engine mode. No parameters. ## Resources | Resource URI | Description | |-------------|-------------| | `gateway://status` | Current gateway status — trunk registration, active calls | | `gateway://call-flows` | List of all stored call flows | | `gateway://active-calls` | All active calls with current status | ## Connecting an AI Assistant Claude Code: ```bash claude mcp add hold-slayer --transport http http://localhost:8000/mcp/ \ --header "Authorization: Bearer hs_pat_..." ``` Generic MCP client configuration: ```json { "mcpServers": { "hold-slayer": { "url": "http://localhost:8000/mcp/", "headers": {"Authorization": "Bearer hs_pat_..."} } } } ``` ## Example Conversation **User:** "Call Chase Bank and dispute the Amazon charge from December 15th" **Assistant actions:** 1. Calls `make_call(number="+18005551234", mode="hold_slayer", intent="dispute Amazon charge Dec 15th", call_flow_id="chase-bank-main")` 2. Receives `call_id: "call_abc123"` 3. Polls `get_call_status("call_abc123")` periodically 4. Status progression: `initiating` → `ringing` → `connected` → `on_hold` 5. Tells user: "I'm on hold with Chase Bank. Currently 4 minutes in. I'll let you know when someone picks up." 6. Status changes to `transferring` — human detected! 7. Tells user: "A live agent just picked up. I'm transferring the call to your desk phone now. Pick up!" 8. After the call, calls `learn_call_flow("call_abc123", company_name="Chase Bank")` to save the IVR path for next time.