Skip to content

MCP Server & Skills

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.

For Claude Code users, the repository root also contains a Claude Code plugin that bundles the MCP server and adds dedicated skills for each VoIP workflow (see Plugin below).

Claude & other agentic frameworks

Claude Code plugin

For a richer experience, install the bundled plugin. It configures the MCP server for you and adds four skills:

Skill Source Description
voip:say MCP server Call a number, speak a message, and hang up.
voip:call MCP server Hold a two-way AI conversation, return transcript.
voip:transcribe CLI Transcribe incoming call audio to text.
voip:echo CLI Echo the caller's speech back to them.

Install from the marketplace

The plugin is published in the codingjoe/claude-plugins marketplace. Add the marketplace and install the plugin:

/plugin marketplace add codingjoe/claude-plugins
/plugin install voip@codingjoe

Then set your SIP_AOR — either edit the plugin's .mcp.json in the local plugin cache, or export the SIP_AOR environment variable before launching Claude Code.

Add the MCP server manually

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.

Load locally during development

The plugin lives at the repository root. Clone the repo and point Claude Code at it:

git clone https://github.com/codingjoe/VoIP.git
claude --plugin-dir ./VoIP

Then edit the SIP_AOR environment variable in the cloned .mcp.json to point 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
 82
 83
 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)
    await ctx.info(f"Dialling {target_uri}")
    dialog = Dialog(sip=connection_pool.sip)
    await dialog.dial(target_uri, session_class=SayCall, text=prompt)
    await ctx.info("Call completed")

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