Skip to main content
Add or remove MCP integrations on a running agent — no restart, no lost session state.

Quick Start

1

Attach an MCP server at runtime

from praisonaiagents import Agent
from praisonaiagents.mcp import MCP

agent = Agent(instructions="You are a helpful assistant")

agent.add_mcp_server("notion", MCP("npx -y @notionhq/notion-mcp-server"))

agent.start("Summarise my latest Notion page about Q3 planning")
Tools from the attached server are available on the next turn of the same run.
2

List and remove servers

print(agent.list_mcp_servers())   # ['notion']

removed = agent.remove_mcp_server("notion")
print(removed)                     # True
print(agent.list_mcp_servers())   # []

How It Works

Runtime-attached MCPs live in a private _mcp_servers registry on the agent. Each add_mcp_server call registers the server, appends it to self.tools, and refreshes the toolset. Removed servers call mcp.shutdown() best-effort before deregistration.

API Reference

MethodReturnsRaises
add_mcp_server(name, mcp)MCPValueError if name is empty or already attached
remove_mcp_server(name)bool (False if name unknown)
refresh_tools()List[Any]
list_mcp_servers()List[str]

Common Patterns

Gateway agent adds Notion mid-session

from praisonaiagents import Agent
from praisonaiagents.mcp import MCP

agent = Agent(instructions="You are a helpful assistant")
agent.add_mcp_server("notion", MCP("npx -y @notionhq/notion-mcp-server"))
agent.start("What pages did I update this week?")

Hot-swap under the same name

agent.remove_mcp_server("notion")
agent.add_mcp_server("notion", MCP("npx -y @notionhq/notion-mcp-server"))
Direct re-attach without removing first raises ValueError("MCP server 'notion' is already attached. Call remove_mcp_server() first to replace it.").

React to tools/list_changed

agent.refresh_tools()
Call refresh_tools() when an MCP server signals that its tool list changed — per-turn assembly already reads self.tools live, but this forces an immediate refresh.

Cleanup

agent.close() and await agent.aclose() both shut down every runtime-attached MCP via _shutdown_runtime_mcp_servers(). Failures on individual servers are logged and skipped so one bad shutdown does not block the rest.
agent.close()       # sync — shuts down all runtime-attached MCPs
await agent.aclose()  # async-safe — same guarantee
You do not need to call remove_mcp_server() manually before closing the agent.

Best Practices

Attach each MCP under a stable name ("notion", "github", "postgres") so you can list and remove servers predictably mid-session.
add_mcp_server raises on duplicate names. Call remove_mcp_server(name) first when hot-swapping credentials or server commands.
New tools are not injected into the current LLM turn. Send another message (or continue the run) after attaching so the agent sees the expanded toolset.
Long-running services should still call close() or aclose() when the agent session ends — runtime MCPs are tracked and shut down automatically.

MCP

Static MCP setup at construction time

Tools

Function tools and tool registry

MCP Lifecycle

Context manager and connection cleanup

Load MCP Tools

Load MCP tools into an agent at build time