Files
OmniRoute/open-sse/mcp-server
Diego Rodrigues de Sa e Souza 24ffdde03d Release v3.7.5 (#1753)
* docs(changelog): record PR #1748 for next release

* fix(models): apply blocked providers filter to non-chat catalog models (#1752)

* chore(release): v3.7.5 — integrate ngrok tunnel and fix models filter (#1753, #1752)

* chore(release): update changelog format for v3.7.5

* Speed up endpoint initial render

* Address endpoint review feedback

* Add endpoint loading model translations

* fix: resolve build issues and implement memory UPSERT logic (#1763)

* fix: resolve build issues for v3.7.5 and apply memory/translation fixes

1. antigravityHeaders.ts: restore ANTIGRAVITY_LOAD_CODE_ASSIST_* exports for oauth.ts compatibility
2. next.config.mjs: add @ngrok/ngrok to serverExternalPackages and webpack externals to handle native .node modules
3. Memory system: UPSERT logic to prevent duplicate entries with same apiKeyId + key
4. Chinese translations: complete CLI tools and memory dashboard localizations
5. Test fixes: unique keys for pagination tests to comply with unique constraint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Gemini Code Assist review feedback

1. store.ts: add expires_at to UPDATE statement in UPSERT logic
   - Previously, expires_at was not being persisted to database on update
   - This caused state mismatch between returned Memory object and actual DB row

2. package-lock.json: revert react-markdown registry to official npmjs.org
   - Mirror-specific registry URL (npmmirror.com) should not be in lockfile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(antigravity): normalize Gemini bridge payloads (#1769)

* fix(antigravity): normalize Gemini bridge payloads

Clamp Claude bridge output tokens, use Gemini-valid system roles and tool names, and serialize antigravity requests from a cloned body so Cloud Code payload shaping stays valid.

* fix(cli): stop fallback after unsafe known paths

Preserve known-path security checks by stopping command discovery when a configured CLI path is suspicious or non-executable, instead of falling through to PATH discovery.

* test(memory): make query result assertion deterministic

Avoid relying on database result ordering when checking filtered memory keys so the unit suite remains stable across runs.

* fix(review): preserve safe cloning and CLI reasons

Handle non-cloneable antigravity request bodies without throwing and preserve specific CLI known-path failure reasons instead of masking them as not_found.

* fix(sse): propagate AbortSignal to pre-fetch semaphore and rate-limit awaits (#1771)

When a combo target takes too long, the request-level deadline fires
and calls abortController.abort() on the stream controller, but the
abort signal never reaches pending awaits in acquireAccountSemaphore()
or withRateLimit(). These awaits sit between stream controller creation
and executor.execute(), causing requests to hang indefinitely past the
600s deadline.

Pass streamController.signal to both functions so they can respond to
abort events and terminate early when the request deadline expires.

Signed-off-by: wucm667 <stevenwucongmin@gmail.com>

* Fix model sync import handling (#1755)

* Fix model sync import handling

* Align model import storage semantics

* Address model review feedback

* fix(codex): stabilize copilot responses reasoning and tool replay (#1750)

* chore(xiaomi): Update Xiaomi provider model list (#1759)

* Move DB health to management API (#1757)

* Move DB health to management API

* Address DB health review feedback

* fix(kiro): support organization IDC OAuth with regional endpoints and refresh (#1754)

* fix(kiro): support organization IDC OAuth with regional endpoints and refresh

* fix(kiro): refresh IDC tokens with stored region

---------

Co-authored-by: ngocdb <ngocdb@ngocdb.local>

* chore(workflows): add strict PR contributor credit policy

- Add ABSOLUTE PROHIBITION section to review-prs.md
- Add PR PROHIBITION rule to resolve-issues.md
- Add contributor credit rule to AGENTS.md Review Focus
- Based on audit finding: 37 PRs had code absorbed without merge credit

* chore(release): acknowledge 29 community contributors with retroactive credit

This commit formally recognizes 29 contributors whose code was manually
integrated across releases v3.4.0 through v3.7.4 without proper GitHub
merge credit. Their PRs were resolved locally due to merge conflicts
but closed instead of merged, preventing them from appearing in the
Contributors graph. We have updated our workflows to ensure this never
happens again.

Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Benson K B <4044180+benzntech@users.noreply.github.com>
Co-authored-by: clousky2020 <33016567+clousky2020@users.noreply.github.com>
Co-authored-by: Raxxoor <7317522+dhaern@users.noreply.github.com>
Co-authored-by: Jason Landbridge <15127381+JasonLandbridge@users.noreply.github.com>
Co-authored-by: slewis3600 <35925982+slewis3600@users.noreply.github.com>
Co-authored-by: Markus Hartung <12826053+hartmark@users.noreply.github.com>
Co-authored-by: Hernan Javier Ardila Sanchez <204746071+herjarsa@users.noreply.github.com>
Co-authored-by: 3_1_3_u <5846351+andruwa13@users.noreply.github.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: i1hwan <35260883+i1hwan@users.noreply.github.com>
Co-authored-by: xandr0s <1709302+xandr0s@users.noreply.github.com>
Co-authored-by: backryun <24198422+backryun@users.noreply.github.com>
Co-authored-by: Owen <36758131+kang-heewon@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <25951435+RaviTharuma@users.noreply.github.com>
Co-authored-by: Chris <3751981+christopher-s@users.noreply.github.com>
Co-authored-by: Wellington Fonseca <5421548+wlfonseca@users.noreply.github.com>
Co-authored-by: Ethan Hunt <136065060+only4copilot@users.noreply.github.com>
Co-authored-by: tombii <6607822+tombii@users.noreply.github.com>
Co-authored-by: AndrewDragonIV <7906124+AndrewDragonIV@users.noreply.github.com>
Co-authored-by: Danh Thanh <50534210+dt418@users.noreply.github.com>
Co-authored-by: Will F <30637450+willbnu@users.noreply.github.com>
Co-authored-by: defhouse <232128212+defhouse@users.noreply.github.com>
Co-authored-by: Skydwest <186351198+mercs2910@users.noreply.github.com>
Co-authored-by: zenobit <6384793+zen0bit@users.noreply.github.com>
Co-authored-by: Ivan <16905671+razllivan@users.noreply.github.com>
Co-authored-by: foxy1402 <45601526+foxy1402@users.noreply.github.com>
Co-authored-by: Luan Dias <65574834+luandiasrj@users.noreply.github.com>
Co-authored-by: Sergei Korolev <891832+knopki@users.noreply.github.com>
Co-authored-by: dail45 <69967573+dail45@users.noreply.github.com>

* fix(combo): include 429 in provider circuit breaker to stop infinite retry on exhausted quotas (#1767)

Previously, PROVIDER_FAILURE_ERROR_CODES only included {408, 500, 502, 503, 504},
meaning 429 responses never counted toward the circuit breaker threshold. This caused
exhausted accounts to be retried every 3-5 seconds indefinitely instead of being
blocked by the provider breaker.

Adding 429 ensures persistent rate limiting triggers the circuit breaker after the
configured failure threshold, giving the provider time to recover.

* fix(claude): respect client thinking/effort params to prevent forced quota drain (#1761)

Previously, OmniRoute unconditionally injected thinking: {type: 'adaptive'} and
output_config: {effort: 'high'} for Claude Opus 4.7 in Claude Code client requests.
This caused Claude Max 5h quota to drain in ~15 minutes.

Now checks the original client body: if thinking or output_config are explicitly set
(even to null or a different value), the injection is skipped. Users can opt-out by
sending thinking: null or output_config: {effort: 'low'}.

* Add MseeP.ai badge to README.md (#1727)

Integrated into release/v3.7.5

* chore(docs): update CHANGELOG for PR #1727

* fix(tests): update stream-utils assertion for responses api compliance

* feat: Fix support for claude-cli using Gemini provider (#1779)

Integrated into release/v3.7.5

* fix(codex): align client identity metadata (#1778)

Integrated into release/v3.7.5

* fix(blackbox-web): correct cookie name and populate session/subscription fields (#1776)

Integrated into release/v3.7.5

* Fix Codex /responses/compact passthrough (#1777)

Integrated into release/v3.7.5

* test(reasoning-cache): isolate DB state using mkdtempSync to prevent 401 middleware errors

* chore(release): v3.7.5 — integrate remaining PRs and finalize stability

* chore(config): remove local patch artifacts and trim workspace config

Delete temporary patch scripts and local OMC session files that should not
ship with the repository.

Also remove the Next.js config file and expand editor and TypeScript
exclusions to ignore large local workspace directories and reduce
unnecessary indexing.

* fix(antigravity): cap Claude bridge output tokens (#1785)

Integrated into release/v3.7.5

* fix(codex): stabilize Copilot responses replay state (#1791)

Integrated into release/v3.7.5

* fix(chatgpt-web): restore validator + expand model catalog to ChatGPT Plus tier (#1792)

Integrated into release/v3.7.5

* fix(antigravity): scrub internal OmniRoute headers (#1794)

Integrated into release/v3.7.5

* fix(grok-web): fix Grok validator and cookie parsing (#1793)

Integrated into release/v3.7.5

* chore(release): v3.7.5 — finalize changelog for LTS patch

* feat(api-keys): add rename support in permissions modal

Add an editable key name field at the top of the permissions modal,
allowing users to rename API keys alongside existing permission settings.

The backend already supported name updates via PATCH /api/keys/:id — this
wires the UI to send the name field and refreshes the key list on success.

Changes:
- Add keyName state and text input to PermissionsModal
- Update handleUpdatePermissions to validate and send name in PATCH body
- Add integration test for rename via PATCH (valid, empty, too-long names)
- Update E2E mock to handle PATCH requests

* chore(release): finalize v3.7.5 LTS release with schema and db initialization fixes

* test: fix json escaping in stream-utilities test

* fix(build): restore next.config.mjs that was accidentally deleted

* fix(sse): decrement pending requests on passthrough mode failure (#1798)

Integrated into release/v3.7.5

* fix(grok-web): repair validator probe + accept full cookie blobs (#1793)

Integrated into release/v3.7.5

* docs(i18n): sync documentation updates to 40 languages

---------

Signed-off-by: wucm667 <stevenwucongmin@gmail.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: R.D. <rogerproself@gmail.com>
Co-authored-by: clousky2020 <33016567+clousky2020@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: cloudy <37777261+uwuclxdy@users.noreply.github.com>
Co-authored-by: wucm667 <109257021+wucm667@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: ivan-mezentsev <ivan@mezentsev.me>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Dao Bao Ngoc <42265865+daongoc315@users.noreply.github.com>
Co-authored-by: ngocdb <ngocdb@ngocdb.local>
Co-authored-by: Benson K B <4044180+benzntech@users.noreply.github.com>
Co-authored-by: Raxxoor <7317522+dhaern@users.noreply.github.com>
Co-authored-by: Jason Landbridge <15127381+JasonLandbridge@users.noreply.github.com>
Co-authored-by: slewis3600 <35925982+slewis3600@users.noreply.github.com>
Co-authored-by: Markus Hartung <12826053+hartmark@users.noreply.github.com>
Co-authored-by: Hernan Javier Ardila Sanchez <204746071+herjarsa@users.noreply.github.com>
Co-authored-by: 3_1_3_u <5846351+andruwa13@users.noreply.github.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: i1hwan <35260883+i1hwan@users.noreply.github.com>
Co-authored-by: xandr0s <1709302+xandr0s@users.noreply.github.com>
Co-authored-by: backryun <24198422+backryun@users.noreply.github.com>
Co-authored-by: Owen <36758131+kang-heewon@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <25951435+RaviTharuma@users.noreply.github.com>
Co-authored-by: Chris <3751981+christopher-s@users.noreply.github.com>
Co-authored-by: Wellington Fonseca <5421548+wlfonseca@users.noreply.github.com>
Co-authored-by: Ethan Hunt <136065060+only4copilot@users.noreply.github.com>
Co-authored-by: tombii <6607822+tombii@users.noreply.github.com>
Co-authored-by: AndrewDragonIV <7906124+AndrewDragonIV@users.noreply.github.com>
Co-authored-by: Danh Thanh <50534210+dt418@users.noreply.github.com>
Co-authored-by: Will F <30637450+willbnu@users.noreply.github.com>
Co-authored-by: defhouse <232128212+defhouse@users.noreply.github.com>
Co-authored-by: Skydwest <186351198+mercs2910@users.noreply.github.com>
Co-authored-by: zenobit <6384793+zen0bit@users.noreply.github.com>
Co-authored-by: Ivan <16905671+razllivan@users.noreply.github.com>
Co-authored-by: foxy1402 <45601526+foxy1402@users.noreply.github.com>
Co-authored-by: Luan Dias <65574834+luandiasrj@users.noreply.github.com>
Co-authored-by: Sergei Korolev <891832+knopki@users.noreply.github.com>
Co-authored-by: dail45 <69967573+dail45@users.noreply.github.com>
Co-authored-by: MseeP.ai <mseep@skydeck.ai>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Raxxoor <manker_lol@hotmail.com>
Co-authored-by: Jack <5443152+hijak@users.noreply.github.com>
Co-authored-by: Sergey Morozov <tr0st@bk.ru>
Co-authored-by: payne <baboialex95@gmail.com>
Co-authored-by: Antigravity Assistant <bot@antigravity.local>
Co-authored-by: Andrew Munsell <andrew@wizardapps.net>
2026-04-30 01:27:03 -03:00
..
2026-04-30 01:27:03 -03:00
2026-04-30 01:27:03 -03:00
2026-04-30 01:27:03 -03:00

OmniRoute MCP Server

Model Context Protocol server that exposes OmniRoute's gateway intelligence as 16 tools for AI agents.

The MCP Server allows any AI agent (Claude Desktop, Cursor, VS Code Copilot, custom agents) to monitor, control, and optimize the OmniRoute AI gateway programmatically.


Architecture

┌──────────────────────────────────────────────────────────────────┐
│                         AI Agent / IDE                           │
│          (Claude Desktop, Cursor, VS Code, Custom)               │
└──────────────────────┬───────────────────────────────────────────┘
                       │  MCP Protocol (stdio or HTTP)
                       ▼
┌──────────────────────────────────────────────────────────────────┐
│                      OmniRoute MCP Server                        │
│  ┌──────────────┐  ┌─────────────────┐  ┌────────────────────┐  │
│  │ Scope        │  │  16 MCP Tools   │  │   Audit Logger     │  │
│  │ Enforcement  │──│  (Phase 1 + 2)  │──│   (SHA-256/SQLite) │  │
│  └──────────────┘  └────────┬────────┘  └────────────────────┘  │
└─────────────────────────────┼────────────────────────────────────┘
                              │  HTTP (internal)
                              ▼
┌──────────────────────────────────────────────────────────────────┐
│                    OmniRoute Gateway (port 20128)                 │
│        /v1/chat/completions  /api/combos  /api/usage  ...        │
└──────────────────────────────────────────────────────────────────┘

Quick Start

1. Environment Variables

# Required: OmniRoute base URL
export OMNIROUTE_BASE_URL="http://localhost:20128"

# Optional: API key for authenticated access
export OMNIROUTE_API_KEY="your-api-key"

# Optional: Scope enforcement (default: disabled)
export OMNIROUTE_MCP_ENFORCE_SCOPES="true"
export OMNIROUTE_MCP_SCOPES="read:health,read:combos,read:quota,read:usage,read:models,execute:completions,write:combos,write:budget,write:resilience"

2. stdio Transport (IDE Integration)

Add to your MCP client configuration:

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "omniroute": {
      "command": "node",
      "args": ["path/to/9router/open-sse/mcp-server/server.ts"],
      "env": {
        "OMNIROUTE_BASE_URL": "http://localhost:20128",
        "OMNIROUTE_API_KEY": "your-key"
      }
    }
  }
}

Cursor (.cursor/mcp.json):

{
  "mcpServers": {
    "omniroute": {
      "command": "npx",
      "args": ["tsx", "open-sse/mcp-server/server.ts"],
      "env": {
        "OMNIROUTE_BASE_URL": "http://localhost:20128"
      }
    }
  }
}

VS Code (.vscode/settings.json):

{
  "mcp": {
    "servers": {
      "omniroute": {
        "command": "npx",
        "args": ["tsx", "open-sse/mcp-server/server.ts"],
        "env": {
          "OMNIROUTE_BASE_URL": "http://localhost:20128"
        }
      }
    }
  }
}

3. Start via CLI

# Direct start (stdio)
npx tsx open-sse/mcp-server/server.ts

# Or via OmniRoute CLI
omniroute --mcp

Tool Reference

Phase 1: Essential Tools (8)

# Tool Scopes Description
1 omniroute_get_health read:health Gateway health, uptime, memory, circuit breakers, rate limits, cache stats
2 omniroute_list_combos read:combos List all combos (model chains) with strategies and optional metrics
3 omniroute_get_combo_metrics read:combos Performance metrics for a specific combo
4 omniroute_switch_combo write:combos Activate or deactivate a combo for routing
5 omniroute_check_quota read:quota Remaining API quota per provider with token health status
6 omniroute_route_request execute:completions Send a chat completion through intelligent routing
7 omniroute_cost_report read:usage Cost report by period (session/day/week/month) with per-provider breakdown
8 omniroute_list_models_catalog read:models List all available models across providers with capabilities and pricing

Phase 2: Advanced Tools (8)

# Tool Scopes Description
9 omniroute_simulate_route read:health, read:combos Dry-run routing simulation showing fallback tree and estimated costs
10 omniroute_set_budget_guard write:budget Set session budget with action on exceed: degrade, block, or alert
11 omniroute_set_resilience_profile write:resilience Apply resilience profile: aggressive, balanced, or conservative
12 omniroute_test_combo execute:completions, read:combos Test each provider in a combo with a real prompt and a real upstream call, report latency/cost
13 omniroute_get_provider_metrics read:health Per-provider metrics with latency percentiles (p50/p95/p99), circuit breaker
14 omniroute_best_combo_for_task read:combos, read:health AI-powered combo recommendation by task type with budget/latency constraints
15 omniroute_explain_route read:health, read:usage Explain why a request was routed to a provider (scoring factors, fallbacks)
16 omniroute_get_session_snapshot read:usage Full session snapshot: cost, tokens, top models, errors, budget status

Client Examples

Python — Full Agent Workflow

"""
OmniRoute MCP Client — Python example using the mcp SDK.
Install: pip install mcp
"""
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server = StdioServerParameters(
        command="npx",
        args=["tsx", "open-sse/mcp-server/server.ts"],
        env={
            "OMNIROUTE_BASE_URL": "http://localhost:20128",
            "OMNIROUTE_API_KEY": "your-key",
        },
    )

    async with stdio_client(server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # 1. Check gateway health
            health = await session.call_tool("omniroute_get_health", {})
            print("Health:", health.content[0].text)

            # 2. List available combos with metrics
            combos = await session.call_tool("omniroute_list_combos", {
                "includeMetrics": True
            })
            print("Combos:", combos.content[0].text)

            # 3. Find the best combo for a coding task
            best = await session.call_tool("omniroute_best_combo_for_task", {
                "taskType": "coding",
                "budgetConstraint": 0.50,
                "latencyConstraint": 5000,
            })
            print("Best combo:", best.content[0].text)

            # 4. Set a session budget guard
            budget = await session.call_tool("omniroute_set_budget_guard", {
                "maxCost": 1.00,
                "action": "degrade",
                "degradeToTier": "cheap",
            })
            print("Budget guard:", budget.content[0].text)

            # 5. Route a request through intelligent pipeline
            response = await session.call_tool("omniroute_route_request", {
                "model": "claude-sonnet-4",
                "messages": [
                    {"role": "user", "content": "Write a Python hello world"}
                ],
                "role": "coding",
            })
            print("Response:", response.content[0].text)

            # 6. Get the session snapshot
            snapshot = await session.call_tool("omniroute_get_session_snapshot", {})
            print("Session:", snapshot.content[0].text)

asyncio.run(main())

TypeScript — Programmatic Agent

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

async function main() {
  const transport = new StdioClientTransport({
    command: "npx",
    args: ["tsx", "open-sse/mcp-server/server.ts"],
    env: {
      OMNIROUTE_BASE_URL: "http://localhost:20128",
      OMNIROUTE_API_KEY: "your-key",
    },
  });

  const client = new Client({ name: "my-agent", version: "1.0.0" });
  await client.connect(transport);

  // Check quota before deciding which model to use
  const quota = await client.callTool({
    name: "omniroute_check_quota",
    arguments: { provider: "claude" },
  });
  console.log("Claude quota:", quota.content);

  // Simulate the route before actually calling
  const simulation = await client.callTool({
    name: "omniroute_simulate_route",
    arguments: {
      model: "claude-sonnet-4",
      promptTokenEstimate: 2000,
    },
  });
  console.log("Route simulation:", simulation.content);

  // Send the actual request
  const result = await client.callTool({
    name: "omniroute_route_request",
    arguments: {
      model: "claude-sonnet-4",
      messages: [{ role: "user", content: "Explain async/await" }],
    },
  });
  console.log("Result:", result.content);

  // Cost report
  const costs = await client.callTool({
    name: "omniroute_cost_report",
    arguments: { period: "session" },
  });
  console.log("Costs:", costs.content);

  await client.close();
}

main();

Go — HTTP Client

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

// Simplified direct-API approach (bypass MCP, hit OmniRoute APIs directly)
// Useful if you don't need MCP protocol framing.

func callTool(baseURL, tool string, args map[string]any) (string, error) {
    // MCP tools map to OmniRoute APIs:
    endpoints := map[string]string{
        "health": "/api/monitoring/health",
        "combos": "/api/combos",
        "quota":  "/api/usage/quota",
        "models": "/v1/models",
    }

    url := baseURL + endpoints[tool]
    resp, err := http.Get(url)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    return string(body), nil
}

func routeRequest(baseURL, model, prompt string) (string, error) {
    payload := map[string]any{
        "model": model,
        "messages": []map[string]string{
            {"role": "user", "content": prompt},
        },
        "stream": false,
    }
    data, _ := json.Marshal(payload)

    resp, err := http.Post(
        baseURL+"/v1/chat/completions",
        "application/json",
        bytes.NewReader(data),
    )
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    return string(body), nil
}

func main() {
    base := "http://localhost:20128"

    health, _ := callTool(base, "health", nil)
    fmt.Println("Health:", health)

    result, _ := routeRequest(base, "auto", "Hello from Go!")
    fmt.Println("Result:", result)
}

Use Cases

🔄 Use Case 1: Auto-Healing Agent

An agent that monitors OmniRoute health and auto-switches combos when providers degrade.

async def auto_healing_loop(session):
    """Monitor health and react to provider issues."""
    while True:
        # Check health
        health = await session.call_tool("omniroute_get_health", {})
        data = json.loads(health.content[0].text)

        # Find providers with open circuit breakers
        broken = [
            cb for cb in data["circuitBreakers"]
            if cb["state"] == "OPEN"
        ]

        if broken:
            # Switch to a different resilience profile
            await session.call_tool("omniroute_set_resilience_profile", {
                "profile": "conservative"
            })

            # Find best alternative combo
            best = await session.call_tool("omniroute_best_combo_for_task", {
                "taskType": "coding"
            })
            best_data = json.loads(best.content[0].text)
            combo_id = best_data["recommendedCombo"]["id"]

            # Activate it
            await session.call_tool("omniroute_switch_combo", {
                "comboId": combo_id, "active": True
            })
            print(f"⚠️ Auto-healed: switched to {combo_id}")

        await asyncio.sleep(30)  # Check every 30 seconds

💰 Use Case 2: Budget-Aware Coding Agent

An agent that monitors costs in real-time and degrades to cheaper models when nearing budget.

async def budget_aware_coding(session, task: str, max_budget: float):
    """Complete a coding task within a budget."""
    # Set budget guard
    await session.call_tool("omniroute_set_budget_guard", {
        "maxCost": max_budget,
        "action": "degrade",
        "degradeToTier": "cheap",
    })

    # Simulate first to estimate cost
    sim = await session.call_tool("omniroute_simulate_route", {
        "model": "claude-sonnet-4",
        "promptTokenEstimate": len(task.split()) * 2,
    })
    sim_data = json.loads(sim.content[0].text)
    estimated_cost = sim_data["fallbackTree"]["bestCaseCost"]
    print(f"Estimated cost: ${estimated_cost:.4f}")

    # Send request
    result = await session.call_tool("omniroute_route_request", {
        "model": "claude-sonnet-4",
        "messages": [{"role": "user", "content": task}],
        "role": "coding",
    })

    # Check remaining budget
    snapshot = await session.call_tool("omniroute_get_session_snapshot", {})
    snap_data = json.loads(snapshot.content[0].text)
    print(f"Session cost: ${snap_data['costTotal']:.4f}")
    if snap_data.get("budgetGuard"):
        print(f"Budget remaining: ${snap_data['budgetGuard']['remaining']:.4f}")

    return json.loads(result.content[0].text)["response"]["content"]

🧪 Use Case 3: Combo Benchmarking Agent

An agent that periodically benchmarks all combos and reports the fastest/cheapest.

async def benchmark_combos(session):
    """Benchmark all enabled combos and rank them."""
    combos = await session.call_tool("omniroute_list_combos", {
        "includeMetrics": True,
    })
    combo_list = json.loads(combos.content[0].text)["combos"]

    results = []
    for combo in combo_list:
        if not combo["enabled"]:
            continue

        test = await session.call_tool("omniroute_test_combo", {
            "comboId": combo["id"],
            "testPrompt": "Return the number 42.",
        })
        test_data = json.loads(test.content[0].text)
        results.append({
            "combo": combo["name"],
            "fastest": test_data["summary"]["fastestProvider"],
            "cheapest": test_data["summary"]["cheapestProvider"],
            "success_rate": f'{test_data["summary"]["successful"]}/{test_data["summary"]["totalProviders"]}',
        })

    print("📊 Combo Benchmark Results:")
    for r in results:
        print(f"  {r['combo']}: fastest={r['fastest']}, cheapest={r['cheapest']}, success={r['success_rate']}")

🔍 Use Case 4: Post-Mortem Debugging Agent

An agent that explains why a request was routed to a specific provider.

async function debugRouting(client: Client, requestId: string) {
  // Explain the routing decision
  const explanation = await client.callTool({
    name: "omniroute_explain_route",
    arguments: { requestId },
  });
  const data = JSON.parse(explanation.content[0].text);

  console.log(`Request ${requestId}:`);
  console.log(`  Provider: ${data.decision.providerSelected}`);
  console.log(`  Model: ${data.decision.modelUsed}`);
  console.log(`  Score: ${data.decision.score}`);
  console.log(`  Factors:`);
  for (const factor of data.decision.factors) {
    console.log(`    ${factor.name}: ${factor.value} (weight: ${factor.weight})`);
  }
  if (data.decision.fallbacksTriggered.length > 0) {
    console.log(`  Fallbacks triggered:`);
    for (const fb of data.decision.fallbacksTriggered) {
      console.log(`    ${fb.provider}: ${fb.reason}`);
    }
  }
}

📋 Use Case 5: Model Discovery Agent

An agent that discovers the cheapest models for a given capability.

async def find_cheapest_models(session, capability="chat"):
    """Find the cheapest available models for a capability."""
    catalog = await session.call_tool("omniroute_list_models_catalog", {
        "capability": capability,
    })
    models = json.loads(catalog.content[0].text)["models"]

    # Filter available models with pricing
    priced = [
        m for m in models
        if m["status"] == "available" and m.get("pricing")
    ]
    priced.sort(key=lambda m: m["pricing"]["inputPerMillion"] or float("inf"))

    print(f"💡 Cheapest {capability} models:")
    for m in priced[:5]:
        input_cost = m["pricing"]["inputPerMillion"] or 0
        output_cost = m["pricing"]["outputPerMillion"] or 0
        print(f"  {m['id']} ({m['provider']}): ${input_cost}/M in, ${output_cost}/M out")

Security & Scope Enforcement

The MCP server supports fine-grained scope enforcement for multi-tenant environments:

Scope Tools
read:health get_health, simulate_route, get_provider_metrics, best_combo_for_task, explain_route
read:combos list_combos, get_combo_metrics, simulate_route, best_combo_for_task, test_combo
read:quota check_quota
read:usage cost_report, explain_route, get_session_snapshot
read:models list_models_catalog
write:combos switch_combo
write:budget set_budget_guard
write:resilience set_resilience_profile
execute:completions route_request, test_combo

Wildcard scopes: Use read:* to grant all read scopes, or * for full access.


Audit Logging

Every tool call is logged to the mcp_tool_audit SQLite table:

  • Input: SHA-256 hashed (never stores raw prompts)
  • Output: Truncated to 200 chars
  • Metadata: Tool name, duration, success/error, API key ID

Access audit data via:

import { getRecentAuditEntries, getAuditStats } from "./audit";

const entries = await getRecentAuditEntries(50);
const stats = await getAuditStats();
// stats: { totalCalls, successRate, avgDurationMs, topTools }

File Structure

mcp-server/
├── server.ts              # MCP server setup, essential tool handlers, entry point
├── index.ts               # Barrel export
├── audit.ts               # SQLite audit logger (SHA-256 input hashing)
├── scopeEnforcement.ts    # Fine-grained scope enforcement
├── schemas/
│   ├── tools.ts           # Zod schemas for all 16 tools (input/output/scopes)
│   ├── a2a.ts             # A2A protocol types (Agent Card, Task, JSON-RPC)
│   ├── audit.ts           # Audit & routing decision types + hash helpers
│   └── index.ts           # Schema barrel export
├── tools/
│   └── advancedTools.ts   # Phase 2 tool handlers (8 advanced tools)
└── __tests__/
    ├── essentialTools.test.ts
    ├── advancedTools.test.ts
    └── a2aLifecycle.test.ts

License

Part of OmniRoute — MIT License.