# How OmniRoute’s MCP Server Exposes 94 Tools with Scoped Authentication

> OmniRoute's MCP server securely exposes 94 tools with granular scoped authentication. Learn how environment-based allow lists prevent unauthorized access before execution.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: architecture
- Published: 2026-07-17

---

**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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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 scopes
- `OMNIROUTE_MCP_ENFORCE_SCOPES`: Boolean flag to toggle enforcement

When a request arrives, the `evaluateToolScopes` function (imported from [`open-sse/mcp-server/scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/scopeEnforcement.ts)) performs the security check:

1. `resolveCallerScopeContext` extracts the caller’s identity and authorized scopes from the API key or session
2. The function compares the tool’s declared scopes against the caller’s allowed scopes
3. 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.

```typescript
// 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/httpTransport.ts) expose the same server instance:

- **stdio**: `StdioServerTransport` for 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/toolCardinality.ts) applies two variables:

- `MCP_TOOL_DENY`: Comma-separated list of tool names to exclude
- `MCP_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

```typescript
// 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-server` workspace.
- **Scope enforcement** relies on per-tool `scopes` arrays declared in [`open-sse/mcp-server/schemas/tools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/tools.ts), validated at runtime against the `OMNIROUTE_MCP_SCOPES` environment variable.
- **Unauthorized access** generates 403 errors before tool execution via `evaluateToolScopes` and `resolveCallerScopeContext` in [`scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/scopeEnforcement.ts).
- **Transport flexibility** allows the same tool registry to serve stdio, SSE, and HTTP clients without security degradation.
- **Cardinality control** via `MCP_TOOL_DENY` and `MCP_TOOL_ALLOW` filters 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`.