Skip to main content

Overview

The Model Context Protocol (MCP) is Routa’s primary coordination interface. It exposes specialized tools that enable agents to collaborate on complex tasks through delegation, messaging, and shared state management. Key Purpose: Enable AI agents (Claude Code, OpenCode, Gemini, etc.) to coordinate work across multiple agents without direct implementation details. Built on: Anthropic’s Model Context Protocol

When to Use MCP

Use MCP when you need agents to:
  • Delegate tasks to other agents (coordinators spawning implementers)
  • Share context through notes and conversation history
  • Track progress across multiple concurrent tasks
  • Coordinate workflows with task dependencies and parallel execution
MCP is ideal for coordinator agents (ROUTA role) that orchestrate work across specialized agents.

Architecture

MCP server is created and configured in src/core/mcp/routa-mcp-server.ts:39:
export function createRoutaMcpServer(
  workspaceIdOrOptions: string | CreateMcpServerOptions,
  system?: RoutaSystem
): RoutaMcpServerResult {
  const server = new McpServer({
    name: "routa-mcp",
    version: "0.1.0",
  });

  const toolManager = new RoutaMcpToolManager(routaSystem.tools, opts.workspaceId);
  toolManager.setToolMode(toolMode); // "essential" or "full"
  toolManager.registerTools(server);

  return { server, system: routaSystem, toolManager };
}

Tool Modes

MCP supports two tool registration modes:
toolMode
ToolMode
default:"essential"
Tool registration mode for the MCP server
  • essential — 12 core coordination tools (Agent + Note) for weak models
  • full — All 34 tools (Task, Agent, Note, Workspace, Git) for stronger models

Available Tools

Essential Mode (12 tools)

Minimum viable set for multi-agent coordination:

Agent Tools (7)

List all agents in the workspace with their status and roles.
// src/core/mcp/routa-mcp-tool-manager.ts:291
server.tool(
  "list_agents",
  "List all agents in the current workspace with their id, name, role, status, and parentId",
  {
    workspaceId: z.string().optional()
  }
);
Read conversation history of another agent.
// src/core/mcp/routa-mcp-tool-manager.ts:305
{
  agentId: z.string(),
  lastN: z.number().optional(),
  startTurn: z.number().optional(),
  endTurn: z.number().optional(),
  includeToolCalls: z.boolean().optional()
}
Create a new agent with a specific role.
// src/core/mcp/routa-mcp-tool-manager.ts:323
{
  name: z.string(),
  role: z.enum(["ROUTA", "CRAFTER", "GATE", "DEVELOPER"]),
  workspaceId: z.string().optional(),
  parentId: z.string().optional(),
  modelTier: z.enum(["SMART", "BALANCED", "FAST"]).optional()
}
Set your agent’s display name to reflect current task.
// src/core/mcp/routa-mcp-tool-manager.ts:343
{
  name: z.string().describe("Short task-focused name (1-5 words)")
}
Assign a task to an existing agent and activate it.
// src/core/mcp/routa-mcp-tool-manager.ts:363
{
  agentId: z.string(),
  taskId: z.string(),
  callerAgentId: z.string()
}
Primary delegation tool — spawns a real agent process.
// src/core/mcp/routa-mcp-tool-manager.ts:239
{
  taskId: z.string(), // UUID from create_task
  callerAgentId: z.string(),
  specialist: z.enum(["CRAFTER", "GATE", "DEVELOPER"]),
  provider: z.string().optional(),
  cwd: z.string().optional(),
  additionalInstructions: z.string().optional(),
  waitMode: z.enum(["immediate", "after_all"]).optional()
}
Important: taskId must be a UUID from create_task, NOT a task name.
Send a message from one agent to another.
// src/core/mcp/routa-mcp-tool-manager.ts:380
{
  fromAgentId: z.string(),
  toAgentId: z.string(),
  message: z.string()
}
Submit completion report to parent agent.
// src/core/mcp/routa-mcp-tool-manager.ts:396
{
  agentId: z.string(),
  taskId: z.string(),
  summary: z.string(),
  filesModified: z.array(z.string()).optional(),
  verificationResults: z.string().optional(),
  success: z.boolean()
}

