* chore(release): open v3.8.27 development cycle * fix(security): polynomial ReDoS in comboAgentMiddleware regex (#3982) * fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos) CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix. On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines (CodeQL js/polynomial-redos, alerts #612/#613). The surrounding runs are irrelevant to detecting/capturing the tag, so the detection pattern now matches only the core `<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the wrapping newlines (combo.ts streaming, #531) but BOUNDED ({0,16}) so it stays linear. Behavior preserved: detection, model extraction, multi-tag stripping (#454) and blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety regression tests (50k-newline inputs complete in <1ms). * docs(changelog): add #3982 ReDoS fix to [3.8.27] * ci(security): harden workflows — artipacked persist-credentials + cache-poisoning + SC2086 (#3965) * Refine provider quota card display (#3969) Integrated into release/v3.8.27 * feat: add sidebar group separator toggles (#3971) Integrated into release/v3.8.27 * Gate control-plane proxy direct fallback (#3963) Integrated into release/v3.8.27 * Capture actual upstream provider requests (#3941) Integrated into release/v3.8.27 * ci(quality): flip require-tighten + osv + Trivy to blocking (v3.8.27 cycle-end) (#3984) * fix(resilience): respect connection cooldown stored as numeric epoch (#3954) (#3995) rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved. * fix(providers): fetch live /models for LLM7 and BytePlus (#3976) (#3996) llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure. * fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref (#3972) (#3997) The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead. * feat(compression): add Indonesian caveman rules and language pack (#3975) Integrated into release/v3.8.27 (cherry picked from commitc9b5b1a892) * fix(combo): shuffle strict-random fallback remainder to spread load (#3959) (#3998) strict-random shuffled only the deck-selected slot 0 and left the fallback remainder in fixed priority order, so after a failing deck pick the chain always fell through to the same top-priority model — a persistently-failing model was retried on essentially every request and fallback load never spread across peers. Shuffle the remainder too (like the random strategy). * Add provider auth visibility controls (#3953) Integrated into release/v3.8.27 * fix(claude): forward client tool-search-tool anthropic-beta on the Claude OAuth path (#3974) (#3999) The client-negotiated anthropic-beta: tool-search-tool-2025-10-19 was dropped on both Claude code paths (default executor rebuilt from static ANTHROPIC_BETA_CLAUDE_OAUTH; selectBetaFlags only read the client beta to gate thinking/effort), so claude.ai rejected deferred-tool requests with 400 'Tool reference not found'. Add an allowlist-merge (mergeClientAnthropicBeta) that unions the client's allowlisted betas into the outbound set on both paths, preserving #3415 (no forced thinking/effort). * feat(providers): add model search filter to provider dashboard (#3950) Integrated into release/v3.8.27 * fix(vision-bridge): force bridge for tokenrouter deepseek models (#3946) Integrated into release/v3.8.27 * fix(executor): strip stream_options on non-streaming requests (#3884) (#4000) Clients that send stream_options:{include_usage:true} regardless of stream (e.g. the OpenAI Python SDK) had it passed through on non-streaming calls; NVIDIA NIM rejected it with 400 'Stream options can only be defined when stream=True'. DefaultExecutor.transformRequest only injected/cleared stream_options on the streaming branch and never stripped a client-sent value when stream=false. Add a !stream strip branch; the streaming injection path is unchanged. Global to openai-compat providers. * fix(qwen-web): cookie validation false-positive - check response body for user object (#3958) Integrated into release/v3.8.27 * fix(db): persist backup retention days (#3970) Integrated into release/v3.8.27 * 大量UI显示和i18n优化 (#3973) Integrated into release/v3.8.27 * deps: bump the npm_and_yarn group across 1 directory with 2 updates (#3943) Integrated into release/v3.8.27 * deps: bump form-data from 4.0.5 to 4.0.6 (#3944) Integrated into release/v3.8.27 * deps: bump vite from 8.0.5 to 8.0.16 (#3942) Integrated into release/v3.8.27 * chore(quality): re-baseline validation.ts 4407->4428 (#3958 qwen body-check) The qwen-web validation body-check merged in #3958 pushed validation.ts past its frozen size on the integrated release tip. Bump the baseline with justification; no logic is separately extractable from the existing qwen-web validation branch. * deps: bump the production group with 13 updates (#3915) Integrated into release/v3.8.27 — low-risk group (playwright 1.60→1.61 minor + transitive patches; fumadocs-core 16.9→16.10 minor). * chore(deps): ignore jscpd major bumps (v5 Rust rewrite breaks the duplication gate) Our duplication ratchet (scripts/check/check-duplication.mjs) is pinned to jscpd@4 and parses jscpd-report.json against a frozen baseline. jscpd v5 is a native Rust binary with no Node.js API and a different report/bin, so a major bump would break the gate. Migrate deliberately, not via dependabot. Closes the noise from #3916. * fix(perplexity-web): parse schematized diff_block stream so answers aren't empty (#4001) Integrated into release/v3.8.27 — schematized diff_block parsing follow-up to #3938. * refactor: modularize providerRegistry.ts into 159 individual provider plugins (#3993) Modularize provider registry (#3594). Integrated into release/v3.8.27 after rebase + behavior-preservation verification (provider-consistency gate 159/232/0, typecheck, registry tests, build 556/556). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(registry): restore byteplus + mimocode dropped by #3993 modularization The provider-registry modularization (#3993) was cut from a base predating the byteplus (#3877) and mimocode (#3837) registry entries, so merging it silently dropped both providers (getRegistryEntry returned undefined → validation reported 'not supported'). Re-add them as registry modules in the new structure; registered count 159→161, provider-consistency 161/232/0. Also align the pre-existing qwen-web validator test to #3958: since the validator now requires a real `user` object in the 200 body, the mock must carry one. * refactor: modularize schemas (non-stacked) (#3988) Modularize validation schemas (#3594). Integrated into release/v3.8.27 after rebase (reconciled the merged hiddenSidebarGroupLabels #3971 + intelligenceSyncRequestSchema into the new modules) + behavior verification (typecheck, 195 schema/settings/validation tests, build 556/556). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(default-executor): honor custom providerSpecificData.baseUrl for OpenAI-format providers (#4002) Integrated into release/v3.8.27 — honor custom providerSpecificData.baseUrl in DefaultExecutor (openai-format), tested. * feat(openai): honor custom base URL in model discovery + complete openai/codex pricing (#4005) Integrated into release/v3.8.27 — openai model-discovery honors custom base URL (SSRF-guarded) + pricing rows for new openai/codex models. Tested + baselines bumped. * fix(live-ws): bridge sidecar events to dashboard (#4004) Integrated into release/v3.8.27 — repair LiveWS sidecar (startup, same-origin /live-ws, main→sidecar compression.completed bridge, early-msg queue). Fixed the cookie-parse regex (\s) + added a focused unit test; baseline bumped for the non-blocking chatCore bridge. * docs(troubleshooting): note MITM proxy cannot intercept Windows-host apps under WSL (#4003) Integrated into release/v3.8.27 — MITM/WSL troubleshooting note. * fix(repo): untrack accidentally-committed root node_modules symlink + gitignore it A worktree node_modules symlink (-> the main checkout's node_modules) was staged by a `git add -A` during the #3988 merge and committed into05213ac6a. The symlink points at the repo's own node_modules path, so checking it out turns the main checkout's node_modules into a self-referential symlink (breaking tsx/all node ops). Untrack it and add a root-anchored /node_modules ignore so the symlink form can't be re-committed (the existing 'node_modules/' only matches directories). * fix(quality): allowlist socks dep (declared by #4004, never allowlisted) socks@^2.8.7 was added to package.json in #4004 (LiveWS sidecar,02302131f) as a phantom-dep cleanup but never added to dependency-allowlist.json, so check:deps has been red on the release tip ever since. socks is the standard SOCKS proxy client (dep of fetch-socks), legitimate and years old. * feat(sse): real LLMLingua-2 ONNX compression engine (stable) (#4014) Integrated into release/v3.8.27. Adjustments before merge: - Synced with the current release tip (was 11 commits behind). - Added the 3 LLMLingua-2 ONNX optional-runtime deps to dependency-allowlist.json (@atjsh/llmlingua-2, @tensorflow/tfjs, js-tiktoken) — the only gate that was red. - socks was allowlisted directly on release (separate fix d7db5c73d; it was declared by #4004 but never allowlisted, leaving check:deps red release-wide). Verified locally: check:deps OK, file-size OK, public-creds OK, provider-consistency 161/232/0, typecheck:core clean, 24/24 LLMLingua tests pass. The only remaining Fast-QG red is the pre-existing #3972 orphan test (request-logger-autorefresh-visibility-3972.test.tsx), which is release-wide and unrelated to this PR. * test(dashboard): rehome #3972 logs auto-refresh test so a runner collects it tests/unit/request-logger-autorefresh-visibility-3972.test.tsx (added by #3972 via #3997) sat at the top level of tests/unit/ as a .tsx vitest test, which NO runner collects: the node runner only globs *.test.ts, and test:vitest:ui only runs tests/unit/ui. So the #3972 regression guard never executed in CI and check:test-discovery was red release-wide. Move it under tests/unit/ui/ (the collected vitest:ui path) and fix the relative import depth. Verified: the test now runs and passes (2/2), and check:test-discovery is green. * feat(compression): capture per-engine analytics (#3960) + Lite schema fix (#3952) (#4018) Captures the net-new value from #3960 (per-engine breakdown analytics) and #3952 (Lite engine schema fix) onto release/v3.8.27. Fast QG green; 622/622 compression+analytics tests pass. * fix(sse): guard model-less registry entries in getUnsupportedParams (mimocode) (#4015) Real bugfix: guard model-less registry entries (mimocode) in getUnsupportedParams so handleChatCore no longer throws 'entry.models is not iterable' / reports 'All models failed' for unrelated requests. Includes a regression test. Fast QG green. * feat(ci): Quality Gate v2 — Onda 0 + Onda 1 (gate flips, TIA, SAST, DAST-smoke, mutation infra) (#4016) * docs(ops): add quality-gate assessment + replication playbook (Fase 9 foundation) * feat(ci): flip oasdiff breaking-change gate to blocking (ratchet) * docs(ops): deliver main branch-protection ruleset for owner to apply * fix(ci): run typecheck:core in PR->release fast-gates (close fast-gates hole, part 1) * perf(mutation): enable Stryker incremental mode + cache (scales the 60/80 rollout) * feat(ci): commit CodeQL advanced config (security-extended), replacing default-setup * feat(ci): version semgrep SAST workflow (owasp/secrets), advisory * feat(quality): TIA test-impact map builder (import-graph; map built at runtime, gitignored) * feat(quality): TIA impacted-test selector with run-all fail-safe * fix(ci): run TIA-impacted unit tests in PR->release fast-gates (build map at runtime, fail-safe full) * feat(ci): DAST-smoke per-PR (schemathesis subset + promptfoo injection-guard, blocking) * fix(ci): unbreak Fase 9 PR CI (MDX frontmatter, CodeQL conflict, dast-smoke advisory) - Add MDX frontmatter to docs/ops/{BRANCH_PROTECTION_MAIN,QUALITY_GATE_PLAYBOOK}.md. fumadocs rejects frontmatter-less docs -> 'npm run build' failed -> broke dast-smoke's build step (the release fast-gates never runs build, so this only surfaced on the PR). - codeql.yml: workflow_dispatch-only until the owner switches repo CodeQL Default->Advanced (advanced configs cannot be processed while default setup is enabled; documented inline). - dast-smoke.yml: job-level continue-on-error (advisory) so this brand-new gate matures before it blocks (repo convention: advisory -> blocking). * ci(quality): make TIA unit-test step advisory until release test-debt is cleared release/v3.8.27 carries ~17 pre-existing failing unit tests (budget #3537, apiKey #3552, several Zod schemas, Puter/Qwen executors, mimocode entry, etc.) unrelated to this PR — the new 'run tests on PR->release' gate surfaced them. Per the repo's advisory->blocking convention, this step enters advisory (it still runs + reports) so pre-existing debt doesn't block the gate program. typecheck:core stays blocking. Flip to blocking (remove continue-on-error) once the release suite is green. * fix(sse): preserve Kiro streaming finish_reason tool_calls (#3980) (#4025) * fix(guardrails): preserve original image when vision-bridge describe fails (#4012) (#4026) * feat(api): advertise combo capabilities on import surfaces (#3979) (#4027) * feat(sse): delegated Anthropic Context Editing for Claude (clear_tool_uses) (#4021) Opt-in Claude-only delegated compression: injects context_management.clear_tool_uses_20250919 at the Claude pre-serialization chokepoint (composes with clear_thinking, thinking first), threaded via ExecuteInput from handleChatCore. Pure edit-builder + 11 tests (7 unit + 4 e2e fetch-capture). Beta context-management-2025-06-27 already advertised; allowlist done. Telemetry/400-fallback/claude-web coverage deferred. * fix(opencode): map x-session-affinity to x-opencode-session for custom providers (#4022) (#4028) * fix(dashboard): Playground Compare tab loading + HTTP method guard (#4024) randomUUID non-HTTPS fallback + static CompareTab import; raw HTTP TRACE->405 method guard wired into dev + standalone servers. Integrated into release/v3.8.27. * refactor(dashboard): settings UI layout + API Keys naming (#4020) Presentation/relabel refactor of the Settings dashboard (API Manager -> API Keys), card relocations, Toggle adoption, present-but-disabled engine steps. Auth-file changes are string/comment-only (no behavior change). Integrated into release/v3.8.27. * fix: restore unit regressions dropped by lossy schema/registry modularizations (#4030) Restores schema fields (combo reasoningTokenBuffer, budget-0 #3537, openrouter preset, proxy family #3777, resilience degradation/providerCooldown), qwen-web v2 endpoint+catalog, mimocode models key — all dropped by #3988/#3993 — and aligns 3 tests to #3941/#3993. Verified: 8 failing regression tests on release tip -> 131/131 green on this branch. Integrated into release/v3.8.27. * fix(api): return 400 (not 500) for malformed JSON on /api/auth/login (#4031) Wrap request.json() so a malformed/non-JSON login body returns a structured 400 instead of falling through to the 500 catch. Fixes the schemathesis high-risk-endpoint DAST finding (verified: schemathesis step now passes). +TDD test. Integrated into release/v3.8.27. * feat(dashboard): real circuit-breaker state in the Combo Live cascade (U1b) (#4029) Overlays real provider circuit-breaker state (GET /api/monitoring/health) onto the Combo Live cascade as a 'CB: OPEN · 41s' badge. Pure enrichRunWithBreakers + fail-soft useProviderBreakerHealth poll; graceful when health is absent. +13 tests. Integrated into release/v3.8.27. * Fix promptfoo security assertion parsing (#4032) * chore(deps): dependabot security bumps + drop unused gray-matter (#4036) Integrated into release/v3.8.27 — dependabot security bumps (form-data/js-yaml/protobufjs/dompurify/hono) + drop unused gray-matter. Unblocks the npm audit:deps gate (Lint) branch-wide. * fix(ci): scope TIA to node:test unit files only (mirror test:unit glob) (#4035) Integrated into release/v3.8.27 — scopes the advisory TIA step to the test:unit node:test glob, fixing the 99 false failures. +4 TDD. * Refine compression settings, storage labels, and sidebar grouping (#4033) Integrated into release/v3.8.27 — relocate Token Saver into Compression Settings (controlled component), reorder Security/Authz tabs, storage labels + i18n relabel. Thanks @rdself! * [codex] add per-key local usage command (#4034) Integrated into release/v3.8.27 — per-key local @@om-usage command (cached quota, no upstream routing). Rebased onto modularized schemas/keys.ts + file-size rebaseline. Thanks @Witroch4! * chore(release): reconcile v3.8.27 CHANGELOG + i18n mirrors * ci(quality): unblock v3.8.27 release gates (zizmor pin + test-masking allowlist) - zizmor ratchet (151→139, no regression): SHA-pin every action ref ADDED this cycle — codeql/dast-smoke/semgrep (3 new workflows) + trivy-action (docker-publish) + actions/cache (nightly-mutation). Pre-existing tag refs keep the repo convention. - test-masking: add config/quality/test-masking-allowlist.json + allowlist support in check-test-masking.mjs (exempts ONLY the net-assert-reduction signal; tautology/skip/ deletion still fire). Allowlists 2 verified-legitimate reductions: appearance-widget-settings-schema (#4033 removed showTokenSaverOnEndpoint field) and dashboard-shell-tabs (#3973 tabs→redirect refactor, asserts replaced). +4 gate tests. * test(quality): reword test-masking self-test comments to avoid literal masking patterns The added allowlist-test comments contained the literal strings 'assert.ok(true)' and '.skip' which the masking detector's own regexes match as text — making the gate flag its own test file (net +1 tautology/skip/extended-tautology vs main). Reworded to plain prose ('a new tautology', 'a new skip marker'); test logic unchanged (24/24 pass). * fix(quality): unblock v3.8.27 release — align 3 stale tests + restore modularized settings-schema parity Release-PR full CI surfaced 3 deterministic test failures (no live product regression), all stale vs legitimate cycle changes: - settings-schema parity (#3988): the modularized updateSettingsSchema barrel (schemas/settings.ts) had diverged from the canonical settingsSchemas.ts (45 vs 85 fields — 40 dropped + 6 extra), a lossy-modularization dead-code copy. Re-export from the canonical source so the barrel can never diverge again (runtime already uses canonical). Parity test now passes. - api-manager permissions modal: #4034 added a 4th self-service switch (per-key usage allowance); a11y invariant (every switch type="button") still holds. Updated the static count 3 -> 4. - pack-artifact policy: dist/http-method-guard.cjs became a required runtime path; added it to the test's expected missing-paths list. Also documents the gate gap for Fase 9 (QUALITY_GATE_PLAYBOOK Parte 6): G1 run the deterministic unit layer + test-masking on PR->release (not just PR->main), G2 a modularization-parity gate (would have caught the #3988 drop at its PR), G3 flake quarantine. Env flakes (LiveWS startup timeout, integration server-startup cascade) are pre-existing/CI-env, triaged separately. --------- Co-authored-by: Randi <55005611+rdself@users.noreply.github.com> Co-authored-by: Veier04 <118300867+Veier04@users.noreply.github.com> Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com> Co-authored-by: WormAlien <164898390+WormAlien@users.noreply.github.com> Co-authored-by: thezukiru <121331256+thezukiru@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: NOXX - Commiter <artur1992123@mail.ru> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: Demiurge The Single <megamen932@gmail.com> Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
31 KiB
title, version, lastUpdated
| title | version | lastUpdated |
|---|---|---|
| OmniRoute MCP Server Documentation | 3.8.8 | 2026-05-30 |
OmniRoute MCP Server Documentation
Model Context Protocol server with 87 tools across routing, cache, compression, memory, skills, proxy, and context source operations.
Source of truth:
open-sse/mcp-server/schemas/tools.ts(33 base) +memoryTools.ts(3) +skillTools.ts(4) +agentSkillTools.ts(3) +gamificationTools.ts(8) +pluginTools.ts(8) +notionTools.ts(6) +obsidianTools.ts(22) = 87 (TOTAL_MCP_TOOL_COUNT). Tool registration and scope wiring lives inopen-sse/mcp-server/server.ts.
Source: diagrams/mcp-tools-87.mmd (regenerate via
npm run docs:render-diagrams).
Installation
OmniRoute MCP is built-in. Start it with:
omniroute --mcp
Or via the open-sse transport:
# HTTP streamable transport (port 20130)
omniroute --dev # MCP auto-starts on /mcp endpoint
Transports
The MCP server exposes three transports, all backed by the same createMcpServer() factory:
| Transport | Where | When to use |
|---|---|---|
stdio |
open-sse/mcp-server/server.ts |
IDE integrations (Claude Desktop, Cursor, etc.) |
sse |
POST/GET /api/mcp/sse via httpTransport |
Browser/agent clients that need an event stream |
streamable-http |
POST/GET/DELETE /api/mcp/stream |
Multi-session HTTP clients (mcp-session-id header) |
The active HTTP transport (sse or streamable-http) is selected by the mcpTransport setting. Switching transports closes existing sessions on the other transport.
Remote access (manage-scope bypass)
/api/mcp/* is in the LOCAL_ONLY tier (src/server/authz/routeGuard.ts) — by default only loopback hosts (localhost, 127.0.0.1, ::1) can reach it. Since v3.8.2, non-loopback clients may connect if they present an Authorization: Bearer <api-key> whose key carries the manage scope. This is the only way to reach the remote MCP server through a tunnel, reverse proxy, or public hostname.
# Grant manage scope: open the dashboard API Keys page and toggle
# "Management Access" on the key, or POST scopes:["manage"] when creating.
# Then connect from a remote MCP client:
curl -i \
-H "Host: your-public-host.example" \
-H "Authorization: Bearer sk-…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"my-client","version":"0"}}}' \
https://your-public-host.example/api/mcp/stream
A non-manage key (or no Bearer) returns 403 LOCAL_ONLY. The sibling prefix /api/cli-tools/runtime/* is intentionally NOT bypassable — see Route Guard Tiers — Manage-scope carve-out.
IDE Configuration
See MCP Client Configuration for Claude Desktop, Cursor, Cline, and compatible MCP client setup.
Essential Tools (8) — Phase 1
| Tool | Scopes | Description |
|---|---|---|
omniroute_get_health |
read:health |
Uptime, memory, circuit breakers, rate limits, cache stats |
omniroute_list_combos |
read:combos |
All configured combos with strategies (optional metrics) |
omniroute_get_combo_metrics |
read:combos |
Performance metrics for a specific combo |
omniroute_switch_combo |
write:combos |
Activate or deactivate a combo |
omniroute_check_quota |
read:quota |
Quota used/total, percent remaining, reset time, token health |
omniroute_route_request |
execute:completions |
Send a chat completion through OmniRoute routing |
omniroute_cost_report |
read:usage |
Cost report by period (session/day/week/month) |
omniroute_list_models_catalog |
read:models |
Full model catalog with capabilities, status, pricing |
Phase 1 — Search
| Tool | Scopes | Description |
|---|---|---|
omniroute_web_search |
execute:search |
Web search through OmniRoute search gateway (Serper/Brave/Perplexity/Exa/Tavily/Google PSE/Linkup/SearchAPI/SearXNG) with failover |
Advanced Tools (11) — Phase 2
| Tool | Scopes | Description |
|---|---|---|
omniroute_simulate_route |
read:health, read:combos |
Dry-run routing simulation with fallback tree |
omniroute_set_budget_guard |
write:budget |
Session budget with degrade/block/alert action |
omniroute_set_routing_strategy |
write:combos |
Update combo strategy at runtime (priority/weighted/auto/etc.) |
omniroute_set_resilience_profile |
write:resilience |
Apply aggressive / balanced / conservative resilience preset |
omniroute_test_combo |
execute:completions, read:combos |
Live test of every provider in a combo using a real upstream call |
omniroute_get_provider_metrics |
read:health |
Per-provider metrics with p50/p95/p99 latency and circuit breaker state |
omniroute_best_combo_for_task |
read:combos, read:health |
Recommend combo by task type with budget/latency constraints |
omniroute_explain_route |
read:health, read:usage |
Explain why a request was routed to a provider (scoring factors + fallbacks) |
omniroute_get_session_snapshot |
read:usage |
Full session snapshot: cost, tokens, top models/providers, errors, budget guard |
omniroute_db_health_check |
read:health, write:resilience |
Diagnose (and optionally auto-repair) database drift like broken combo refs / orphan rows |
omniroute_sync_pricing |
pricing:write |
Sync pricing data from external sources (LiteLLM); supports dryRun |
Cache Tools (2)
| Tool | Scopes | Description |
|---|---|---|
omniroute_cache_stats |
read:cache |
Semantic cache, prompt-cache, and idempotency stats |
omniroute_cache_flush |
write:cache |
Flush cache globally or by signature/model |
Compression Tools (5)
| Tool | Scopes | Description |
|---|---|---|
omniroute_compression_status |
read:compression |
Compression settings, analytics summary, and cache-aware stats (includes analytics.mcpDescriptionCompression metadata) |
omniroute_compression_configure |
write:compression |
Configure compression mode, threshold, target ratio, system-prompt preservation, MCP description compression toggle |
omniroute_set_compression_engine |
write:compression |
Pick the active engine (off/caveman/rtk/stacked) and Caveman/RTK intensity |
omniroute_list_compression_combos |
read:compression |
List named compression combos and their engine pipelines |
omniroute_compression_combo_stats |
read:compression |
Analytics grouped by compression combo and engine |
omniroute_compression_status reports MCP description compression separately under
analytics.mcpDescriptionCompression. Those values are metadata-size estimates for MCP listable
descriptions (tools, prompts, resources, and resourceTemplates); they are not provider usage
receipts and are marked with source: "mcp_metadata_estimate".
MCP Accessibility Tree Filter (v3.8.0)
Separate from the 5 compression tools above, OmniRoute includes a post-execution filter that compresses the tool results of MCP browser/accessibility tools before they are returned to the agent. This filter is not itself a tool — it runs transparently on any tool result that contains verbose accessibility-tree or browser-snapshot text (≥2000 chars).
Key behaviors:
- Collapses ≥30 consecutive repeated sibling lines into head + tail summary
- Preserves
[ref=eXX]anchors required by Playwright/computer-use - Hard-truncates oversized text (>50,000 chars) with a navigation hint
- Expected savings: 60–80% on browser snapshot payloads
Configuration: compression.mcpAccessibility in global settings (migration 056).
Implementation: open-sse/services/compression/engines/mcpAccessibility/.
Full docs: Compression Engines — MCP Accessibility Tree Filter.
See Compression Engines and RTK Compression for the runtime compression model behind these tools.
1Proxy Tools (3)
| Tool | Scopes | Description |
|---|---|---|
omniroute_oneproxy_fetch |
read:proxies |
Fetch free proxies from the 1proxy marketplace (protocol/country/quality/limit filters) |
omniroute_oneproxy_rotate |
read:proxies |
Get the next available proxy by strategy (random / quality / sequential) |
omniroute_oneproxy_stats |
read:proxies |
Pool stats, sync status, distribution by protocol and country |
Memory Tools (3)
Defined in open-sse/mcp-server/tools/memoryTools.ts. Auth/scope is enforced through the standard MCP scope pipeline.
| Tool | Scopes | Description |
|---|---|---|
omniroute_memory_search |
read:memory |
Search memories by query / type / API key with token-budget enforcement |
omniroute_memory_add |
write:memory |
Add a new memory entry (factual / episodic / procedural / semantic) |
omniroute_memory_clear |
write:memory |
Clear memories for an API key, optionally filtered by type or olderThan timestamp |
Skill Tools (4)
Defined in open-sse/mcp-server/tools/skillTools.ts. Backed by src/lib/skills/registry + src/lib/skills/executor.
| Tool | Scopes | Description |
|---|---|---|
omniroute_skills_list |
read:skills |
List registered skills with optional filtering by API key, name, or enabled state |
omniroute_skills_enable |
write:skills |
Enable or disable a specific skill by ID |
omniroute_skills_execute |
execute:skills |
Execute a skill with provided input and return the execution record |
omniroute_skills_executions |
read:skills |
List recent skill execution history |
Notion Context Source (6)
Defined in open-sse/mcp-server/tools/notionTools.ts. Token stored in key_value table via src/lib/db/notion.ts. REST client in src/lib/notion/api.ts. Settings API in src/app/api/settings/notion/route.ts. Dashboard UI in src/app/(dashboard)/dashboard/endpoint/components/NotionSourceCard.tsx.
Configure your Notion integration token from the Context Sources tab in the Endpoint dashboard, or via the REST API:
# Set token
curl -X POST http://localhost:20128/api/settings/notion \
-H "Content-Type: application/json" \
-d '{"token": "ntn_..."}'
# Check status
curl http://localhost:20128/api/settings/notion
# Disconnect
curl -X DELETE http://localhost:20128/api/settings/notion
| Tool | Scopes | Description |
|---|---|---|
omniroute_notion_search |
read:notion |
Full-text search across all pages and databases |
omniroute_notion_list_databases |
read:notion |
List all accessible databases with schema metadata |
omniroute_notion_get_database |
read:notion |
Get database schema by ID |
omniroute_notion_query_database |
read:notion |
Query a database with filters, sorts, and pagination |
omniroute_notion_read |
read:notion |
Read a page or block by ID with its content |
omniroute_notion_append_blocks |
write:notion |
Append children blocks to a parent block (max 100 per request) |
Agent Skill Catalog Tools (3)
Defined in open-sse/mcp-server/tools/agentSkillTools.ts. Backed by src/lib/agentSkills/catalog. These tools expose the 42-entry Agent Skills documentation catalog to MCP clients and external agents. Scope: read:catalog.
| Tool | Scopes | Description |
|---|---|---|
omniroute_agent_skills_list |
read:catalog |
List all 42 agent skills with optional category (api|cli) and area filters; returns metadata + coverage |
omniroute_agent_skills_get |
read:catalog |
Get full metadata + SKILL.md content for a single skill by canonical id |
omniroute_agent_skills_coverage |
read:catalog |
Coverage stats: how many of the 22 API and 20 CLI skills have SKILL.md files on the filesystem vs catalog totals |
See AGENT-SKILLS.md for the full catalog and how external agents consume it.
Related Frameworks (v3.8.0)
The MCP tool inventory above (87 tools = 33 core + 3 memory + 4 skills + 3 agent-skills + 8 gamification + 8 plugins + 6 notion + 22 obsidian) is intentionally scoped to runtime routing/cache/compression/memory/skills/proxy/context-source operations. Two adjacent frameworks ship alongside the MCP server in v3.8.0 and are documented separately:
Cloud Agents
Cloud Agents are out-of-process AI coding agents (codex-cloud, devin, jules) wired into
OmniRoute through the same connection model used for LLM providers. They are exposed via
their own REST surface (/api/v1/agents/*) and are not part of the MCP tool catalog
— calling a Cloud Agent does not consume an MCP scope.
- Implementation:
src/lib/cloudAgent/(registry.ts,agents/codex-cloud.ts,agents/devin.ts,agents/jules.ts). - Lifecycle:
createTask,getStatus,approvePlan,sendMessage,listSources. - Documentation: docs/frameworks/CLOUD_AGENT.md.
Guardrails
Guardrails are pre/post-execution filters (vision-bridge, pii-masker, prompt-injection) applied inside the chat pipeline. They run before the MCP tool/route layer is reached and emit structured violations to the audit pipeline; they are not invoked as MCP tools.
- Implementation:
src/lib/guardrails/. - Documentation: docs/security/GUARDRAILS.md.
When debugging an MCP call that appears blocked, check both the MCP audit log
(scope_denied:* entries) and the guardrails audit trail — a request may be rejected by
a guardrail before it ever reaches the MCP scope enforcement layer.
REST API Endpoints
| Endpoint | Method | Description | Auth |
|---|---|---|---|
/api/mcp/status |
GET |
Server status: heartbeat, HTTP transport state, audit activity summary | Management (session/admin) |
/api/mcp/tools |
GET |
Tool catalog (name, description, scopes, phase, source endpoints) | Management |
/api/mcp/sse |
GET / POST |
SSE transport endpoint (gated by mcpEnabled + mcpTransport === "sse") |
API key + scopes |
/api/mcp/stream |
POST/GET/DELETE |
Streamable HTTP transport (uses mcp-session-id header; DELETE ends the session) |
API key + scopes |
/api/mcp/audit |
GET |
Audit log entries from mcp_tool_audit (filters: limit, offset, tool, success, apiKeyId) |
Management |
/api/mcp/audit/stats |
GET |
Aggregated audit stats (totalCalls, successRate, avgDurationMs, top tools) |
Management |
Source files: src/app/api/mcp/{status,tools,sse,stream,audit,audit/stats}/route.ts.
Both SSE and Streamable HTTP transports are blocked until the MCP server is enabled in Settings (mcpEnabled) and the appropriate mcpTransport is selected. If the wrong transport is configured the route returns HTTP 400 with a hint to switch settings.
Authentication & Scopes
MCP tools are authenticated through API key scopes. Scope enforcement is centralized in
open-sse/mcp-server/scopeEnforcement.ts. Each tool requires specific scopes:
| Scope | Tools |
|---|---|
read:health |
get_health, get_provider_metrics, simulate_route, explain_route, best_combo_for_task, db_health_check |
read:combos |
list_combos, get_combo_metrics, simulate_route, best_combo_for_task, test_combo |
write:combos |
switch_combo, set_routing_strategy |
read:quota |
check_quota |
read:usage |
cost_report, get_session_snapshot, explain_route |
read:models |
list_models_catalog |
execute:completions |
route_request, test_combo |
execute:search |
web_search |
write:budget |
set_budget_guard |
write:resilience |
set_resilience_profile, db_health_check |
pricing:write |
sync_pricing |
read:cache |
cache_stats |
write:cache |
cache_flush |
read:compression |
compression_status, list_compression_combos, compression_combo_stats |
write:compression |
compression_configure, set_compression_engine |
read:proxies |
oneproxy_fetch, oneproxy_rotate, oneproxy_stats |
read:notion |
notion_search, notion_list_databases, notion_get_database, notion_query_database, notion_read |
write:notion |
notion_append_blocks |
read:memory |
memory_search |
write:memory |
memory_add, memory_clear |
read:skills |
skills_list, skills_executions |
write:skills |
skills_enable |
execute:skills |
skills_execute |
read:catalog |
agent_skills_list, agent_skills_get, agent_skills_coverage |
Wildcard scopes are supported: read:* grants all read-scopes, * grants full access.
Environment Variables
| Variable | Default | Purpose |
|---|---|---|
OMNIROUTE_BASE_URL |
http://localhost:20128 |
Base URL the MCP server uses when calling OmniRoute internal APIs |
OMNIROUTE_API_KEY |
(empty) | API key forwarded as Authorization: Bearer to internal API calls |
OMNIROUTE_MCP_ENFORCE_SCOPES |
false (only "true" enables it) |
When enabled, missing scopes deny tool calls and log scope_denied:<reason> in audit log |
OMNIROUTE_MCP_SCOPES |
(empty) | Comma-separated allowlist of scopes considered "available" by default (used when caller does not provide its own scopes) |
OMNIROUTE_MCP_COMPRESS_DESCRIPTIONS |
(unset = on) | When set to 0/false/off/no, disables MCP description compression at registration time |
OMNIROUTE_MCP_DESCRIPTION_COMPRESSION |
(unset = on) | Alternate alias for the same toggle as above |
DATA_DIR |
~/.omniroute |
Heartbeat file is written to ${DATA_DIR}/runtime/mcp-heartbeat.json |
Description Compression
MCP tool, prompt, and resource registries can compress descriptions at registration/list time to reduce the metadata footprint exposed to clients (and therefore the prompt context cost). The implementation lives in open-sse/mcp-server/descriptionCompressor.ts and is wired into the MCP server via compressMcpRegistryMetadata inside createMcpServer().
- Compression runs over the description text using the Caveman ruleset (
getRulesForContext("all", "full")) with preserved-block extraction (code spans, fenced blocks, etc.) so structural content is not altered. - Toggle per-deployment via the
compression.mcpDescriptionCompressionEnabledvalue in thekey_valuesettings table (default: enabled) — exposed in the UI as Analytics → MCP description compression. - Toggle process-wide via either
OMNIROUTE_MCP_COMPRESS_DESCRIPTIONS=falseorOMNIROUTE_MCP_DESCRIPTION_COMPRESSION=false. - Realtime stats are surfaced via
omniroute_compression_statusunderanalytics.mcpDescriptionCompressionand taggedsource: "mcp_metadata_estimate"to disambiguate from real provider usage receipts.
Runtime Heartbeat
The stdio transport persists liveness to ${DATA_DIR}/runtime/mcp-heartbeat.json every 5 seconds. The dashboard (/api/mcp/status) reads this file plus PID liveness to derive online. HTTP transports report state from in-process getMcpHttpStatus() instead (no file write).
The heartbeat snapshot contains:
{
"pid": 12345,
"startedAt": "2026-05-13T12:34:56.000Z",
"lastHeartbeatAt": "2026-05-13T12:35:01.000Z",
"version": "1.8.1",
"transport": "stdio",
"scopesEnforced": false,
"allowedScopes": [],
"toolCount": 43
}
Audit Logging
Every tool call is logged to the SQLite mcp_tool_audit table by open-sse/mcp-server/audit.ts:
- Tool name, arguments (hashed/truncated as per per-tool
auditLevel), result - Duration in ms, success/failure flag, error message (when applicable)
- API key hash, timestamp
- Scope denials are logged as
scope_denied:<reason>with the missing scope list
Use the dashboard or the /api/mcp/audit and /api/mcp/audit/stats REST endpoints to inspect recent calls.
Files
| File | Purpose |
|---|---|
open-sse/mcp-server/server.ts |
MCP server factory, stdio entry point, scoped tool registrations |
open-sse/mcp-server/httpTransport.ts |
SSE + Streamable HTTP transport (session management) |
open-sse/mcp-server/scopeEnforcement.ts |
Tool scope evaluation and caller resolution |
open-sse/mcp-server/audit.ts |
Tool call audit logging (mcp_tool_audit) |
open-sse/mcp-server/runtimeHeartbeat.ts |
stdio heartbeat writer (mcp-heartbeat.json) |
open-sse/mcp-server/descriptionCompressor.ts |
Description compression for tool / prompt / resource registries |
open-sse/mcp-server/schemas/tools.ts |
Zod schemas + tool registry (MCP_TOOLS, 30 entries) |
open-sse/mcp-server/tools/advancedTools.ts |
Phase 2 + cache + 1proxy tool handlers |
open-sse/mcp-server/tools/compressionTools.ts |
Compression tool handlers |
open-sse/mcp-server/tools/memoryTools.ts |
Memory tool definitions (3 tools) |
open-sse/mcp-server/tools/skillTools.ts |
Skill tool definitions (4 tools) |
open-sse/mcp-server/tools/notionTools.ts |
Notion context source tool definitions (6 tools) |
open-sse/mcp-server/tools/gamificationTools.ts |
Gamification tool definitions (8 tools) |
open-sse/mcp-server/tools/pluginTools.ts |
Plugin registration and management tools (8 tools) |
src/app/api/mcp/status/route.ts |
/api/mcp/status endpoint |
src/app/api/mcp/tools/route.ts |
/api/mcp/tools endpoint |
src/app/api/mcp/sse/route.ts |
/api/mcp/sse SSE transport route |
src/app/api/mcp/stream/route.ts |
/api/mcp/stream Streamable HTTP transport route |
src/app/api/mcp/audit/route.ts |
/api/mcp/audit audit log query |
src/app/api/mcp/audit/stats/route.ts |
/api/mcp/audit/stats aggregated audit metrics |
src/lib/notion/api.ts |
Notion REST API client (retry, timeout, error classification) |
src/lib/db/notion.ts |
Notion token persistence (key_value table) |
src/app/api/settings/notion/route.ts |
Notion settings API (GET/POST/DELETE) |
src/app/(dashboard)/dashboard/endpoint/components/NotionSourceCard.tsx |
Notion token management UI |
tests/unit/notion-api.test.ts |
Notion API client tests (7) |
tests/unit/notion-tools.test.ts |
Notion tools scope enforcement tests (10) |
tests/unit/db/notion.test.mjs |
Notion DB module tests (3) |