brainwires-relay
MCP server framework and agent communication backbone for Brainwires.
Overview
brainwires-relay provides three layers of functionality: an MCP server framework with composable middleware, an encrypted IPC system for local inter-agent communication, and a remote bridge for backend connectivity via Supabase Realtime or HTTP polling.
Design principles:
- Trait-driven —
McpHandler,KeyStore,AgentSpawner, and friends decouple the framework from any concrete CLI implementation - Middleware-composable — auth, rate limiting, logging, and tool filtering stack via an onion model
- Encryption-first — IPC sockets use ChaCha20-Poly1305 authenticated encryption by default
- Dual-mode remote — Supabase Realtime (preferred) with automatic HTTP polling fallback
┌──────────────────────────────────────────────────────┐
│ brainwires-relay │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ MCP Server Framework │ │
│ │ │ │
│ │ McpHandler ──► MiddlewareChain ──► Transport │ │
│ │ (onion model) │ │
│ │ │ │
│ │ ┌──────────┐ ┌────────────┐ │ │
│ │ │ToolReg- │ │ Auth │Rate │ │ │
│ │ │ istry │ │ Limit│Log │ │ │
│ │ └──────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ IPC Layer │ │ Remote Bridge │ │
│ │ │ │ │ │
│ │ Unix Socket │ │ Supabase Realtime / │ │
│ │ + ChaCha20 │ │ HTTP Polling Fallback │ │
│ │ + Discovery │ │ + Priority Queue │ │
│ └─────────────────┘ └──────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ Auth System │ │ Agent Manager │ │
│ │ Session + │ │ Spawn / List / Stop / │ │
│ │ Keyring │ │ Await / Pool Stats │ │
│ └─────────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────┘
Quick Start
Add to your Cargo.toml:
[]
= "0.1"
Minimal MCP server:
use *;
;
async
Features
| Feature | Default | Description |
|---|---|---|
auth-keyring |
No | Secure API key storage via system keyring (keyring crate) |
IPC encryption, MCP transport, middleware, and remote bridge are always available.
# With keyring support
= { = "0.1", = ["auth-keyring"] }
Architecture
MCP Server Framework
The framework follows the Model Context Protocol specification for tool-based AI server integration.
Traits:
McpHandler— defines server identity, capabilities, and tool dispatchServerTransport— reads requests and writes responses (stdio, etc.)ToolHandler— per-tool call handler
McpServer<H: McpHandler> wires handler, middleware chain, and transport together:
let server = new
.with_transport
.with_middleware
.with_middleware;
server.run.await?;
McpToolRegistry provides declarative tool registration with automatic dispatch:
let mut registry = new;
registry.register;
let tools = registry.list_tool_defs;
let result = registry.dispatch.await?;
Middleware
Middleware follows an onion model: requests flow forward through layers, responses flow back.
Trait:
MiddlewareResult::Continue passes to the next layer; MiddlewareResult::Reject(err) short-circuits the chain.
Built-in middleware:
| Layer | Description |
|---|---|
AuthMiddleware |
Bearer token validation, rejects unauthorized requests |
RateLimitMiddleware |
Token-bucket rate limiter with per-tool limits |
LoggingMiddleware |
Structured request/response logging via tracing |
ToolFilterMiddleware |
Allow-list or deny-list for tool access |
ToolFilterMiddleware modes:
| Mode | Constructor | Description |
|---|---|---|
AllowList |
ToolFilterMiddleware::allow_only(["tool_a", "tool_b"]) |
Only listed tools are accessible |
DenyList |
ToolFilterMiddleware::deny(["dangerous_tool"]) |
Listed tools are blocked |
IPC (Inter-Process Communication)
Local agent-to-agent communication over Unix domain sockets with authenticated encryption.
Connection lifecycle:
IpcConnection::connect(socket_path)— plain-text connectionHandshakeexchange — session ID, token, model, working directoryconnection.upgrade_to_encrypted(session_token)— ChaCha20-Poly1305 from this point
IpcCipher derives a 256-bit key from the session token via SHA-256:
let cipher = from_session_token;
let encrypted = cipher.encrypt?;
let decrypted = cipher.decrypt?;
Reader/Writer pairs:
| Variant | Description |
|---|---|
IpcReader / IpcWriter |
Plain-text newline-delimited JSON |
EncryptedIpcReader / EncryptedIpcWriter |
Encrypted + base64-encoded JSON |
IpcConnection |
Combined reader + writer with split() and upgrade_to_encrypted() |
Protocol messages — ViewerMessage (inbound from viewer):
| Group | Variants |
|---|---|
| Chat | UserInput, Cancel, SyncRequest, Exit, Detach, Disconnect |
| Commands | SlashCommand, SetToolMode, QueueMessage |
| Locks | AcquireLock, ReleaseLock, QueryLocks, UpdateLockStatus |
| Agents | ListAgents, SpawnAgent, NotifyChildren, ParentSignal |
| Plan mode | EnterPlanMode, ExitPlanMode, PlanModeUserInput, PlanModeSyncRequest |
Protocol messages — AgentMessage (outbound from agent):
| Group | Variants |
|---|---|
| Streaming | StreamChunk, StreamEnd |
| Tools | ToolCallStart, ToolProgress, ToolResult |
Agent discovery:
| Function | Description |
|---|---|
list_agent_sessions(dir) |
List all agent session IDs |
list_agent_sessions_with_metadata(dir) |
List with full AgentMetadata |
is_agent_alive(dir, id) |
Check if socket responds |
cleanup_stale_sockets(dir) |
Remove dead agent sockets |
format_agent_tree(dir, root_only) |
ASCII tree of parent/child agents |
get_child_agents(dir, parent_id) |
List children of a parent agent |
Authentication
Session-based authentication with optional keyring storage.
AuthClient authenticates against a backend:
let client = new;
client.validate_api_key_format?;
let response = client.authenticate.await?;
SessionManager persists sessions to disk with keyring integration:
let manager = new;
manager.save?;
let session = manager.load?;
let api_key = manager.get_api_key?;
Key types:
| Type | Description |
|---|---|
AuthSession |
User profile, Supabase config, backend URL, timestamp |
UserProfile |
User ID, username, display name, role |
SupabaseConfig |
Supabase URL and anonymous key |
Agent Manager
Trait-based agent lifecycle management for MCP server mode.
AgentManager trait:
AgentToolRegistry registers 10 MCP tools for agent management:
| Tool | Description |
|---|---|
agent_spawn |
Spawn a new task agent with optional MDAP |
agent_list |
List running agents |
agent_status |
Get agent status by ID |
agent_stop |
Stop an agent by ID |
agent_await |
Wait for agent completion |
agent_pool_stats |
Pool statistics |
agent_file_locks |
List file locks |
self_improve_start |
Start autonomous self-improvement session |
self_improve_status |
Get improvement session status |
self_improve_stop |
Stop improvement session |
Remote Bridge
Backend connectivity with protocol negotiation, heartbeats, and priority command queuing.
RemoteBridge manages the connection lifecycle:
let bridge = new;
// State: Disconnected → Connecting → Connected → Authenticated
let state = bridge.state.await;
let mode = bridge.connection_mode.await; // Realtime or Polling
Protocol negotiation:
// Client sends ProtocolHello with supported versions and capabilities
// Server responds with ProtocolAccept selecting version and capabilities
CommandQueue — priority-based queue with deadline tracking and retry:
| Feature | Description |
|---|---|
| Priority levels | Critical, High, Normal, Low |
| Deadline tracking | Commands expire if not processed in time |
| Exponential backoff | Configurable retry with multiplier |
| Max depth | Bounded queue prevents memory exhaustion |
HeartbeatCollector detects agent lifecycle changes:
let mut collector = new;
let data: HeartbeatData = collector.collect.await?;
let events: = collector.detect_changes?;
Framework Traits
Trait abstractions decouple the relay from CLI-specific implementations:
| Trait | Purpose |
|---|---|
SessionDir |
Path resolution for IPC sessions and data storage |
KeyStore |
Secure credential storage (keyring, file, etc.) |
AuthEndpoints |
Authentication endpoint configuration |
AgentSpawner |
Agent process spawning |
AgentDiscovery |
Agent listing and stale cleanup |
BridgeConfigProvider |
Remote bridge configuration |
Error Handling
RelayError variants:
| Variant | Description |
|---|---|
ParseError |
JSON-RPC parse failure |
MethodNotFound |
Unknown RPC method |
InvalidParams |
Invalid method parameters |
Internal |
Internal error (wraps anyhow::Error) |
Transport |
Transport-level I/O error |
ToolNotFound |
Requested tool not registered |
RateLimited |
Rate limit exceeded |
Unauthorized |
Authentication failure |
All variants map to standard JSON-RPC error codes via to_json_rpc_error().
Usage Examples
MCP Server with Auth and Rate Limiting
use *;
let server = new
.with_transport
.with_middleware
.with_middleware
.with_middleware;
server.run.await?;
Tool Registry with Dispatch
use ;
let mut registry = new;
registry.register;
assert!;
let result = registry.dispatch.await?;
Encrypted IPC Connection
use ;
let mut conn = connect.await?;
// Exchange handshake in plaintext
conn.writer.write.await?;
let response = conn.reader..await?;
// Upgrade to encrypted channel
let encrypted = conn.upgrade_to_encrypted;
let = ;
Agent Discovery
use *;
let sessions = list_agent_sessions_with_metadata?;
let tree = format_agent_tree?;
println!;
// Cleanup dead agents
cleanup_stale_sockets.await?;
Session Management
use ;
let client = new;
let response = client.authenticate.await?;
let session = create_session;
let manager = new;
manager.save?;
Tool Filtering
use ToolFilterMiddleware;
// Only allow specific tools
let filter = allow_only;
// Or block dangerous ones
let filter = deny;
Remote Bridge State
use ;
let bridge = new;
match bridge.state.await
Configuration
BridgeConfig (Remote)
RequestContext
let mut ctx = new
.with_client_info;
ctx.set_metadata;
ctx.set_initialized;
Integration with Brainwires
Use via the brainwires facade crate:
[]
= { = "0.1", = ["relay"] }
Or use standalone — brainwires-relay depends only on brainwires-core and brainwires-mcp.
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.