Skip to main content

Model Context Protocol (MCP)

Use MCP servers to expose tools to your agent.

ONLY Available in
Python

Overview

LiveKit Agents has first-class support for Model Context Protocol (MCP) servers.

To use MCP, install the optional dependencies:

uv add livekit-agents[mcp]~=1.5

Wrap an MCP server in an MCPToolset and pass it to the agent's tools parameter:

from livekit.agents import Agent, mcp
class MyAgent(Agent):
def __init__(self):
super().__init__(
instructions="You are a helpful assistant.",
tools=[
mcp.MCPToolset(
id="my-mcp-server",
mcp_server=mcp.MCPServerHTTP("https://your-mcp-server.com/mcp"),
)
],
)
Caution

The mcp_servers parameter on AgentSession and Agent is deprecated and will be removed in a future version. Use MCPToolset in the tools parameter instead. When mcp_servers is used, the SDK auto-generates toolset IDs that aren't stable across sessions, so switching to explicit MCPToolset gives you predictable IDs.

HTTP servers

Use MCPServerHTTP to connect to a remote MCP server over HTTP:

from livekit.agents import AgentSession, mcp
session = AgentSession(
# ... other arguments ...
tools=[
mcp.MCPToolset(
id="my-api",
mcp_server=mcp.MCPServerHTTP(
"https://your-mcp-server.com/tools",
transport_type="streamable_http",
),
)
],
)
Tip

The transport type is auto-detected from the URL path: URLs ending in /mcp use streamable HTTP transport, and URLs ending in /sse use Server-Sent Events transport. To override auto-detection, pass transport_type explicitly as shown above.

Local servers (stdio)

Use MCPServerStdio to launch a local MCP server process and communicate over stdin/stdout. This is useful for MCP servers distributed as CLI tools:

from livekit.agents import AgentSession, mcp
session = AgentSession(
# ... other arguments ...
tools=[
mcp.MCPToolset(
id="filesystem",
mcp_server=mcp.MCPServerStdio(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
),
)
],
)

The env and cwd parameters let you customize the child process environment and working directory.

Authentication

Pass authentication headers to an MCP server with the headers parameter:

import os
from livekit.agents import AgentSession, mcp
session = AgentSession(
# ... other arguments ...
tools=[
mcp.MCPToolset(
id="zapier",
mcp_server=mcp.MCPServerHTTP(
"https://actions.zapier.com/mcp/sse",
headers={
"Authorization": f"Bearer {os.environ['ZAPIER_API_KEY']}"
},
),
)
],
)

Filtering tools

Limit which tools from an MCP server are exposed to the LLM. Use server-level filtering when you know the exact tool names, or toolset-level filtering when you need to inspect tools after they load.

Server-level filtering

Use the allowed_tools parameter on the MCP server to filter by tool name. Tools not in the list are excluded.

The following example creates an MCPToolset, wrapping MCPServerHTTP with allowed_tools=["search_products", "get_product_details"]. Only those two tools are available, and everything else the server exposes is excluded.

from livekit.agents import AgentSession, mcp
session = AgentSession(
# ... other arguments ...
tools=[
mcp.MCPToolset(
id="products",
mcp_server=mcp.MCPServerHTTP(
"https://your-mcp-server.com/mcp",
allowed_tools=["search_products", "get_product_details"],
),
)
],
)

Toolset-level filtering

Use MCPToolset.filter_tools() for more control. It accepts a predicate function and filters tools in-place after setup.

The following example creates an MCPToolset, manually calls setup() to connect and fetch tools, then calls filter_tools() with a lambda that only keeps tools with IDs containing "search".

from livekit.agents import mcp
toolset = mcp.MCPToolset(
id="my-api",
mcp_server=mcp.MCPServerHTTP("https://your-mcp-server.com/mcp"),
)
# When using outside of AgentSession, call setup() manually
await toolset.setup()
toolset.filter_tools(lambda tool: "search" in tool.id)

Transforming tool results

Use the tool_result_resolver parameter to transform MCP tool results before they reach the LLM. The resolver receives an MCPToolResultContext with tool_name, arguments, and result. It can be sync or async.

By default, the SDK serializes content items to JSON. This example truncates large results instead:

import json
from livekit.agents import mcp
MAX_CHARS = 4000
async def truncating_resolver(ctx: mcp.MCPToolResultContext) -> str:
"""Truncate large MCP tool results to avoid excessive LLM context usage."""
if len(ctx.result.content) == 1:
text = str(ctx.result.content[0].model_dump_json())
elif len(ctx.result.content) > 1:
text = json.dumps([item.model_dump() for item in ctx.result.content])
else:
return "Tool returned no content."
if len(text) > MAX_CHARS:
text = text[:MAX_CHARS] + "\n... [truncated]"
return text
session = AgentSession(
# ... other arguments ...
tools=[
mcp.MCPToolset(
id="my-api",
mcp_server=mcp.MCPServerHTTP(
"https://your-mcp-server.com/mcp",
tool_result_resolver=truncating_resolver,
),
)
],
)

The resolver is available on all MCP server types and is only called for successful tool calls. Errors raise a ToolError before the resolver runs.

Multiple servers

Pass multiple MCPToolset instances in the tools list. You can combine different server types, and all servers are initialized in parallel:

from livekit.agents import AgentSession, mcp
session = AgentSession(
# ... other arguments ...
tools=[
mcp.MCPToolset(
id="api-server",
mcp_server=mcp.MCPServerHTTP("https://api.example.com/mcp"),
),
mcp.MCPToolset(
id="filesystem",
mcp_server=mcp.MCPServerStdio(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/data"],
),
),
],
)

If an individual server fails to connect, the error is logged but does not prevent the agent from starting.

Combining MCP tools with function tools

Your agent can use both MCP-provided tools and locally defined function tools. Pass them together in the tools list.

In the following example, the LLM can call both the MCP server's tools and the save_note function tool.

from livekit.agents import Agent, function_tool, RunContext, mcp
class MyAgent(Agent):
def __init__(self):
super().__init__(
instructions="You are a helpful assistant with access to a knowledge base and local tools.",
tools=[
mcp.MCPToolset(
id="knowledge-base",
mcp_server=mcp.MCPServerHTTP("https://your-mcp-server.com/sse"),
)
],
)
@function_tool()
async def save_note(self, context: RunContext, text: str) -> str:
"""Save a note for the user.
Args:
text: The note content to save.
"""
# your custom logic here
return "Note saved successfully."

In this example, the LLM can call both the MCP server's tools and the save_note function tool.

Agent vs AgentSession placement

MCPToolset follows the same tools override pattern as other tools. If an Agent specifies tools, those tools replace (not merge with) any tools set on the AgentSession. The toolset lifecycle depends on where you place it. Agent toolsets close when switching to a different agent, and session toolsets close when the session ends. See Toolsets for details.

Tip

Set toolsets on the AgentSession for shared defaults across multiple agents in a workflow. Set toolsets on a specific Agent only when that agent needs a different set of tools. This is the same override pattern used by stt, llm, tts, and other agent properties.