How the OmniRoute MCP Server with 104 IO4 Tools Works: Architecture, Tool Registry, and Scope System Explained
The OmniRoute MCP server is a JSON‑RPC gateway that exposes 104+ IO4 tools through a unified HTTP/WebSocket endpoint, enforcing OAuth‑style scopes via a three‑tier resolution system before invoking domain‑specific handlers.
The OmniRoute Multi‑Client Protocol (MCP) server provides a lightweight, secure RPC layer that bridges external clients to the IO4 (Input/Output‑oriented) tool ecosystem. This article examines how the server processes requests, registers tools, and enforces granular permissions across functional domains like skills, memory, Obsidian, and gamification.
MCP Server Core Architecture
The server entry point resides in open‑sse/mcp‑server/server.ts, which initializes an HTTP and WebSocket listener on port 20128 (configurable). The core flow follows three stages:
- Transport handling —
httpTransport.tsmanages raw request/response cycles and protocol upgrade logic - Authentication context building —
httpAuthContext.tsextracts caller identity from headers and constructs anMcpToolExtraLikeobject containingauthInfoand optionalmetafields - Request dispatch — The server parses JSON‑RPC payloads, maps
methodnames to registered tools, and orchestrates scope enforcement before handler execution
The protocol endpoint is /api/mcp, accepting POST requests with standard JSON‑RPC 2.0 envelopes.
IO4 Tool Registry Structure
All 104+ tools are defined under open‑sse/mcp‑server/tools/ as individual TypeScript modules. Each tool exports a standardized interface:
// open-sse/mcp-server/tools/skillTools.ts
export const readSkill = {
description: "Read a skill definition",
scopes: ["read:skills"], // Required OAuth-style permissions
handler: async (args) => {
// Implementation returns JSON-serializable result
}
};
The toolchain spans multiple functional domains. Tool categories include:
- skillTools (
read:skills,write:skills,execute:skills) - memoryTools (
read:memory,write:memory) - obsidianTools (
read:obsidian,write:obsidian) - notionTools (
read:notion,write:notion) - pluginTools (
read:plugins,write:plugins) - poolTools (
read:health,write:resilience) - compressionTools (
read:compression,write:compression) - gamificationTools (
read:gamification,write:gamification) - localCorpusTools (
read:local-corpus)
Additional utility and meta-tools are registered through the same pattern, with scope requirements varying from highly restrictive to implicitly granted fallback scopes.
Three-Tier Scope Resolution System
Before any handler executes, scopeEnforcement.ts resolves caller permissions through resolveCallerScopeContext (lines 72‑96). The resolution hierarchy prioritizes sources in this order:
- authInfo scopes (lines 81‑84) — Extracted directly from the OAuth token's
scopesclaim in theAuthorizationheader - meta scopes (lines 86‑89) — Override or supplement via a
scopesfield inside the request'smetaobject - fallback scopes (lines 91‑94) — Environment-defined default scopes applied when no authentication is present
The resulting CallerScopeContext contains callerId, scopes array, and source indicator for audit logging.
Scope Matching and Authorization Logic
The evaluateToolScopes function (lines 99‑135) performs the actual authorization check using scopeMatches (lines 61‑68), which supports:
- Exact matches —
read:skillsrequiresread:skills - Wildcards —
*grants access to all scopes - Prefix patterns —
read:*satisfiesread:skills,read:memory, etc.
When authorization fails, the server returns a JSON‑RPC error object with code: -32001 and data.missing_scopes listing the unsatisfied requirements.
Domain-Specific Scope Catalog
The following table maps functional domains to their defined scopes with source file locations:
| Domain | Scopes | Definition Location |
|---|---|---|
| Skills | read:skills, write:skills, execute:skills |
skillTools.ts, L59, L75 |
| Memory | read:memory, write:memory |
memoryTools.ts, L77, L103 |
| Obsidian | read:obsidian, write:obsidian |
obsidianTools.ts‑L154, L174‑L281 |
| Notion | read:notion, write:notion |
notionTools.ts‑L55, L95 |
| Plugins | read:plugins, write:plugins |
pluginTools.ts‑L182, L67‑L206 |
| Pool / Health | read:health, write:resilience |
poolTools.ts‑L201, L177‑L185 |
| Compression | read:compression, write:compression |
compressionTools.ts‑L651, L559‑L622 |
| Gamification | read:gamification, write:gamification |
gamificationTools.ts‑L41, L90‑L116 |
| Local Corpus | read:local-corpus |
localCorpusTools.ts‑L43 |
| IO4 Core | read:io4 |
Implicit fallback scope |
This taxonomy ensures clients receive principle of least privilege access—an automation script syncing Obsidian notes never receives memory write permissions unless explicitly granted.
Complete Request Lifecycle
The execution flow from client call to tool response:
// Step 1: Client constructs JSON-RPC payload
const payload = {
jsonrpc: "2.0",
id: 1,
method: "readSkill",
params: { skillId: "my-awesome-skill" }
};
// Step 2: HTTP POST with bearer token containing required scope
const response = await fetch("http://localhost:20128/api/mcp", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer <token-with-read:skills>"
},
body: JSON.stringify(payload)
});
Server-side processing:
server.tsreceives POST to/api/mcphttpAuthContext.tsbuildsMcpToolExtraLikefrom headersresolveCallerScopeContextextracts["read:skills", "read:memory"]from tokenevaluateToolScopesverifiesread:skillssatisfiesread:skillsrequirementreadSkill.handlerexecutes with{ skillId: "my-awesome-skill" }- JSON‑RPC response returned with
resultor standardized error
Scope-Free Tools and Fallback Behavior
Certain utility tools require no explicit caller scopes but still pass through the enforcement layer. The pickFastestModel tool exemplifies this pattern:
import { pickFastestModel } from "omniroute-mcp-client";
const best = await pickFastestModel({
comboId: "auto",
candidates: [{ provider: "openai", model: "gpt-4o" }]
});
// Returns fastest model per telemetry in catalog.ts
These tools typically declare empty scopes: [] or rely on the implicit read:io4 fallback granted to all authenticated sessions. The catalog.ts module provides dynamic model rankings derived from performance telemetry.
Authorization Failure Handling
Scope violations return structured errors for programmatic handling:
// Attempting write:memory without authorization
{
jsonrpc: "2.0",
id: 2,
error: {
code: -32001,
message: "Insufficient scopes",
data: {
missing_scopes: ["write:memory"],
available_scopes: ["read:memory", "read:skills"]
}
}
}
This design enables clients to implement scope escalation flows, requesting additional permissions when operations fail.
Key Implementation Files
| File | Purpose |
|---|---|
open‑sse/mcp‑server/server.ts |
Main entry point, HTTP/WebSocket listener |
open‑sse/mcp‑server/httpTransport.ts |
Low-level request handling |
open‑sse/mcp‑server/httpAuthContext.ts |
Authentication context extraction |
open‑sse/mcp‑server/scopeEnforcement.ts |
Scope resolution and authorization logic |
open‑sse/mcp‑server/tools/*.ts |
Individual tool definitions and handlers |
open‑sse/mcp‑server/catalog.ts |
Model catalog and telemetry aggregation |
Summary
- The OmniRoute MCP server exposes 104+ IO4 tools through a JSON‑RPC endpoint at
/api/mcp, implementing the Multi‑Client Protocol for standardized client integration - Scope enforcement operates through three resolution tiers: OAuth token claims, request meta overrides, and environment fallbacks, evaluated by
resolveCallerScopeContextinscopeEnforcement.ts - Authorization logic in
evaluateToolScopessupports exact, wildcard, and prefix matching, returning structuredmissing_scopeserrors on failure - Tools are organized by functional domain (skills, memory, Obsidian, gamification, etc.) with granular
read:andwrite:permissions, preventing unauthorized cross-domain access - The handler invocation flow guarantees scope verification precedes execution, ensuring the IO4 ecosystem maintains strict security boundaries
Frequently Asked Questions
What protocol does the OmniRoute MCP server use?
The OmniRoute MCP server implements JSON‑RPC 2.0 over HTTP and WebSocket transports. Clients POST requests to /api/mcp with standard JSON‑RPC envelopes containing method, params, id, and jsonrpc: "2.0" fields. This protocol choice enables language-agnostic integration and straightforward debugging with standard HTTP tools.
How are the 104 tools organized in the codebase?
Tools reside in open‑sse/mcp‑server/tools/ as individual TypeScript modules grouped by functional domain. Each module exports tool objects with description, scopes array, and handler function properties. The server discovers and registers these tools at startup, creating a unified dispatch table that maps method names to their implementations.
Can I call MCP tools without authentication?
Unauthenticated calls receive only fallback scopes defined in environment configuration, typically limited to read:io4 or empty arrays. Most production tools require explicit OAuth tokens with domain-specific scopes. The scopeEnforcement.ts module processes unauthenticated requests through the same code path but with restricted callerId and minimal permission grants.
What happens when a tool call lacks required scopes?
The server returns a JSON‑RPC error response with code: -32001 and a data.missing_scopes array listing the unsatisfied requirements. The response includes available_scopes for debugging. This standardized error format allows clients to detect permission gaps and trigger re-authentication or scope escalation workflows.
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 →