# MCP to Arbor: Complete Request Flow Architecture

This document traces a request from MCP entry through Plexus dispatch to Arbor method execution and tree rendering response.

## Overview Diagram

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                                    CLIENT                                            │
│                          (Claude Code, CLI, Web UI)                                  │
└─────────────────────────────────────────┬───────────────────────────────────────────┘
                                          │
                                          │ JSON-RPC 2.0 over HTTP/Stdio
                                          │
                                          ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              LAYER 1: TRANSPORT                                      │
│  ┌─────────────────────────┐    ┌─────────────────────────┐                         │
│  │   HTTP Transport        │    │   Stdio Transport       │                         │
│  │   POST /mcp             │    │   Line-delimited JSON   │                         │
│  │   transport.rs:42       │    │   main.rs:28            │                         │
│  └───────────┬─────────────┘    └───────────┬─────────────┘                         │
│              │                              │                                        │
│              └──────────────┬───────────────┘                                        │
│                             │                                                        │
│                             ▼                                                        │
│                    JsonRpcRequest { method, params, id }                            │
└─────────────────────────────┬───────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                         LAYER 2: MCP INTERFACE                                       │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │                     McpInterface::handle()                                      │ │
│  │                     interface.rs:59-92                                          │ │
│  │                                                                                 │ │
│  │   "initialize"      → handle_initialize()    → ServerInfo                      │ │
│  │   "ping"            → handle_ping()          → {}                              │ │
│  │   "tools/list"      → handle_tools_list()    → Vec<Tool>                       │ │
│  │   "tools/call"      → handle_tools_call()    → CallToolResult                  │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│  State Machine: Uninitialized → Initializing → Ready                                │
│                                                                                      │
└─────────────────────────────┬───────────────────────────────────────────────────────┘
                              │
                              │ plexus.call("arbor.tree_render", params)
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                        LAYER 3: PLEXUS DISPATCHER                                    │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │                     Plexus::call()                                              │ │
│  │                     plexus.rs:386-446                                           │ │
│  │                                                                                 │ │
│  │   1. parse_method("arbor.tree_render")                                         │ │
│  │      → ("arbor", "tree_render")                                                │ │
│  │                                                                                 │ │
│  │   2. activations.get("arbor")                                                  │ │
│  │      → Arc<dyn ActivationObject>                                               │ │
│  │                                                                                 │ │
│  │   3. activation.call("tree_render", params)                                    │ │
│  │      → Result<PlexusStream, PlexusError>                                       │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│  Registry: HashMap<String, Arc<dyn ActivationObject>>                               │
│            { "arbor" → Arbor, "cone" → Cone, "health" → Health, ... }               │
│                                                                                      │
└─────────────────────────────┬───────────────────────────────────────────────────────┘
                              │
                              │ activation.call("tree_render", params)
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                      LAYER 4: ACTIVATION WRAPPER                                     │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │                  ActivationWrapper<A: Activation>                               │ │
│  │                  plexus.rs:187-248                                              │ │
│  │                                                                                 │ │
│  │   Type erasure bridge:                                                          │ │
│  │   - Converts Activation (has Methods type) → ActivationObject (trait object)   │ │
│  │   - Preserves schema generation via A::Methods                                  │ │
│  │   - Delegates call() to inner Activation                                        │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
└─────────────────────────────┬───────────────────────────────────────────────────────┘
                              │
                              │ arbor.tree_render(tree_id)
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                       LAYER 5: ARBOR ACTIVATION                                      │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │    #[hub_methods(namespace = "arbor")]                                          │ │
│  │    impl Arbor { ... }                                                           │ │
│  │    activation.rs:38-433                                                         │ │
│  │                                                                                 │ │
│  │    Methods generated by hub_methods macro:                                      │ │
│  │    ┌──────────────────┬────────────────────────────────────────────┐           │ │
│  │    │ tree_create      │ Create new conversation tree               │           │ │
│  │    │ tree_get         │ Retrieve complete tree with all nodes      │           │ │
│  │    │ tree_list        │ List all active trees                      │           │ │
│  │    │ tree_render      │ Render tree as text visualization          │           │ │
│  │    │ node_create_text │ Create text node in tree                   │           │ │
│  │    │ context_get_path │ Get full path from root to node            │           │ │
│  │    │ ...              │ 20+ more methods                           │           │ │
│  │    └──────────────────┴────────────────────────────────────────────┘           │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │    async fn tree_render(&self, tree_id: TreeId)                                 │ │
│  │        -> impl Stream<Item = ArborEvent>                                        │ │
│  │    activation.rs:417-433                                                        │ │
│  │                                                                                 │ │
│  │    stream! {                                                                    │ │
│  │        match storage.tree_get(&tree_id).await {                                │ │
│  │            Ok(tree) => yield ArborEvent::TreeRender {                          │ │
│  │                tree_id,                                                        │ │
│  │                render: tree.render()   ─────────────────────────┐              │ │
│  │            },                                                    │              │ │
│  │            Err(e) => yield ArborEvent::TreeRender {             │              │ │
│  │                tree_id,                                         │              │ │
│  │                render: format!("Error: {}", e)                  │              │ │
│  │            }                                                    │              │ │
│  │        }                                                        │              │ │
│  │    }                                                            │              │ │
│  └─────────────────────────────────────────────────────────────────┼──────────────┘ │
│                                                                    │                │
└────────────────────────────────────────────────────────────────────┼────────────────┘
                                                                     │
                                                                     ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                         LAYER 6: ARBOR STORAGE                                       │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │    ArborStorage (SQLite)                                                        │ │
