Skip to content

MCP Server

The voip package ships a ready-made Model Context Protocol (MCP) server that exposes tools to make phone calls on your behalf to any MCP client.

Claude Code setup

Add the server to your MCP config (see Claude Code MCP docs):

{
  "mcpServers": {
    "VoIP": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "voip[mcp]",
        "mcp"
      ],
      "env": {
        "SIP_AOR": "sip:****:****@example.com:5060?transport=tcp"
      }
    }
  }
}

Set SIP_AOR to your SIP address-of-record.

Tools

voip.mcp.say(ctx, target, prompt='') async

Call a phone number and speak a message.

Dials target, synthesises prompt as speech, then hangs up automatically once the message has been delivered.

Parameters:

Name Type Description Default
ctx Context

FastMCP context (injected automatically by the framework).

required
target str

Phone number or SIP URI to call, e.g. "tel:+1234567890" or "sip:alice@example.com".

required
prompt str

Text to speak during the call.

''
Source code in voip/mcp.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@mcp.tool
async def say(ctx: Context, target: str, prompt: str = "") -> None:
    """Call a phone number and speak a message.

    Dials `target`, synthesises `prompt` as speech, then hangs
    up automatically once the message has been delivered.

    Args:
        ctx: FastMCP context (injected automatically by the framework).
        target: Phone number or SIP URI to call, e.g. `"tel:+1234567890"`
            or `"sip:alice@example.com"`.
        prompt: Text to speak during the call.
    """
    if not hasattr(connection_pool, "sip"):
        raise RuntimeError("VoIP not connected: call run() before using tools.")
    target_uri = parse_uri(target, connection_pool.sip.aor)
    dialog = Dialog(sip=connection_pool.sip)
    await dialog.dial(target_uri, session_class=SayCall, text=prompt)

voip.mcp.call(ctx, target, initial_prompt='', system_prompt=None) async

Call a phone number, hold a conversation, and return the transcript.

Dials target, optionally speaks initial_prompt, then drives a conversation.

Returns once the remote party hangs up.

Parameters:

Name Type Description Default
target str

Phone number or SIP URI to call, e.g. "tel:+1234567890" or "sip:alice@example.com".

required
initial_prompt str

Opening message spoken when the call connects. Pass an empty string to suppress the default greeting.

''
system_prompt str | None

System instruction passed to the language model. Defaults to AgentCall.system_prompt.

None

Returns:

Type Description
str

The full conversation transcript with Caller: / Agent: prefixes.

Source code in voip/mcp.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
@mcp.tool
async def call(
    ctx: Context,
    target: str,
    initial_prompt: str = "",
    system_prompt: str | None = None,
) -> str:
    """Call a phone number, hold a conversation, and return the transcript.

    Dials `target`, optionally speaks `initial_prompt`, then drives a conversation.

    Returns once the remote party hangs up.

    Args:
        target: Phone number or SIP URI to call, e.g. `"tel:+1234567890"`
            or `"sip:alice@example.com"`.
        initial_prompt: Opening message spoken when the call connects.
            Pass an empty string to suppress the default greeting.
        system_prompt: System instruction passed to the language model.
            Defaults to [`AgentCall.system_prompt`][voip.ai.AgentCall].

    Returns:
        The full conversation transcript with `Caller:` / `Agent:` prefixes.
    """
    if not hasattr(connection_pool, "sip"):
        raise RuntimeError("VoIP not connected: call run() before using tools.")
    target_uri = parse_uri(target, connection_pool.sip.aor)
    dialog = Dialog(sip=connection_pool.sip)
    kwargs: dict[str, typing.Any] = {"ctx": ctx, "salutation": initial_prompt}
    if system_prompt is not None:
        kwargs["system_prompt"] = system_prompt
    await dialog.dial(target_uri, session_class=MCPAgentCall, **kwargs)
    return dialog.session.transcript