OmniRoute MCP Server Implementation: 104 Tools, IO4 Transports, and Scope Security Explained

The OmniRoute MCP server implements 104 tools through a unified RPC interface with four transport modes (HTTP, SSE, StdIO, and Streamable HTTP), enforcing fine-grained scope-based access control at the tool level.

This deep dive examines the Multi-Channel Protocol (MCP) server architecture in the diegosouzapw/OmniRoute repository. The server provides a central hub for tool execution, with every tool declaring required scopes and every request validated against caller permissions before execution.

MCP Server Architecture Overview

The implementation spans three logical layers under open-sse/mcp-server/:

Layer File Responsibility
Transport Layer httpTransport.ts IO4 transports: HTTP, SSE, StdIO, Streamable HTTP
Core Server server.ts Tool registration, RPC dispatch, middleware wiring
Scope Enforcement scopeEnforcement.ts Permission validation before tool execution

All transports share a common McpCallerIdentity object containing callerId and scopes extracted from the authentication token. This identity flows through every request to enable consistent access control regardless of transport mode.

The IO4 Transport System

The "IO4" designation refers to the four supported I/O modes implemented in httpTransport.ts:

Transport Factory Function Endpoint/Mechanism Use Case
HTTP createHttpTransport() POST to /api/mcp/http Standard REST clients
SSE createSseTransport() EventSource stream Real-time updates
StdIO createStdioTransport() process.stdin/process.stdout CLI integration
Streamable HTTP createStreamTransport() ReadableStream response Large payload transfers

Each transport extracts scopes from the incoming token and constructs the McpCallerIdentity passed to the core server. The server then invokes scopeEnforcement.ts before any tool handler executes.

Source: [open-sse/mcp-server/server.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/server.ts) and [open-sse/mcp-server/httpTransport.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/httpTransport.ts)

Tool Registration and Manifest Structure

The 104 tools reside in open-sse/mcp-server/tools/ and follow a standardized manifest pattern. During server initialization, registerTools() in server.ts dynamically imports all tool files and builds two lookup structures:

  • toolMap – direct name-to-manifest lookup for RPC dispatch
  • toolCardinality – filtered view based on caller scope intersection

Every tool exports a manifest matching this TypeScript interface:

{
  name: string,
  description: string,
  scopes: readonly string[],        // required permission scopes
  inputSchema: z.ZodSchema,        // runtime input validation
  handler: (args, ctx) => Promise<any>
}

Tools without a scopes declaration are treated as public and require no special permissions.

Source: [open-sse/mcp-server/toolCardinality.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/toolCardinality.ts)

Scope Enforcement Implementation

The scopeEnforcement.ts middleware implements a strict intersection check for every tool invocation:

// From scopeEnforcement.ts
if (!scopeIntersects(tool.scopes, callerIdentity.scopes)) {
  throw new McpError(403, `Insufficient MCP scopes for ${toolName}.`);
}

The enforcement flow:

  1. Transport layer extracts allowScopes from JWT/API key
  2. Core server receives McpCallerIdentity with validated scopes
  3. Before handler execution, scopes are compared using intersection logic
  4. Mismatch triggers immediate 403 rejection with descriptive message

This design allows horizontal scope filtering (which tools are visible) and runtime enforcement (authorization at invocation time).

Source: [open-sse/mcp-server/scopeEnforcement.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/scopeEnforcement.ts)

Complete Scope Inventory

The 104 tools declare permissions across 12 functional categories. All scopes follow the pattern {action}:{resource}:

Category Available Scopes Representative Tool Files
Skills read:skills, write:skills, execute:skills skillTools.ts (lines 28-98)
Memory read:memory, write:memory memoryTools.ts
Plugins read:plugins, write:plugins pluginTools.ts (lines 39-206)
Obsidian read:obsidian, write:obsidian obsidianTools.ts (lines 44-315)
Notion read:notion, write:notion notionTools.ts (lines 15-95)
Health/Resilience read:health, write:resilience poolTools.ts (lines 161-201)
Local Corpus read:local-corpus corpus indexing tools
Compression read:compression, write:compression compressionTools.ts (lines 550-649)
Gamification read:gamification, write:gamification achievement/leaderboard tools
System read:system, admin:system server configuration tools

