Files
hold-slayer/docs/mcp-server.md
Robert Helewka ff7ea8623a Stage 6: learn_call_flow rebuilt on the learner, docs truth sweep
The call-flow learner finally gets fed: exploration mode records its
IVR discoveries on the call (ActiveCall.exploration_steps) instead of
throwing them away, persistence stores them in the call record's
metadata, and the rebuilt learn_call_flow MCP tool turns a completed
exploration call into a stored flow via CallFlowLearner — correct
constructor (llm_client from get_llm, heuristic labels when the LLM
is unavailable), build for a new number, merge/refine when a flow
already exists. save_learned_flow/update_flow_from_model keep the
CallFlow↔row mapping in call_persistence.

Test gaps closed: tests/test_learner.py (discoveries→linked steps,
exploration persistence, learn-then-refine through the in-memory MCP
client, no-data and unknown-call answers) and tests/test_websocket.py
(4401 without token, trunk-status-then-replay on connect, per-call
stream filtering).

Docs aligned to code: README (15 tools incl. learn_call_flow, HTTP
not SSE, Python 3.12+, PostgreSQL+Alembic — no SQLite fallback, media
pipeline marked stub-mode until pjsua2 installed, Alembic and honest
/health checked off); docs/mcp-server.md rewritten against the actual
tool surface (hangup not end_call, real params, 3 real resources,
/mcp/ streamable HTTP + bearer auth); architecture/development/
configuration drift fixed.

pyproject: pruned never-imported deps (websockets, librosa,
soundfile, python-multipart).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 13:45:35 -04:00

6.0 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 the same static bearer token as the REST API and WebSocket.

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 $API_TOKEN"

Generic MCP client configuration:

{
  "mcpServers": {
    "hold-slayer": {
      "url": "http://localhost:8000/mcp/",
      "headers": {"Authorization": "Bearer <API_TOKEN>"}
    }
  }
}

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: initiatingringingconnectedon_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.