Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
208f76b
db/migrations: defer dependent FTS migration on unsupported drivers
HouMinXi Sep 15, 2026
7c04c5b
tests/cleanup: isolate database fixtures from file logging
HouMinXi Sep 15, 2026
af0e15e
db/health: bound snapshot scans and isolate native diagnostics
HouMinXi Sep 15, 2026
1c136e5
db/health: preserve safe teardown in production-like tests
HouMinXi Sep 15, 2026
fbc5b67
changelog: record the db health resource bounds fix
HouMinXi Sep 15, 2026
297dc93
merge: resolve release/v3.8.51 tip into rework/13717
diegosouzapw Sep 16, 2026
f3580de
feat(flags): add DB_HEALTHCHECK_STARTUP_DEFERRED_ENABLED (default off)
diegosouzapw Sep 16, 2026
d1851da
fix(db): keep startup health-check repair best-effort on a failed bac…
diegosouzapw Sep 16, 2026
2a8feb0
fix(security): classify /api/db/health as loopback-only (HR #15/#17)
HouMinXi Sep 16, 2026
4e1ccb9
Merge remote-tracking branch 'origin/release/v3.8.51' into HEAD
diegosouzapw Sep 16, 2026
7ca6479
chore(ci): clear the last two release/v3.8.51 base-reds (agent-skills…
diegosouzapw Sep 16, 2026
5574eab
fix(api): restore function_call name on non-streaming /v1/responses (…
diegosouzapw Sep 16, 2026
4de7197
fix(security): random per-process self-loop admission bearer (#13679)…
diegosouzapw Sep 16, 2026
c06ac9a
fix(security): remove literal secrets from podman manifest, block CHA…
diegosouzapw Sep 16, 2026
8e3d06b
fix(security): isolate CDP proxy network + auth gate (#13679 PR F) (#…
diegosouzapw Sep 16, 2026
3fd2440
fix(sse): stop re-prepending Kiro tool docs onto every subsequent tur…
diegosouzapw Sep 16, 2026
771c4ce
fix(providers): echo reasoning_content for bai DeepSeek thinking-mode…
diegosouzapw Sep 16, 2026
9e53fad
fix(sse): recognize bare delta.reasoning in combo streaming quality-g…
diegosouzapw Sep 16, 2026
b33c00b
fix(db): cap per-request batch sweep and guard shared files (#13680, …
diegosouzapw Sep 16, 2026
79c4197
fix(security): reject unverifiable X-Cloud-Sig in cloud-sync HMAC che…
diegosouzapw Sep 16, 2026
d7d518a
fix(api): record audio transcription/translation/speech requests in c…
diegosouzapw Sep 16, 2026
61d6152
fix(usage): thread real error/exit-code through callLogs worker failO…
diegosouzapw Sep 16, 2026
0ba661e
fix(providers): surface real Antigravity upstream error detail (#1359…
diegosouzapw Sep 16, 2026
675ab18
fix(api): persist hideAutoCombos/hideNoThinkVariants in settings sche…
diegosouzapw Sep 16, 2026
68927fc
fix(providers): MiniMax-M3 reasoning leaks into delta.content instead…
diegosouzapw Sep 16, 2026
2bbe6e5
fix(sse): stop unhydrated compatible connections routing to the real …
diegosouzapw Sep 16, 2026
2e2dff7
fix(cli): redraw Windows tray icon with dark outline + ship icon.ico …
diegosouzapw Sep 16, 2026
5d1f468
fix(sse): fail closed on background token-refresh for dead proxy pool…
diegosouzapw Sep 16, 2026
edeb76b
fix(sse): stop PII sanitizer splicing OpenRouter metadata into conten…
diegosouzapw Sep 16, 2026
ad4d286
fix(sse): forward cache-creation tokens through the responses usage h…
diegosouzapw Sep 16, 2026
842c32f
fix(compression): keep tool messages through lite redundant-remove (#…
diegosouzapw Sep 16, 2026
d2fadb0
fix(db): reconcile INCREMENTAL auto_vacuum drift via the vacuum sched…
diegosouzapw Sep 16, 2026
57f41f5
fix(sse): frame post-keepalive /v1/responses stream errors with a typ…
diegosouzapw Sep 16, 2026
f5501cf
fix(providers): gemini-web no longer drops system instructions or the…
diegosouzapw Sep 16, 2026
05b44fa
fix(db): stop backoff-reset from busting the model catalog cache (#13…
diegosouzapw Sep 16, 2026
94220af
fix(providers): stop zed-hosted claude-haiku-4-5 thinking from inflat…
diegosouzapw Sep 16, 2026
0459c6d
fix(cli): surface fatal [STARTUP] boot diagnostics without --log (#13…
diegosouzapw Sep 16, 2026
74e44d7
fix(db): defer process.exit(0) by a macrotask on graceful shutdown (#…
diegosouzapw Sep 16, 2026
0dbd7f4
fix(sse): classify missing Chromium as a Z.ai host/config cooldown (#…
diegosouzapw Sep 16, 2026
aeba6b1
fix(routing): publish dashboard events from the round-robin combo loo…
diegosouzapw Sep 16, 2026
9342e4b
fix(sse): accept Responses API custom tool_choice (#13122) (#13775)
diegosouzapw Sep 16, 2026
da58ce5
fix(skills): repair nested malformed schemas in injected skill tools …
diegosouzapw Sep 16, 2026
9578eeb
fix(sse): widen max_tokens/tool_use exemption to lone empty text bloc…
diegosouzapw Sep 16, 2026
694c1b7
fix(cli): add POST /api/mcp/restart and mcp enable/disable subcommand…
diegosouzapw Sep 16, 2026
8f6205e
fix(providers): backfill combo context limit from snapshot after cold…
diegosouzapw Sep 16, 2026
5ff85c6
fix(oauth): fall back to public Code Suggestions on any GitLab Duo di…
diegosouzapw Sep 16, 2026
8939ccb
fix(sse): dynamic-specifier require for wreq-js in Codex WS transport…
diegosouzapw Sep 16, 2026
47032e7
fix(providers): correct Magnific key validation probe path (#12927) (…
diegosouzapw Sep 16, 2026
19b9d05
fix(providers): xAI translators drop legacy function_call and zero to…
diegosouzapw Sep 16, 2026
cdcde97
feat(providers): add Agnes AI (China) as agnes-cn on api.agnes-ai.cn …
HouMinXi Sep 16, 2026
54f19c7
feat(providers): fetch live xAI catalog for xai-oauth (#13518)
HouMinXi Sep 16, 2026
b4f51b2
fix(providers): restore grok-4.6/4.5 default reasoning effort (#13628)
HouMinXi Sep 16, 2026
0cc0169
fix(db): give conversation_turn_nodes its own 1-day retention (#13344)
HouMinXi Sep 16, 2026
ec98525
fix(tests): list dist/httpClientAbortGuard.mjs in the pack-artifact p…
diegosouzapw Sep 16, 2026
5faf44f
fix(docs): restore the env/docs contract broken by the #13679 vars (#…
diegosouzapw Sep 16, 2026
1cb4c82
fix(ci): bound the forgotten-sibling report so an advisory step stops…
diegosouzapw Sep 16, 2026
7f496c7
fix(ci): drain three base reds blocking every PR — generated SKILL.md…
diegosouzapw Sep 16, 2026
3c9d90a
fix(authz): carry the contributor's /api/db/health local-only classif…
diegosouzapw Sep 16, 2026
0f554b2
Merge the contributor's rebased head into the maintainer rework of #1…
diegosouzapw Sep 16, 2026
060f331
fix(i18n): pt-BR review pass over the retranslated leaves (172 correc…
diegosouzapw Sep 16, 2026
9e36dde
feat(i18n): retranslate-site rewrites the verbatim-English leaves of …
diegosouzapw Sep 16, 2026
9d92844
fix(cli): translate the CLI for every locale (38 catalogs were nearly…
diegosouzapw Sep 16, 2026
9e800b7
Merge release/v3.8.51 into #13717
diegosouzapw Sep 16, 2026
e402716
chore: reconcile #13717 with the release tip (flag counts, stale supp…
diegosouzapw Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 14 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2748,6 +2748,13 @@ APP_LOG_TO_FILE=true
# tokens (accessToken / refreshToken / providerSpecificData). Default OFF —
# only non-credential metadata is synced. See docs/security/SOCKET_DEV_FINDINGS.md §5.
# OMNIROUTE_CLOUD_SYNC_SECRETS=false
#
# Set to "true" to reject an UNSIGNED Cloud sync response when no local secret
# is configured (#13679). Default OFF keeps v3.8.x back-compat for peers that
# have not rotated in a shared secret yet; v3.9 flips the default to enforced.
# A signature that IS present is always verified, and always rejected when
# OMNIROUTE_CLOUD_SYNC_SECRET is unset, regardless of this flag.
# OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE=false

# ─── Zed import legacy compat (v3.8.6) ──────────────────────────────────────
# Set to "true" to fall back to the v3.8.5 one-step "import everything from
Expand Down Expand Up @@ -3043,6 +3050,13 @@ QUOTA_STORE_DRIVER=sqlite
# CHATGPT_WEB_CODEX_CHROME_PATH=/usr/bin/chromium
# CHROME_PATH=/usr/bin/chromium
# CHATGPT_WEB_CODEX_CDP_URL=http://chatgpt-web-codex-browser:9223
# CDP_PROXY_TOKEN required by docker/chatgpt-web-codex-browser/cdp-proxy.mjs (#13679):
# when set, every request to the CDP proxy sidecar must present it as an
# `X-Omni-Cdp-Token` header. Left unset, the proxy keeps forwarding requests
# unauthenticated (network isolation via docker-compose.yml's dedicated
# `chatgpt-web-codex-net` is the default mitigation). Generate with:
# `openssl rand -hex 32`
# CDP_PROXY_TOKEN=
# CHATGPT_WEB_CODEX_TUNNEL_ID=tunnel_0123456789abcdef0123456789abcdef
# CHATGPT_WEB_CODEX_RUNTIME_KEY=
# CHATGPT_WEB_CODEX_CONNECTOR_NAME=OmniRoute Codex v2
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,11 @@ jobs:
# in eight. Fix is `sync-ui-keys --locale=<codes> --translate-markers`.
- name: i18n key completeness (every locale carries every en.json key)
run: node scripts/i18n/check-key-completeness.mjs
# Same gate for the CLI catalogs (bin/cli/locales). check:cli-i18n only compares
# pt-BR / zh-CN / zh-TW; 38 locales shipped with 1–24 of 830 keys for months
# (audit 2026-09-16) and the CLI silently fell back to English for them.
- name: i18n key completeness (CLI catalogs)
run: node scripts/i18n/check-key-completeness.mjs --catalog=cli

# #8038: cheap glossary/protected-terms consistency gate —
# complements i18n-ui-coverage (key parity) and the ICU `i18n` job below
Expand Down
42 changes: 0 additions & 42 deletions .github/workflows/release-acceptance.yml

This file was deleted.

2 changes: 1 addition & 1 deletion @omniroute/opencode-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/telemetry.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts tests/naming.test.ts tests/free-budget-magnitude.test.ts tests/models-fetcher.test.ts",
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/telemetry.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts tests/naming.test.ts tests/free-budget-magnitude.test.ts tests/models-fetcher.test.ts tests/issue-13000-cold-start-combo-limit.test.ts",
"prepublishOnly": "npm run clean && npm run build && npm test"
},
"keywords": [
Expand Down
42 changes: 40 additions & 2 deletions @omniroute/opencode-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4631,9 +4631,21 @@ export function buildStaticProviderEntry(
.map((m) => m.max_output_tokens)
.filter((v): v is number => typeof v === "number" && v > 0);

if (contextValues.length > 0 && outputValues.length > 0) {
// Prefer the server-computed aggregate (accounts for explicit
// context_length overrides and members outside memberEntries, e.g.
// not yet resolved in /v1/models) over the raw Math.min(member)
// lower bound. Mirrors mapComboToModelV2's limit.context logic
// (#13000) so the static catalog and the dynamic hook agree.
const preferredContext =
typeof combo.computed_context_length === "number" && combo.computed_context_length > 0
? combo.computed_context_length
: contextValues.length > 0
? Math.min(...contextValues)
: undefined;

if (preferredContext !== undefined && outputValues.length > 0) {
entry.limit = {
context: Math.min(...contextValues),
context: preferredContext,
output: Math.min(...outputValues),
};
}
Expand Down Expand Up @@ -5511,6 +5523,32 @@ export function createOmniRouteConfigHook(

const modelsFetchOk = !modelsFetchThrew && localRawModels.length > 0;

// Snapshot backfill for computed_context_length: a live /api/combos
// response can come back without this field (server hasn't finished
// recomputing it yet, e.g. just after a restart) even though the
// combo's members and identity are otherwise unchanged. When that
// happens, prefer the last-known-good value from the warm disk
// snapshot over the Math.min(member) fallback in
// mapComboToModelV2() — never overwrite any other combo field
// (models/name/etc.) with stale data, only this one derived number.
if (warmSnapshot) {
const snapshotComboById = new Map(warmSnapshot.rawCombos.map((c) => [c.id, c]));
for (const combo of localRawCombos) {
const hasLive =
typeof combo.computed_context_length === "number" &&
combo.computed_context_length > 0;
if (hasLive) continue;
const stale = snapshotComboById.get(combo.id);
if (
stale &&
typeof stale.computed_context_length === "number" &&
stale.computed_context_length > 0
) {
combo.computed_context_length = stale.computed_context_length;
}
}
}

// Disk-cache fallback (cold first run, no warm snapshot): when the
// live fetch returned no models AND features.diskCache !== false,
// hydrate from the last-known-good snapshot so OC still surfaces a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/**
* Repro for #13000: combo context limits fall back to Math.min(member)
* instead of using computed_context_length after cold start — no disk
* snapshot fallback.
*
* Scenario (mirrors the report): a warm disk snapshot holds the combo with
* its correct server-computed `computed_context_length` (245000, from all 6
* members). After a restart, the live refresh's combos fetch returns the
* SAME combo but without `computed_context_length` (e.g. the value hasn't
* propagated yet), and the live models fetch only resolves 2 of the 6
* members (the rest not yet in /v1/models). The background refresh then
* republishes the provider block built from this degraded live data,
* downgrading a previously-known-good 245000 limit to Math.min(163840,
* 1_000_000) = 163840 — exactly the member-minimum described in the issue.
*/

import test from "node:test";
import assert from "node:assert/strict";
import type { Config } from "@opencode-ai/plugin";

import {
createOmniRouteConfigHook,
_resetInflightRefresh,
type OmniRouteAutoCombosFetcher,
type OmniRouteCombosFetcher,
type OmniRouteCompressionMetaFetcher,
type OmniRouteEnrichmentFetcher,
type OmniRouteFetchCache,
type OmniRouteModelsFetcher,
type OmniRouteProvidersFetcher,
type OmniRouteRawCombo,
type OmniRouteRawModelEntry,
type OmniRouteReadAuthJson,
type OmniRouteStaticProviderEntry,
type OmniRouteDiskSnapshotReader,
type OmniRouteDiskSnapshotWriter,
} from "../src/index.js";

test.beforeEach(() => {
_resetInflightRefresh();
});

function stubReadAuthJson(value: Record<string, unknown>): OmniRouteReadAuthJson {
return async () => value as never;
}

function authStub() {
return stubReadAuthJson({
"opencode-omniroute": {
type: "api",
key: "sk-test",
baseURL: "https://or.example.com/v1",
},
});
}

function makeInput(): Config {
return { provider: {} } as unknown as Config;
}

// The two members resolvable in the degraded live /v1/models response.
const MEMBER_DEEPSEEK: OmniRouteRawModelEntry = {
id: "deepseek-v4-pro",
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
context_length: 163_840,
max_output_tokens: 64_000,
input_modalities: ["text"],
output_modalities: ["text"],
};

const MEMBER_GLM: OmniRouteRawModelEntry = {
id: "glm-5.2",
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
context_length: 1_000_000,
max_output_tokens: 16_384,
input_modalities: ["text"],
output_modalities: ["text"],
};

// The other member that IS present once the server is fully warm.
const MEMBER_GLM_53_HIGH: OmniRouteRawModelEntry = {
id: "GLM-5.3-high",
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
context_length: 245_000,
max_output_tokens: 128_000,
input_modalities: ["text"],
output_modalities: ["text"],
};

const COMBO_MODELS: OmniRouteRawCombo["models"] = [
{ kind: "model", model: "deepseek-v4-pro", weight: 25 },
{ kind: "model", model: "glm-5.2", weight: 25 },
{ kind: "model", model: "GLM-5.3-high", weight: 50 },
];

test("issue #13000: warm combo limit (245000) survives a degraded post-restart refresh instead of downgrading to Math.min(member)", async () => {
const warmSnapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> = {
rawModels: [MEMBER_DEEPSEEK, MEMBER_GLM, MEMBER_GLM_53_HIGH],
rawCombos: [
{
id: "orchestrator",
name: "orchestrator",
models: COMBO_MODELS,
computed_context_length: 245_000,
},
],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
};

const fetcher: OmniRouteModelsFetcher = async () => [MEMBER_DEEPSEEK, MEMBER_GLM];
const combosFetcher: OmniRouteCombosFetcher = async () => [
{
id: "orchestrator",
name: "orchestrator",
models: COMBO_MODELS,
// computed_context_length intentionally omitted.
},
];
const autoCombosFetcher: OmniRouteAutoCombosFetcher = async () => [];
const enrichmentFetcher: OmniRouteEnrichmentFetcher = async () => new Map();
const compressionMetaFetcher: OmniRouteCompressionMetaFetcher = async () => [];
const providersFetcher: OmniRouteProvidersFetcher = async () => [];

const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => warmSnapshot;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};

const sharedCache: OmniRouteFetchCache = new Map();

const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", modelCacheTtl: 60_000 },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
cache: sharedCache,
}
);

const input = makeInput();
await hook(input);

// Let the detached background refresh (degraded live data) complete and
// republish the block.
await new Promise((r) => setTimeout(r, 100));

const entryAfter = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
const comboModelAfter = entryAfter.models["orchestrator"];
assert.ok(comboModelAfter, "combo model still published after refresh");

assert.equal(
comboModelAfter.limit.context,
245_000,
`expected the combo limit to stay at the known-good 245000, but got ${comboModelAfter.limit.context} ` +
`(Math.min(member) fallback — the exact bug described in #13000)`
);
});

test("issue #13000 (control): no warm snapshot exists — Math.min(member) fallback is still used (expected, documented behavior)", async () => {
const fetcher: OmniRouteModelsFetcher = async () => [MEMBER_DEEPSEEK, MEMBER_GLM];
const combosFetcher: OmniRouteCombosFetcher = async () => [
{
id: "orchestrator",
name: "orchestrator",
models: COMBO_MODELS,
// computed_context_length intentionally omitted.
},
];
const autoCombosFetcher: OmniRouteAutoCombosFetcher = async () => [];
const enrichmentFetcher: OmniRouteEnrichmentFetcher = async () => new Map();
const compressionMetaFetcher: OmniRouteCompressionMetaFetcher = async () => [];
const providersFetcher: OmniRouteProvidersFetcher = async () => [];

// No prior snapshot on disk.
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};

const sharedCache: OmniRouteFetchCache = new Map();

const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", modelCacheTtl: 60_000 },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
cache: sharedCache,
}
);

const input = makeInput();
await hook(input);

const entryAfter = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
const comboModelAfter = entryAfter.models["orchestrator"];
assert.ok(comboModelAfter, "combo model published on cold first run");

// No snapshot to backfill from — Math.min(163840, 1_000_000) = 163840.
assert.equal(
comboModelAfter.limit.context,
163_840,
"pure cold start with no snapshot must keep using the Math.min(member) fallback"
);
});
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.

## Project at a Glance

**OmniRoute** — unified AI proxy/router. One endpoint, 358 LLM providers, auto-fallback.
**OmniRoute** — unified AI proxy/router. One endpoint, 359 LLM providers, auto-fallback.

| Layer | Location | Purpose |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -56,7 +56,7 @@ Repository map and Reference Documentation sections below.
| 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 (177 migrations) |
| Database | `src/lib/db/` | SQLite domain modules (176 migrations) |
| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
| MCP Server | `open-sse/mcp-server/` | 110 tools (45 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 |
Expand Down
Loading
Loading