│  │    storage.rs                                                                   │ │
│  │                                                                                 │ │
│  │    tree_get(&tree_id) → Result<Tree, ArborError>                               │ │
│  │                                                                                 │ │
│  │    Returns Tree struct:                                                         │ │
│  │    ┌────────────────────────────────────────────────────────────────┐          │ │
│  │    │  Tree {                                                        │          │ │
│  │    │      tree_id: Uuid,                                            │          │ │
│  │    │      owner_id: String,                                         │          │ │
│  │    │      ref_count: i64,                                           │          │ │
│  │    │      metadata: Option<Value>,                                  │          │ │
│  │    │      root: Node { id, content: Text|External, children }       │          │ │
│  │    │  }                                                             │          │ │
│  │    └────────────────────────────────────────────────────────────────┘          │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
└─────────────────────────────┬───────────────────────────────────────────────────────┘
                              │
                              │ tree.render()
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                         LAYER 7: TREE RENDERING                                      │
│                                                                                      │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │    Tree::render() → String                                                      │ │
│  │    types.rs:260-302                                                             │ │
│  │                                                                                 │ │
│  │    render_node(node, prefix, is_last) → String                                 │ │
│  │                                                                                 │ │
│  │    Uses box-drawing characters:                                                 │ │
│  │    └── (last child)   ├── (middle child)   │   (continuation)                  │ │
│  │                                                                                 │ │
│  │    Text nodes: truncated to 60 chars, newlines → ↵                             │ │
│  │    External handles: rendered as [source:identifier] (NOT resolved!)           │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│  IMPORTANT: tree_render shows STRUCTURE with handle references, NOT content.        │
│                                                                                      │
│  Actual Output (handles not resolved):                                               │
│  ┌────────────────────────────────────────────────────────────────────────────────┐ │
│  │  └──                                                                           │ │
│  │      ├── [claudecode:msg-a4a4fa79-...:user:user]                               │ │
│  │      └── [claudecode:msg-5bcd487d-...:user:user]                               │ │
│  │          └── [claudecode:msg-7e6d13f9-...:assistant:assistant]                 │ │
│  │              └── [claudecode:msg-0ef96316-...:user:user]                       │ │
│  │                  └── [claudecode:msg-73513f74-...:assistant:assistant]         │ │
│  └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│  To get actual message content, you must:                                            │
│  1. Call context_get_path() to get nodes with handles                               │
│  2. Resolve each handle through its source (cone, claudecode, bash, etc.)           │
│  This happens in Cone's chat flow, NOT in Arbor's render.                           │
│                                                                                      │
└─────────────────────────────┬───────────────────────────────────────────────────────┘
                              │
                              │ ArborEvent::TreeRender { tree_id, render }
                              │
                              ▼
                    RESPONSE FLOW (REVERSE)
