tools/list and registered alongside webhook and built-in tools.
Configuration
Addmcp_servers to your agent config:
Transport Types
Server Config Fields
How It Works
1
Call starts
TurnCall connects to each configured MCP server.
2
Tool discovery
Calls
tools/list on each server to discover available tools.3
Registration
MCP tools are merged with webhook and built-in tools in the pipeline.
4
Execution
When the LLM calls an MCP tool, TurnCall routes it through the MCP client.
5
Cleanup
MCP sessions are cleaned up when the call ends.
stdio Transport
Where MCP tools are available
MCP servers are connected at the start of a voice call — Twilio, WebRTC and WhatsApp voice alike — and disconnected when it ends. Discovery adds onetools/list round trip per server to call setup, so keep timeout_seconds
tight and use tool_filter to trim a large catalogue down to the handful the
agent actually needs.
MCP also works on the text channels — SMS, the Chat API and WhatsApp text —
where servers are connected per inbound message and closed once the reply is
sent.
Server URLs are allowlisted
An MCP server URL is an outbound target chosen in the agent config and reached from inside your network, so it goes through the same allowlist as custom LLM and S2S gateway endpoints:BYOM_ALLOWED_URL_PATTERNS.
An empty list allows everything, which keeps local development and a
self-hosted server on the same docker network working. Set it in production:
stdio is gated separately by MCP_STDIO_ENABLED
and MCP_STDIO_ALLOWED_COMMANDS.
Tool names must be unique
Tool names are advertised flat, so a name can only belong to one place. The precedence is: built-in, then the agent’s owntools, then MCP in server
order. A discovered tool whose name is already taken is skipped and logged
rather than shadowing the winner.
A server may not claim a built-in name at all — end_call, transfer_call,
handoff_to_agent, send_dtmf, do_not_call. Dispatch checks built-ins first, so an MCP
tool called end_call would have been advertised to the model and then hung
up the call instead of running.
Two MCP servers exposing a common name — search, get — is the usual way
this happens. Use tool_filter to keep each server to the tools you want, or
rename on the server side.
Limits
Custom webhook tools are capped the same way under their own
TOOL_MAX_RESPONSE_BYTES.
The per-server cap doesn’t compose — ten servers at it would put 500 tools in
every request — so the total is the one that protects the context window.
Servers are consumed in config order; once the total is reached, later ones
contribute nothing.
Discovery is also bounded in time. Past MCP_CONNECT_TIMEOUT_SECONDS the agent
starts with no MCP tools at all rather than keeping the caller waiting — an
unreachable server costs you its tools, never the call.
A result over the size cap doesn’t reach the model. It gets a JSON error with
a short preview of what the tool started to say, so it can narrow the request
and try again rather than dying on context length.
SDK versions
TurnCall requires the 2.x line of the Python MCP SDK,mcp>=2.1.1,<3 —
Pipecat 1.12’s mcp extra forces it, so 1.x is unreachable. Nothing about your
agent config depends on the SDK version.
TurnCall briefly read both lines’ spellings of two renamed model fields
(
Tool.inputSchema, CallToolResult.isError). Those reads are gone: under the
2.x pin they could never match, and on 2.x the old names survive as pydantic
aliases anyway. The reason they existed is worth knowing — reading only one
spelling failed per tool, and server connection logs and continues per server,
so the symptom was every MCP server returning no tools at all with nothing in
the response to say why. An integration test opens a real server and calls a
real tool over each transport — HTTP, SSE and stdio — precisely because a
mocked test can’t see a field rename.