shutdown() so subprocesses, streams, and sockets close reliably.
MCP(...) accepts three equivalent construction forms — see Three equivalent forms.
Quick Start
1
Simple Usage
2
With Configuration
How It Works
Agent-Managed Cleanup
When you pass an MCP client through the constructor (tools=[MCP(...)]), agent.close() and agent.aclose() now walk the agent’s tools and shut down anything exposing .shutdown() / .aclose() — so the MCP subprocess and its background thread are cleaned up with the agent.
Constructor-pattern MCP clients (
tools=[MCP(...)]) are now auto-shut-down by agent.close() / agent.aclose() — you don’t need remove_mcp_server() any more just for cleanup. aclose() prefers a tool’s aclose() and falls back to shutdown().Manual Cleanup
For cases where a context manager is not suitable:Request Cancellation
An MCP client can abort an in-flight request by sending anotifications/cancelled notification naming the requestId it wants to stop.
1
Send the request
The client calls a tool with a JSON-RPC id it can reference later.
2
Cancel it
The client sends a cancellation notification with the same id as
requestId.3
Receive the cancelled response
The server cancels the running task and replies with a JSON-RPC error.
Only id-bearing requests can be cancelled. Fire-and-forget notifications have no id to reference and return nothing.
Lifecycle Methods
Authenticating the HTTP transport
Whenapi_key is configured on the MCP HTTP-stream server, all of GET, POST, and DELETE require:
hmac.compare_digest (timing-attack resistant). Missing or wrong tokens return 401 Unauthorized with {"error": "Unauthorized"}. DELETE returning 401 instead of 405 prevents information disclosure about whether sessions exist.
__enter__ / __exit__
Context manager protocol for automatic resource management:
shutdown()
Explicitly close all connections and cleanup resources:
shutdown() dispatches to the right teardown call per transport. HTTP-stream and WebSocket clients expose close() (not shutdown()), so shutdown() falls back to close() for them — previously these two transports leaked on shutdown.
Since PraisonAI #4423, a failed construction (handshake timeout or init error) invokes an internal
MCPToolRunner.stop() — a best-effort teardown that joins the background thread so a failed handshake does not leak a stdio subprocess. It runs automatically inside MCP.__init__; it is not a public API. Public shutdown stays mcp.shutdown() or the context-manager form. See MCP → Errors on construction.__del__
Destructor ensures cleanup even if shutdown() was not called:
Connection Types
MCP supports multiple connection types, all with proper cleanup:shutdown(). HTTP-stream and WebSocket close via the close() fallback inside MCP.shutdown() — previously these two leaked subprocesses/threads on shutdown, while stdio and SSE cleaned up correctly.
Best Practices
Always use a context manager
Always use a context manager
Prefer
with MCP(...) as mcp: — cleanup runs even when an exception is raised.Handle exceptions inside the block
Handle exceptions inside the block
Wrap tool calls in
try/except inside the with block; __exit__ still closes the connection.Nest multiple MCP instances carefully
Nest multiple MCP instances carefully
Open each MCP in its own
with block or nest them — both instances shut down in reverse order.Pass secrets via env, not inline
Pass secrets via env, not inline
Use the
env= parameter with os.getenv(...) rather than hard-coding API keys in recipe files.Related
MCP CLI
Run and inspect MCP servers from the terminal
MCP Transports
Stdio, SSE, HTTP stream, and WebSocket options
Agent Lifecycle Cleanup
How agent.close() shuts down MCP tools and breakers

