How OmniRoute’s MCP Server Exposes 94 Tools with Scoped Authentication
OmniRoute’s MCP server aggregates 94 tools from multiple domains—memory, skills, plugins, and integrations—while enforcing per-tool scope restrictions via environment-based allow lists that reject unauthorized requests with 403 errors before execution.
The OmniRoute repository implements a Model-Context-Protocol (MCP) server in the open-sse/mcp-server workspace that centralizes tool discovery and execution under a unified security model. This article examines how the server constructs the tool catalog from distributed TypeScript schemas and applies scope-based access control to every invocation.
Tool Registry Architecture: Assembling the 94-Tool Catalog
The server publishes a fixed catalog of 94 tools built from modular collections across the codebase.
The core registry begins with 34 base tools defined in open-sse/mcp-server/schemas/tools.ts within the MCP_TOOLS array. These entries include Zod schemas and metadata for fundamental operations like health checks and system queries.
Additional dynamic groups are imported in open-sse/mcp-server/server.ts lines 68-79:
- Memory tools (3 entries)
- Skill tools (4 entries)
- Agent-skill tools (3 entries)
- GitHub-skill tools (variable count)
- Pool tools (6 entries)
- Gamification tools (8 entries)
- Plugin tools (8 entries)
- Notion tools (6 entries)
- Obsidian tools (22 entries)
All collections merge into a single array, then countUniqueMcpTools deduplicates entries and stores the final count in TOTAL_MCP_TOOL_COUNT (lines 100-104). This ensures the 94-tool total represents a single source of truth without duplicates.
Scope-Based Authentication and Authorization
Every tool declaration includes a scopes array defining required permissions. For example, the health tool in open-sse/mcp-server/schemas/tools.ts lines 71-78 declares ["read:health"] within its McpToolDefinition.
The server loads authorization policy from two environment variables:
OMNIROUTE_MCP_SCOPES: Comma-separated global allow-list of permitted scopesOMNIROUTE_MCP_ENFORCE_SCOPES: Boolean flag to toggle enforcement
When a request arrives, the evaluateToolScopes function (imported from open-sse/mcp-server/scopeEnforcement.ts) performs the security check:
resolveCallerScopeContextextracts the caller’s identity and authorized scopes from the API key or session- The function compares the tool’s declared scopes against the caller’s allowed scopes
- If no intersection exists, the server returns a 403 Forbidden error before the tool handler executes
This check runs uniformly across all transport layers, ensuring consistent policy enforcement regardless of entry point.
Runtime Registration and Transport Layer
The server instantiates McpServer from @modelcontextprotocol/sdk/server/mcp.js with a compiled tool map.
// open-sse/mcp-server/server.ts
const server = new McpServer({
tools: filteredTools,
enforceScopes: process.env.OMNIROUTE_MCP_ENFORCE_SCOPES === "true",
allowedScopes: new Set(
(process.env.OMNIROUTE_MCP_SCOPES ?? "")
.split(",")
.map((s) => s.trim())
.filter(Boolean)
),
});
The constructor receives MCP_TOOL_MAP, which binds each tool name to its definition and compiled Zod validator.
Three transport layers defined in open-sse/mcp-server/httpTransport.ts expose the same server instance:
- stdio:
StdioServerTransportfor CLI integration - SSE: Server-Sent Events for streaming HTTP connections
- Plain HTTP: Direct REST endpoints for programmatic access
All transports share identical scope enforcement and tool registry instances, maintaining a consistent security surface.
Tool Cardinality Reduction
Operators can prune the 94-tool catalog at startup via environment filters. The reduceToolManifest function in open-sse/mcp-server/toolCardinality.ts applies two variables:
MCP_TOOL_DENY: Comma-separated list of tool names to excludeMCP_TOOL_ALLOW: Optional whitelist restricting available tools
This filtering occurs after the full registry assembly but before server instantiation, allowing security teams to minimize the attack surface without modifying source code.
Complete Implementation Example
// Bootstrap the MCP server with full scope enforcement
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { MCP_TOOLS } from "./schemas/tools.ts";
import { memoryTools, skillTools, agentSkillTools } from "./tools/memory.ts";
import { githubSkillTools } from "./tools/github.ts";
import { poolTools } from "./tools/pool.ts";
import { gamificationTools } from "./tools/gamification.ts";
import { pluginTools } from "./tools/plugins.ts";
import { notionTools } from "./tools/notion.ts";
import { obsidianTools } from "./tools/obsidian.ts";
import { reduceToolManifest } from "./toolCardinality.ts";
import { createMcpHttpServer } from "./httpTransport.ts";
// Merge all tool collections
const ALL_TOOLS = [
...MCP_TOOLS, // 34 core tools
...memoryTools, // 3 tools
...skillTools, // 4 tools
...agentSkillTools, // 3 tools
...githubSkillTools, // GitHub integration
...poolTools, // 6 tools
...gamificationTools, // 8 tools
...pluginTools, // 8 tools
...notionTools, // 6 tools
...obsidianTools, // 22 tools
];
// Apply cardinality filters from environment
const filteredTools = reduceToolManifest(ALL_TOOLS);
// Initialize server with scope enforcement
const server = new McpServer({
tools: filteredTools,
enforceScopes: process.env.OMNIROUTE_MCP_ENFORCE_SCOPES === "true",
allowedScopes: new Set(
(process.env.OMNIROUTE_MCP_SCOPES ?? "").split(",").map(s => s.trim()).filter(Boolean)
),
});
// Start stdio transport (CLI)
const stdioTransport = new StdioServerTransport(server);
stdioTransport.listen();
// Start HTTP transport (REST/SSE)
const httpServer = createMcpHttpServer(server);
httpServer.listen(3000);
Summary
- OmniRoute’s MCP server exposes exactly 94 tools aggregated from 9 distinct functional domains across the
open-sse/mcp-serverworkspace. - Scope enforcement relies on per-tool
scopesarrays declared inopen-sse/mcp-server/schemas/tools.ts, validated at runtime against theOMNIROUTE_MCP_SCOPESenvironment variable. - Unauthorized access generates 403 errors before tool execution via
evaluateToolScopesandresolveCallerScopeContextinscopeEnforcement.ts. - Transport flexibility allows the same tool registry to serve stdio, SSE, and HTTP clients without security degradation.
- Cardinality control via
MCP_TOOL_DENYandMCP_TOOL_ALLOWfilters enables operators to reduce the tool surface area at startup.
Frequently Asked Questions
How does OmniRoute arrive at exactly 94 tools?
The count derives from merging 34 core tools in MCP_TOOLS with 60 additional tools across memory (3), skills (4), agent-skills (3), pools (6), gamification (8), plugins (8), Notion (6), and Obsidian (22) categories. The countUniqueMcpTools function in open-sse/mcp-server/server.ts deduplicates entries before setting TOTAL_MCP_TOOL_COUNT.
What happens if a client requests a tool without the required scope?
The evaluateToolScopes function intercepts the request and returns a 403 Forbidden response. The scope check occurs in open-sse/mcp-server/scopeEnforcement.ts before the tool’s handler function executes, preventing any unauthorized access to underlying resources.
Can operators disable specific tools without modifying code?
Yes. Setting the MCP_TOOL_DENY environment variable to a comma-separated list of tool names triggers the reduceToolManifest function in open-sse/mcp-server/toolCardinality.ts to remove those entries from the catalog before server startup. Conversely, MCP_TOOL_ALLOW whitelists only specified tools.
Where are the scope definitions stored for each tool?
Scope arrays reside in the McpToolDefinition interface implementations within open-sse/mcp-server/schemas/tools.ts. Each tool definition pairs its Zod validation schema with a scopes property (e.g., ["read:health"]), which the server references during the authorization check in evaluateToolScopes.
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 →