```

## Response Flow Diagram

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                            RESPONSE ASSEMBLY                                         │
│                                                                                      │
│   Layer 7: String render output                                                      │
│       │                                                                              │
│       ▼                                                                              │
│   Layer 5: ArborEvent::TreeRender { tree_id, render }                               │
│       │                                                                              │
│       │  impl ActivationStreamItem for ArborEvent                                   │
│       │      into_plexus_item(provenance, plexus_hash)                              │
│       ▼                                                                              │
│   Layer 4: PlexusStreamItem {                                                        │
│               plexus_hash: "abc123...",                                              │
│               event: PlexusStreamEvent::Data {                                       │
│                   content_type: "arbor.event",                                       │
│                   data: { "tree_id": "...", "render": "└── ..." }                   │
│               }                                                                      │
│           }                                                                          │
│       │                                                                              │
│       │  into_plexus_stream() wraps in Box<dyn Stream>                              │
│       ▼                                                                              │
│   Layer 3: PlexusStream (pinned boxed stream)                                        │
│       │                                                                              │
│       │  Buffer all data events                                                      │
│       ▼                                                                              │
│   Layer 2: CallToolResult {                                                          │
│               content: [{ type: "text", text: "└── ..." }],                         │
│               isError: false                                                         │
│           }                                                                          │
│       │                                                                              │
│       │  Wrap in JsonRpcResponse                                                     │
│       ▼                                                                              │
│   Layer 1: JsonRpcResponse {                                                         │
│               jsonrpc: "2.0",                                                        │
│               id: 42,                                                                │
│               result: { content: [...], isError: false }                            │
│           }                                                                          │
│       │                                                                              │
│       │  HTTP 200 or stdio write                                                     │
│       ▼                                                                              │
│   CLIENT receives rendered tree                                                      │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

## Key Data Types

### At Each Layer

| Layer | Input Type | Output Type | File |
|-------|-----------|-------------|------|
| 1. Transport | HTTP POST / stdin line | `JsonRpcRequest` | `transport.rs:42`, `main.rs:28` |
| 2. MCP Interface | `(method, params)` | `Result<Value, McpError>` | `interface.rs:59` |
| 3. Plexus Dispatcher | `"namespace.method", params` | `Result<PlexusStream, PlexusError>` | `plexus.rs:386` |
| 4. Activation Wrapper | `"method", params` | `Result<PlexusStream, PlexusError>` | `plexus.rs:187` |
| 5. Arbor Activation | Typed params (e.g., `TreeId`) | `impl Stream<Item=ArborEvent>` | `activation.rs:417` |
| 6. Arbor Storage | `&TreeId` | `Result<Tree, ArborError>` | `storage.rs` |
| 7. Tree Renderer | `&Tree` | `String` | `types.rs:260` |

### Stream Event Types

```rust
// Plexus-level events (plexus/types.rs:38-82)
enum PlexusStreamEvent {
    Progress { message: String, percentage: Option<f32> },
    Data { content_type: String, data: Value },
    Error { error: String, recoverable: bool },
    Done,
    Guidance { error_type: String, suggestion: String },
}

