converse-mcp-server 3.7.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/PROVIDERS.md CHANGED
@@ -31,7 +31,7 @@ This guide documents all supported AI providers in the Converse MCP Server and t
31
31
  - `gemini-2.5-pro` (alias: `pro 2.5`) - Deep reasoning with thinking budget (1M context, 65K output)
32
32
  - `gemini-2.5-flash` (alias: `flash`) - Ultra-fast model with thinking budget (1M context, 65K output)
33
33
  - `gemini-2.5-flash-lite` (alias: `flash-lite`) - Lightweight fast model (1M context, 65K output)
34
- - **Note**: The short model name `gemini` (and `gemini:flash` / `gemini:pro`) routes to the **Antigravity CLI** (`agy`, OAuth-based access). For Google API access, use specific model names like `gemini-3.1-pro-preview` or `gemini-2.5-flash` (bare `gemini-pro`/`gemini-flash` also route to the Google API).
34
+ - **Note**: The **Antigravity CLI** provider (`agy`, OAuth-based access) serves `gemini-3.8-flash` and `gemini-3.1-pro-preview` under the same IDs and aliases, and it comes before the Google API in bare-name order. When `agy` is installed, bare `pro`, `gemini-pro`, `flash`, `gemini-3.1-pro-preview` and `gemini-3.8-flash` go to Antigravity first; the bare name `gemini` is the Antigravity namespace. Use `google:<model>` (e.g. `google:pro`, `google:gemini-2.5-flash`) to always use the Google API. Names only the Google API serves (e.g. `gemini-2.5-pro`, `gemini-3.5-flash`) route there directly.
35
35
 
36
36
  ### X.AI (Grok)
37
37
  - **API Key Format**: `xai-...` (starts with `xai-`)
@@ -41,7 +41,7 @@ This guide documents all supported AI providers in the Converse MCP Server and t
41
41
  - `grok-4.5` (default; aliases: `grok`, `grok-4.5-latest`, `grok-build-latest`) - Flagship model with image input, reasoning content, and native web/X search (500K context)
42
42
  - **Reasoning**: `reasoning_effort` maps to Grok's `low`/`medium`/`high`. Grok 4.5 always reasons and cannot be turned off, so `none`/`minimal`/`low` clamp to `low`, `medium` stays `medium`, and `high`/`max` clamp to `high`.
43
43
  - **Web search**: Automatic — native web/X search (Agent Tools) is attached on every Grok 4.5 request; the model decides per-request whether to search, and any citations are returned in metadata.
44
- - **Retired IDs**: Older Grok identifiers (e.g. `grok-4-0709`, `grok-code-fast-1`) still pass through as explicit model strings, but xAI does not surface a retirement error for them — it silently remaps them upstream to a current model (HTTP 200). Use `grok-4.5` for predictable results.
44
+ - **Retired IDs**: Older Grok identifiers (e.g. `grok-4-0709`, `grok-code-fast-1`) are not in the catalog and are rejected as unknown models. Use `grok-4.5`.
45
45
 
46
46
  ### Anthropic (Claude)
47
47
  - **API Key Format**: `sk-ant-...` (starts with `sk-ant-`)
@@ -100,15 +100,20 @@ This guide documents all supported AI providers in the Converse MCP Server and t
100
100
  ### Codex
101
101
  - **API Key Format**: Optional (uses ChatGPT login by default)
102
102
  - **Authentication**: ChatGPT login (system-wide) OR `CODEX_API_KEY`
103
+ - **Availability**: The Codex SDK is installed and either `~/.codex/auth.json` exists (from `codex login`; `$CODEX_HOME/auth.json` when `CODEX_HOME` is set) or `CODEX_API_KEY` is set
103
104
  - **Environment Variables**:
104
105
  - `CODEX_API_KEY` - Optional API key for headless deployments
105
106
  - `CODEX_SANDBOX_MODE` - Filesystem access control (default: read-only)
106
107
  - `CODEX_SKIP_GIT_CHECK` - Skip Git repository validation (default: true)
107
108
  - `CODEX_APPROVAL_POLICY` - Command approval behavior (default: never)
108
- - `CODEX_MODEL` - Underlying model for Codex sessions (default: gpt-6-sol; e.g. gpt-6-luna, gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5)
109
+ - `CODEX_DEFAULT_MODEL` - Model used for bare `codex` and `auto` (default: `gpt-6-sol`; any Codex model ID or alias below). The legacy name `CODEX_MODEL` is honored when `CODEX_DEFAULT_MODEL` is unset.
109
110
  - **Supported Models**:
110
111
  - `codex` - OpenAI Codex agentic coding assistant (GPT-6 Sol by default)
