Context Management
Overview
The MCP protocol eagerly loads all tool signatures when a server connects. Each tool's name, description, and full JSON Schema consumes tokens in the AI's context window before the conversation even starts. With 49 core tools across five servers plus external integrations, this baseline cost reaches 10,000-20,000+ tokens — a significant fraction of the available context on smaller-window models and a cost multiplier on every request.
Crow addresses this with two complementary strategies: the Gateway Tool Router (HTTP deployments) and On-Demand Server Activation (stdio deployments). Both build on the same server factory pattern and InMemoryTransport plumbing.
Gateway Tool Router
The router (servers/gateway/router.js) consolidates all core and external tools behind 7 category tools, reducing context usage by approximately 75%.
Architecture
+-----------------------+
[AI Client] -------->| /router/mcp |
| crow-router McpServer|
+-----------+-----------+
|
+-----------+-----------+-----------+-----------+
| | | | |
crow_memory crow_projects crow_blog crow_sharing crow_storage
| | | | |
[InMemory [InMemory [InMemory [InMemory [InMemory
Transport] Transport] Transport] Transport] Transport]
| | | | |
[Memory [Projects [Blog [Sharing [Storage
McpServer] McpServer] McpServer] McpServer] McpServer]
crow_tools -----> connectedServers (from proxy.js)
crow_discover --> static manifests + live schema lookupEach category tool creates an in-process Client connected to the underlying McpServer via InMemoryTransport.createLinkedPair(). Clients are created lazily on first use within each session.
Router Tools
| Tool | Dispatches To | Actions |
|---|---|---|
crow_memory | Memory server | 12 (store, search, recall, list, update, delete, stats, context ops) |
crow_projects | Project server | 12 (projects, sources, notes, bibliography, stats) |
crow_blog | Blog server | 12 (create, edit, publish, list, delete, export, themes, stats) |
crow_sharing | Sharing server | 8 (invite, contacts, share, inbox, messaging, revoke) |
crow_storage | Storage server | 5 (upload, list, download URL, delete, stats) |
crow_tools | External proxy servers | Dynamic (Trello, Canvas, Slack, etc.) |
crow_discover | Static manifests + live schemas | Discovery protocol |
Parameter Schema
Every category tool accepts the same shape:
{
action: z.string(), // Action name, e.g. "store_memory" or "crow_store_memory"
params: z.record(z.any()).optional() // Parameters forwarded to the underlying tool
}The router resolves tool names with or without the crow_ prefix, so both store_memory and crow_store_memory work.
Compressed Manifests
Each category tool's description is built by buildCompressedDescription() from tool-manifests.js. It packs all action names and parameter signatures into the tool description string:
Persistent memory: store, search, recall, list... Actions:
- store_memory(content, category?, context?, tags?, source?, importance?): Store a memory
- search_memories(query, category?, min_importance?, limit?): Search memories (FTS5)
...This gives the AI enough information to call most actions without discovery, while keeping the schema footprint small.
Discovery Protocol
The crow_discover tool provides on-demand access to full JSON Schemas:
crow_discover() → List all categories with action counts
crow_discover(category="memory") → List memory actions with param summaries
crow_discover(category="memory", action="crow_store_memory") → Full JSON Schema
crow_discover(category="tools") → List external integration tools
crow_discover(category="tools", action="github_create_issue") → Full external tool schemaCategory-level discovery uses static manifests (no server instantiation). Action-level discovery calls client.listTools() on the underlying server to return the live schema.
Context Savings
| Mode | Tools Loaded | Estimated Tokens |
|---|---|---|
| Individual servers (no router) | 49 x ~200 tokens | ~9,800 |
| Router mode | 7 x ~350 tokens | ~2,450 |
| Reduction | ~75% |
Feature Flag
Disable the router to mount servers individually:
CROW_DISABLE_ROUTER=1 npm run gatewayOn-Demand Server Activation (crow-core)
For stdio deployments, servers/core/ provides a single MCP server that starts with one server active and adds others on demand.
Architecture
[AI Client] <--stdio--> [crow-core McpServer]
|
+-----+-----+
| |
[Active Tools] [Management Tools (3)]
(memory: 12) crow_activate_server
crow_deactivate_server
crow_server_status
crow_activate_server("projects")
|
v
[registeredTool.enable()] --> toolListChanged notification
|
v
[AI re-fetches tool list] --> project tools now visibleManagement Tools
| Tool | Parameters | Description |
|---|---|---|
crow_activate_server | server: string | Enable a server's tools (memory, projects, sharing, storage, blog) |
crow_deactivate_server | server: string | Disable a server's tools (default server cannot be deactivated) |
crow_server_status | none | Show active/inactive servers with tool counts |
Startup Behavior
- All servers are connected via
InMemoryTransportand their tools registered on the coreMcpServer - Only the default server's tools are enabled; all others are registered but disabled
- The AI sees 15 tools at startup: 12 memory tools + 3 management tools
- Calling
crow_activate_server("projects")flips the registered tools to enabled and triggers atoolListChangednotification - The AI client re-fetches the tool list and sees the newly available tools
The default server is configurable:
CROW_DEFAULT_SERVER=projects node servers/core/index.jsAutomatic Behavioral Context (MCP Instructions)
The MCP protocol supports an instructions field in the InitializeResult — a string sent during the connection handshake before any tool calls. Per the spec, this "can be used by clients to improve the LLM's understanding of available tools" and "MAY be added to the system prompt."
Crow uses this to deliver behavioral context automatically to every connected AI client, eliminating the need for users to manually ask the AI to load crow.md.
How It Works
Gateway startup
|
v
generateInstructions() ──> queries crow_context table
| extracts 5 essential sections
v condenses to ~1KB string
instructions string (pre-computed)
|
+──> createMemoryServer(undefined, { instructions })
+──> createProjectServer(undefined, { instructions })
+──> createRouterServer({ instructions: routerInstructions })
+──> ...
|
v
McpServer({ name, version }, { instructions })
|
v
Client connects ──> InitializeResult includes instructions
|
v
AI sees behavioral context before any tool callsThe instructions string is generated once at gateway startup and passed to all server factories as a pre-computed string. This avoids per-session database queries and keeps factories synchronous.
Content
The condensed instructions (~1KB) include:
| Section | Content |
|---|---|
| Identity | Who Crow is (1-2 sentences) |
| Session Protocol | "Call crow_recall_by_context on session start" |
| Memory Protocol | Categories, importance levels, deduplication rules |
| Transparency Rules | [crow: action] notation for autonomous actions |
| Capabilities | Tool routing table (direct names or category names for router) |
Two variants are generated:
- Direct style: Uses tool names like
crow_store_memory(for individual server endpoints) - Router style: Uses category names like
crow_memory action: "store_memory"(for/router/mcp)
Fallback
If the crow_context table doesn't exist or the database is unavailable, a static ~500-byte fallback is used that provides minimal behavioral guidance.
Per-Device Overrides
All context generation functions (generateCrowContext, generateCondensedContext, generateInstructions) accept an optional deviceId parameter. When provided, the system queries all crow_context rows and merges global sections (device_id IS NULL) with device-specific sections (device_id = ?). Device-specific sections override globals with the same section_key; device-only sections are appended.
The crow_context table uses two partial unique indexes to enforce uniqueness: one for global sections (WHERE device_id IS NULL) and one for device-specific sections (WHERE device_id IS NOT NULL).
stdio Servers
stdio entry points (servers/*/index.js) generate instructions at startup using top-level await, then pass the string to the factory. crow-core does the same inside its async createCoreServer() function.
MCP Prompts (Skill Equivalents)
MCP prompts are first-class prompt templates that clients can list and request on demand. Crow registers prompts as skill equivalents for non-Claude-Code platforms, giving the AI access to detailed workflow guidance without consuming context window space upfront.
Available Prompts
| Prompt | Server(s) | Description |
|---|---|---|
session-start | Memory, Router | Session start/end protocol from crow.md |
crow-guide | Memory, Router | Full crow.md document (accepts platform argument) |
research-guide | Projects, Router | Research workflow: projects, sources, citations, bibliography |
blog-guide | Blog, Router | Blog publishing: posts, themes, RSS, export |
sharing-guide | Sharing, Router | P2P sharing: invites, contacts, messaging |
The router registers all 5 prompts so clients connected to /router/mcp have access to everything. Individual servers register only their own relevant prompts.
How Clients Use Prompts
Client: prompts/list
Server: [{ name: "session-start", description: "..." }, ...]
Client: prompts/get { name: "crow-guide", arguments: { platform: "chatgpt" } }
Server: { messages: [{ role: "user", content: { type: "text", text: "# crow.md — ..." } }] }The AI can request a prompt when it needs detailed guidance for a specific workflow, keeping the initial context footprint small while making comprehensive instructions available on demand.
Server Factory Integration
Both the router and crow-core reuse the same factory functions (createMemoryServer, createProjectServer, etc.) and the same InMemoryTransport + Client pattern. The factory creates a standalone McpServer; the caller wires it to whatever transport the deployment needs:
// stdio (individual server)
const server = createMemoryServer();
await server.connect(new StdioServerTransport());
// Gateway router (in-process)
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await server.connect(serverTransport);
await client.connect(clientTransport);
// crow-core (same in-process pattern, tools proxied onto core McpServer)Static tool metadata lives in servers/gateway/tool-manifests.js. Both the router (buildCompressedDescription) and crow-core (getToolNames) import from it.
Configuration
.mcp.json Generation
# Individual servers (one entry per server in .mcp.json)
npm run mcp-config
# Combined crow-core mode (single entry)
npm run mcp-config -- --combinedThe --combined flag writes a single crow-core entry pointing to servers/core/index.js instead of separate entries for each server. The server registry in scripts/server-registry.js defines both modes.
Health Endpoint
The gateway /health response includes tool count telemetry:
{
"status": "ok",
"servers": ["crow-memory", "crow-projects", "crow-sharing", "crow-storage", "crow-blog"],
"externalServers": [{ "id": "github", "name": "GitHub", "tools": 15 }],
"toolCounts": {
"core": 49,
"external": 15,
"total": 64,
"routerMode": 7
}
}The routerMode field is the number of router tools exposed (7), or null when the router is disabled via CROW_DISABLE_ROUTER=1.
API Reference
| Endpoint | Method | Description |
|---|---|---|
/router/mcp | POST, GET, DELETE | Streamable HTTP transport for the router McpServer |
/health | GET | Status including toolCounts object |
For usage guidance and optimization tips, see the Context Performance Guide.