// Arbor-specific events (activations/arbor/types.rs:353-508)
enum ArborEvent {
    TreeCreated { tree_id, owner_id, metadata },
    TreeData { tree },
    TreeRender { tree_id, render },
    NodeCreated { tree_id, node_id, parent, content },
    ContextPath { tree_id, node_id, path },
    // ... 25+ more variants
}
```

## Stream Conversion Pipeline

```
┌──────────────────────────────────────────────────────────────────────────────┐
│                          STREAM TRANSFORMATION                                │
│                                                                               │
│   Arbor Method                                                                │
│   ┌─────────────────────────────────────────────────────────────────────┐    │
│   │  async fn tree_render(&self, tree_id) -> impl Stream<Item=ArborEvent>│    │
│   └──────────────────────────────────┬──────────────────────────────────┘    │
│                                      │                                        │
│                                      ▼                                        │
│   Stream<Item = ArborEvent>                                                   │
│   ┌─────────────────────────────────────────────────────────────────────┐    │
│   │  ArborEvent::TreeRender { tree_id: "...", render: "└── ..." }       │    │
│   └──────────────────────────────────┬──────────────────────────────────┘    │
│                                      │                                        │
│                                      │ .map(|e| e.into_plexus_item(...))      │
│                                      ▼                                        │
│   Stream<Item = PlexusStreamItem>                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐    │
│   │  PlexusStreamItem {                                                  │    │
│   │      plexus_hash: "abc123",                                          │    │
│   │      event: PlexusStreamEvent::Data {                                │    │
│   │          content_type: "arbor.event",                                │    │
│   │          data: { "tree_id": "...", "render": "└── ..." }            │    │
│   │      }                                                               │    │
│   │  }                                                                   │    │
│   └──────────────────────────────────┬──────────────────────────────────┘    │
│                                      │                                        │
│                                      │ Box::pin(stream)                       │
│                                      ▼                                        │
│   PlexusStream = Pin<Box<dyn Stream<Item = PlexusStreamItem>>>               │
│                                                                               │
└──────────────────────────────────────────────────────────────────────────────┘
```

## Initialization and Registration

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                               STARTUP SEQUENCE                                       │
│                               builder.rs:36-70                                       │
│                                                                                      │
│   1. Create shared storage                                                           │
│      ┌────────────────────────────────────────────────────────────────────────────┐ │
│      │  let arbor_storage = Arc::new(ArborStorage::new(arbor_config).await?);     │ │
│      │                                                                             │ │
│      │  Storage is shared between:                                                 │ │
│      │  - Arbor activation (tree/node operations)                                  │ │
│      │  - Cone activation (conversation context)                                   │ │
│      │  - ClaudeCode activation (session management)                               │ │
│      └────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│   2. Register activations                                                            │
│      ┌────────────────────────────────────────────────────────────────────────────┐ │
│      │  Plexus::new()                                                              │ │
│      │      .register(Health::new())           // namespace: "health"              │ │
│      │      .register(Bash::new())             // namespace: "bash"                │ │
│      │      .register(Arbor::with_storage(     // namespace: "arbor"               │ │
│      │          arbor_storage.clone()                                              │ │
│      │      ))                                                                     │ │
│      │      .register(Cone::new(               // namespace: "cone"                │ │
│      │          cone_config,                                                       │ │
│      │          arbor_storage.clone()                                              │ │
│      │      ).await?)                                                              │ │
│      │      .register(ClaudeCode::new(         // namespace: "claudecode"          │ │
│      │          claudecode_storage                                                 │ │
│      │      ))                                                                     │ │
│      └────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│   3. Data directory structure                                                        │
│      ┌────────────────────────────────────────────────────────────────────────────┐ │
│      │  .substrate/                                                                │ │
│      │  ├── arbor.db      ← Shared tree storage (SQLite)                          │ │
│      │  ├── cone.db       ← Cone-specific data                                    │ │
│      │  └── claudecode.db ← Session data                                          │ │
│      └────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│   4. Start transports                                                                │
│      ┌────────────────────────────────────────────────────────────────────────────┐ │
│      │  Stdio mode (--stdio):                                                      │ │
│      │      stdin → JsonRpcRequest → Plexus → JsonRpcResponse → stdout            │ │
│      │                                                                             │ │
│      │  Server mode (default):                                                     │ │
│      │      WebSocket :4444 → Plexus RPC subscriptions                             │ │
│      │      HTTP :4445      → MCP JSON-RPC                                         │ │
│      └────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

## Method Dispatch Detail

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                            METHOD DISPATCH                                           │
│                            plexus.rs:386-446                                         │
│                                                                                      │
│   Input: "arbor.tree_render", { "tree_id": "550e8400-..." }                         │
│                                                                                      │
│   ┌─────────────────────────────────────────────────────────────────────────────┐   │
│   │  Step 1: Parse method name                                                   │   │
│   │                                                                              │   │
│   │  parse_method("arbor.tree_render")                                          │   │
│   │      → Ok(("arbor", "tree_render"))                                         │   │
│   │                                                                              │   │
│   │  Invalid format → Error stream with guidance:                                │   │
│   │      "Call PlexusSchema to see available methods"                           │   │
│   └─────────────────────────────────────────────────────────────────────────────┘   │
│                              │                                                       │
│                              ▼                                                       │
│   ┌─────────────────────────────────────────────────────────────────────────────┐   │
│   │  Step 2: Find activation                                                     │   │
│   │                                                                              │   │
│   │  self.activations.get("arbor")                                              │   │
│   │      → Some(Arc<dyn ActivationObject>)                                      │   │
│   │                                                                              │   │
│   │  Not found → Error stream with guidance:                                     │   │
│   │      "Unknown namespace 'arbor'. Available: [health, bash, arbor, ...]"     │   │
│   └─────────────────────────────────────────────────────────────────────────────┘   │
│                              │                                                       │
│                              ▼                                                       │
│   ┌─────────────────────────────────────────────────────────────────────────────┐   │
│   │  Step 3: Call activation method                                              │   │
│   │                                                                              │   │
│   │  activation.call("tree_render", params).await                               │   │
│   │      → Ok(PlexusStream)  // Success: return stream directly                 │   │
│   │      → Err(e)            // Failure: wrap in guidance stream                │   │
│   │                                                                              │   │
│   │  Error handling (always returns Ok):                                         │   │
│   │      Err(e) → Ok(stream![                                                   │   │
│   │          Guidance { error_type: "activation_error", ... },                  │   │
│   │          Error { error: e.to_string(), recoverable: false },                │   │
│   │          Done                                                                │   │
│   │      ])                                                                      │   │
│   └─────────────────────────────────────────────────────────────────────────────┘   │
│                                                                                      │
│   Output: Result<PlexusStream, PlexusError>  (always Ok in practice)                │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

## Critical Design Patterns

### 1. Streaming-First Architecture

Every activation method returns a stream, enabling:
- **Progress updates** during long operations
- **Chunked data** for large responses
- **Guidance events** for error recovery
- **Consistent interface** for all consumers

```rust
// All methods return streams, never direct values
async fn tree_render(&self, tree_id: TreeId) -> impl Stream<Item = ArborEvent> {
    stream! {
        yield ArborEvent::TreeRender { tree_id, render };
    }
}
```

### 2. Always-Successful Error Handling

`Plexus::call()` returns `Ok(stream)` even for errors:

```rust
// Errors delivered as stream events, not Result::Err
match activation.call(method, params).await {
    Ok(stream) => Ok(stream),
    Err(e) => Ok(error_stream_with_guidance(e)),  // Still Ok!
}
```

This ensures consistent streaming interface regardless of error state.

### 3. Type Erasure with Schema Preservation

```
┌───────────────────────────────────────────────────────────────┐
│   Activation<Methods = ArborMethods>                          │
│       │                                                       │
│       │  ActivationWrapper wraps                              │
│       ▼                                                       │
│   dyn ActivationObject                                        │
│       │                                                       │
│       │  Still can generate schemas via A::Methods            │
│       ▼                                                       │
│   Stored in HashMap<String, Arc<dyn ActivationObject>>        │
└───────────────────────────────────────────────────────────────┘
```

### 4. Provenance Tracking

Each stream item includes its path through the system:

```
"plexus" → "plexus/arbor" → "plexus/arbor/tree_render"
```

Enables debugging and audit trails.

### 5. Hash-Based Cache Invalidation

Every response includes `plexus_hash`:
- Computed from all registered activations and methods
- Clients detect schema changes and refresh
- Enables safe caching of capability discovery

## Example Request: Complete Trace

### Request

```json
{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "arbor.tree_render",
    "arguments": {
      "tree_id": "550e8400-e29b-41d4-a716-446655440000"
    }
  }
}
```

### Flow

1. **Transport** (`transport.rs:42`): Parse JSON-RPC, extract method
2. **MCP Interface** (`interface.rs:76`): Route `tools/call` to handler
3. **Plexus** (`plexus.rs:397`): Parse `arbor.tree_render` → `("arbor", "tree_render")`
4. **Registry** (`plexus.rs:412`): Look up Arbor activation
5. **Wrapper** (`plexus.rs:199`): Delegate to `arbor.call("tree_render", {...})`
6. **Arbor** (`activation.rs:422`): Call `storage.tree_get(&tree_id)`
7. **Storage** (`storage.rs`): Query SQLite, build `Tree` struct
8. **Render** (`types.rs:260`): `tree.render()` → box-drawing visualization
9. **Event** (`activation.rs:425`): Yield `ArborEvent::TreeRender { tree_id, render }`
10. **Convert** (`types.rs:515`): `into_plexus_item()` → `PlexusStreamItem::data(...)`
11. **Buffer** (`rmcp_mcp_server.rs:209`): Collect data events
12. **Response** (`rmcp_mcp_server.rs:278`): Build `CallToolResult`

### Response

```json
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [{
      "type": "text",
      "text": "└──\n    ├── [cone:msg-aaa:system:init]\n    └── [cone:msg-bbb:user:q1]\n        └── [cone:msg-ccc:assistant:a1]"
    }],
    "isError": false
  }
}
```

**Note:** The render shows handle references, not resolved content. Arbor only knows structure.
To get actual message text, use `context_get_path` + handle resolution (see Handle Resolution section).

## Handle Resolution: Arbor → Context → LLM Messages

This is the critical flow: how Arbor's external handles become actual LLM context.

### The Handle Indirection Pattern

Arbor doesn't store message content directly. Instead, nodes contain **handles** pointing to data stored elsewhere:

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              ARBOR TREE STRUCTURE                                    │
│                                                                                      │
│   Tree (conversation structure)                                                      │
│   └── Node (root)                                                                    │
│       ├── Node { type: "external", handle: { source: "cone", id: "msg-abc:system" }}│
│       └── Node { type: "external", handle: { source: "cone", id: "msg-def:user" }}  │
│           └── Node { type: "external", handle: { source: "cone", id: "msg-ghi:assistant" }}│
│               └── Node { type: "external", handle: { source: "cone", id: "msg-jkl:user" }}│
│                   └── ... (current head)                                             │
│                                                                                      │
│   Arbor stores STRUCTURE only. Content lives in handle sources.                     │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

### Handle Type Definition

```rust
// arbor/types.rs:110-124
pub struct Handle {
    /// Source system identifier (e.g., "cone", "bash", "postgres")
    pub source: String,