111
- - `codex:<model>` - Same, with an explicit backend: `codex:sol`, `codex:luna`, `codex:astra` (GPT-6), `codex:gpt-5.6-sol`, `codex:terra`, `codex:gpt-5.6-luna`, `codex:gpt-5.5`, `codex:spark`, or any slug the Codex CLI knows
112
+ - `codex:<model>` - Same, with an explicit backend from the Codex catalog:
113
+ - `gpt-6-sol` (aliases: `sol`, `gpt-6`), `gpt-6-luna` (`luna`), `gpt-6-astra` (`astra`)
114
+ - `gpt-5.6-sol` (`gpt-5.6`), `gpt-5.6-terra` (`terra`), `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.3-codex-spark` (`spark`)
115
+ - Names outside this catalog are rejected with suggestions
116
+ - These IDs are also bare names: `gpt-6-astra`, `luna` or `terra` alone go to Codex first and fail over to the OpenAI API (see [Model Routing Logic](#model-routing-logic))
112
117
  - `reasoning_effort` is clamped onto what the backend accepts (Sol/Luna: `none`–`max`; GPT-6 Astra: `low`–`max`, no `none`)
113
118
  - Thread-based sessions with persistent context
114
119
  - Direct filesystem access from working directory
@@ -134,10 +139,13 @@ This guide documents all supported AI providers in the Converse MCP Server and t
134
139
  - Windows (PowerShell): `irm https://antigravity.google/cli/install.ps1 | iex`
135
140
  - macOS/Linux: `curl -fsSL https://antigravity.google/cli/install.sh | bash`
136
141
  2. Authenticate: run `agy` once interactively and complete the Google OAuth login. This also establishes workspace trust for your home directory (the provider spawns each call in a per-call subdirectory under `~/.converse/agy-runs`).
137
- - **Environment Variables**: None (the provider detects the `agy` binary on PATH or at the platform install location)
142
+ - **Availability**: The `agy` binary is found on PATH or at the platform install location
143
+ - **Environment Variables**:
144
+ - `AGY_DEFAULT_MODEL` - Model used for bare `gemini` and `auto` (default: `gemini-3.8-flash`)
145
+ - **Namespaces**: `gemini`, `agy`, `antigravity`, `gemini-cli` (all equivalent, e.g. `agy:pro`)
138
146
  - **Supported Models** (text-only — print mode has no image input channel):
139
- - `gemini` (= `gemini:flash`) - Gemini 3.8 Flash (default)
140
- - `gemini:pro` - Gemini 3.1 Pro
147
+ - `gemini-3.8-flash` (aliases: `flash`, `gemini-3.8`, `flash-3.8`, ...) - Gemini 3.8 Flash (default; `gemini` alone = `gemini:flash`)
148
+ - `gemini-3.1-pro-preview` (aliases: `pro`, `gemini-pro`, `gemini-3.1-pro`, `gemini-3`, ...) - Gemini 3.1 Pro (`gemini:pro`)
141
149
  - `reasoning_effort` selects the variant: `low` → (Low), `medium` → (Medium) for Flash / (High) for Pro, `high`/`max` → (High); unset defaults to (High)
142
150
 
143
151
  **Key Features:**
@@ -184,27 +192,30 @@ agy
184
192
 
185
193
  **Best Practices:**
186
194
  - Authenticate before first use (run `agy` once interactively to log in)
187
- - Use specific model names for Google API access (e.g., `gemini-2.5-pro`)
188
- - Model names `gemini`, `gemini:pro`, and `gemini:flash` are reserved for Antigravity CLI access
195
+ - Use the `google:` namespace for Google API access (e.g., `google:pro`, `google:gemini-2.5-pro`)
196
+ - The `gemini:` / `agy:` namespaces always use the Antigravity CLI; bare `pro`, `gemini-pro` and `flash` also use it first when `agy` is installed
189
197
  - If a call returns an empty response, the CLI is likely not authenticated — run `agy` interactively once
190
198
 
191
199
  **Differences from Google API Provider:**
192
200
  - **Authentication**: Google OAuth via `agy` vs API Key (Google API)
193
201
  - **Billing**: Antigravity subscription/compute allowance vs pay-per-use API
194
- - **Model Routing**: `gemini` / `gemini:flash` / `gemini:pro` → Antigravity CLI provider, specific names (e.g., `gemini-2.5-pro`, bare `gemini-pro`) → Google API provider
202
+ - **Model Routing**: `gemini` / `gemini:<model>` / `agy:<model>` → Antigravity CLI provider; `google:<model>` → Google API provider; bare names both serve (`pro`, `gemini-pro`, `gemini-3.1-pro-preview`, `gemini-3.8-flash`) → Antigravity first, then the Google API on authentication/availability failure; bare names only the Google API serves (e.g., `gemini-2.5-pro`) → Google API provider. Bare `flash` is Gemini 3.8 Flash on Antigravity but Gemini 2.5 Flash on the Google API, so it does not fail over between them.
195
203
  - **Images**: Not supported (text-only) vs full multimodal on the Google API provider
204
+ - **Permissions**: `agy` runs with `--dangerously-skip-permissions` (headless calls cannot prompt), so every tool request, including shell commands and file writes, is auto-approved. Bare names and `auto` reach it whenever `agy` is installed; use `google:<model>` to keep a request on the plain API.
196
205
 
197
206
  ### Claude Agent SDK
198
207
  - **Authentication**: Claude Code CLI login (no API key needed)
199
208
  - **Setup Required**: Authenticate once with `claude login` (Claude Code CLI)
200
- - **Environment Variables**: None (uses Claude Code credentials)
201
- - **Supported Models**:
202
- - `claude` (aliases: `claude-sdk`, `claude-code`) - Defaults to Claude Opus 5.5 (`claude-opus-5-5`)
203
- - `claude:opus` or `claude:opus-5.5` - Claude Opus 5.5 explicitly
204
- - `claude:opus-5` - Claude Opus 5 (`claude-opus-5`)
205
- - `claude:fable` or `claude:fable-5.1` - Claude Fable 5.1 (`claude-fable-5-1`)
206
- - `claude:fable-5` - Claude Fable 5.0 (`claude-fable-5`)
207
- - Other `claude:`-prefixed names pass through to the SDK (e.g. `claude:claude-sonnet-4-6`)
209
+ - **Availability**: The Claude Agent SDK is installed and a credential is present: `~/.claude/.credentials.json` (`$CLAUDE_CONFIG_DIR/.credentials.json` when set), or `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_API_KEY` in the environment. On macOS the login lives in the Keychain and is assumed present. An expired login is caught at call time, and bare-name/`auto` routing fails over to the next provider.
210
+ - **Environment Variables**:
211
+ - `CLAUDE_DEFAULT_MODEL` - Model used for bare `claude` and `auto` (default: `claude-opus-5-5`)
212
+ - **Supported Models** (namespaces: `claude`, `claude-code`, `claude-sdk`):
213
+ - `claude` - Defaults to Claude Opus 5.5 (`claude-opus-5-5`)
214
+ - `claude-opus-5-5` (aliases: `opus`, `claude-opus`, `opus-5.5`) - Claude Opus 5.5 (`claude:opus`)
215
+ - `claude-opus-5` (alias: `opus-5`) - Claude Opus 5
216
+ - `claude-fable-5-1` (aliases: `fable`, `claude-fable`, `fable-5.1`) - Claude Fable 5.1 (`claude:fable`)
217
+ - `claude-fable-5` (alias: `fable-5`) - Claude Fable 5.0
218
+ - Names outside this catalog are rejected with suggestions (e.g. `claude:sonnet` suggests `copilot:sonnet` and `anthropic:sonnet`)
208
219
 
209
220
  **Key Features:**
210
221
  - **Subscription Access**: Uses your Claude subscription instead of API credits
@@ -217,19 +228,22 @@ agy
217
228
  **Differences from Anthropic API Provider:**
218
229
  - **Authentication**: Claude Code login vs `ANTHROPIC_API_KEY`
219
230
  - **Billing**: Claude subscription vs pay-per-use API
220
- - **Model Routing**: `claude` and `claude:*` → SDK provider; specific names (e.g., `claude-fable-5`, `opus`, `sonnet`) → API provider
231
+ - **Model Routing**: `claude` and `claude:<model>` → SDK provider; `anthropic:<model>` → API provider; bare names both serve as the same model (`opus`, `claude-opus-5-5`, `claude-opus-5`, `claude-fable-5`) → SDK first, then the API on authentication/availability failure; bare names only the API serves (e.g., `sonnet`, `haiku`) → API provider. Bare `fable` is Fable 5.1 on the SDK but Fable 5 on the API, so it does not fail over between them.
232
+ - **Permissions**: The SDK runs with `bypassPermissions`, and bare names and `auto` reach it whenever it is available. Use `anthropic:<model>` to keep a request on the plain API.
221
233
 
222
234
  ### GitHub Copilot SDK
223
235
  - **Authentication**: GitHub Copilot subscription via the Copilot CLI (`gh auth login` with an active Copilot subscription) — no API key needed
224
236
  - **Setup Required**: Authenticate the GitHub CLI and ensure your account has an active Copilot subscription
225
- - **Environment Variables**: None
226
- - **Supported Models** (reach them with the `copilot:` namespace, e.g. `copilot:gpt-6-sol`):
227
- - `copilot` - Uses Copilot's default or env-configured model
228
- - OpenAI: `gpt-6-sol` (aliases: bare `gpt-6`, `gpt-5`, `sol`), `gpt-6-luna` (alias: `luna`), `gpt-5.6-sol` (alias: `gpt-5.6`), `gpt-5.6-terra`, `gpt-5.6-luna`
237
+ - **Availability**: The Copilot SDK (`@github/copilot-sdk`) is installed
238
+ - **Environment Variables**:
239
+ - `COPILOT_DEFAULT_MODEL` - Model used for bare `copilot` and `auto` (default: `gpt-6-sol`). The legacy name `COPILOT_MODEL` is honored when `COPILOT_DEFAULT_MODEL` is unset.
240
+ - **Supported Models** (namespace-only: reach them with `copilot:`, `github-copilot:` or `copilot-sdk:`, e.g. `copilot:gpt-6-sol`; Copilot never serves bare model names):
241
+ - `copilot` - GPT-6 Sol, or `COPILOT_DEFAULT_MODEL`
242
+ - OpenAI: `gpt-6-sol` (aliases: `gpt-6`, `gpt-5`, `sol`), `gpt-6-luna` (alias: `luna`), `gpt-5.6-sol` (alias: `gpt-5.6`), `gpt-5.6-terra`, `gpt-5.6-luna`
229
243
  - Anthropic: `claude-opus-5.5` (aliases: `opus`, `claude`; Copilot Pro+/Max/Business/Enterprise), `claude-fable-5` (alias: `fable`), `claude-sonnet-5` (alias: `sonnet`), `claude-opus-5`, `claude-opus-4.8`
230
244
  - Google: `gemini-3.1-pro-preview` (aliases: `gemini`, `gemini-3.1-pro`), `gemini-3.8-flash` (aliases: `gemini-3.8`, `flash-3.8`), `gemini-3.5-flash` (alias: `gemini-flash`)
231
245
  - **Reasoning**: The GPT-6 and GPT-5.6 tiers accept `reasoning_effort` (clamped onto Copilot's `low`–`xhigh`).
232
- - **Explicit pass-through**: Any other `copilot:<id>` model string is forwarded to the Copilot backend verbatim, so IDs outside the curated list still work while the backend accepts them.
246
+ - **Unknown IDs**: Any other `copilot:<id>` is rejected with suggestions; only the IDs and aliases above are accepted.
233
247
 
234
248
  **Key Features:**
235
249
  - **Subscription Access**: Uses your GitHub Copilot subscription instead of API credits
@@ -259,6 +273,24 @@ CODEX_SKIP_GIT_CHECK=true # true (default), false
259
273
  CODEX_APPROVAL_POLICY=never # never (default), untrusted, on-failure, on-request
260
274
  ```
261
275
 
276
+ ### Default Model Overrides (.env file)
277
+
278
+ Each provider has a `<PROVIDER>_DEFAULT_MODEL` variable that sets the model used for its bare provider name (`codex`, `openai`, ...) and for `auto`. The value must be a model ID or alias from that provider's list; startup fails with "Did you mean" suggestions otherwise. OpenRouter also accepts any `vendor/model` slug.
279
+
280
+ ```bash
281
+ CODEX_DEFAULT_MODEL=gpt-6-sol # CODEX_MODEL is honored as a legacy fallback
282
+ CLAUDE_DEFAULT_MODEL=claude-opus-5-5
283
+ AGY_DEFAULT_MODEL=gemini-3.8-flash # Antigravity CLI (gemini)
284
+ COPILOT_DEFAULT_MODEL=gpt-6-sol # COPILOT_MODEL is honored as a legacy fallback
285
+ OPENAI_DEFAULT_MODEL=gpt-6-sol
286
+ GOOGLE_DEFAULT_MODEL=gemini-3.1-pro-preview
287
+ XAI_DEFAULT_MODEL=grok-4.5
288
+ ANTHROPIC_DEFAULT_MODEL=claude-opus-5-5
289
+ MISTRAL_DEFAULT_MODEL=mistral-medium-3-5
290
+ DEEPSEEK_DEFAULT_MODEL=deepseek-v4-pro
291
+ OPENROUTER_DEFAULT_MODEL=z-ai/glm-5.2 # any vendor/model slug is accepted
292
+ ```
293
+
262
294
  ### Claude Configuration (claude_desktop_config.json)
263
295
  ```json
264
296
  {
@@ -311,6 +343,7 @@ All providers support streaming responses for real-time output.
311
343
 
312
344
  ### Local Execution
313
345
  - **Codex**: Runs locally with direct filesystem access and thread-based sessions
346
+ - **Claude Agent SDK, Antigravity CLI, Copilot SDK**: Run through a local SDK or CLI using your subscription login
314
347
  - **All Others**: API-based remote execution
315
348
 
316
349
  ## Model Selection in Tools
@@ -319,42 +352,47 @@ When using the chat tool in any mode, specify models using their identifiers:
319
352
 
320
353
  ### Model Routing Logic
321
354
 
322
- 1. **SDK Providers** (exact matches and prefixes, checked first):
355
+ Routing is derived entirely from each provider's model list (canonical IDs plus aliases, matched case-insensitively). Every entry in `models` takes one of four forms:
356
+
357
+ 1. **`provider`** — that provider's default model (hardcoded, or `<PROVIDER>_DEFAULT_MODEL`). Namespace tokens:
323
358
  - `codex` → Codex
324
- - `gemini`, `gemini-cli`, and any `gemini:`-prefixed name (e.g., `gemini:flash`, `gemini:pro`) → Gemini via Antigravity CLI
325
- - `claude`, `claude-sdk`, `claude-code` and any `claude:`-prefixed name (e.g., `claude:fable`, `claude:opus`) → Claude Agent SDK
326
- - `copilot`, `copilot-sdk`, `github-copilot` and any `copilot:`-prefixed name (e.g., `copilot:codex`) → Copilot SDK
359
+ - `gemini`, `agy`, `antigravity`, `gemini-cli` → Gemini via Antigravity CLI
360
+ - `claude`, `claude-code`, `claude-sdk` → Claude Agent SDK
361
+ - `copilot`, `github-copilot`, `copilot-sdk` → Copilot SDK
362
+ - `openai`, `google`, `xai`, `anthropic`, `mistral`, `deepseek`, `openrouter` → the matching API provider
363
+
364
+ 2. **`provider:model`** — that model on that provider only (e.g. `codex:astra`, `gemini:pro`, `google:pro`, `anthropic:opus`, `copilot:sonnet`). The model must be in that provider's list; there is no failover to another provider.
365
+
366
+ 3. **Bare `model`** — an ID or alias without a namespace goes to the first provider, in this order, whose list contains the name and that is set up: Codex, Antigravity CLI, Claude Agent SDK, OpenAI, Google, X.AI, Anthropic, Mistral, DeepSeek, OpenRouter.
367
+ - "Set up" means an API key for API providers; for Codex, the SDK plus a login file or `CODEX_API_KEY`; for the Claude Agent SDK, the SDK plus a login file, a macOS login, or `CLAUDE_CODE_OAUTH_TOKEN`/`ANTHROPIC_API_KEY`; for Antigravity, the `agy` binary.
368
+ - If that provider fails with an authentication or availability error (including an expired login, which is only detected at call time), the next set-up provider that serves **the same model** takes over. A provider whose alias of that name points at a different model is never substituted (bare `fable` is Fable 5.1 on the Claude Agent SDK and Fable 5 on the Anthropic API; bare `flash` is Gemini 3.8 Flash on Antigravity and Gemini 2.5 Flash on the Google API).
369
+ - Copilot never serves bare names; use `copilot:<model>`.
370
+
371
+ 4. **`auto`** — the first available provider's default model, in the priority order above with Copilot between the Claude Agent SDK and OpenAI.
327
372
 
328
- 2. **Simple Names**: Other models without "/" are routed by keyword matching:
329
- - Contains "gpt", "o1", "o3", "o4" → OpenAI
330
- - Contains "claude", "fable", "opus", "sonnet", "haiku" → Anthropic
331
- - Contains "gemini", "flash", "pro" → Google
332
- - Contains "grok" → X.AI
333
- - Contains "mistral", "magistral" → Mistral
334
- - Contains "deepseek", "reasoner", "r1" → DeepSeek
335
- - Contains "qwen", "kimi", "k2" → OpenRouter
373
+ **Unknown names are rejected**, never guessed or forwarded: a typo or an unlisted model returns an error with up to three close matches, e.g. `Unknown model "gtp-6-astra". Did you mean: gpt-6-astra?` or `Unknown openai model "spark" in "openai:spark". Did you mean: codex:spark?`.
336
374
 
337
- 3. **Slash Format**: Models with "/" check native providers first:
338
- - If exact model exists in a native provider → Routes to that provider
339
- - If not found in any native provider → Routes to OpenRouter
340
- - This allows using models like "anthropic/claude-3.5-sonnet" via OpenRouter
375
+ **OpenRouter slugs** are the one open-ended case: a full `vendor/model` slug (bare, or with the `openrouter:` namespace) routes to OpenRouter and is validated against OpenRouter's live catalog. `openrouter/auto` (aliases `auto-router`, `openrouter-auto`) selects OpenRouter's auto-router.
341
376
 
342
- 4. **OpenRouter Auto**: Special aliases route to OpenRouter's auto-selection:
343
- - "openrouter/auto", "openrouter auto", "auto router", "auto-router"
377
+ **Local agent permissions**: Bare names and `auto` reach the local agent providers whenever they are set up. The Antigravity CLI auto-approves every tool request (`--dangerously-skip-permissions`) and the Claude Agent SDK runs with `bypassPermissions`. Name the API provider (`google:pro`, `anthropic:opus`, `openai:gpt-6-astra`) to keep a request off the local agents.
344
378
 
345
- The `models` array always holds plain model-name strings. Each string routes as follows:
379
+ Examples:
346
380
 
347
381
  ```text
348
- "gpt-6" // OpenAI (keyword match -> gpt-6-sol)
349
- "fable" // Anthropic (keyword match -> claude-fable-5)
350
- "opus" // Anthropic (keyword match -> claude-opus-5-5)
351
- "sonnet" // Anthropic (keyword match -> claude-sonnet-4-6)
382
+ "gpt-6" // Codex (gpt-6-sol), else OpenAI API
383
+ "openai:gpt-6" // OpenAI API only
384
+ "fable" // Claude Agent SDK (claude-fable-5-1) when set up, otherwise Anthropic API (claude-fable-5)
385
+ "opus" // Claude Agent SDK (claude-opus-5-5), else Anthropic API
386
+ "anthropic:opus" // Anthropic API only
387
+ "sonnet" // Anthropic API (claude-sonnet-4-6)
352
388
  "claude" // Claude Agent SDK (defaults to Claude Opus 5.5)
353
389
  "claude:fable" // Claude Agent SDK (Claude Fable 5.1)
354
- "gemini-2.5-pro" // Google (keyword match)
355
- "grok-4.5" // X.AI (keyword match)
390
+ "pro" // Antigravity CLI (gemini-3.1-pro-preview), else Google API
391
+ "google:pro" // Google API only
392
+ "gemini-2.5-pro" // Google API
393
+ "grok-4.5" // X.AI
356
394
  "mistral-large" // Mistral (alias -> mistral-large-2512)
357
- "deepseek" // DeepSeek (alias -> deepseek-v4-pro)
395
+ "deepseek" // DeepSeek default model (deepseek-v4-pro)
358
396
  "z-ai/glm-5.2" // OpenRouter (curated slug)
359
397
  "z-ai/glm-5.2:online" // OpenRouter with web search opt-in
360
398
  "anthropic/claude-sonnet-5" // OpenRouter (any full slug routes as-is)
@@ -381,9 +419,11 @@ The `models` array always holds plain model-name strings. Each string routes as
381
419
  - Verify API keys are active and have available quota
382
420
 
383
421
  ### Model Not Found
384
- - Use exact model identifiers as listed above
385
- - Some providers support aliases (e.g., "fable" → "claude-fable-5", "opus" → "claude-opus-5-5")
422
+ - Use model identifiers or aliases as listed above; anything else is rejected with up to three "Did you mean" suggestions
423
+ - Check the namespace: a model must be in that provider's list (`openai:spark` fails and suggests `codex:spark`)
424
+ - Aliases can differ per provider (e.g., bare "fable" → "claude-fable-5-1" on the Claude Agent SDK, `anthropic:fable` → "claude-fable-5")
386
425
  - Note: bare "claude" routes to the Claude Agent SDK provider, not the Anthropic API
426
+ - A "none is available" error means the model exists but no provider serving it is set up; the message names the setup step for each
387
427
  - Check provider documentation for model availability in your region
388
428
 
389
429
  ### Rate Limits
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "converse-mcp-server",
3
- "version": "3.7.1",
3
+ "version": "4.1.0",
4
4
  "description": "Converse MCP Server - Converse with other LLMs with chat and consensus tools",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/config.js CHANGED
@@ -13,6 +13,10 @@ import { fileURLToPath } from 'url';
13
13
  import { dirname, join, resolve } from 'path';
14
14
  import { readFileSync } from 'fs';
15
15
  import { findAgyBinary } from './providers/gemini-cli.js';
16
+ import {
17
+ DEFAULT_MODEL_ENV_VARS,
18
+ validateDefaultModelOverrides,
19
+ } from './utils/modelRouting.js';
16
20
 
17
21
  // Load environment variables from appropriate .env file
18
22
  // Priority: .env.test (for test env) > .env (default)
@@ -210,6 +214,12 @@ const CONFIG_SCHEMA = {
210
214
  secret: true,
211
215
  description: 'OpenRouter API key',
212
216
  },
217
+ TYPESAFE_API_KEY: {
218
+ type: 'string',
219
+ required: false,
220
+ secret: true,
221
+ description: 'TypeSafe API key (System One decision models for the decide tool)',
222
+ },
213
223
  },
214
224
 
215
225
  // Provider-specific configuration
@@ -280,9 +290,8 @@ const CONFIG_SCHEMA = {
280
290
  },
281
291
  CODEX_MODEL: {
282
292
  type: 'string',
283
- default: 'gpt-6-sol',
284
- description:
285
- 'Default Codex backend model (e.g., gpt-6-sol, gpt-6-luna, gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5)',
293
+ required: false,
294
+ description: 'Deprecated alias of CODEX_DEFAULT_MODEL',
286
295
  },
287
296
 
288
297
  // Copilot configuration
@@ -294,8 +303,7 @@ const CONFIG_SCHEMA = {
294
303
  COPILOT_MODEL: {
295
304
  type: 'string',
296
305
  required: false,
297
- description:
298
- 'Default model for Copilot SDK sessions (e.g., gpt-6-sol, claude-opus-5.5, claude-sonnet-5)',
306
+ description: 'Deprecated alias of COPILOT_DEFAULT_MODEL',
299
307
  },
300
308
  COPILOT_CLI_PATH: {
301
309
  type: 'string',
@@ -303,6 +311,20 @@ const CONFIG_SCHEMA = {
303
311
  description:
304
312
  'Explicit path to the Copilot CLI runtime (index.js or copilot binary). Overrides automatic resolution.',
305
313
  },
314
+
315
+ // Per-provider default models: the model a bare provider name (`codex`,
316
+ // `openai`, ...) or "auto" uses. Each must name a model or alias in that
317
+ // provider's catalog; startup fails with suggestions otherwise.
318
+ ...Object.fromEntries(
319
+ Object.entries(DEFAULT_MODEL_ENV_VARS).map(([provider, envVar]) => [
320
+ envVar,
321
+ {
322
+ type: 'string',
323
+ required: false,
324
+ description: `Default model for the ${provider} provider`,
325
+ },
326
+ ]),
327
+ ),
306
328
  },
307
329
 
308
330
  // MCP configuration
@@ -691,7 +713,7 @@ export async function loadConfig() {
691
713
 
692
714
  if (availableKeys.length === 0 && !hasVertexAI && !hasSdkProvider) {
693
715
  errors.push(
694
- 'At least one API key must be configured: OPENAI_API_KEY, XAI_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, DEEPSEEK_API_KEY, or OPENROUTER_API_KEY. Alternatively, configure Google Vertex AI or use an SDK-based provider (codex, claude, copilot) or the Antigravity CLI (gemini-cli).',
716
+ 'At least one API key must be configured: OPENAI_API_KEY, XAI_API_KEY, GOOGLE_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, DEEPSEEK_API_KEY, OPENROUTER_API_KEY, or TYPESAFE_API_KEY. Alternatively, configure Google Vertex AI or use an SDK-based provider (codex, claude, copilot) or the Antigravity CLI (gemini-cli).',
695
717
  );
696
718
  }
697
719
 
@@ -892,6 +914,14 @@ export async function validateRuntimeConfig(config) {
892
914
  // Validate Codex configuration
893
915
  validateCodexConfig(config);
894
916
 
917
+ // Validate per-provider default-model overrides against provider catalogs.
918
+ // Imported lazily: the provider registry pulls in every provider SDK.
919
+ const { getProviders } = await import('./providers/index.js');
920
+ const defaultModelErrors = validateDefaultModelOverrides(getProviders(), config);
921
+ if (defaultModelErrors.length > 0) {
922
+ throw new ConfigurationError(defaultModelErrors.join('\n'));
923
+ }
924
+
895
925
  // Validate environment
896
926
  const validEnvs = ['development', 'production', 'test'];
897
927
  if (!validEnvs.includes(config.environment.nodeEnv)) {
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Decision Providers
3
+ *
4
+ * Hosts of System One decision models (TypeSafe's Jev family). These are kept
5
+ * apart from the chat provider registry on purpose: a decision model takes a
6
+ * state plus typed questions and returns probabilities, never text, so it must
7
+ * never be reachable from `chat` routing ("auto", bare names, failover), and
8
+ * the `decide` tool must never reach a chat model.
9
+ *
10
+ * Spec grammar mirrors chat routing:
11
+ * - `auto` (or empty) — every configured provider's default, in priority order
12
+ * - `provider` / `provider:` — that provider's default model
13
+ * - `provider:model` — that provider only
14
+ * - `model` — every provider that serves the name, in priority order; the
15
+ * first is used and the rest are failover candidates
16
+ */
17
+
18
+ import { getCustomHeaders as getOpenRouterAttributionHeaders } from '../providers/openrouter.js';
19
+
20
+ /**
21
+ * Model catalogs map each provider's own model ID to the names that route to
22
+ * it. OpenRouter only knows `~typesafe/jev-latest` and `typesafe/jev-1.13`
23
+ * (it rejects `jev-1.13.0` and `jev-preview`), so native IDs are translated
24
+ * per provider rather than by a blanket prefix rewrite.
25
+ */
26
+ export const DECISION_PROVIDERS = {
27
+ typesafe: {
28
+ name: 'typesafe',
29
+ label: 'TypeSafe',
30
+ baseURL: 'https://api.typesafe.ai',
31
+ apiKeyEnv: 'TYPESAFE_API_KEY',
32
+ defaultModel: 'jev-latest',
33
+ models: {
34
+ 'jev-latest': ['jev', '~typesafe/jev-latest'],
35
+ 'jev-1.13.0': ['jev-1.13', 'typesafe/jev-1.13'],
36
+ 'jev-preview': [],
37
+ },
38
+ // TypeSafe accepts every published versioned ID, listed or not.
39
+ passthrough: (name) => /^jev-\d+\.\d+(\.\d+)?$/i.test(name),
40
+ headers: (config) => ({ Authorization: `Bearer ${config.apiKeys.typesafe}` }),
41
+ isAvailable: (config) => Boolean(config?.apiKeys?.typesafe),
42
+ },
43
+ openrouter: {
44
+ name: 'openrouter',
45
+ label: 'OpenRouter',
46
+ baseURL: 'https://openrouter.ai/api',
47
+ apiKeyEnv: 'OPENROUTER_API_KEY',
48
+ defaultModel: '~typesafe/jev-latest',
49
+ models: {
50
+ '~typesafe/jev-latest': ['jev-latest', 'jev'],
51
+ 'typesafe/jev-1.13': ['jev-1.13', 'jev-1.13.0'],
52
+ },
53
+ // Decision models OpenRouter adds later are reachable by full slug.
54
+ passthrough: (name) => name.includes('/'),
55
+ headers: (config) => ({
56
+ Authorization: `Bearer ${config.apiKeys.openrouter}`,
57
+ ...getOpenRouterAttributionHeaders(config),
58
+ }),
59
+ isAvailable: (config) => Boolean(config?.apiKeys?.openrouter),
60
+ },
61
+ };
62
+
63
+ /** Failover order: the native host first, then OpenRouter. */
64
+ export const DECISION_PROVIDER_PRIORITY = ['typesafe', 'openrouter'];
65
+
66
+ /**
67
+ * Resolve a name against one provider's catalog, then its passthrough rule.
68
+ * @returns {string|null} The model ID to send to that provider
69
+ */
70
+ export function findDecisionModel(provider, name) {
71
+ const wanted = String(name).trim().toLowerCase();
72
+ if (!wanted) return null;
73
+ for (const [id, aliases] of Object.entries(provider.models)) {
74
+ if (id.toLowerCase() === wanted || aliases.some((a) => a.toLowerCase() === wanted)) {
75
+ return id;
76
+ }
77
+ }
78
+ return provider.passthrough(String(name).trim()) ? String(name).trim() : null;
79
+ }
80
+
81
+ function setupHint(provider) {
82
+ return `set ${provider.apiKeyEnv}`;
83
+ }
84
+
85
+ function knownModels() {
86
+ const names = new Set();
87
+ for (const providerName of DECISION_PROVIDER_PRIORITY) {
88
+ const provider = DECISION_PROVIDERS[providerName];
89
+ for (const [id, aliases] of Object.entries(provider.models)) {
90
+ names.add(id);
91
+ aliases.forEach((a) => names.add(a));
92
+ }
93
+ }
94
+ return [...names];
95
+ }
96
+
97
+ function ok(candidates) {
98
+ return { status: 'ok', candidates, error: null };
99
+ }
100
+
101
+ function fail(status, error) {
102
+ return { status, candidates: [], error };
103
+ }
104
+
105
+ /**
106
+ * Resolve a decision model spec into an ordered candidate list.
107
+ * @param {string} spec - Model spec (see module grammar)
108
+ * @param {object} config - Server configuration (for API keys)
109
+ * @returns {{ status: 'ok'|'unknown'|'unavailable', candidates: Array<{ providerName: string, provider: object, model: string }>, error: string|null }}
110
+ */
111
+ export function resolveDecisionModel(spec, config) {
112
+ const raw = String(spec ?? '').trim();
113
+ const namespaces = DECISION_PROVIDER_PRIORITY.join(', ');
114
+
115
+ if (!raw || raw.toLowerCase() === 'auto') {
116
+ const candidates = DECISION_PROVIDER_PRIORITY
117
+ .map((name) => DECISION_PROVIDERS[name])
118
+ .filter((provider) => provider.isAvailable(config))
119
+ .map((provider) => ({ providerName: provider.name, provider, model: provider.defaultModel }));
120
+ if (candidates.length === 0) {
121
+ return fail(
122
+ 'unavailable',
123
+ `No decision provider is configured: ${DECISION_PROVIDER_PRIORITY.map((n) => `${n} (${setupHint(DECISION_PROVIDERS[n])})`).join('; ')}.`,
124
+ );
125
+ }
126
+ return ok(candidates);
127
+ }
128
+
129
+ const colon = raw.indexOf(':');
130
+ const hasNamespace = colon > 0 && !raw.slice(0, colon).includes('/');
131
+ const namespace = hasNamespace ? raw.slice(0, colon).toLowerCase() : raw.toLowerCase();
132
+
133
+ if (hasNamespace || DECISION_PROVIDERS[namespace]) {
134
+ const provider = DECISION_PROVIDERS[namespace];
135
+ if (!provider) {
136
+ return fail('unknown', `Unknown decision provider "${raw.slice(0, colon)}". Providers: ${namespaces}.`);
137
+ }
138
+ const name = hasNamespace ? raw.slice(colon + 1).trim() : '';
139
+ const model = name ? findDecisionModel(provider, name) : provider.defaultModel;
140
+ if (!model) {
141
+ return fail(
142
+ 'unknown',
143
+ `${provider.label} does not serve "${name}". Models: ${Object.keys(provider.models).join(', ')}.`,
144
+ );
145
+ }
146
+ if (!provider.isAvailable(config)) {
147
+ return fail('unavailable', `${provider.label} is not configured (${setupHint(provider)}).`);
148
+ }
149
+ return ok([{ providerName: provider.name, provider, model }]);
150
+ }
151
+
152
+ const matches = DECISION_PROVIDER_PRIORITY
153
+ .map((providerName) => {
154
+ const provider = DECISION_PROVIDERS[providerName];
155
+ const model = findDecisionModel(provider, raw);
156
+ return model ? { providerName, provider, model } : null;
157
+ })
158
+ .filter(Boolean);
159
+
160
+ if (matches.length === 0) {
161
+ return fail(
162
+ 'unknown',
163
+ `Unknown decision model "${raw}". Use "auto", a provider (${namespaces}), "provider:model", or one of: ${knownModels().join(', ')}.`,
164
+ );
165
+ }
166
+ const available = matches.filter((m) => m.provider.isAvailable(config));
167
+ if (available.length === 0) {
168
+ return fail(
169
+ 'unavailable',
170
+ `"${raw}" is served by ${matches.map((m) => `${m.providerName} (${setupHint(m.provider)})`).join('; ')}, but none is configured.`,
171
+ );
172
+ }
173
+ return ok(available);
174
+ }