Note Tools (5)

Shared document management for agent collaboration:
Create a new note for agent collaboration.
// src/core/mcp/routa-mcp-tool-manager.ts:527
{
  title: z.string(),
  content: z.string().optional(),
  noteId: z.string().optional(),
  type: z.enum(["spec", "task", "general"]).optional(),
  workspaceId: z.string().optional(),
  sessionId: z.string().optional()
}
Read note content. Use noteId='spec' for workspace spec.
// src/core/mcp/routa-mcp-tool-manager.ts:553
{
  noteId: z.string(),
  workspaceId: z.string().optional()
}
List all notes, optionally filtered by type.
// src/core/mcp/routa-mcp-tool-manager.ts:574
{
  type: z.enum(["spec", "task", "general"]).optional(),
  workspaceId: z.string().optional()
}
Replace note content. Spec note is auto-created if missing.
// src/core/mcp/routa-mcp-tool-manager.ts:596
{
  noteId: z.string(),
  content: z.string(),
  title: z.string().optional(),
  workspaceId: z.string().optional(),
  sessionId: z.string().optional()
}
Convert @@@task blocks in notes into structured Task records.
// src/core/mcp/routa-mcp-tool-manager.ts:666
{
  noteId: z.string(),
  workspaceId: z.string().optional()
}
Parses markdown blocks like:
@@@task[task-1]
# Task Title
Objective: What to achieve
Scope: Files to modify
Acceptance: Definition of done
@@@

Full Mode (34 tools)

Includes all essential tools plus:
  • Task tools (4) — create_task, list_tasks, update_task_status, update_task
  • Extended agent tools (6) — wake_or_create_task_agent, send_message_to_task_agent, get_agent_status, get_agent_summary, subscribe_to_events, unsubscribe_from_events
  • Extended note tools (2) — append_to_note, get_my_task
  • Workspace tools (8) — git_status, git_diff, git_commit, get_workspace_info, get_workspace_details, set_workspace_title, list_workspaces, create_workspace, list_specialists

Configuration Options

workspaceId
string
required
Workspace identifier for all operations
toolMode
ToolMode
default:"essential"
Tool registration mode:
  • "essential" — 12 core tools for weak models
  • "full" — All 34 tools for strong models
system
RoutaSystem
Optional existing RoutaSystem instance (auto-created if omitted)
sessionId
string
ACP session ID to scope note/task creation to a specific session

Example Usage

Coordinator Delegating Work

// Coordinator agent using MCP tools

// 1. Create a task
const taskResult = await mcp.call_tool("create_task", {
  title: "Implement user authentication",
  objective: "Add JWT-based authentication to API",
  workspaceId: "workspace-123",
  scope: "src/auth/",
  acceptanceCriteria: [
    "Login endpoint returns valid JWT",
    "Protected routes verify token",
    "Tests pass"
  ]
});

const taskId = taskResult.data.taskId;

// 2. Delegate to a CRAFTER agent
await mcp.call_tool("delegate_task_to_agent", {
  taskId,
  callerAgentId: "coordinator-agent-id",
  specialist: "CRAFTER",
  provider: "opencode",
  cwd: "/workspace/project",
  waitMode: "immediate"
});

// 3. Read agent conversation later
const conversation = await mcp.call_tool("read_agent_conversation", {
  agentId: "crafter-agent-id",
  lastN: 10
});

Spec-Driven Workflow

// 1. Create spec note
await mcp.call_tool("set_note_content", {
  noteId: "spec",
  content: `
# Project Spec

@@@task[auth]
Objective: Implement authentication
Scope: src/auth/
Acceptance:
- JWT login works
- Tests pass
@@@

@@@task[api]
Objective: Add REST endpoints
Scope: src/api/
Acceptance:
- CRUD operations work
- OpenAPI spec generated
@@@
  `,
  workspaceId: "workspace-123"
});

