# `cleveragents.mcp` — Model Context Protocol

The `mcp` package bridges external MCP servers into the CleverAgents
`ToolRegistry`.  It handles server lifecycle (lazy start, auto-stop,
health monitoring), tool discovery, invocation with JSON Schema validation,
sandbox path rewriting, and skill refresh notifications.

See [ADR-029](../adr/ADR-029-model-context-protocol.md) for design rationale.

---

## `MCPServerConfig`

```python
from cleveragents.mcp import MCPServerConfig

config = MCPServerConfig(
    name="my-server",
    transport="stdio",
    command="python",
    args=["-m", "my_mcp_server"],
    env={"API_KEY": "..."},
)
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | `str` | Server identifier |
| `transport` | `str` | `"stdio"` \| `"sse"` \| `"streamable-http"` |
| `command` | `str \| None` | Spawn command (stdio only) |
| `args` | `list[str]` | Command-line arguments |
| `env` | `dict[str, str]` | Environment variables |
| `url` | `str \| None` | Server URL (sse/http only) |
| `headers` | `dict[str, str]` | HTTP headers (sse/http only) |

---

## `MCPToolAdapter`

The primary integration point.  Manages the full lifecycle of an MCP
server connection.

```python
from cleveragents.mcp import MCPToolAdapter, MCPServerConfig
from cleveragents.tool import ToolRegistry

config = MCPServerConfig(name="bash-tools", transport="stdio",
                         command="uvx", args=["mcp-server-bash"])
adapter = MCPToolAdapter(config)

await adapter.connect()
tools = await adapter.discover_tools()

registry = ToolRegistry()
adapter.register_tools(registry)   # bulk-register with source="mcp"

result = await adapter.invoke("bash", {"command": "ls -la"})
await adapter.disconnect()
```

### Methods

| Method | Description |
|--------|-------------|
| `connect()` | Start server process / open HTTP session |
| `disconnect()` | Clean shutdown |
| `discover_tools(filter)` | Enumerate tools from the server |
| `invoke(name, arguments)` | Call a tool with input validation |
| `register_tools(registry)` | Bulk-register discovered tools |

---

## `MCPToolDescriptor`

```python
class MCPToolDescriptor(BaseModel):
    name: str
    description: str
    input_schema: dict[str, Any]   # JSON Schema
    annotations: dict[str, Any]
```

Descriptor for a tool discovered from an MCP server.

---

## `MCPToolResult`

```python
class MCPToolResult(BaseModel):
    success: bool
    content: Any
    error: str | None = None
```

---

## `MCPCapabilityMetadata`

```python
class MCPCapabilityMetadata(BaseModel):
    tools: bool
    resources: bool
    prompts: bool
    raw_capabilities: dict[str, Any]
```

Full capability information returned during the MCP initialize handshake.

---

## `McpClient`

Manages a single MCP server connection with lazy start and auto-stop.

```python
from cleveragents.mcp import McpClient, McpClientConfig

client_config = McpClientConfig(server=server_config, auto_stop_idle_secs=60)
client = McpClient(client_config)

async with client:
    tools = await client.list_tools()
```

### `McpClientState`

Enum: `STOPPED`, `STARTING`, `RUNNING`, `STOPPING`, `ERROR`.

---

## `McpRegistry`

Namespace-isolated registry of `McpClient` instances.  Prevents duplicate
server registrations and provides lookup by server name.

```python
from cleveragents.mcp import McpRegistry

reg = McpRegistry()
reg.register("my-server", client)
client = reg.get("my-server")
```

---

## `MCPRefreshHook`

Wires MCP `notifications/tools/list_changed` events to
`SkillRegistry.refresh_all()` so that skill registrations stay in sync
when an MCP server's tool list changes at runtime.

```python
from cleveragents.mcp import MCPRefreshHook

hook = MCPRefreshHook(skill_registry)
adapter.on_tools_changed(hook.on_tools_changed)
```

---

## `SandboxPathRewriter`

Rewrites absolute host paths in MCP tool arguments and results to
sandbox-relative paths, preventing path traversal outside the sandbox root.

```python
from cleveragents.mcp import SandboxPathRewriter, SandboxPathRewriterConfig

rewriter = SandboxPathRewriter(
    SandboxPathRewriterConfig(sandbox_root="/sandbox", host_root="/home/user/project")
)
rewritten_args = rewriter.rewrite_args(tool_args)
```
