How to Configure MCP Tools and Custom MCP Servers in Routa
Routa provides a built-in MCP coordination server and a REST API to register custom stdio, HTTP, or SSE-based MCP servers, allowing fine-grained control over which tools are exposed to agents.
Routa is an open-source AI agent orchestration framework that ships with a persistent MCP coordination server (routa-coordination) for managing agent lifecycles. Developers can extend this capability by configuring custom MCP servers through either the React-based settings UI or direct API calls to the Node.js backend, with configurations stored in Postgres or SQLite.
Understanding the MCP Architecture in Routa
Routa's MCP implementation consists of four core components that bridge the Model Context Protocol with the agent runtime:
-
Custom MCP Server Store (
src/core/store/custom-mcp-server-store.ts) – Persists server definitions including type, command, URL, and environment variables using Prisma. Supports both Postgres and SQLite backends. -
MCP Server Builder (
src/core/mcp/routa-mcp-server.ts) – Factory class that instantiatesMcpServerobjects from the merged configuration of built-in and custom servers. -
Tool Manager (
src/core/mcp/routa-mcp-tool-manager.ts) – Registers either 12 essential coordination tools or the full 34-tool suite based on thetoolModesetting. -
Management API (
src/app/api/mcp-servers/route.ts) – Expresses CRUD operations for custom servers via GET, POST, PUT, and DELETE methods.
When the backend initializes a provider-specific MCP configuration, it calls mergeCustomMcpServers() to combine built-in coordination endpoints with enabled custom servers, filtering out disabled entries before passing the final profile to the orchestration layer.
Adding Custom MCP Servers
You can register custom MCP servers through the Settings UI or programmatically via HTTP requests to the backend.
Via the Settings UI
Navigate to Settings → MCP in the Routa interface, rendered by src/client/components/settings-panel-mcp-tab.tsx. Click "Add new server" to open the configuration form:
- ID: Unique identifier used as the query key (e.g.,
my-filesystem-server) - Name: Human-readable label displayed in the tool picker
- Type: Select
stdiofor local command-based tools,httpfor REST gateways, orssefor Server-Sent Events streams - Command/Args (stdio only): Executable and arguments array (e.g.,
npx,["@modelcontextprotocol/server-filesystem", "/workspace"]) - URL (HTTP/SSE only): Endpoint address
- Headers/Env: Optional JSON objects for authentication or environment variables
Submitting the form sends a POST request to /api/mcp-servers with a payload matching the CustomMcpServerCreateInput interface defined in src/core/store/custom-mcp-server-store.ts.
Via the REST API
For automation or headless deployments, send HTTP requests directly to the management endpoint:
# Register a stdio-based filesystem server
curl -X POST http://localhost:3210/api/mcp-servers \
-H "Content-Type: application/json" \
-d '{
"id": "filesystem-mcp",
"name": "Filesystem Tools",
"type": "stdio",
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem", "/home/user/workspace"],
"enabled": true
}'
# Register an HTTP gateway with authentication
curl -X POST http://localhost:3210/api/mcp-servers \
-H "Content-Type: application/json" \
-d '{
"id": "custom-gateway",
"name": "Custom HTTP Gateway",
"type": "http",
"url": "http://localhost:9000/mcp",
"headers": {"Authorization": "Bearer token123"},
"enabled": true
}'
The backend validates the payload against the schema, persists it via CustomMcpServerStore.create(), and returns the stored record with generated timestamps.
Managing Server State and Persistence
Each custom server includes an enabled boolean flag that determines availability without deleting configuration data.
Enabling and Disabling Servers
In the Settings UI, toggle the activation switch next to any server entry. This sends a PUT request to /api/mcp-servers containing only the ID and the new enabled state:
{
"id": "filesystem-mcp",
"enabled": false
}
Disabled servers are filtered out during the merge process in mergeCustomMcpServers(), ensuring agents cannot access tools from inactive endpoints. The underlying configuration remains in the database for quick reactivation.
How Custom Servers Merge with Built-ins
When building the final MCP profile, Routa executes mergeCustomMcpServers(builtIn, custom) from src/core/store/custom-mcp-server-store.ts:
- The
routa-coordinationbuilt-in server always takes precedence and cannot be overridden. - Enabled custom servers transform into their respective transport configurations:
- stdio:
{ type: "stdio", command, args, env? } - http/sse:
{ type, url, headers?, env? }
- stdio:
- Duplicate names favor built-in definitions to prevent coordination conflicts.
The resulting merged object becomes the profile passed to RoutaMcpServer during initialization.
Configuring Which MCP Tools Are Exposed
Control tool visibility through the RoutaMcpToolManager class to limit agent capabilities or reduce token consumption.
Switching Between Essential and Full Tool Sets
The tool manager supports two predefined modes via setToolMode():
import { RoutaMcpToolManager } from "./core/mcp/routa-mcp-tool-manager";
const manager = new RoutaMcpToolManager(agentTools, workspaceId);
// Expose only 12 core coordination tools (create_task, list_agents, etc.)
manager.setToolMode("essential");
// Expose all 34 available tools including advanced workspace operations
manager.setToolMode("full");
When registerTools(server) executes, it checks this.toolMode and registers only the appropriate subset. The "essential" mode is recommended for restricted environments or when minimizing context window usage.
Creating Custom Tool Whitelists
For granular control, use setAllowedTools() with a Set<string> of specific tool names:
const allowedTools = new Set(["create_task", "read_note", "search_files"]);
manager.setAllowedTools(allowedTools);
The manager filters the registration to include only tools present in this whitelist, ignoring all others regardless of the current tool mode.
Complete Implementation Example
Combine API configuration with programmatic initialization to deploy a fully customized MCP stack:
// Step 1: Register a custom HTTP server via API
await fetch("/api/mcp-servers", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
id: "brave-search",
name": "Brave Search MCP",
type: "http",
url: "http://localhost:8080/sse",
headers: { "X-API-Key": process.env.BRAVE_API_KEY },
enabled: true
})
});
// Step 2: Initialize the MCP server with merged configuration
import { getCustomMcpServerStore, mergeCustomMcpServers } from "./core/store/custom-mcp-server-store";
import { RoutaMcpServer } from "./core/mcp/routa-mcp-server";
const store = getCustomMcpServerStore(); // Auto-detects Postgres or SQLite
const customServers = await store?.listEnabled() ?? [];
const builtIn = {
"routa-coordination": {
type: "http",
url: "http://127.0.0.1:3210/mcp"
}
};
const mergedProfile = mergeCustomMcpServers(builtIn, customServers);
const mcpServer = new RoutaMcpServer({
profile: mergedProfile,
port: 3001,
transport: "stdio"
});
await mcpServer.start();
This configuration exposes both the built-in coordination tools and the Brave Search MCP capabilities to connected agents.
Summary
- Routa's architecture separates server persistence (
custom-mcp-server-store.ts), tool registration (routa-mcp-tool-manager.ts), and HTTP management (route.ts) into distinct layers. - Custom servers support three transport types (
stdio,http,sse) and store configurations in Postgres or SQLite via the/api/mcp-serversendpoint. - Tool visibility is controlled through
setToolMode("essential" | "full")orsetAllowedTools(Set<string>)in theRoutaMcpToolManagerclass. - Built-in protection ensures the
routa-coordinationserver always takes precedence over custom definitions with identical names.
Frequently Asked Questions
What is the difference between stdio and HTTP/SSE MCP servers in Routa?
stdio servers spawn a local subprocess (e.g., npx @modelcontextprotocol/server-filesystem) and communicate over standard input/output, ideal for local tools without network exposure. HTTP/SSE servers connect to remote endpoints via HTTP requests or Server-Sent Events streams, suitable for cloud-hosted gateways or microservices. Both types persist in src/core/store/custom-mcp-server-store.ts but serialize into different transport configurations during the merge process.
How do I prevent custom MCP servers from conflicting with the built-in routa-coordination server?
The mergeCustomMcpServers() function in src/core/store/custom-mcp-server-store.ts automatically prioritizes built-in definitions. If a custom server shares an ID with routa-coordination, the built-in configuration wins and the custom entry is discarded, protecting critical orchestration functionality from accidental overrides.
Can I restrict which tools are available to specific agents or workspaces?
Yes. Instantiate RoutaMcpToolManager with a specific workspaceId and call setAllowedTools() with a whitelist of tool names before registering tools. This creates a filtered view of available capabilities per workspace or agent session, though you must implement the workspace-specific instantiation logic in your orchestration layer.
Where are custom MCP server configurations stored?
Configurations persist in either Postgres (production) or SQLite (development) through Prisma ORM, handled by CustomMcpServerStore in src/core/store/custom-mcp-server-store.ts. The store provides type-safe CRUD operations and validates the CustomMcpServerConfig schema before writing to the database.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →