Copy architecture docs, ADRs, storage domain specs, research, reviews, and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for standalone @alkdev/hub repo structure (src/ not packages/hub/). Sanitize all sensitive information: - Replace private IPs (10.0.0.1) with localhost defaults - Remove internal server hostnames (dev1, ns528096) - Replace /workspace/ private paths with npm package references - Remove hardcoded credentials from examples - Rewrite infrastructure.md without private network details Add Deno project scaffolding: deno.json (pinned deps), .gitignore, AGENTS.md, entry point. Migrate existing code stubs (crypto, config types, logger) with updated import paths.
4.8 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-04-16 |
MCP Server: Discovery + Call Interface
Overview
The hub exposes an MCP endpoint using @hono/mcp's StreamableHTTPTransport. Instead of exposing many operations as individual MCP tools (which bloats the LLM's context with tool definitions it mostly doesn't need), the MCP server exposes four tools for discovery and execution. Agents list or search for what they need, get the schema, then call it. Everything else goes through the operation registry via the call protocol.
This pattern is proven: the toolEnv POC ran it as HTTP endpoints (/list, /search, /schema/:tool, /call). We wrap the same four operations as MCP tools instead.
@hono/mcp is the Hono MCP middleware (npm: @hono/mcp). Source for reference: @hono/mcp (npm package).
Why Discovery + Call, Not Direct Exposure
| Direct Exposure (many MCP tools) | Discovery + Call (4 MCP tools) |
|---|---|
| Every operation becomes an MCP tool definition | Agent lists or searches for what it needs, gets schema on demand |
| N operations = N tool defs in context | 4 tool defs in context always |
| LLM sees worktree/git tools irrelevant to task | LLM only loads schemas for operations it will use |
| Adding operations = restart MCP, re-discover | Adding operations = automatic, search finds them |
| No namespace awareness | list and search support namespace filtering |
The core problem with direct exposure: MCP tool definitions sit in the LLM's context for the entire conversation. An implementation specialist working on a React component doesn't need git.worktreeCreate and coord.spawn cluttering its thinking. With discovery + call, it searches coord, gets the schemas for coord.message, and calls it. Four tool definitions, not thirty.
How It Works
Hub MCP Endpoint
The hub MCP endpoint creates an McpServer from @modelcontextprotocol/sdk, connects it to a StreamableHTTPTransport from @hono/mcp, and mounts it at /mcp. Four tools are registered:
| Tool | Input | Output | Description |
|---|---|---|---|
| list | { namespace?: string } | OperationSpec[] | List available operations, optionally filtered by namespace |
| search | { q?: string, namespace?: string } | { tool, description }[] | Search operations by name, description, or namespace |
| schema | { tool: string } | { inputSchema, outputSchema } | Get schemas for a specific operation |
| call | { calls: [{ tool, input? }] } | { success, result/error }[] | Execute operations via call protocol |
list returns all available operations (or those in a given namespace) — useful when the agent needs to browse what's available. search filters the operation registry by query string and/or namespace — useful when the agent knows roughly what it needs. schema returns the TypeBox input/output schemas for a given operation. call accepts an array of operation calls and returns results.
call routes through callMap.call() (the call protocol), not registry.execute() directly. This gives full call graph tracking, abort cascading, and structured error handling.
Agent Workflow Example
Agent: "I need to spawn a worktree for the auth feature"
→ search({ q: "spawn" }) → [{ tool: "coord.spawn", description: "..." }]
→ schema({ tool: "coord.spawn" }) → { inputSchema: { sessionId, task, branch, ... }, ... }
→ call({ calls: [{ tool: "coord.spawn", input: { sessionId: "...", task: "implement auth", branch: "feat/auth" } }] })
Agent: "Let me check on the spawned sessions"
→ search({ namespace: "coord" }) → [{ tool: "coord.status", ... }, { tool: "coord.message", ... }, ...]
→ schema({ tool: "coord.status" }) → { inputSchema: { parentSessionId }, ... }
→ call({ calls: [{ tool: "coord.status", input: { parentSessionId: "..." } }] })
Only the tool definitions the agent actually needs enter context, and only when it needs them.
Auth
The MCP endpoint uses bearer token auth. Each runner gets a token at registration. The hub validates the token and attaches the runner's identity to the operation context for access control.
What This Replaces
| Previous | Now |
|---|---|
mcp-visible tag, many MCP tool defs |
4 MCP tools, operations discovered dynamically |
| Per-container MCP servers (websearch, etc.) | Shared hub registry, call dispatches to any operation |
| Manual tool exposure per operation | Automatic — all registered operations are searchable |