// 2. Convert @@@task blocks to structured tasks
const result = await mcp.call_tool("convert_task_blocks", {
  noteId: "spec",
  workspaceId: "workspace-123"
});

// Returns: { taskIds: ["uuid-1", "uuid-2"], noteIds: ["task-auth", "task-api"] }

// 3. Delegate each task
for (const taskId of result.data.taskIds) {
  await mcp.call_tool("delegate_task_to_agent", {
    taskId,
    callerAgentId: "coordinator-id",
    specialist: "CRAFTER"
  });
}

Connection Types

MCP server supports multiple transport mechanisms:

SSE (Server-Sent Events)

For web-based agents (Claude Code):
// src/core/mcp/routa-mcp-http-server.ts
const httpServer = new RoutaMcpHttpServer({
  workspaceId: "workspace-123",
  toolMode: "essential"
});

await httpServer.start(8080);
// Agent connects to: http://localhost:8080/api/mcp

WebSocket

For real-time bidirectional communication:
// src/core/mcp/ws-server-transport.ts
const transport = new WebSocketServerTransport(ws);
const { server } = createRoutaMcpServer({
  workspaceId: "workspace-123",
  toolMode: "essential"
});

await server.connect(transport);

stdio

For local process communication (OpenCode, Codex):
const { server } = createRoutaMcpServer({
  workspaceId: "workspace-123",
  toolMode: "essential"
});

const transport = new StdioServerTransport();
await server.connect(transport);

Tool Execution

Tool calls are executed via src/core/mcp/mcp-tool-executor.ts:30:
export async function executeMcpTool(
  tools: AgentTools,
  name: string,
  args: Record<string, unknown>,
  noteTools?: NoteTools,
  workspaceTools?: WorkspaceTools
) {
  // Validate workspaceId
  const workspace = args.workspaceId as string;
  if (!workspace) {
    return { content: [{ type: "text", text: JSON.stringify({ error: "workspaceId is required" }) }], isError: true };
  }

  // Dispatch to appropriate tool
  switch (name) {
    case "list_agents":
      return formatResult(await tools.listAgents(workspace));
    case "delegate_task_to_agent":
      const orchestrator = getRoutaOrchestrator();
      return formatResult(
        await orchestrator.delegateTaskWithSpawn({ /* ... */ })
      );
    // ...
  }
}

Best Practices

Models with limited context windows (Claude Haiku, GPT-3.5) should use toolMode: "essential" to reduce token usage.The 12 essential tools provide full coordination capabilities without overwhelming the model.
Never pass task names to delegate_task_to_agent. Always use UUIDs returned by create_task:
// WRONG
await mcp.call_tool("delegate_task_to_agent", {
  taskId: "implement-auth", // ❌ Task name
  // ...
});

// CORRECT
const task = await mcp.call_tool("create_task", { /* ... */ });
await mcp.call_tool("delegate_task_to_agent", {
  taskId: task.data.taskId, // ✅ UUID
  // ...
});
For multi-task projects, create a spec note with @@@task blocks, then convert them:
// 1. Write spec
await mcp.call_tool("set_note_content", {
  noteId: "spec",
  content: "# Spec\n@@@task[t1]\n...\n@@@"
});

// 2. Convert to tasks
const result = await mcp.call_tool("convert_task_blocks", {
  noteId: "spec"
});

// 3. Delegate all tasks
for (const taskId of result.data.taskIds) {
  await mcp.call_tool("delegate_task_to_agent", { taskId, /* ... */ });
}
All child agents must call report_to_parent when done:
await mcp.call_tool("report_to_parent", {
  agentId: "crafter-id",
  taskId: "task-uuid",
  summary: "Implemented authentication with JWT",
  filesModified: ["src/auth/jwt.ts", "src/auth/middleware.ts"],
  success: true
});

API Reference

MCP Server

Create and configure MCP servers

Tool Manager

Register and execute coordination tools

Agent Tools

Core agent coordination operations

Note Tools

Shared document management

Build docs developers (and LLMs) love