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.
6.3 KiB
MCP Server
The MCP (Model Context Protocol) server lets any MCP-compatible AI assistant
control the Hold Slayer gateway. Built with 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:
claude mcp add hold-slayer --transport http http://localhost:8000/mcp/ \
--header "Authorization: Bearer hs_pat_..."
Generic MCP client configuration:
{
"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:
- Calls
make_call(number="+18005551234", mode="hold_slayer", intent="dispute Amazon charge Dec 15th", call_flow_id="chase-bank-main") - Receives
call_id: "call_abc123" - Polls
get_call_status("call_abc123")periodically - Status progression:
initiating→ringing→connected→on_hold - Tells user: "I'm on hold with Chase Bank. Currently 4 minutes in. I'll let you know when someone picks up."
- Status changes to
transferring— human detected! - Tells user: "A live agent just picked up. I'm transferring the call to your desk phone now. Pick up!"
- After the call, calls
learn_call_flow("call_abc123", company_name="Chase Bank")to save the IVR path for next time.