Files
hold-slayer/docs/mcp-server.md
Robert Helewka 4a3c14d4af
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 45s
CVE Scan & Docker Build / build-and-push (push) Successful in 1m53s
docs: add Claude AI assistant rules and configuration
Add comprehensive rule documentation for AI-assisted development covering
authentication surfaces, outbound-call safety invariants, and other project
conventions to guide Claude's understanding of critical system behaviors.
2026-07-28 19:01:38 -04:00

194 lines
6.3 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 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.