Tools may declare multiple scopes, requiring caller possession of any (not necessarily all) matching scope through the intersection logic.

MCP Server Startup Patterns

HTTP/SSE Server

import { createMcpServer } from './server';
import { createHttpTransport } from './httpTransport';

const server = createMcpServer();               // registers all 104 tools
createHttpTransport(server).listen(3000);       // exposes HTTP + SSE endpoints

StdIO Transport (CLI Mode)


# Start MCP server on stdin/stdout for Claude Desktop or similar

omniroute --mcp

The CLI uses createStdioTransport() for JSON-RPC line-delimited communication.

Programmatic Scope Restriction

import { createMcpServer } from './server';

// Restrict this server instance to read-only memory operations
const limitedServer = createMcpServer({
  callerIdentity: {
    callerId: 'sandbox-client',
    scopes: ['read:memory']
  }
});

Practical Usage Examples

Calling Tools via HTTP Transport

import fetch from 'node-fetch';

const response = await fetch('http://localhost:3000/api/mcp/http', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <token-with-read-plugins>'
  },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'list_plugins',
    params: {},
    id: 1
  })
});

const { result } = await response.json();
// result: array of installed plugin manifests

StdIO Transport for CLI Scripts

echo '{"jsonrpc":"2.0","method":"read_health","params":{},"id":1}' \
  | omniroute --mcp \
  | jq '.result'

Scope Violation Handling

When a caller lacks required scopes, the server returns a structured JSON-RPC error:

{
  "jsonrpc": "2.0",
  "error": {
    "code": 403,
    "message": "Insufficient MCP scopes for write_memory."
  },
  "id": 2
}

Key Implementation Files

File Path Function
open-sse/mcp-server/server.ts Core server factory, tool registration, RPC routing
open-sse/mcp-server/scopeEnforcement.ts Permission validation middleware
open-sse/mcp-server/toolCardinality.ts Scope-based tool filtering logic
open-sse/mcp-server/httpTransport.ts Transport implementations (IO4)
open-sse/mcp-server/index.ts Public API exports
open-sse/mcp-server/tools/*.ts Individual tool implementations with scope declarations

Summary

  • Four transports (HTTP, SSE, StdIO, Streamable HTTP) provide flexible connectivity options for the MCP server
  • 104 tools are registered dynamically from open-sse/mcp-server/tools/ with standardized Zod-validated manifests
  • Scope enforcement operates at two levels: cardinality filtering (which tools are visible) and runtime authorization (pre-handler validation)
  • 12 scope categories cover skills, memory, plugins, Obsidian, Notion, health, compression, gamification, and system functions
  • No special scope required for public tools; all others fail fast with 403 errors for insufficient permissions

Frequently Asked Questions

How do I determine which scopes my API token needs for specific MCP tools?

Inspect the tool manifest directly in the source file. Each tool in open-sse/mcp-server/tools/ exports a scopes array. For example, obsidianTools.ts lines 44-315 shows Obsidian tools require read:obsidian or write:obsidian. Alternatively, call the get_tool_catalog method with a valid token to receive only tools matching your scopes.

Can I run the MCP server without scope enforcement for local development?

The server requires a callerIdentity object but accepts an empty scopes array. Pass { callerId: 'dev', scopes: [] } to createMcpServer() — this grants access only to tools without scope requirements. For full access, include all relevant scopes in the array or use a wildcard scope if your deployment supports it.

What is the difference between transport-layer and tool-layer scope checking?

Transport scope checking (toolCardinality.ts) filters the tool list exposed to a caller, affecting discovery and catalog operations. Tool-layer checking (scopeEnforcement.ts) validates permissions immediately before handler execution. Both use the same intersection logic, but the cardinality layer optimizes performance while enforcement provides defense in depth against direct RPC calls.

How does the SSE transport handle scope validation for long-lived connections?

The SSE transport validates scopes at connection establishment using the initial request's authentication token. The McpCallerIdentity is bound to the connection context and reused for all subsequent tool invocations on that stream. To change scopes, a client must establish a new SSE connection with a different token.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →