# How OmniRoute MCP Implements Scope-Based Access Control

> Learn how OmniRoute MCP implements scope-based access control using JWT tokens or request metadata. It evaluates scopes against tool requirements and enforces access with audit logs.

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

---

**OmniRoute MCP enforces scope-based access control by resolving caller scopes from JWT tokens or request metadata, evaluating them against tool-specific requirements with wildcard support, and wrapping every handler in an enforcement layer that rejects unauthorized requests with detailed audit logs.**

The diegosouzapw/OmniRoute repository provides a Model Context Protocol (MCP) server that secures tool invocations using OAuth-style scope validation. This mechanism ensures that only clients with explicit permissions can execute sensitive operations, with enforcement centralized in the `open-sse/mcp-server` directory.

## Resolving Caller Scope Context

When an MCP request arrives, the system first determines what permissions the caller possesses. The `resolveCallerScopeContext` function in [`open-sse/mcp-server/scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/scopeEnforcement.ts) (lines 72-96) inspects the request's extra data (`McpToolExtraLike`) and extracts scopes from three potential sources in order of precedence:

1. **AuthInfo** — JWT scopes embedded in `extra.authInfo.scopes` (typically containing the client ID's authorized permissions)
2. **Meta** — Scope arrays found in `scopes` or `auth.scopes` fields within the `_meta` payload
3. **Environment fallback** — A static list defined in the `MCP_ALLOWED_SCOPES` environment variable when no explicit scopes are provided

The function normalizes all extracted scopes by trimming whitespace and deduplicating entries to prevent accidental mismatches due to formatting inconsistencies.

## Evaluating Required vs. Provided Scopes

Once caller scopes are resolved, `evaluateToolScopes` (lines 99-135 in [`open-sse/mcp-server/scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/scopeEnforcement.ts)) performs the authorization check. This function receives the tool name, caller scopes, the global `MCP_ENFORCE_SCOPES` flag, and optional inline-scope overrides.

The evaluation process:

- Retrieves the tool's declared scopes from `MCP_TOOL_MAP`, a centralized registry defined in [`open-sse/mcp-server/schemas/tools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/tools.ts) (lines 15-54), or uses the inline scopes provided as arguments
- Supports **wildcard matching**: a caller with `*` (full access) or prefix wildcards like `read:*` can satisfy multiple specific requirements (e.g., `read:health`, `read:metrics`)
- Returns a boolean indicating whether the caller possesses all required permissions

If a tool has no declared scopes in the registry, the enforcement layer treats the invocation as explicitly disallowed and returns a `tool_definition_missing` error.

## Enforcing Scope Restrictions at Runtime

The `withScopeEnforcement` wrapper, implemented in [`open-sse/mcp-server/server.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/server.ts) (lines 24-71), protects every MCP tool handler. This middleware:

1. Invokes `resolveCallerScopeContext` to gather caller permissions
2. Passes these to `evaluateToolScopes` along with the tool's requirements
3. Short-circuits the request with a descriptive error if validation fails, including the missing scopes, caller ID, and source
4. Logs denied attempts for auditability

When enforcement fails, the client receives an error message formatted as: `Insufficient MCP scopes for [tool_name]. Missing: [scope_list]. Caller=[id], source=[source].`

## Declaring Tool Scopes in the Registry

Tool permissions are defined centrally in [`open-sse/mcp-server/schemas/tools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/tools.ts) where the `MCP_TOOLS` array constructs `MCP_TOOL_MAP`. Each tool entry includes a `scopes` property declaring its requirements:

```typescript
// From open-sse/mcp-server/schemas/tools.ts
{
  name: "omniroute_get_health",
  handler: getHealthHandler,
  scopes: ["read:health"]
},
{
  name: "omniroute_set_routing_strategy",
  handler: setRoutingHandler,
  scopes: ["write:combos"]
}

```

This registry ensures the enforcement layer can look up required permissions at runtime without hardcoding logic in individual handlers.

## Practical Implementation Examples

A client with appropriate permissions can invoke tools by including scope data in the request extras:

```typescript
import { invokeMcpTool } from "@omniroute/open-sse/mcp-server/client";

// Caller presents JWT with "read:health" scope
const result = await invokeMcpTool("omniroute_get_health", {}, {
  authInfo: { clientId: "service-a", scopes: ["read:health"] },
});

```

The enforcement wrapper can also apply inline scope overrides for specific handler instances:

```typescript
const protectedHandler = withScopeEnforcement(
  "omniroute_set_routing_strategy",
  setRoutingStrategyHandler,
  ["write:combos"]  // inline requirement override
);

```

If a caller lacking the `write:combos` scope attempts the above, the wrapper rejects the call before the handler executes:

```typescript
// Attempt with insufficient permissions
await protectedHandler({ authInfo: { scopes: ["read:health"] } });
// Throws: Error: Insufficient MCP scopes for omniroute_set_routing_strategy. 
// Missing: write:combos. Caller=anonymous, source=none.

```

## Summary

- **Triple-resolution strategy**: Caller scopes resolve from JWT AuthInfo, metadata fields, or the `MCP_ALLOWED_SCOPES` environment variable
- **Flexible matching**: Supports exact matches, universal wildcards (`*`), and prefix wildcards (`read:*`) for hierarchical permissions
- **Centralized registry**: Tool scopes are declared in `MCP_TOOL_MAP` ([`open-sse/mcp-server/schemas/tools.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/tools.ts)) and evaluated by `evaluateToolScopes`
- **Middleware enforcement**: `withScopeEnforcement` wraps all handlers in [`open-sse/mcp-server/server.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/server.ts) to provide consistent access control and audit logging
- **Fail-closed design**: Missing tool definitions result in explicit `tool_definition_missing` errors rather than silent acceptance

## Frequently Asked Questions

### What happens if a caller provides no scopes in OmniRoute MCP?

If the incoming request lacks scopes in AuthInfo or Meta fields, `resolveCallerScopeContext` falls back to the `MCP_ALLOWED_SCOPES` environment variable. If no fallback is configured, the caller is treated as having an empty scope set, which will fail any tool requiring specific permissions.

### How does OmniRoute MCP handle wildcard permissions?

The scope evaluator in [`open-sse/mcp-server/scopeEnforcement.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/scopeEnforcement.ts) recognizes two wildcard patterns: a universal asterisk (`*`) that grants all permissions, and prefix wildcards like `read:*` that satisfy any scope starting with that prefix. This allows broad permissions to satisfy multiple specific requirements without listing each individually.

### What error is returned when scope validation fails?

When `evaluateToolScopes` determines a caller lacks required permissions, `withScopeEnforcement` throws an error formatted as: `Insufficient MCP scopes for [tool_name]. Missing: [scope_list]. Caller=[caller_id], source=[source].` Additionally, if a tool has no scope definition in `MCP_TOOL_MAP`, the system returns `tool_definition_missing`.

### Where are tool scopes defined in the OmniRoute codebase?

Tool scopes are 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, where each tool object includes a `scopes` property. These definitions are compiled into `MCP_TOOL_MAP` and exported via [`open-sse/mcp-server/schemas/index.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/mcp-server/schemas/index.ts) for runtime lookup by the enforcement layer.