How the MCP Server Implements Tool Scope Enforcement Across 30 Scopes in OmniRoute

The OmniRoute MCP server enforces tool scope enforcement across 30 scopes by validating API key permissions against tool-specific scope requirements before every tool invocation.

The OmniRoute repository implements a robust scope-based access control system for its Model Context Protocol (MCP) server. This architecture ensures that API keys can only invoke tools matching their explicitly granted permissions, with 30 distinct scopes governing access to different tool categories like memory, plugin, skill, and advanced operations.

Scope Definition and Enumeration

All 30 available scopes are centrally defined in OMNIROUTE_MCP_SCOPES within [src/shared/constants/mcpScopes.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/src/shared/constants/mcpScopes.ts). Each entry represents a logical grouping:

  • memory – Memory storage and retrieval tools
  • skill – Skill execution and management
  • plugin – Plugin installation and configuration
  • notion – Notion integration tools
  • obsidian – Obsidian vault operations
  • compression – Data compression utilities
  • agentSkill – Agent-specific capabilities
  • advanced – Extended tool functionality

The constant exports a complete enumeration that both the registration system and enforcement middleware reference at runtime.

API Key to Scope Mapping

When an API key is created, the server persists scope assignments in the SQLite database via [src/lib/db/apiKeys.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/src/lib/db/apiKeys.ts). The scopes column stores a JSON-encoded array of scope names:

// Example API key record structure
{
  key_id: "key_abc123",
  name: "Production Memory Worker",
  scopes: ["memory", "skill"], // JSON array
  created_at: "2024-01-15T10:30:00Z"
}

This database layer provides the foundation for runtime permission verification.

Request Authentication and Context Attachment

Every incoming MCP request flows through [open-sse/mcp-server/httpAuthContext.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/httpAuthContext.ts). This module:

  1. Extracts the bearer token from the Authorization header
  2. Queries the api_keys table for the corresponding record
  3. Parses the JSON scopes array
  4. Attaches a request-context object containing allowedScopes to the request object
// Conceptual flow in httpAuthContext.ts
const apiKey = extractBearerToken(request.headers.authorization);
const keyRecord = await db.apiKeys.findByKey(apiKey);
request.context = {
  apiKeyId: keyRecord.key_id,
  allowedScopes: JSON.parse(keyRecord.scopes), // ["memory", "skill"]
  authenticatedAt: Date.now()
};

This context becomes available to all downstream middleware and tool handlers.

Scope Enforcement Middleware

The critical enforcement logic resides in [open-sse/mcp-server/scopeEnforcement.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/scopeEnforcement.ts). This middleware receives:

  • The request context (allowedScopes array)
  • The tool definition (ToolDefinition with requiredScope: string)

It performs a subset check: allowedScopes must contain tool.requiredScope.

// Scope check logic in scopeEnforcement.ts
function enforceScope(
  context: RequestContext,
  tool: ToolDefinition
): void {
  const hasScope = context.allowedScopes.includes(tool.requiredScope);
  
  if (!hasScope) {
    throw new MCPError(
      403,
      `Insufficient scope for tool ${tool.name}. ` +
      `Required: "${tool.requiredScope}". ` +
      `Allowed: [${context.allowedScopes.join(", ")}]`
    );
  }
}

If the check fails, the server immediately returns 403 Forbidden without executing the tool handler, preventing any unauthorized access.

Tool Registration with Scope Assignment

Each tool registers its definition via [open-sse/mcp-server/toolSearch/register.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/toolSearch/register.ts). The registration includes a mandatory scope property:

// Example from memoryTools.ts
registerTool({
  name: "memory_add",
  description: "Store a value in memory",
  scope: "memory", // One of the 30 OMNIROUTE_MCP_SCOPES
  handler: async (args) => { /* implementation */ }
});

// Example from pluginTools.ts
registerTool({
  name: "plugin_install",
  description: "Install a new plugin",
  scope: "plugin",
  handler: async (args) => { /* implementation */ }
});

The registration system maintains an internal map of tool names to their definitions, enabling efficient scope lookup during request processing.

Runtime Enforcement Flow

