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.
71 lines
4.8 KiB
Markdown
71 lines
4.8 KiB
Markdown
---
|
|
status: draft
|
|
last_updated: 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 | |