# How to Configure MCP Tools and Custom MCP Servers in Routa

> Learn how to configure MCP tools and custom MCP servers in Routa. Control tool exposure to agents with built-in coordination and REST API registration for stdio, HTTP, and SSE servers.

- Repository: [Fengda Huang/routa](https://github.com/phodal/routa)
- Tags: how-to-guide
- Published: 2026-05-26

---

**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`](https://github.com/phodal/routa/blob/main/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`](https://github.com/phodal/routa/blob/main/src/core/mcp/routa-mcp-server.ts)) – Factory class that instantiates `McpServer` objects from the merged configuration of built-in and custom servers.

- **Tool Manager** ([`src/core/mcp/routa-mcp-tool-manager.ts`](https://github.com/phodal/routa/blob/main/src/core/mcp/routa-mcp-tool-manager.ts)) – Registers either 12 essential coordination tools or the full 34-tool suite based on the `toolMode` setting.

- **Management API** ([`src/app/api/mcp-servers/route.ts`](https://github.com/phodal/routa/blob/main/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`](https://github.com/phodal/routa/blob/main/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 `stdio` for local command-based tools, `http` for REST gateways, or `sse` for 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`](https://github.com/phodal/routa/blob/main/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:

```bash

# 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:

```json
{
  "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`](https://github.com/phodal/routa/blob/main/src/core/store/custom-mcp-server-store.ts):

1. The `routa-coordination` built-in server always takes precedence and cannot be overridden.
2. Enabled custom servers transform into their respective transport configurations:
   - **stdio**: `{ type: "stdio", command, args, env? }`
   - **http/sse**: `{ type, url, headers?, env? }`
3. 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()`:

```typescript
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:

```typescript
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:

```typescript
// 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`](https://github.com/phodal/routa/blob/main/custom-mcp-server-store.ts)), tool registration ([`routa-mcp-tool-manager.ts`](https://github.com/phodal/routa/blob/main/routa-mcp-tool-manager.ts)), and HTTP management ([`route.ts`](https://github.com/phodal/routa/blob/main/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-servers` endpoint.
- **Tool visibility** is controlled through `setToolMode("essential" | "full")` or `setAllowedTools(Set<string>)` in the `RoutaMcpToolManager` class.
- **Built-in protection** ensures the `routa-coordination` server 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`](https://github.com/phodal/routa/blob/main/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`](https://github.com/phodal/routa/blob/main/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`](https://github.com/phodal/routa/blob/main/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.