When a client invokes a tool via the HTTP transport in [open-sse/mcp-server/httpTransport.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/httpTransport.ts), the server executes this sequence:

  1. Parse request – Extract tool name and arguments
  2. Resolve tool – Look up ToolDefinition in the registration map
  3. Enforce scope – Call scopeEnforcement with context and tool definition
  4. Execute or reject – Run handler if scope check passes; return 403 if it fails

This guarantees that knowing a tool's name provides no capability without proper scope authorization.

Audit Logging for Security Traceability

Every tool invocation—successful or blocked—is recorded by [open-sse/mcp-server/audit.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/audit.ts). Audit entries include:

{
  timestamp: "2024-01-15T10:35:22Z",
  apiKeyId: "key_abc123",
  toolName: "memory_add",
  scopeCheck: {
    required: "memory",
    allowed: ["memory", "skill"],
    result: "granted" // or "denied"
  },
  executionTimeMs: 12
}

This logging supports security reviews, compliance reporting, and incident investigation.

Calling Tools with Proper Scope Authorization

Authorized Memory Tool Call

import fetch from "node-fetch";

const response = await fetch("https://my.omniroute.dev/api/mcp/sse", {
  method: "POST",
  headers: {
    "Authorization": "Bearer key_with_memory_scope",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    tool: "memory_add",
    args: { key: "session1", value: "Hello world!" },
  }),
});

// Response: 200 OK with result

The API key includes the "memory" scope, so memory_add executes successfully.

Blocked Plugin Tool Call

const response = await fetch("https://my.omniroute.dev/api/mcp/sse", {
  method: "POST",
  headers: {
    "Authorization": "Bearer key_without_plugin_scope",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    tool: "plugin_install",
    args: { name: "unauthorized-plugin" },
  }),
});

// Response: 403 Forbidden
// Body: { "error": "Insufficient scope for tool plugin_install. Required: \"plugin\". Allowed: [\"memory\", \"skill\"]" }

The server rejects the request because the key lacks the required "plugin" scope, even though the tool name is valid.

Key Implementation Files

File Purpose
[src/shared/constants/mcpScopes.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/src/shared/constants/mcpScopes.ts) Defines all 30 MCP scopes as constants
[src/lib/db/apiKeys.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/src/lib/db/apiKeys.ts) Persists API key scope assignments
[open-sse/mcp-server/httpAuthContext.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/httpAuthContext.ts) Authenticates requests and attaches scope context
[open-sse/mcp-server/scopeEnforcement.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/scopeEnforcement.ts) Validates tool scope requirements
[open-sse/mcp-server/toolSearch/register.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/toolSearch/register.ts) Associates tools with their required scopes
[open-sse/mcp-server/audit.ts](https://github.com/diegosouzapw/OmniRoute/blob/release/v3.8.50/open-sse/mcp-server/audit.ts) Logs all scope check outcomes

Summary

  • 30 scopes are defined centrally in mcpScopes.ts covering all tool categories
  • API keys store granted scopes as JSON arrays in the SQLite database
  • httpAuthContext.ts extracts and attaches allowedScopes to every request
  • scopeEnforcement.ts validates that allowedScopes contains the tool's requiredScope
  • register.ts binds each tool to exactly one scope during registration
  • 403 Forbidden responses prevent unauthorized tool execution without revealing sensitive information
  • Audit logging provides complete traceability of scope-based access decisions

Frequently Asked Questions

What happens when an API key has no scopes assigned?

An API key with an empty scopes array cannot invoke any MCP tools. Every tool call returns 403 Forbidden because no scope requirements can be satisfied. The OmniRoute system requires at least one scope for functional access.

Can a single tool require multiple scopes?

No. According to the OmniRoute implementation, each tool definition specifies exactly one scope property. If multi-scope requirements are needed, you create separate tools or implement composite checks in custom middleware layers.

How do I add a new scope to the 30 existing ones?

Add the scope identifier to the OMNIROUTE_MCP_SCOPES constant in src/shared/constants/mcpScopes.ts, then assign it to tools during registration. Existing API keys will not automatically receive the new scope; administrators must update key records in the database to grant it.

Is scope enforcement bypassable through SSE transport directly?

No. The open-sse/mcp-server/httpTransport.ts channel routes all requests through the same httpAuthContext and scopeEnforcement pipeline. Whether clients use HTTP POST or SSE connections, the scope validation executes identically before any tool handler runs.

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 →