# MCP Tool Adapter

This document covers the MCP (Model Context Protocol) adapter introduced in
**M2.3.mcp-runtime**: connecting to external MCP servers, discovering tools,
invoking them with schema validation, and registering them in the ToolRegistry.

## Overview

The `MCPToolAdapter` bridges any MCP-compatible tool server into the
CleverAgents runtime. It manages the full lifecycle:

| Operation | Method | Description |
|-----------|--------|-------------|
| Connect | `connect(timeout?)` | Start the server process or HTTP session, perform handshake |
| Disconnect | `disconnect()` | Clean shutdown of the server connection |
| Reconnect | `reconnect(timeout?)` | Disconnect and re-connect; clears cached tool list |
| Discover | `discover_tools(filter?)` | Enumerate available tools from the server |
| Invoke | `invoke(name, args)` | Call a tool with automatic input validation |
| Register | `register_tools(registry, namespace)` | Bulk-register into a ToolRegistry |

## Server Configuration

### `MCPServerConfig` Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | `string` | Yes | Server identifier |
| `transport` | `string` | Yes | `stdio`, `sse`, or `streamable-http` |
| `command` | `string` | stdio only | Command to spawn the server process |
| `args` | `list[string]` | No | Command-line arguments |
| `env` | `dict` | No | Environment variables for the server process |
| `url` | `string` | sse/http only | Server URL for remote connections |
| `headers` | `dict` | No | HTTP headers for remote connections |

### Validation Rules

- `stdio` transport **requires** `command`
- `sse` / `streamable-http` transport **requires** `url`

## Tool Discovery

After connecting, call `discover_tools()` to enumerate all tools exposed by
the MCP server. Each returned `MCPToolDescriptor` includes the tool name,
description, and JSON Schema for inputs.

### Filtering

Use `MCPToolFilter` with `include` or `exclude` lists to control which
tools are exposed.

### `MCPToolDescriptor` Fields

| Field | Type | Description |
|-------|------|-------------|
| `name` | `string` | Tool name as exposed by the server |
| `description` | `string` | Human-readable description |
| `input_schema` | `dict` | JSON Schema for tool inputs |

## Tool Invocation

Call `invoke(name, arguments)` to execute a discovered tool. Input arguments
are validated against the tool's JSON Schema before the call is made. If
validation fails, the result is returned immediately with `success=False`.

### `MCPToolResult` Fields

| Field | Type | Description |
|-------|------|-------------|
| `success` | `bool` | Whether the invocation succeeded |
| `data` | `dict` | Result payload from the server |
| `error` | `string` or `null` | Error message on failure |
| `duration_ms` | `float` | Wall-clock execution time in milliseconds |

## Reconnect

Use `reconnect(timeout?)` to recover from a dropped connection or after a
server restart. It performs a clean `disconnect()` followed by a fresh
`connect()` and clears the cached tool list. Call `discover_tools()` again
after reconnecting.

```python
try:
    adapter.invoke("create_issue", {"title": "Bug"})
except RuntimeError:
    # Connection dropped — attempt recovery
    adapter.reconnect(timeout=10.0)
    adapter.discover_tools()
```

If the new connection attempt fails or times out, `reconnect()` raises
`ConnectionError`.

## ToolRegistry Integration

Discovered MCP tools can be bulk-registered into any `ToolRegistry` via
`register_tools(registry, namespace)`. Each tool is registered with the
name `{namespace}/{tool_name}`. Re-registration after tool list changes
replaces stale entries automatically.

Registered tools always carry `source="mcp"` and
`source_metadata={"server": "<server-name>"}` for provenance tracking.
`checkpointable` is always `False` for MCP tools — external server calls
cannot be rolled back transactionally.

## Capability Inference

MCP tools expose limited metadata (name, description, inputSchema). The adapter
automatically infers extended `ToolCapability` metadata using name-based heuristics:

| Tool name keywords | Inferred capability |
|---|---|
| `read`, `get`, `list`, `search`, `find` | `read_only: true` |
| `write`, `create`, `update`, `delete`, `set` | `writes: true` |
| Both read and write keywords | `writes: true` (write takes precedence) |
| Neither | `read_only: false`, `writes: false` |

Inferences are applied automatically when registering tools via
`register_tools()`. They can be overridden via the `overrides` block in
tool or skill YAML.

Use `MCPToolAdapter.infer_capabilities(tool_name)` directly to check
what would be inferred for a given tool name.

## Error Classification

| Error Type | When | Exception / Field |
|------------|------|-------------------|
| Connection failure | `connect()` with unreachable server | `ConnectionError` |
| Not connected | `invoke()` / `discover_tools()` before `connect()` | `RuntimeError` |
| Tool not found | `invoke()` with unknown tool name | `result.error` contains "not found" |
| Validation failure | `invoke()` with invalid input | `result.error` contains "validation" |
| Timeout | Server call exceeds deadline | `result.error` contains "timeout" |
| Server error | Server returns `isError: true` | `result.error` contains "server error" |

## Transport Layer

The `MCPTransport` base class defines the abstract interface. Concrete
implementations handle stdio (child process) and HTTP (SSE / streamable-http)
protocols. For testing, provide a mock transport to the adapter constructor.

## Thread Safety

`MCPToolAdapter` uses `threading.RLock` internally. All public methods are
safe to call from multiple threads concurrently.
