LiveKit docs › Logic & Structure › Tool definition & use › Model Context Protocol (MCP)

---

# Model Context Protocol (MCP)

> Use MCP servers to expose tools to your agent.

Available in:
- [ ] Node.js
- [x] Python

## Overview

LiveKit Agents has first-class support for [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers.

To use MCP, install the optional dependencies:

```shell
uv add livekit-agents[mcp]~=1.5

```

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

```python
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:

```python
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:

```python
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:

```python
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](#server-level-filtering) when you know the exact tool names, or [toolset-level filtering](#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.

```python
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".

```python
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:

```python
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:

```python
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.

```python
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](https://docs.livekit.io/agents/logic/tools/toolsets.md) 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.

- **[MCP Agent](https://docs.livekit.io/reference/recipes/http_mcp_client.md)**: A voice AI agent with an integrated Model Context Protocol (MCP) client for the LiveKit API.

- **[Shopify voice shopper](https://github.com/livekit-examples/python-agents-examples/tree/main/complex-agents/shopify-voice-shopper)**: Advanced example combining MCP with function tools and dynamic agent switching.

---

This document was rendered at 2026-06-07T11:35:39.675Z.
For the latest version of this document, see [https://docs.livekit.io/agents/logic/tools/mcp.md](https://docs.livekit.io/agents/logic/tools/mcp.md).

To explore all LiveKit documentation, see [llms.txt](https://docs.livekit.io/llms.txt).