> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Lifecycle Management

> Context manager and cleanup for MCP connections

MCP connections in PraisonAI Agents support context managers and explicit `shutdown()` so subprocesses, streams, and sockets close reliably.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, MCP

with MCP("uvx mcp-server-time") as mcp:
    agent = Agent(name="TimeAgent", instructions="Report the current time.", tools=mcp)
    agent.start("What time is it in UTC?")
```

The user runs an agent inside an MCP context manager; connections shut down cleanly when the session ends.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    A[🤖 Agent] --> M[MCP]
    M --> CM[with MCP ...]
    CM --> SD[shutdown]
    SD --> X[✅ Clean exit]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class A agent
    class M,CM process
    class SD,X result
```

## Quick Start

<Steps>
  <Step title="Simple Usage">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent, MCP

    with MCP("uvx mcp-server-time") as mcp:
        agent = Agent(
            name="TimeAgent",
            instructions="Get the current time",
            tools=mcp
        )
        print(agent.start("What time is it?"))
    # Connection closed automatically
    ```
  </Step>

  <Step title="With Configuration">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import os
    from praisonaiagents import Agent, MCP

    with MCP(
        command="npx",
        args=["-y", "@modelcontextprotocol/server-brave-search"],
        env={"BRAVE_API_KEY": os.getenv("BRAVE_API_KEY")},
        timeout=30
    ) as mcp:
        agent = Agent(name="SearchAgent", tools=mcp)
        agent.start("Search for Python tutorials")
    ```
  </Step>
</Steps>

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant MCP as MCP Connection

    User->>MCP: with MCP(...) as mcp
    MCP->>MCP: Start subprocess / open stream
    User->>Agent: agent.start(request)
    Agent->>MCP: Call tools
    MCP-->>Agent: Tool results
    Agent-->>User: Response
    MCP->>MCP: __exit__ calls shutdown()
```

| Phase    | What happens                                           |
| -------- | ------------------------------------------------------ |
| 1. Enter | The `with` block opens the MCP connection              |
| 2. Use   | The agent calls MCP-provided tools                     |
| 3. Exit  | `shutdown()` closes subprocesses, streams, and sockets |

## Manual Cleanup

For cases where a context manager is not suitable:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import MCP

mcp = MCP("uvx mcp-server-time")

try:
    tools = mcp.get_tools()
finally:
    mcp.shutdown()
```

## Lifecycle Methods

### Authenticating the HTTP transport

When `api_key` is configured on the MCP HTTP-stream server, **all** of GET, POST, and DELETE require:

```
Authorization: Bearer <key>
```

Comparison uses constant-time `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.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -H "Authorization: Bearer ${MCP_API_KEY:?Set MCP_API_KEY}" https://localhost:8080/mcp
```

### `__enter__` / `__exit__`

Context manager protocol for automatic resource management:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
with MCP(command) as mcp:
    pass
# __exit__ calls shutdown() automatically
```

### `shutdown()`

Explicitly close all connections and cleanup resources:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
mcp = MCP("uvx mcp-server-time")
mcp.shutdown()
```

### `__del__`

Destructor ensures cleanup even if `shutdown()` was not called:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
mcp = MCP("uvx mcp-server-time")
del mcp  # __del__ calls shutdown()
```

## Connection Types

MCP supports multiple connection types, all with proper cleanup:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Stdio
with MCP("uvx mcp-server-time") as mcp: ...

# SSE
with MCP("http://localhost:8080/sse") as mcp: ...

# HTTP Stream
with MCP("http://localhost:8080") as mcp: ...

# WebSocket
with MCP("ws://localhost:8080") as mcp: ...
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always use a context manager">
    Prefer `with MCP(...) as mcp:` — cleanup runs even when an exception is raised.
  </Accordion>

  <Accordion title="Handle exceptions inside the block">
    Wrap tool calls in `try/except` inside the `with` block; `__exit__` still closes the connection.
  </Accordion>

  <Accordion title="Nest multiple MCP instances carefully">
    Open each MCP in its own `with` block or nest them — both instances shut down in reverse order.
  </Accordion>

  <Accordion title="Pass secrets via env, not inline">
    Use the `env=` parameter with `os.getenv(...)` rather than hard-coding API keys in recipe files.
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="MCP CLI" icon="terminal" href="/docs/cli/mcp">
    Run and inspect MCP servers from the terminal
  </Card>

  <Card title="MCP Transports" icon="plug" href="/docs/mcp/transports">
    Stdio, SSE, HTTP stream, and WebSocket options
  </Card>
</CardGroup>