    /// Source system version (semantic version)
    pub source_version: String,

    /// Identifier within that source system
    pub identifier: String,

    /// Optional metadata
    pub metadata: Option<Value>,
}

// Node types - either inline text or external handle
pub enum NodeType {
    Text { content: String },           // Legacy, rarely used
    External { handle: Handle },        // Standard - pointer to data
}
```

### Context Resolution Flow

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                         CONE CHAT: CONTEXT RESOLUTION                                │
│                         activation.rs:162-289                                        │
│                                                                                      │
│   Step 1: Load cone configuration                                                    │
│   ┌────────────────────────────────────────────────────────────────────────────────┐│
│   │  let cone = storage.cone_get(&cone_id).await?;                                 ││
│   │                                                                                 ││
│   │  Cone {                                                                         ││
│   │      id, name, model_id, system_prompt,                                        ││
│   │      head: Position { tree_id, node_id }  ← current position in conversation  ││
│   │  }                                                                              ││
│   └────────────────────────────────────────────────────────────────────────────────┘│
│                              │                                                       │
│                              ▼                                                       │
│   Step 2: Get path from root to head (handles only)                                 │
│   ┌────────────────────────────────────────────────────────────────────────────────┐│
│   │  let context_nodes = storage.arbor()                                           ││
│   │      .context_get_path(&cone.head.tree_id, &cone.head.node_id)                ││
│   │      .await?;                                                                   ││
│   │                                                                                 ││
│   │  Returns: Vec<Node> from root to current head                                  ││
│   │  [Node{handle: msg-abc}, Node{handle: msg-def}, Node{handle: msg-ghi}, ...]   ││
│   └────────────────────────────────────────────────────────────────────────────────┘│
│                              │                                                       │
│                              ▼                                                       │
│   Step 3: Resolve handles to messages                                                │
│   ┌────────────────────────────────────────────────────────────────────────────────┐│
│   │  let messages = resolve_context_to_messages(&storage, &context_nodes).await?; ││
│   │                                                                                 ││
│   │  For each node:                                                                 ││
│   │    match node.data {                                                           ││
│   │        NodeType::External { handle } => {                                      ││
│   │            match handle.source.as_str() {                                      ││
│   │                "cone" => storage.resolve_message_handle(&handle.identifier)    ││
│   │                "bash" => "[Tool output from bash: {id}]"                       ││
│   │                _ => "[External reference: {source}:{id}]"                      ││
│   │            }                                                                    ││
│   │        }                                                                        ││
│   │    }                                                                            ││
│   └────────────────────────────────────────────────────────────────────────────────┘│
│                              │                                                       │
│                              ▼                                                       │
│   Step 4: Build LLM request                                                          │
│   ┌────────────────────────────────────────────────────────────────────────────────┐│
│   │  let mut llm_messages = messages;           // resolved context                ││
│   │  llm_messages.push(Message::user(&prompt)); // add new user message           ││
│   │                                                                                 ││
│   │  // Send to LLM via cllient                                                    ││
│   │  let response = request_builder                                                ││
│   │      .messages(llm_messages)                                                   ││
│   │      .send().await?;                                                           ││
│   └────────────────────────────────────────────────────────────────────────────────┘│
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

### Handle Resolution Detail

```rust
// cone/storage.rs:467-495

