diff --git a/AGENTS.md b/AGENTS.md
index f88c9056a5..feda2fb478 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -48,20 +48,20 @@ Repository map and Reference Documentation sections below.
**OmniRoute** — unified AI proxy/router. One endpoint, 291 LLM providers, auto-fallback.
-| Layer | Location | Purpose |
-| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| API Routes | `src/app/api/v1/` | Next.js App Router — entry points |
-| Handlers | `open-sse/handlers/` | Request processing (chat, embeddings, etc) |
-| Executors | `open-sse/executors/` | Provider-specific HTTP dispatch |
-| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
-| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
-| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
-| Database | `src/lib/db/` | SQLite domain modules (130 migrations) |
-| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
-| MCP Server | `open-sse/mcp-server/` | 105 tools (42 base + memory/skill/agentSkill/pool/notion/obsidian/gamification/plugin modules), 3 transports (stdio / SSE / Streamable HTTP), 31 scopes |
-| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
-| Skills | `src/lib/skills/` | Extensible skill framework |
-| Memory | `src/lib/memory/` | Persistent conversational memory |
+| Layer | Location | Purpose |
+| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| API Routes | `src/app/api/v1/` | Next.js App Router — entry points |
+| Handlers | `open-sse/handlers/` | Request processing (chat, embeddings, etc) |
+| Executors | `open-sse/executors/` | Provider-specific HTTP dispatch |
+| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
+| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
+| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
+| Database | `src/lib/db/` | SQLite domain modules (130 migrations) |
+| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
+| MCP Server | `open-sse/mcp-server/` | 109 tools (44 canonical + memory/skill/GitHub/pool/gamification/plugin/Notion/Obsidian/local-corpus/RTK modules), 3 transports (stdio / SSE / Streamable HTTP), 33 scopes |
+| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
+| Skills | `src/lib/skills/` | Extensible skill framework |
+| Memory | `src/lib/memory/` | Persistent conversational memory |
Monorepo: `src/` (Next.js 16 app), `open-sse/` (streaming engine workspace), `electron/` (desktop app), `tests/`, `bin/` (CLI entry point).
diff --git a/README.md b/README.md
index 352619a7f0..5edc2d69c7 100644
--- a/README.md
+++ b/README.md
@@ -188,7 +188,7 @@ curl http://localhost:20128/v1/chat/completions \
-
+
@@ -439,7 +439,7 @@ All **19** strategies — mix & match per combo step:
-
+
📊 Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)
@@ -723,7 +723,7 @@ Expose OmniRoute over **MCP**, **A2A**, a **REST API**, **webhooks** or a **remo
| Interface | Endpoint / command | Use it for |
| 🧰 MCP (stdio) | omniroute --mcp | Plug into Claude Desktop, Cursor, any MCP client |
- | 🌊 MCP (HTTP) | /api/mcp/stream | Remote MCP — 105 tools, 31 scopes, full audit trail |
+ | 🌊 MCP (HTTP) | /api/mcp/stream | Remote MCP — 109 tools, 33 scopes, full audit trail |
| 📡 MCP (SSE) | /api/mcp/sse | Streaming MCP transport |
| 🤝 A2A | /.well-known/agent.json | Agent-to-agent, JSON-RPC 2.0 + SSE, 6 skills |
| 🌐 REST API | /v1/* | OpenAI-compatible — chat, embeddings, images, audio, OCR |
@@ -1110,7 +1110,7 @@ same process on one port, so there is no separate CLI-only package today.
| Document | Description |
| API Reference | All endpoints with examples |
| OpenAPI Spec | OpenAPI 3.0 specification |
- | MCP Server | 104 MCP tools, IDE configs, Python/TS/Go clients |
+ | MCP Server | 109 MCP tools, IDE configs, Python/TS/Go clients |
| MCP Server Guide | MCP installation, transports, and tool reference |
| A2A Server | JSON-RPC 2.0 protocol, skills, streaming, task mgmt |
| A2A Server Guide | A2A agent card, tasks, skills, and streaming |
diff --git a/docs/architecture/QUALITY_GATES.md b/docs/architecture/QUALITY_GATES.md
index 06c55e99cf..b7e279525f 100644
--- a/docs/architecture/QUALITY_GATES.md
+++ b/docs/architecture/QUALITY_GATES.md
@@ -186,10 +186,10 @@ Runs on pull requests only.
Runs after `build`. Blocks merge on failure.
-| Suite | Validates | Blocking |
-| ---------------- | ------------------------------------------------------- | -------------------------------------------------------------------------- |
-| `test:vitest` | MCP server (94 tools), autoCombo, cache — vitest runner | Yes |
-| `test:vitest:ui` | UI component tests — vitest runner | **Advisory** (`continue-on-error: true`) — failing until Fase 6A UI triage |
+| Suite | Validates | Blocking |
+| ---------------- | -------------------------------------------------------- | -------------------------------------------------------------------------- |
+| `test:vitest` | MCP server (109 tools), autoCombo, cache — vitest runner | Yes |
+| `test:vitest:ui` | UI component tests — vitest runner | **Advisory** (`continue-on-error: true`) — failing until Fase 6A UI triage |
### Nightly workflows (scheduled, advisory)
diff --git a/docs/frameworks/MCP-SERVER.md b/docs/frameworks/MCP-SERVER.md
index 710315cf49..7a8e40db39 100644
--- a/docs/frameworks/MCP-SERVER.md
+++ b/docs/frameworks/MCP-SERVER.md
@@ -1,14 +1,14 @@
---
title: "OmniRoute MCP Server Documentation"
-version: 3.8.40
-lastUpdated: 2026-06-28
+version: 3.8.50
+lastUpdated: 2026-08-08
---
# OmniRoute MCP Server Documentation
-> Model Context Protocol server with 105 tools across routing, cache, compression, memory, skills, proxy, pool, and context source operations.
+> Model Context Protocol server with 109 tools across routing, cache, compression, memory, skills, proxy, pool, Radar, and context source operations.
>
-> Source of truth: `open-sse/mcp-server/server.ts` computes **104 unique tools** with `countUniqueMcpTools()`: 42 canonical definitions (including the six CCR lifecycle tools and the agent-skills trio), plus memory (3), skills (4), GitHub skills (3), pool (6), gamification (8), plugins (8), Notion (6), Obsidian (22), and two RTK-only compression tools.
+> Source of truth: `open-sse/mcp-server/server.ts` computes **109 unique tools** with `countUniqueMcpTools()`: 44 canonical definitions (including the six CCR lifecycle tools, the agent-skills trio, and `omniroute_radar_catalog`), plus memory (3), skills (4), GitHub skills (3), pool (6), gamification (8), plugins (8), Notion (6), Obsidian (22), local corpus (3), and two RTK-only compression tools.
## Installation
@@ -64,7 +64,7 @@ Cursor, Cline, and compatible MCP client setup.
---
-## Essential Tools (8) — Phase 1
+## Essential Tools (13) — Phase 1
| Tool | Scopes | Description |
| :------------------------------ | :-------------------- | :------------------------------------------------------------ |
@@ -72,16 +72,15 @@ Cursor, Cline, and compatible MCP client setup.
| `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_create_combo` | `write:combos` | Create a validated combo through the existing combo API |
| `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 |
+| `omniroute_radar_catalog` | `read:radar` | Local signed Radar catalog; optional provider/family filters |
+| `omniroute_tool_search` | `read:tools` | Discover tools from the registered MCP catalog |
+| `omniroute_web_search` | `execute:search` | Web search through the configured search providers |
+| `omniroute_web_fetch` | `execute:search` | Fetch web content through the configured fetch providers |
## Advanced Tools (11) — Phase 2
@@ -227,7 +226,7 @@ See [AGENT-SKILLS.md](./AGENT-SKILLS.md) for the full catalog and how external a
## Related Frameworks (v3.8.0)
-The MCP tool inventory above (104 unique tools, computed by `countUniqueMcpTools()`) is intentionally
+The MCP tool inventory above (109 unique tools, computed by `countUniqueMcpTools()`) 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:
@@ -369,7 +368,7 @@ MCP tool, prompt, and resource registries can compress descriptions at registrat
Description compression shrinks each tool's metadata; **tool-cardinality reduction** goes one step further by reducing _how many_ tools are announced at all. Advertising fewer tools in the `tools/list` manifest cuts the per-request token cost the client's model pays for the tool catalog ("layer 5" compression). The implementation is a pure, stateless filter in `open-sse/mcp-server/toolCardinality.ts` (`reduceToolManifest`), wired into the registration loop in `createMcpServer()` (`open-sse/mcp-server/server.ts`).
-**Opt-in, off by default.** The filter only runs when at least one of two environment variables is set; with neither set, all 105 tools are announced unchanged.
+**Opt-in, off by default.** The filter only runs when at least one of two environment variables is set; with neither set, all 109 tools are announced unchanged.
| Variable | Mode |
| :--------------- | :-------------------------------------------------------------------------------------- |
diff --git a/docs/frameworks/RADAR.md b/docs/frameworks/RADAR.md
index 1d1d1015d7..1e36a4e228 100644
--- a/docs/frameworks/RADAR.md
+++ b/docs/frameworks/RADAR.md
@@ -290,6 +290,26 @@ The dashboard exposes four local actions:
A feed `enabled: false` remains the safety exception: it wins over a stale local
`enabled: true`, keeps the merged entry disabled, and records `disabledBy: "radar"`.
+### Guided combos and MCP access
+
+Confirmed `familyId` values survive the read-time overlay and drive the pure
+`buildRadarComboSuggestions()` module (`src/lib/radar/comboSuggestions.ts`). A family is suggested
+only when at least two distinct providers have active connections and expose the exact curated model
+ID. Disabled models, inactive providers, missing model IDs, singleton families, and ambiguous
+alias/prefix matches fail closed. Suggestions use the existing `priority` strategy, ordering the
+largest recurring monthly budget first; the UI creates them only through `POST /api/combos`.
+
+The guided UI lives at `/dashboard/radar/combos`. It reads only the local
+`GET /api/radar/catalog` and `GET /api/combos/builder/options` endpoints. It never triggers Radar sync,
+reads provider credentials, or writes directly to the combo database.
+
+MCP clients can read the same local projection with `omniroute_radar_catalog` (`read:radar`). The
+optional `provider`, `familyId`, and `enabledOnly` filters are evaluated after one local
+`GET /api/radar/catalog` read. Its closed output includes catalog metadata plus provider/model,
+display name, `familyId`, quota, capabilities, enabled state, origin, and `disabledBy`; setup URLs,
+steps, connections, e-mail addresses, keys, and referral data are never returned. This tool is
+read-only and never invokes `/api/radar/sync`.
+
### Provenance markers
Every merged entry carries an `origin` field the UI renders as a badge:
@@ -303,7 +323,7 @@ Every merged entry carries an `origin` field the UI renders as a badge:
## Local surfaces — never a feed proxy
-Six local endpoints back the UI, all under `src/app/api/radar/`:
+The local Radar route families below back the UI under `src/app/api/radar/`:
| Route | Method | Purpose |
| ------------------------------ | ------ | --------------------------------------------------------------------------------------------------------------------- |
@@ -323,14 +343,14 @@ to the local OmniRoute server. The two modules that touch the Radar service are
always run server-side, never client-side. This keeps the feed URL and any supporter key
out of client-facing network traffic entirely.
-All six endpoints return `404` when `RADAR_ENABLED` is off (see
+All Radar endpoints return `404` when `RADAR_ENABLED` is off (see
[Flag](#flag-radar_enabled-default-off) above), and route error responses through
`buildErrorBody()`/`sanitizeErrorMessage()` per the repo-wide error-sanitization rule
(`docs/security/ERROR_SANITIZATION.md`).
### Authentication
-All six endpoints require authentication via `isAuthenticated()`
+All Radar endpoints require authentication via `isAuthenticated()`
(`src/shared/utils/apiAuth.ts`) — a dashboard session cookie or a management-scoped
API key, the same gate that protects the rest of `/api/settings/*`. The flag-off
`404` check always runs **before** the auth check, so an install with `RADAR_ENABLED`
diff --git a/open-sse/mcp-server/README.md b/open-sse/mcp-server/README.md
index 1ef5f8aa5f..cf3d5ce748 100644
--- a/open-sse/mcp-server/README.md
+++ b/open-sse/mcp-server/README.md
@@ -1,6 +1,6 @@
# OmniRoute MCP Server
-> **Model Context Protocol server** that exposes OmniRoute's gateway intelligence as **104 tools** for AI agents.
+> **Model Context Protocol server** that exposes OmniRoute's gateway intelligence as **109 tools** for AI agents.
>
> **Source of truth for the full tool catalog and REST surface:** [`docs/frameworks/MCP-SERVER.md`](../../docs/frameworks/MCP-SERVER.md). This README focuses on architecture, configuration, and integration examples; the catalog below is a summary subset.
@@ -20,7 +20,7 @@ The MCP Server allows any AI agent (Claude Desktop, Cursor, VS Code Copilot, cus
┌──────────────────────────────────────────────────────────────────┐
│ OmniRoute MCP Server │
│ ┌──────────────┐ ┌─────────────────┐ ┌────────────────────┐ │
-│ │ Scope │ │ 104 MCP Tools │ │ Audit Logger │ │
+│ │ Scope │ │ 109 MCP Tools │ │ Audit Logger │ │
│ │ Enforcement │──│ (core + memory │──│ (SHA-256/SQLite) │ │
│ │ │ │ + skills + …) │ │ │ │
│ └──────────────┘ └────────┬────────┘ └────────────────────┘ │
@@ -120,18 +120,23 @@ omniroute --mcp
## Tool Reference
-### Phase 1: Essential Tools (8)
+### Phase 1: Essential Tools (13)
| # | 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 |
+| 1 | `omniroute_tool_search` | `read:tools` | Discover tools from the registered MCP catalog |
+| 2 | `omniroute_get_health` | `read:health` | Gateway health, uptime, memory, circuit breakers, rate limits, cache stats |
+| 3 | `omniroute_list_combos` | `read:combos` | List all combos (model chains) with strategies and optional metrics |
+| 4 | `omniroute_get_combo_metrics` | `read:combos` | Performance metrics for a specific combo |
+| 5 | `omniroute_switch_combo` | `write:combos` | Activate or deactivate a combo for routing |
+| 6 | `omniroute_create_combo` | `write:combos` | Create a validated combo through the existing combo API |
+| 7 | `omniroute_check_quota` | `read:quota` | Remaining API quota per provider with token health status |
+| 8 | `omniroute_route_request` | `execute:completions` | Send a chat completion through intelligent routing |
+| 9 | `omniroute_cost_report` | `read:usage` | Cost report by period (session/day/week/month) with per-provider breakdown |
+| 10 | `omniroute_list_models_catalog` | `read:models` | List all available models across providers with capabilities and pricing |
+| 11 | `omniroute_radar_catalog` | `read:radar` | Read the local signed Radar catalog with provider/family filters |
+| 12 | `omniroute_web_search` | `execute:search` | Search the web through configured search providers |
+| 13 | `omniroute_web_fetch` | `execute:search` | Fetch web content through configured fetch providers |
### Phase 2: Advanced Tools (8)
diff --git a/scripts/check/check-docs-counts-sync.mjs b/scripts/check/check-docs-counts-sync.mjs
index c0e4cf83f6..01ad1aeef1 100644
--- a/scripts/check/check-docs-counts-sync.mjs
+++ b/scripts/check/check-docs-counts-sync.mjs
@@ -147,9 +147,10 @@ function readCodeFacts() {
'import {pluginTools} from "./open-sse/mcp-server/tools/pluginTools.ts";',
'import {notionTools} from "./open-sse/mcp-server/tools/notionTools.ts";',
'import {obsidianTools} from "./open-sse/mcp-server/tools/obsidianTools.ts";',
+ 'import {localCorpusTools} from "./open-sse/mcp-server/tools/localCorpusTools.ts";',
'import {compressionTools} from "./open-sse/mcp-server/tools/compressionTools.ts";',
"const cols={MCP_TOOLS,memoryTools,skillTools,agentSkillTools,githubSkillTools,poolTools,",
- "gamificationTools,pluginTools,notionTools,obsidianTools,compressionTools};",
+ "gamificationTools,pluginTools,notionTools,obsidianTools,localCorpusTools,compressionTools};",
"const sc=new Set();",
"for(const col of Object.values(cols))for(const t of Object.values(col))",
"for(const x of (t?.scopes||[]))sc.add(x);",
@@ -313,7 +314,7 @@ export function buildChecks() {
// total ("33 tools (25 CLI Code's …)") are not the MCP aggregate
// per-module rows read "… tool definitions (N tools" / "… management tools
// (N tools" — the word tool(s)/definitions sits right before the paren. The
- // aggregate ("MCP Server (104 tools", "all 104 tools") never does.
+ // aggregate ("MCP Server (109 tools", "all 109 tools") never does.
skipBefore: /(tools?|definitions?)\s*\(\s*$/i,
skipAfter: /^\s*\(\d+ CLI/,
},
diff --git a/tests/unit/check-docs-counts-sync.test.ts b/tests/unit/check-docs-counts-sync.test.ts
index 27deae3057..7f7aaaaffd 100644
--- a/tests/unit/check-docs-counts-sync.test.ts
+++ b/tests/unit/check-docs-counts-sync.test.ts
@@ -148,7 +148,7 @@ test("free-tier gate passes when a file carries no headline at all", () => {
// --- Generic numeric-claim gate (engines / MCP tools / scopes / CLI) --------
// Extends the same drift guard to the counts that silently drifted in v3.8.49:
-// 11→12 engines, 94→104 MCP tools, 30→31 scopes, 26→33 CLI tools.
+// 11→12 engines, 94→109 MCP tools, 30→33 scopes, 26→33 CLI tools.
import { makeNumberClaimValidator } from "../../scripts/check/check-docs-counts-sync.mjs";
const makeValidator = makeNumberClaimValidator as (
@@ -157,19 +157,19 @@ const makeValidator = makeNumberClaimValidator as (
) => (content: string) => { ok: boolean; detail: string };
test("MCP-tools gate accepts the aggregate and rejects a stale one", () => {
- const v = makeValidator(104, {
+ const v = makeValidator(109, {
what: "MCP tools",
pattern: /(\d+) tools/gi,
skipBefore: /(tools?|definitions?)\s*\(\s*$/i,
skipAfter: /^\s*\(\d+ CLI/,
});
- assert.equal(v("MCP Server (104 tools)").ok, true);
- assert.equal(v("with 104 tools total").ok, true);
+ assert.equal(v("MCP Server (109 tools)").ok, true);
+ assert.equal(v("with 109 tools total").ok, true);
assert.equal(v("MCP Server (94 tools)").ok, false);
});
test("MCP-tools gate ignores per-module counts and the CLI catalog total", () => {
- const v = makeValidator(104, {
+ const v = makeValidator(109, {
what: "MCP tools",
pattern: /(\d+) tools/gi,
skipBefore: /(tools?|definitions?)\s*\(\s*$/i,