Files
hub/docs/architecture/mcp-server.md
glm-5.1 2b63cda1c7 Setup repo: migrate architecture specs, code stubs, and tasks from alkhub_ts
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.
2026-05-25 10:56:32 +00:00

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