Complete project scaffolding and core implementation of an AI-powered telephony system that calls companies, navigates IVR menus, waits on hold, and transfers to the user when a human answers. Key components: - FastAPI server with REST API, WebSocket, and MCP (SSE) interfaces - SIP/VoIP call management via PJSUA2 with RTP audio streaming - LLM-powered IVR navigation using OpenAI/Anthropic with tool calling - Hold detection service combining audio analysis and silence detection - Real-time STT (Whisper/Deepgram) and TTS (OpenAI/Piper) pipelines - Call recording with per-channel and mixed audio capture - Event bus (asyncio pub/sub) for real-time client updates - Web dashboard with live call monitoring - SQLite persistence via SQLAlchemy with call history and analytics - Notification support (email, SMS, webhook, desktop) - Docker Compose deployment with Opal VoIP and Opal Media containers - Comprehensive test suite with unit, integration, and E2E tests - Simplified .gitignore and full project documentation in README
156 lines
5.0 KiB
Markdown
156 lines
5.0 KiB
Markdown
# 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.
|
|
|
|
## 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.
|
|
|
|
## 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 | Call mode: `direct`, `hold_slayer`, `ai_assisted` (default: `hold_slayer`) |
|
|
| `intent` | string | No | What you want to accomplish on the call |
|
|
| `call_flow_id` | string | No | ID of a stored call flow to follow |
|
|
|
|
Returns: Call ID and initial status.
|
|
|
|
### end_call
|
|
|
|
Hang up an active call.
|
|
|
|
| Param | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `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.
|
|
|
|
### get_call_summary
|
|
|
|
Get analytics summary — hold times, success rates, call volume. No parameters.
|
|
|
|
Returns: Aggregate statistics across all calls.
|
|
|
|
### search_call_history
|
|
|
|
Search past calls by number, company, or date range.
|
|
|
|
| Param | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `query` | string | Yes | Search term (phone number, company name) |
|
|
| `limit` | int | No | Max results (default: 20) |
|
|
|
|
### learn_call_flow
|
|
|
|
Build a reusable call flow from a completed exploration call.
|
|
|
|
| Param | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `call_id` | string | Yes | The exploration call to learn from |
|
|
| `company` | string | No | Company name for the flow |
|
|
|
|
Returns: The generated CallFlow object.
|
|
|
|
## 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 |
|
|
|
|
## Configuration
|
|
|
|
The MCP server is mounted on the FastAPI app at `/mcp`.
|
|
|
|
### Connecting an AI Assistant
|
|
|
|
Add to your MCP client configuration (e.g., Claude Desktop, Cline):
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"hold-slayer": {
|
|
"url": "http://localhost:8000/mcp/sse"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 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:**
|
|
|
|
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`
|
|
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."
|