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

> Implement the OmniRoute MCP server with 104 tools using unified RPC and 4 transports. Discover scope security for fine-grained access control in your projects.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: deep-dive
- Published: 2026-08-04

---

**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`](https://github.com/diegosouzapw/OmniRoute/blob/main/httpTransport.ts) | IO4 transports: HTTP, SSE, StdIO, Streamable HTTP |
| **Core Server** | [`server.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/server.ts) | Tool registration, RPC dispatch, middleware wiring |
| **Scope Enforcement** | [`scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/scopeEnforcement.ts) before any tool handler executes.

*Source:* [[`open-sse/mcp-server/server.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

```typescript
{
  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/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/scopeEnforcement.ts) middleware implements a strict intersection check for every tool invocation:

```typescript
// 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/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/skillTools.ts) (lines 28-98) |
| **Memory** | `read:memory`, `write:memory` | [`memoryTools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/memoryTools.ts) |
| **Plugins** | `read:plugins`, `write:plugins` | [`pluginTools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/pluginTools.ts) (lines 39-206) |
| **Obsidian** | `read:obsidian`, `write:obsidian` | [`obsidianTools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/obsidianTools.ts) (lines 44-315) |
| **Notion** | `read:notion`, `write:notion` | [`notionTools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/notionTools.ts) (lines 15-95) |
| **Health/Resilience** | `read:health`, `write:resilience` | [`poolTools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/poolTools.ts) (lines 161-201) |
| **Local Corpus** | `read:local-corpus` | corpus indexing tools |
| **Compression** | `read:compression`, `write:compression` | [`compressionTools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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

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

```bash

# 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

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

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

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

```json
{
  "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`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/server.ts) | Core server factory, tool registration, RPC routing |
| [`open-sse/mcp-server/scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/scopeEnforcement.ts) | Permission validation middleware |
| [`open-sse/mcp-server/toolCardinality.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/toolCardinality.ts) | Scope-based tool filtering logic |
| [`open-sse/mcp-server/httpTransport.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/httpTransport.ts) | Transport implementations (IO4) |
| [`open-sse/mcp-server/index.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/toolCardinality.ts)) filters the tool list exposed to a caller, affecting discovery and catalog operations. Tool-layer checking ([`scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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.