|
|
|
|
@@ -1,10 +1,20 @@
|
|
|
|
|
# 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 exposes tools and resources over SSE.
|
|
|
|
|
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 the same static bearer token as the
|
|
|
|
|
REST API and WebSocket.
|
|
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
An AI assistant connects via SSE to the MCP server and gains access to tools for placing calls, checking status, sending DTMF, getting transcripts, and managing call flows. The assistant can orchestrate an entire call through natural language.
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
@@ -15,13 +25,32 @@ 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 | Call mode: `direct`, `hold_slayer`, `ai_assisted` (default: `hold_slayer`) |
|
|
|
|
|
| `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.
|
|
|
|
|
Returns: call ID and initial status.
|
|
|
|
|
|
|
|
|
|
### end_call
|
|
|
|
|
### 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.
|
|
|
|
|
|
|
|
|
|
@@ -29,102 +58,117 @@ Hang up an active call.
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `call_id` | string | Yes | The call to hang up |
|
|
|
|
|
|
|
|
|
|
### send_dtmf
|
|
|
|
|
|
|
|
|
|
Send touch-tone digits to an active call (for manual IVR navigation).
|
|
|
|
|
|
|
|
|
|
| Param | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `call_id` | string | Yes | The call to send digits to |
|
|
|
|
|
| `digits` | string | Yes | DTMF digits to send (e.g., "1", "3#", "1234") |
|
|
|
|
|
|
|
|
|
|
### get_call_status
|
|
|
|
|
|
|
|
|
|
Check the current state of a call.
|
|
|
|
|
|
|
|
|
|
| Param | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `call_id` | string | Yes | The call to check |
|
|
|
|
|
|
|
|
|
|
Returns: Status, duration, hold time, audio classification, transcript excerpt.
|
|
|
|
|
|
|
|
|
|
### get_call_transcript
|
|
|
|
|
|
|
|
|
|
Get the live transcript of a call.
|
|
|
|
|
|
|
|
|
|
| Param | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `call_id` | string | Yes | The call to get transcript for |
|
|
|
|
|
|
|
|
|
|
Returns: Array of transcript chunks with timestamps and speaker labels.
|
|
|
|
|
|
|
|
|
|
### get_call_recording
|
|
|
|
|
|
|
|
|
|
Get recording metadata and file path for a call.
|
|
|
|
|
|
|
|
|
|
| Param | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `call_id` | string | Yes | The call to get recording for |
|
|
|
|
|
|
|
|
|
|
Returns: Recording path, duration, file size.
|
|
|
|
|
|
|
|
|
|
### list_active_calls
|
|
|
|
|
|
|
|
|
|
List all calls currently in progress. No parameters.
|
|
|
|
|
|
|
|
|
|
Returns: Array of active calls with status, number, duration.
|
|
|
|
|
### 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
|
|
|
|
|
|
|
|
|
|
Get analytics summary — hold times, success rates, call volume. No parameters.
|
|
|
|
|
Stored summary, action items, and sentiment for a persisted call.
|
|
|
|
|
|
|
|
|
|
Returns: Aggregate statistics across all calls.
|
|
|
|
|
| Param | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `call_id` | string | Yes | The call to look up |
|
|
|
|
|
|
|
|
|
|
### search_call_history
|
|
|
|
|
|
|
|
|
|
Search past calls by number, company, or date range.
|
|
|
|
|
Search past call records.
|
|
|
|
|
|
|
|
|
|
| Param | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `query` | string | Yes | Search term (phone number, company name) |
|
|
|
|
|
| `limit` | int | No | Max results (default: 20) |
|
|
|
|
|
| `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 a reusable call flow from a completed exploration call.
|
|
|
|
|
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 | The exploration call to learn from |
|
|
|
|
|
| `company` | string | No | Company name for the flow |
|
|
|
|
|
| `call_id` | string | Yes | A completed call that ran in exploration mode |
|
|
|
|
|
| `company_name` | string | No | Company name for labeling a new flow |
|
|
|
|
|
|
|
|
|
|
Returns: The generated CallFlow object.
|
|
|
|
|
### 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
|
|
|
|
|
|
|
|
|
|
MCP resources provide read-only data that assistants can reference:
|
|
|
|
|
|
|
|
|
|
| Resource URI | Description |
|
|
|
|
|
|-------------|-------------|
|
|
|
|
|
| `gateway://status` | Current gateway status — trunk registration, active calls, service health |
|
|
|
|
|
| `gateway://calls` | List of all active calls with current status |
|
|
|
|
|
| `gateway://calls/{call_id}` | Detailed status for a specific call |
|
|
|
|
|
| `gateway://flows` | List of all stored call flows |
|
|
|
|
|
| `gateway://analytics` | Call analytics summary |
|
|
|
|
|
| `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 |
|
|
|
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
## Connecting an AI Assistant
|
|
|
|
|
|
|
|
|
|
The MCP server is mounted on the FastAPI app at `/mcp`.
|
|
|
|
|
Claude Code:
|
|
|
|
|
|
|
|
|
|
### Connecting an AI Assistant
|
|
|
|
|
```bash
|
|
|
|
|
claude mcp add hold-slayer --transport http http://localhost:8000/mcp/ \
|
|
|
|
|
--header "Authorization: Bearer $API_TOKEN"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Add to your MCP client configuration (e.g., Claude Desktop, Cline):
|
|
|
|
|
Generic MCP client configuration:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"mcpServers": {
|
|
|
|
|
"hold-slayer": {
|
|
|
|
|
"url": "http://localhost:8000/mcp/sse"
|
|
|
|
|
"url": "http://localhost:8000/mcp/",
|
|
|
|
|
"headers": {"Authorization": "Bearer <API_TOKEN>"}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -132,8 +176,6 @@ Add to your MCP client configuration (e.g., Claude Desktop, Cline):
|
|
|
|
|
|
|
|
|
|
## Example Conversation
|
|
|
|
|
|
|
|
|
|
Here is how an AI assistant would use the MCP tools to handle a complete call:
|
|
|
|
|
|
|
|
|
|
**User:** "Call Chase Bank and dispute the Amazon charge from December 15th"
|
|
|
|
|
|
|
|
|
|
**Assistant actions:**
|
|
|
|
|
@@ -141,15 +183,8 @@ Here is how an AI assistant would use the MCP tools to handle a complete call:
|
|
|
|
|
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: `trying` → `ringing` → `connected` → `on_hold`
|
|
|
|
|
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="Chase Bank")` to save the IVR path for next time.
|
|
|
|
|
|
|
|
|
|
**User:** "How long was I on hold?"
|
|
|
|
|
|
|
|
|
|
**Assistant actions:**
|
|
|
|
|
|
|
|
|
|
1. Calls `get_call_summary()`
|
|
|
|
|
2. Reports: "Your Chase Bank call lasted 12 minutes total, with 8 minutes on hold. The disputes department averages 6 minutes hold time on Tuesdays."
|
|
|
|
|
8. After the call, calls `learn_call_flow("call_abc123", company_name="Chase Bank")` to save the IVR path for next time.
|
|
|
|
|
|