// Handle identifier format: "msg-{uuid}:{role}:{name}"
// Example: "msg-550e8400-e29b-41d4-a716-446655440000:user:question"

pub async fn resolve_message_handle(&self, identifier: &str) -> Result<Message, ConeError> {
    // Parse: "msg-{uuid}:{role}:{name}"
    let parts: Vec<&str> = identifier.splitn(3, ':').collect();
    let msg_id = parts[0].strip_prefix("msg-").unwrap();

    // Query SQLite for message content
    let message = sqlx::query_as!(Message,
        "SELECT * FROM messages WHERE id = ?",
        msg_id
    ).fetch_one(&self.pool).await?;

    Ok(message)
}

// Creating a handle from a message
pub fn message_to_handle(message: &Message, name: &str) -> Handle {
    Handle {
        source: "cone".to_string(),
        source_version: "1.0.0".to_string(),
        identifier: format!("msg-{}:{}:{}", message.id, message.role.as_str(), name),
        metadata: None,
    }
}
```

### Storage Separation Diagram

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                           DATA STORAGE SEPARATION                                    │
│                                                                                      │
│   ┌─────────────────────────────────┐    ┌─────────────────────────────────────────┐│
│   │         ARBOR (arbor.db)        │    │           CONE (cone.db)                ││
│   │                                 │    │                                          ││
│   │  Trees:                         │    │  Cones:                                  ││
│   │  ┌─────────────────────────────┐│    │  ┌──────────────────────────────────────┐││
│   │  │ id | owner | ref_count     ││    │  │ id | name | model_id | head_tree_id │││
│   │  └─────────────────────────────┘│    │  └──────────────────────────────────────┘││
│   │                                 │    │                                          ││
│   │  Nodes:                         │    │  Messages:                               ││
│   │  ┌─────────────────────────────┐│    │  ┌──────────────────────────────────────┐││
│   │  │ id | tree_id | parent_id   ││    │  │ id | cone_id | role | content       │││
│   │  │ type | handle_source       ││    │  │ model_id | input_tokens | ...       │││
│   │  │ handle_identifier          ││    │  └──────────────────────────────────────┘││
│   │  └─────────────────────────────┘│    │                                          ││
│   │                                 │    │  Role: user | assistant | system        ││
│   │  Stores: STRUCTURE              │    │  Stores: CONTENT                         ││
│   │  (who replied to whom)          │    │  (what was actually said)               ││
│   └─────────────────────────────────┘    └─────────────────────────────────────────┘│
│                                                                                      │
│   Connection: handle.identifier = "msg-{messages.id}:{role}:{name}"                 │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

### Why This Separation?

| Concern | Arbor | Cone |
|---------|-------|------|
| **Purpose** | Conversation structure | Message content |
| **Sharing** | Trees shared across cones | Messages owned by cone |
| **Branching** | Supports branching/forking | Content immutable |
| **Cleanup** | Ref-counted deletion | Cascade with cone |
| **Query Pattern** | Path traversal | Single lookup |

### Complete Chat Flow: Handle → String Context

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                    COMPLETE: Handle Resolution to LLM Context                        │
│                                                                                      │
│   User sends: cone_chat("my-assistant", "What is 2+2?")                             │
│                                                                                      │
│   1. Load cone → head at node N5                                                     │
│                                                                                      │
│   2. Get arbor path: [N1, N2, N3, N4, N5]                                           │
│      Each node has handle like: { source: "cone", id: "msg-xxx:role:name" }        │
│                                                                                      │
│   3. Resolve each handle:                                                            │
│      ┌────────────────────────────────────────────────────────────────────────────┐ │
│      │  N1: msg-aaa:system:init   → "You are a helpful assistant"                 │ │
│      │  N2: msg-bbb:user:q1       → "Hello!"                                      │ │
│      │  N3: msg-ccc:assistant:a1  → "Hi there! How can I help?"                   │ │
│      │  N4: msg-ddd:user:q2       → "What's the weather?"                         │ │
│      │  N5: msg-eee:assistant:a2  → "I don't have weather access."                │ │
│      └────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                      │
│   4. Build messages Vec:                                                             │
│      [                                                                               │
│        Message::system("You are a helpful assistant"),                              │
│        Message::user("Hello!"),                                                     │
│        Message::assistant("Hi there! How can I help?"),                             │
│        Message::user("What's the weather?"),                                        │
│        Message::assistant("I don't have weather access."),                          │
│        Message::user("What is 2+2?")  ← NEW USER MESSAGE                           │
│      ]                                                                               │
│                                                                                      │
│   5. Send to LLM via cllient                                                        │
│      → Response: "2+2 equals 4"                                                     │
│                                                                                      │
│   6. Store response:                                                                 │
│      - Create message in cone.db: msg-fff:assistant:a3                             │
│      - Create arbor node N6 with handle pointing to msg-fff                        │
│      - Advance cone head to N6                                                      │
│                                                                                      │
│   7. Return streamed response to client                                             │
│                                                                                      │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

### Cone Chat Implementation Detail

This section traces through the actual `cone_chat` code to show exactly how handles become LLM context.

**Entry Point: `activation.rs:168-436`**

```rust
async fn chat(&self, identifier: ConeIdentifier, prompt: String, ephemeral: Option<bool>) {

    // 1. RESOLVE CONE (lines 181-196)
    let cone_id = storage.resolve_cone_identifier(&identifier).await?;
    let cone = storage.cone_get(&cone_id).await?;
    // cone.head = Position { tree_id, node_id }  ← current conversation position

    // 2. GET ARBOR PATH (lines 198-205)
    let context_nodes = storage.arbor()
        .context_get_path(&cone.head.tree_id, &cone.head.node_id)
        .await?;
    // Returns Vec<Node> from root to head, each with External { handle }

    // 3. RESOLVE HANDLES → MESSAGES (lines 207-214)
    let messages = resolve_context_to_messages(&storage, &context_nodes, &cone.system_prompt).await?;
    // This is where handles become actual text ↓

    // 4. BUILD LLM REQUEST (lines 287-303)
    let mut llm_messages = messages;           // resolved history
    llm_messages.push(Message::user(&prompt)); // add new prompt

    let response = request_builder
        .system(cone.system_prompt)
        .messages(llm_messages)
        .stream().await?;

    // 5. STREAM RESPONSE (lines 305-344)
    while let Some(event) = stream_result.next().await {
        // yield ConeEvent::ChatContent { content: text }
        full_response.push_str(&text);
    }

    // 6. STORE RESPONSE (lines 346-407)
    // Create message in SQLite
    let assistant_message = storage.message_create(
        &cone_id, MessageRole::Assistant, full_response, ...
    ).await?;

    // Create arbor node with handle pointing to message
    let assistant_handle = ConeStorage::message_to_handle(&assistant_message, &cone.name);
    let response_node_id = storage.arbor().node_create_external(
        &cone.head.tree_id,
        Some(user_node_id),  // parent is the user's message node
        assistant_handle,
        None,
    ).await?;

    // 7. ADVANCE HEAD (lines 409-417)
    storage.cone_update_head(&cone_id, response_node_id).await?;
}
```

**The Core Resolution: `resolve_context_to_messages` (lines 499-553)**

```rust
async fn resolve_context_to_messages(
    storage: &ConeStorage,
    nodes: &[Node],
    _system_prompt: &Option<String>,
) -> Result<Vec<Message>, String> {
    let mut messages = Vec::new();

    for node in nodes {
        match &node.data {
            NodeType::Text { content } => {
                // Legacy - skip empty root nodes
                if !content.is_empty() {
                    messages.push(Message::user(content));
                }
            }
            NodeType::External { handle } => {
                match handle.source.as_str() {
                    "cone" => {
                        // THIS IS THE KEY RESOLUTION ↓
                        let msg = storage
                            .resolve_message_handle(&handle.identifier)
                            .await?;

                        // Convert to cllient::Message based on role
                        let cllient_msg = match msg.role {
                            MessageRole::User => Message::user(&msg.content),
                            MessageRole::Assistant => Message::assistant(&msg.content),
                            MessageRole::System => Message::system(&msg.content),
                        };
                        messages.push(cllient_msg);
                    }
                    "bash" => {
                        messages.push(Message::user(&format!(
                            "[Tool output from bash: {}]", handle.identifier
                        )));
                    }
                    _ => {
                        messages.push(Message::user(&format!(
                            "[External reference: {}:{}]", handle.source, handle.identifier
                        )));
                    }
                }
            }
        }
    }
    Ok(messages)
}
```

**SQLite Lookup: `resolve_message_handle` (storage.rs:467-484)**

```rust
pub async fn resolve_message_handle(&self, identifier: &str) -> Result<Message, ConeError> {
    // identifier = "msg-{uuid}:{role}:{name}"
    // Example: "msg-550e8400-e29b-41d4-a716-446655440000:user:question"

    let parts: Vec<&str> = identifier.splitn(3, ':').collect();
    let msg_part = parts[0];  // "msg-550e8400..."
    let message_id_str = &msg_part[4..];  // strip "msg-" prefix

    let message_id = Uuid::parse_str(message_id_str)?;

    // Query SQLite
    self.message_get(&message_id).await
    // SELECT id, cone_id, role, content, ... FROM messages WHERE id = ?
}
```

**Visual Data Flow:**

```
cone_chat("assistant", "What is 2+2?")
         │
         ▼
