# MCP Bridge via rmcp

This document describes the architecture of the MCP (Model Context Protocol) bridge that exposes Plexus activations as MCP tools using the `rmcp` crate.

## Overview

The MCP bridge provides a standards-compliant MCP server that dynamically exposes all Plexus activations as MCP tools. This allows any MCP client (like Claude Code) to discover and invoke Plexus methods.

```
┌─────────────────┐     MCP Protocol      ┌──────────────────┐
│   MCP Client    │◄────────────────────►│  PlexusMcpBridge │
│  (Claude Code)  │   tools/list          │                  │
│                 │   tools/call          │   ServerHandler  │
└─────────────────┘                       └────────┬─────────┘
                                                   │
                                                   ▼
                                          ┌──────────────────┐
                                          │     Plexus       │
                                          │                  │
                                          │  ┌────────────┐  │
                                          │  │ Activations│  │
                                          │  │  - health  │  │
                                          │  │  - plexus  │  │
                                          │  │  - ...     │  │
                                          │  └────────────┘  │
                                          └──────────────────┘
```

## Key Components

### PlexusMcpBridge (`src/mcp_bridge.rs`)

Implements `rmcp::ServerHandler` to bridge MCP protocol to Plexus:

```rust
impl ServerHandler for PlexusMcpBridge {
    fn get_info(&self) -> ServerInfo { ... }
    async fn list_tools(&self, ...) -> Result<ListToolsResult, McpError> { ... }
    async fn call_tool(&self, ...) -> Result<CallToolResult, McpError> { ... }
}
```

**Dynamic Tool Discovery:**
- `list_tools()` calls `plexus.list_full_schemas()` on each request
- New activations registered in Plexus automatically appear as MCP tools
- No MCP-specific code changes needed when adding activations

**Tool Naming Convention:**
- MCP tool names follow `{namespace}.{method}` format
- Example: `health.check`, `plexus.list_activations`

### Schema Transformation

Plexus activation schemas are converted to MCP Tool format:

```rust
fn schemas_to_rmcp_tools(schemas: Vec<ActivationFullSchema>) -> Vec<Tool>
```

Key transformations:
- Ensures `"type": "object"` exists at schema root (MCP requirement)
- Maps activation namespace + method name to tool name
- Preserves method descriptions

### Streaming Support

Tool calls support streaming via `PlexusStreamItem`:

```
PlexusStreamItem::Data     → Buffered for final result + logging notification
PlexusStreamItem::Progress → MCP progress notification (if token provided)
PlexusStreamItem::Error    → Buffered + logging notification
PlexusStreamItem::Done     → Terminates stream
```

## Transport

Uses `rmcp::transport::streamable_http_server::StreamableHttpService`:

```rust
let service = StreamableHttpService::new(
    move || Ok(bridge.clone()),
    LocalSessionManager::default().into(),
    StreamableHttpServerConfig::default(),
);

let app = axum::Router::new().nest_service("/mcp", service);
```

- HTTP endpoint at `/mcp`
- Supports MCP Streamable HTTP transport (2025-03-26 spec)
- Session management via `LocalSessionManager`

## Error Mapping

Plexus errors are mapped to MCP error codes:

| PlexusError | MCP Error |
|-------------|-----------|
| ActivationNotFound | invalid_params |
| MethodNotFound | invalid_params |
| InvalidParams | invalid_params |
| ExecutionError | internal_error |
| HandleNotSupported | invalid_params |

## Integration with Main Binary

The substrate binary runs both WebSocket (jsonrpsee) and MCP (rmcp) servers:

```
┌─────────────────────────────────────────────┐
│              substrate binary               │
│                                             │
│  ┌─────────────────┐  ┌─────────────────┐  │
│  │  WebSocket:4444 │  │  MCP HTTP:4445  │  │
│  │   (jsonrpsee)   │  │    (rmcp)       │  │
│  └────────┬────────┘  └────────┬────────┘  │
│           │                    │           │
│           └──────────┬─────────┘           │
│                      ▼                     │
│               ┌──────────┐                 │
│               │  Plexus  │                 │
│               └──────────┘                 │
└─────────────────────────────────────────────┘
```

## Adding New Activations

To add a new activation that appears as MCP tools:

1. Implement the `Activation` trait
2. Register in `build_plexus()`:
   ```rust
   Arc::new(Plexus::new()
       .register(Health::new())
       .register(MyNewActivation::new())  // Auto-exposed as MCP tools
   )
   ```

No changes to MCP bridge code required.

## Design Decisions

1. **rmcp over custom implementation**: Using the standard rmcp crate provides protocol compliance, streaming support, and reduces maintenance burden.

2. **Dynamic schema generation**: Schemas are generated at request time rather than cached, ensuring consistency with Plexus state.

3. **Plexus as activation**: Plexus itself implements the Activation trait and includes itself in `list_activations()`, `list_methods()`, and `compute_hash()`. It is a self-describing activation that also serves as the registry.

4. **Dual transport**: Both WebSocket (for programmatic clients) and HTTP/MCP (for AI assistants) are supported from the same Plexus instance.