┌─────────────────────────────────────┐
│  Arbor Path: [N1, N2, N3, N4, N5]   │
│                                     │
│  N1: handle → msg-aaa:system:sys    │
│  N2: handle → msg-bbb:user:q1       │
│  N3: handle → msg-ccc:assistant:a1  │
│  ...                                │
└─────────────────────────────────────┘
         │ resolve_context_to_messages
         ▼
┌─────────────────────────────────────┐
│  SQLite queries (cone.db):          │
│                                     │
│  msg-aaa → "You are helpful..."     │
│  msg-bbb → "Hello!"                 │
│  msg-ccc → "Hi! How can I help?"    │
└─────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────┐
│  Vec<cllient::Message>:             │
│                                     │
│  [System("You are helpful..."),     │
│   User("Hello!"),                   │
│   Assistant("Hi! How can I help?"), │
│   User("What is 2+2?")]  ← NEW      │
└─────────────────────────────────────┘
         │
         ▼
      LLM API
```

### Extensibility: Other Handle Sources

The handle pattern supports multiple data sources:

```rust
match handle.source.as_str() {
    "cone" => {
        // Resolve from cone's SQLite message store
        storage.resolve_message_handle(&handle.identifier).await?
    }
    "bash" => {
        // TODO: Resolve bash command output
        Message::user(&format!("[Tool output: {}]", handle.identifier))
    }
    "file" => {
        // TODO: Resolve file contents
        Message::user(&format!("[File: {}]", handle.identifier))
    }
    "web" => {
        // TODO: Resolve web fetch result
        Message::user(&format!("[Web: {}]", handle.identifier))
    }
    _ => {
        // Unknown source - include as reference
        Message::user(&format!("[External: {}:{}]", handle.source, handle.identifier))
    }
}
```

Future integrations can add new handle sources without modifying Arbor's structure.

## File Reference

| Component | File | Key Lines |
|-----------|------|-----------|
| HTTP Transport | `src/mcp/transport.rs` | 42-92 |
| Stdio Transport | `src/main.rs` | 28-82 |
| MCP Interface | `src/mcp/interface.rs` | 59-92 |
| Handle Type | `src/activations/arbor/types.rs` | 110-137 |
| **Cone Chat (full flow)** | `src/activations/cone/activation.rs` | 168-436 |
| Context Resolution | `src/activations/cone/activation.rs` | 198-214 |
| Handle→Message | `src/activations/cone/activation.rs` | 499-553 |
| Message Handle Resolution | `src/activations/cone/storage.rs` | 467-495 |
| Message Creation | `src/activations/cone/storage.rs` | 366-404 |
| MCP State Machine | `src/mcp/state.rs` | - |
| Plexus Dispatcher | `src/plexus/plexus.rs` | 386-446 |
| Activation Wrapper | `src/plexus/plexus.rs` | 187-248 |
| Plexus Builder | `src/builder.rs` | 36-70 |
| Arbor Activation | `src/activations/arbor/activation.rs` | 38-433 |
| Tree Render Method | `src/activations/arbor/activation.rs` | 417-433 |
| Tree Render Impl | `src/activations/arbor/types.rs` | 260-302 |
| Arbor Events | `src/activations/arbor/types.rs` | 353-508 |
| Stream Item Trait | `src/activations/arbor/types.rs` | 510-528 |
| Plexus Stream Types | `src/plexus/types.rs` | 36-144 |
| RMCP Example | `examples/rmcp_mcp_server.rs` | 147-302 |
