converse-mcp-server 4.5.2 → 4.6.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/.env.example CHANGED
@@ -57,8 +57,15 @@ ABLITERATION_API_KEY=your_abliteration_api_key_here
57
57
  # Get your TypeSafe API key from: https://typesafe.ai
58
58
  # Enables the decide tool's native host for Jev decision models. Without it,
59
59
  # decide reaches Jev through OPENROUTER_API_KEY instead.
60
+ # (OPENAI_API_KEY above also enables decide's default model, gpt-6-luna.)
60
61
  TYPESAFE_API_KEY=your_typesafe_api_key_here
61
62
 
63
+ # Cloudflare Workers AI: the decide tool's native host for the Clef decision
64
+ # models (clef, clef-flash). The token needs Workers AI permission. Without
65
+ # these, decide reaches Clef through OPENROUTER_API_KEY instead.
66
+ CLOUDFLARE_ACCOUNT_ID=your_cloudflare_account_id_here
67
+ CLOUDFLARE_API_TOKEN=your_cloudflare_api_token_here
68
+
62
69
  # ============================================
63
70
  # Codex Configuration (Optional)
64
71
  # ============================================
package/README.md CHANGED
@@ -187,7 +187,7 @@ Cancel running asynchronous operations when needed.
187
187
 
188
188
  ### 4. Decide Tool
189
189
 
190
- Ask a System One decision model (TypeSafe's Jev) typed questions about a state and get calibrated answers instead of text: a yes probability (`noul`), a chosen option with per-option probabilities (`choice`), or a rubric position with per-level probabilities (`score`). Batch many questions into one call; they are judged in parallel against the same state. Needs `TYPESAFE_API_KEY` or `OPENROUTER_API_KEY` (TypeSafe first, falling back to OpenRouter).
190
+ Ask a decision model (OpenAI's `gpt-6-luna`, TypeSafe's Jev, or Cloudflare's Clef) typed questions about a state and get calibrated answers instead of text: a yes probability (`noul`), a chosen option with per-option probabilities (`choice`), or a rubric position with per-level probabilities (`score`). Batch many questions into one call; they are judged in parallel against the same state. Needs any of `OPENAI_API_KEY`, `TYPESAFE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN`, or `OPENROUTER_API_KEY`; each model runs on its native host first and falls back to OpenRouter. `model: "auto"` tries `gpt-6-luna`, then `jev-latest`, then `clef`. Pass `images` to judge pictures with `gpt-6-luna` or Clef.
191
191
 
192
192
  ```javascript
193
193
  {
@@ -280,7 +280,8 @@ SUMMARIZATION_MODEL=gpt-5-nano # Default: gpt-5-nano
280
280
  - **claude-opus-4-5** / **claude-opus-4-1**: Legacy Opus models with extended thinking (64K / 32K output)
281
281
  - **claude-sonnet-5-5** (aliases: `sonnet`, `sonnet-5.5`): Current Sonnet with adaptive thinking and effort (1M context, 128K output)
282
282
  - **claude-sonnet-4-6** (alias: `sonnet-4.6`): Previous Sonnet generation with adaptive thinking (64K output)
283
- - **claude-haiku-4-5** (alias: `haiku`): Fast and intelligent for simple queries (64K output)
283
+ - **claude-haiku-5-5** (aliases: `haiku`, `haiku-5.5`): Current Haiku, fastest and cheapest Claude, with adaptive thinking and effort (1M context, 128K output)
284
+ - **claude-haiku-4-5** (alias: `haiku-4.5`): Previous Haiku generation with extended thinking (64K output)
284
285
 
285
286
  ### Mistral Models
286
287
 
@@ -337,6 +338,7 @@ Claude via the Claude Agent SDK. `claude` uses its default model (Opus 5.5, or `
337
338
  - **claude-opus-5-5** (default; aliases: `opus`, `claude-opus`, `opus-5.5`), **claude-opus-5** (`opus-5`)
338
339
  - **claude-fable-5-1** (aliases: `fable`, `claude-fable`, `fable-5.1`), **claude-fable-5** (`fable-5`)
339
340
  - **claude-sonnet-5-5** (aliases: `sonnet`, `claude-sonnet`, `sonnet-5.5`)
341
+ - **claude-haiku-5-5** (aliases: `haiku`, `claude-haiku`, `haiku-5.5`)
340
342
  - Uses Claude Code CLI authentication (`claude login`) - no API key needed
341
343
  - Direct filesystem access from working directory
342
344
 
@@ -345,7 +347,7 @@ Claude via the Claude Agent SDK. `claude` uses its default model (Opus 5.5, or `
345
347
  Reach these only with the `copilot:` namespace (e.g. `copilot:gpt-6.1-sol`) — Copilot never serves bare model names. `copilot` alone uses GPT-6.1 Sol, or `COPILOT_DEFAULT_MODEL`. Uses your GitHub Copilot subscription (`gh auth login`) - no API key needed:
346
348
 
347
349
  - **OpenAI**: `gpt-6.1-sol` (aliases: `gpt-6`, `gpt-6.1`, `gpt-5`, `sol`), `gpt-6-sol`, `gpt-6-luna` (alias: `luna`), `gpt-5.6-sol` (alias: `gpt-5.6`), `gpt-5.6-terra`, `gpt-5.6-luna` (all support `reasoning_effort`)
348
- - **Anthropic**: `claude-opus-5.5` (aliases: `opus`, `claude`), `claude-fable-5` (alias: `fable`), `claude-sonnet-5.5` (alias: `sonnet`), `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4.8`
350
+ - **Anthropic**: `claude-opus-5.5` (aliases: `opus`, `claude`), `claude-fable-5.1` (alias: `fable`), `claude-fable-5`, `claude-sonnet-5.5` (alias: `sonnet`), `claude-haiku-5.5` (alias: `haiku`), `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4.8`
349
351
  - **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`)
350
352
 
351
353
  ## 📚 Help & Documentation
package/docs/API.md CHANGED
@@ -368,7 +368,7 @@ Only jobs in a `queued` or `running` state can be cancelled; already-completed,
368
368
 
369
369
  ## Decide Tool
370
370
 
371
- **Description**: Ask a System One decision model (TypeSafe's Jev family) typed questions about a state. Decision models return calibrated probabilities, never text, so they have their own tool and their own providers: `chat` routing never reaches them, and `decide` never reaches a chat model.
371
+ **Description**: Ask a decision model (OpenAI's `gpt-6-luna`, TypeSafe's Jev, Cloudflare's Clef) typed questions about a state. Decision models return calibrated probabilities, never text, so they have their own tool and their own providers: `chat` routing never reaches them, and `decide` never reaches a chat model. Requests and answers use TypeSafe's System One schema for every model; providers whose API differs translate it.
372
372
 
373
373
  ### Request Schema
374
374
 
@@ -390,7 +390,8 @@ Only jobs in a `queued` or `running` state can be cancelled; already-completed,
390
390
  }
391
391
  },
392
392
  "model": { "type": "string", "description": "Default: \"auto\"" },
393
- "files": { "type": "array", "items": { "type": "string" } }
393
+ "files": { "type": "array", "items": { "type": "string" } },
394
+ "images": { "type": "array", "items": { "type": "string" } }
394
395
  },
395
396
  "required": ["questions"],
396
397
  "additionalProperties": false
@@ -399,30 +400,50 @@ Only jobs in a `queued` or `running` state can be cancelled; already-completed,
399
400
 
400
401
  - **`state`**: the material to judge. An object with descriptively named fields works best; use an array for sequences such as chat messages. Optional when `files` is given.
401
402
  - **`questions`**: named questions, each judged in parallel and in isolation against the same state. The name is your own label and becomes the answer key. Batching many questions into one call adds almost no latency or cost.
402
- - **`files`**: text files added to the state as `{ "files": { "<path>": "<content>" } }`. When `state` is also given, it moves to `input`. Line ranges (`file.txt{10:50}`) are supported; images are rejected.
403
+ - **`files`**: text files added to the state as `{ "files": { "<path>": "<content>" } }`. When `state` is also given, it moves to `input`. Line ranges (`file.txt{10:50}`) are supported; image files belong in `images`.
404
+ - **`images`**: PNG, JPEG or WebP images judged together with the state, as file paths or base64 data URLs; `state` is optional when images are given. Only `gpt-6-luna` (OpenAI) and `clef` / `clef-flash` (Cloudflare) take images, so `auto` skips other models and naming a text-only model is rejected before sending. OpenAI receives the state and images as one user message. Clef takes at most 4 images, and Cloudflare rejects a request whose size estimate (one token per 4 characters, base64 included) exceeds its 65,536-token window, so Clef images must total about 180 KB; `decide` checks this locally and suggests downscaling. OpenRouter routes are text only.
403
405
 
404
406
  | Type | `criteria` | Answer |
405
407
  |---|---|---|
406
408
  | `noul` | Optional `{ "true": "...", "false": "..." }` | `noul`: probability 0..1 of yes |
407
409
  | `choice` | Required `{ "<option>": "description" \| null }`, 2–255 options | `choice`, per-option `probabilities`, `confidence` |
408
- | `score` | Required ordered array of levels, lowest first, 2–10 levels | `score` (probability-weighted level position), `legend`, per-level `probabilities`, `confidence` |
410
+ | `score` | Required ordered array of levels, lowest first, 2–10 levels; each a description or `{ "label": "...", "description": ... }` | `score` (probability-weighted level position), `legend`, per-level `probabilities`, `confidence` |
409
411
 
410
412
  `instructions` and criteria descriptions may be objects or arrays that bundle reference data with the question; refer to their fields by `` `name` `` in the text. Questions are validated before any request is sent.
411
413
 
414
+ A labeled score level (`{ "label": "Very angry", "description": "Hostile, threatening to leave" }`) keeps a short name apart from its full description. OpenAI receives the pair as-is; System One hosts (TypeSafe, Cloudflare, OpenRouter) take one description per level, so they receive `"label: description"`. Either way the answer's `legend` shows the labels.
415
+
416
+ Any answer from `gpt-6-luna` may be `{ "type": "refusal" }` instead of a value, shown as `- name (refusal): the model declined to answer`. Treat it as unanswered, never as a no. Jev and Clef have no refusal answer and always return a value.
417
+
412
418
  ### Models and Providers
413
419
 
414
- | Provider | Key | Models |
415
- |---|---|---|
416
- | `typesafe` (native, `https://api.typesafe.ai/v1/systemone`) | `TYPESAFE_API_KEY` | `jev-latest`, `jev-1.13.0` (alias `jev-1.13`), `jev-preview`, any versioned `jev-X.Y.Z` |
417
- | `openrouter` (`https://openrouter.ai/api/v1/systemone`) | `OPENROUTER_API_KEY` | `~typesafe/jev-latest` (alias `jev-latest`), `typesafe/jev-1.13` (aliases `jev-1.13`, `jev-1.13.0`), any `vendor/model` slug |
420
+ | Model | Aliases | Native host | OpenRouter slug |
421
+ |---|---|---|---|
422
+ | `gpt-6-luna` | `luna`, `gpt-6-luna-decisions` | `openai` | `openai/gpt-6-luna-decisions` |
423
+ | `jev-latest` | `jev` | `typesafe` (`jev-latest`) | `~typesafe/jev-latest` |
424
+ | `jev-1.13` | `jev-1.13.0` | `typesafe` (`jev-1.13.0`) | `typesafe/jev-1.13` |
425
+ | `jev-preview` | | `typesafe` | not served |
426
+ | `clef` | `@cf/cloudflare/clef` | `cloudflare` | `cloudflare/clef` |
427
+ | `clef-flash` | `@cf/cloudflare/clef-flash` | `cloudflare` | `cloudflare/clef-flash` |
428
+
429
+ | Provider | Endpoint | Configuration | Wire format |
430
+ |---|---|---|---|
431
+ | `openai` | `POST https://api.openai.com/v1/decisions` | `OPENAI_API_KEY` | OpenAI Decisions (translated) |
432
+ | `typesafe` | `POST https://api.typesafe.ai/v1/systemone` | `TYPESAFE_API_KEY` | System One; also any versioned `jev-X.Y.Z` |
433
+ | `cloudflare` | `POST https://api.cloudflare.com/client/v4/accounts/{account}/ai/run/@cf/cloudflare/{model}` | `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_API_TOKEN` (Workers AI permission) | System One in the Workers AI envelope |
434
+ | `openrouter` | `POST https://openrouter.ai/api/v1/systemone` | `OPENROUTER_API_KEY` | System One; also any `vendor/model` slug |
435
+
436
+ - `auto` (default): `gpt-6-luna`, then `jev-latest`, then `clef`, each on every configured host (native first, then OpenRouter).
437
+ - A model name goes to every configured provider that serves it, native first, and each provider receives its own model ID.
438
+ - `provider` alone uses that provider's default (`openai` and `openrouter`: `gpt-6-luna`; `typesafe`: `jev-latest`; `cloudflare`: `clef`). `provider:model` pins one provider, e.g. `cloudflare:clef-flash` or `openrouter:jev-latest`.
439
+
440
+ OpenAI's Decisions API takes the same three kinds of question in its own schema, and only strings for the input and instructions. `decide` translates: object or array `state` and `instructions` are sent as JSON text, `noul` becomes a predicate whose `true`/`false` criteria are appended to the instructions, choice options and score levels become `choices` and `levels` arrays, and answers are mapped back to System One shapes.
418
441
 
419
- - `auto` (default): TypeSafe's default model, falling back to OpenRouter's.
420
- - A bare name such as `jev-1.13` goes to every configured provider that serves it, native first, and each provider receives its own model ID. OpenRouter does not serve `jev-preview` or patch-level IDs other than those listed above.
421
- - `typesafe:jev-1.13.0` or `openrouter:~typesafe/jev-latest` pins one provider.
442
+ Clef accepts at most 64 questions per call, named with letters, digits, `_`, `.` and `-` (up to 100 characters); `decide` checks this before sending.
422
443
 
423
- Each provider call retries timeouts, 408, 429 and 5xx with backoff, honoring `Retry-After`. Auth failures, exhausted retries and malformed responses fall back to the next provider. A 400/422 request fault stops immediately, because every host would reject it the same way.
444
+ Each provider call retries timeouts, 408, 429 and 5xx with backoff, honoring `Retry-After`. Auth failures, exhausted retries and malformed responses fall back to the next candidate. A 400/422 request fault skips the remaining hosts of the same model, which would reject it the same way, but `auto` still moves on to its next model.
424
445
 
425
- TypeSafe's API sits behind a Cloudflare firewall that rejects some request bodies containing SQL-injection or shell-command patterns (for example `-- ; DROP TABLE`, or a quoted `'...; DROP TABLE ...;'`) with a 403 before the model sees them. This applies through OpenRouter too, since it forwards to the same edge. `decide` reports it as `Blocked by typesafe.ai's Cloudflare firewall before reaching the model (Ray ID …)` and stops without retrying or falling back. Treat a block as an unanswered question, not as a decision, and report the Ray ID to TypeSafe.
446
+ TypeSafe's API sits behind a Cloudflare firewall that rejects some request bodies containing SQL-injection or shell-command patterns (for example `-- ; DROP TABLE`, or a quoted `'...; DROP TABLE ...;'`) with a 403 before the model sees them. This applies to Jev through OpenRouter too, since it forwards to the same edge. `decide` reports it as `Blocked by typesafe.ai's Cloudflare firewall before reaching the model (Ray ID …)` and does not retry Jev on another host. Treat a block as an unanswered question, not as a decision, and report the Ray ID to TypeSafe.
426
447
 
427
448
  ### Example Usage
428
449
 
@@ -466,11 +487,11 @@ Decision · typesafe/jev-1.13-20260917 via OpenRouter · 394 input tokens · $0.
466
487
  ```
467
488
  ````
468
489
 
469
- `usage.cost` is reported by OpenRouter only (`null` from TypeSafe). A fallback is noted under the summary line.
490
+ `usage.cost` is reported by OpenRouter only (`null` from the native hosts). A fallback is noted under the summary line.
470
491
 
471
492
  ### Usage Guidance
472
493
 
473
- Jev reads questions literally and is weak at counting, arithmetic, date comparison, and multi-hop reasoning; do those in code and ask narrow, atomic questions. Split compound judgments into separate questions and combine them in code. Treat low `confidence` as a signal to escalate to a generative model or a human. Limits: text only, about 64k tokens per request and 32k for the state plus the longest single question.
494
+ Decision models read questions literally and are weak at counting, arithmetic, date comparison, and multi-hop reasoning; do those in code and ask narrow, atomic questions. Split compound judgments into separate questions and combine them in code. Treat low `confidence` as a signal to escalate to a generative model or a human. Limits: Jev is text only and takes about 64k tokens per request and 32k for the state plus the longest single question; Clef has a 65,536-token context.
474
495
 
475
496
  ## Supported Models
476
497
 
@@ -527,9 +548,10 @@ Provide models as plain name strings in the `models` array. Each entry is `auto`
527
548
  | `claude-opus-4-5-20251101`, `claude-opus-4-1-20250805` | `opus-4.5`, `opus-4.1` | 200K | 64K / 32K | Earlier Opus tiers |
528
549
  | `claude-sonnet-5-5` | `sonnet`, `sonnet-5.5`, `claude-sonnet` | 1M | 128K | Current Sonnet, adaptive thinking + effort, compaction |
529
550
  | `claude-sonnet-4-6` | `sonnet-4.6` | 200K (1M beta) | 64K | Previous Sonnet, adaptive thinking |
530
- | `claude-haiku-4-5-20251001` | `haiku`, `haiku-4.5` | 200K | 64K | Fast and intelligent |
551
+ | `claude-haiku-5-5` | `haiku`, `haiku-5.5`, `claude-haiku` | 1M | 128K | Current Haiku, adaptive thinking + effort, compaction |
552
+ | `claude-haiku-4-5-20251001` | `haiku-4.5` | 200K | 64K | Previous Haiku, extended thinking |
531
553
 
532
- Models with adaptive thinking control depth via `reasoning_effort`, which is passed by name to Anthropic's `effort` parameter and clamped to what each model accepts: Opus 5.5, Sonnet 5.5, Fable 5, Opus 5, Opus 4.8, and Opus 4.7 take `low`–`max`; Opus 4.6 and Sonnet 4.6 lack `xhigh` (it becomes `max`); Opus 4.5 tops out at `high`. `none` and `minimal` become `low` everywhere. System prompts are automatically cached for 1 hour; cache stats appear in response metadata as `cache_creation_input_tokens` / `cache_read_input_tokens`.
554
+ Models with adaptive thinking control depth via `reasoning_effort`, which is passed by name to Anthropic's `effort` parameter and clamped to what each model accepts: Opus 5.5, Sonnet 5.5, Haiku 5.5, Fable 5, Opus 5, Opus 4.8, and Opus 4.7 take `low`–`max`; Opus 4.6 and Sonnet 4.6 lack `xhigh` (it becomes `max`); Opus 4.5 tops out at `high`. `none` and `minimal` become `low` everywhere. System prompts are automatically cached for 1 hour; cache stats appear in response metadata as `cache_creation_input_tokens` / `cache_read_input_tokens`.
533
555
 
534
556
  ### Mistral Models
535
557
 
@@ -592,7 +614,7 @@ All three are uncensored ("abliterated") reasoning models using Abliteration's O
592
614
  **Claude** is available through the Claude Agent SDK, using Claude Code CLI authentication instead of an API key:
593
615
 
594
616
  - **Model**: `claude` (namespaces: `claude`, `claude-code`, `claude-sdk`) — defaults to Claude Opus 5.5 (`claude-opus-5-5`), or `CLAUDE_DEFAULT_MODEL`
595
- - **Model selection**: `claude-opus-5-5` (`opus`, `claude-opus`, `opus-5.5`), `claude-opus-5` (`opus-5`), `claude-fable-5-1` (`fable`, `claude-fable`, `fable-5.1`), `claude-fable-5` (`fable-5`), `claude-sonnet-5-5` (`sonnet`, `claude-sonnet`, `sonnet-5.5`), e.g. `claude:opus`, `claude:fable`, `claude:sonnet`. Other names are rejected with suggestions.
617
+ - **Model selection**: `claude-opus-5-5` (`opus`, `claude-opus`, `opus-5.5`), `claude-opus-5` (`opus-5`), `claude-fable-5-1` (`fable`, `claude-fable`, `fable-5.1`), `claude-fable-5` (`fable-5`), `claude-sonnet-5-5` (`sonnet`, `claude-sonnet`, `sonnet-5.5`), `claude-haiku-5-5` (`haiku`, `claude-haiku`, `haiku-5.5`), e.g. `claude:opus`, `claude:fable`, `claude:sonnet`, `claude:haiku`. Other names are rejected with suggestions.
596
618
  - **Authentication**: `claude login` — no `ANTHROPIC_API_KEY` needed
597
619
  - **Availability**: the Claude Agent SDK is installed and `~/.claude/.credentials.json` exists (`$CLAUDE_CONFIG_DIR` when set), or `CLAUDE_CODE_OAUTH_TOKEN`/`ANTHROPIC_API_KEY` is in the environment; on macOS the Keychain login is assumed. An expired login is caught at call time and bare-name/`auto` routing fails over.
598
620
  - **Permissions**: runs with `bypassPermissions`
@@ -633,7 +655,7 @@ agy
633
655
  Reach these only with the `copilot:` namespace (also `github-copilot:`, `copilot-sdk:`; e.g. `copilot:gpt-6.1-sol`) — Copilot never serves bare model names. `copilot` alone uses GPT-6.1 Sol, or `COPILOT_DEFAULT_MODEL`. Available when the Copilot SDK is installed; uses your GitHub Copilot subscription (`gh auth login`) — no API key needed:
634
656
 
635
657
  - **OpenAI**: `gpt-6.1-sol` (aliases: `gpt-6`, `gpt-6.1`, `gpt-5`, `sol`), `gpt-6-sol`, `gpt-6-luna` (alias: `luna`), `gpt-5.6-sol` (alias: `gpt-5.6`), `gpt-5.6-terra`, `gpt-5.6-luna` (all accept `reasoning_effort`)
636
- - **Anthropic**: `claude-opus-5.5` (aliases: `opus`, `claude`), `claude-fable-5` (alias: `fable`), `claude-sonnet-5.5` (alias: `sonnet`), `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4.8`
658
+ - **Anthropic**: `claude-opus-5.5` (aliases: `opus`, `claude`), `claude-fable-5.1` (alias: `fable`), `claude-fable-5`, `claude-sonnet-5.5` (alias: `sonnet`), `claude-haiku-5.5` (alias: `haiku`), `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4.8`
637
659
  - **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`)
638
660
  - Any other `copilot:<id>` is rejected with suggestions
639
661
 
@@ -836,6 +858,8 @@ DEEPSEEK_API_KEY=...
836
858
  OPENROUTER_API_KEY=sk-or-...
837
859
  ABLITERATION_API_KEY=ak_...
838
860
  TYPESAFE_API_KEY=... # decide tool only
861
+ CLOUDFLARE_ACCOUNT_ID=... # decide tool only (Clef), with CLOUDFLARE_API_TOKEN
862
+ CLOUDFLARE_API_TOKEN=...
839
863
  ```
840
864
 
841
865
  **MCP client configuration:**
package/docs/PROVIDERS.md CHANGED
@@ -57,7 +57,8 @@ This guide documents all supported AI providers in the Converse MCP Server and t
57
57
  - `claude-sonnet-5-5` (aliases `sonnet`, `sonnet-5.5`, `claude-sonnet`) - Current Sonnet: speed and capability for everyday coding and agentic work; adaptive thinking, effort `low`–`max` (1M context, 128K output)
58
58
  - `claude-sonnet-4-6` (alias `sonnet-4.6`) - Previous Sonnet generation with adaptive thinking (64K output)
59
59
  - `claude-sonnet-4-5-20250929` - Legacy Sonnet (64K output)
60
- - `claude-haiku-4-5-20251001` (alias `haiku`) - Fast and intelligent with extended thinking (64K output)
60
+ - `claude-haiku-5-5` (aliases `haiku`, `haiku-5.5`, `claude-haiku`) - Current Haiku: fastest and cheapest Claude for high-volume and latency-sensitive work; adaptive thinking, effort `low`–`max` (1M context, 128K output)
61
+ - `claude-haiku-4-5-20251001` (alias `haiku-4.5`) - Previous Haiku generation with extended thinking (64K output)
61
62
 
62
63
  ### Mistral
63
64
  - **API Key Format**: 32+ character string
@@ -229,7 +230,8 @@ agy
229
230
  - `claude-fable-5-1` (aliases: `fable`, `claude-fable`, `fable-5.1`) - Claude Fable 5.1 (`claude:fable`)
230
231
  - `claude-fable-5` (alias: `fable-5`) - Claude Fable 5.0
231
232
  - `claude-sonnet-5-5` (aliases: `sonnet`, `claude-sonnet`, `sonnet-5.5`) - Claude Sonnet 5.5 (`claude:sonnet`)
232
- - Names outside this catalog are rejected with suggestions (e.g. `claude:haiku` suggests `anthropic:haiku`)
233
+ - `claude-haiku-5-5` (aliases: `haiku`, `claude-haiku`, `haiku-5.5`) - Claude Haiku 5.5 (`claude:haiku`)
234
+ - Names outside this catalog are rejected with suggestions (e.g. `claude:sonnet-4.6` suggests `anthropic:sonnet-4.6`)
233
235
 
234
236
  **Key Features:**
235
237
  - **Subscription Access**: Uses your Claude subscription instead of API credits
@@ -242,7 +244,7 @@ agy
242
244
  **Differences from Anthropic API Provider:**
243
245
  - **Authentication**: Claude Code login vs `ANTHROPIC_API_KEY`
244
246
  - **Billing**: Claude subscription vs pay-per-use API
245
- - **Model Routing**: `claude` and `claude:<model>` → SDK provider; `anthropic:<model>` → API provider; bare names both serve as the same model (`opus`, `sonnet`, `claude-opus-5-5`, `claude-opus-5`, `claude-sonnet-5-5`, `claude-fable-5`) → SDK first, then the API on authentication/availability failure; bare names only the API serves (e.g., `haiku`, `sonnet-4.6`) → 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.
247
+ - **Model Routing**: `claude` and `claude:<model>` → SDK provider; `anthropic:<model>` → API provider; bare names both serve as the same model (`opus`, `sonnet`, `haiku`, `claude-opus-5-5`, `claude-opus-5`, `claude-sonnet-5-5`, `claude-haiku-5-5`, `claude-fable-5`) → SDK first, then the API on authentication/availability failure; bare names only the API serves (e.g., `haiku-4.5`, `sonnet-4.6`) → 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.
246
248
  - **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.
247
249
 
248
250
  ### GitHub Copilot SDK
@@ -254,7 +256,7 @@ agy
254
256
  - **Supported Models** (namespace-only: reach them with `copilot:`, `github-copilot:` or `copilot-sdk:`, e.g. `copilot:gpt-6.1-sol`; Copilot never serves bare model names):
255
257
  - `copilot` - GPT-6.1 Sol, or `COPILOT_DEFAULT_MODEL`
256
258
  - OpenAI: `gpt-6.1-sol` (aliases: `gpt-6`, `gpt-6.1`, `gpt-5`, `sol`), `gpt-6-sol`, `gpt-6-luna` (alias: `luna`), `gpt-5.6-sol` (alias: `gpt-5.6`), `gpt-5.6-terra`, `gpt-5.6-luna`
257
- - Anthropic: `claude-opus-5.5` (aliases: `opus`, `claude`; Copilot Pro+/Max/Business/Enterprise), `claude-fable-5` (alias: `fable`), `claude-sonnet-5.5` (alias: `sonnet`), `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4.8`
259
+ - Anthropic: `claude-opus-5.5` (aliases: `opus`, `claude`; Copilot Pro+/Max/Business/Enterprise), `claude-fable-5.1` (alias: `fable`), `claude-fable-5`, `claude-sonnet-5.5` (alias: `sonnet`), `claude-haiku-5.5` (alias: `haiku`), `claude-sonnet-5`, `claude-opus-5`, `claude-opus-4.8`
258
260
  - 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`)
259
261
  - **Reasoning**: The GPT-6 and GPT-5.6 tiers accept `reasoning_effort` (clamped onto Copilot's `low`–`xhigh`).
260
262
  - **Unknown IDs**: Any other `copilot:<id>` is rejected with suggestions; only the IDs and aliases above are accepted.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "converse-mcp-server",
3
- "version": "4.5.2",
3
+ "version": "4.6.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",
@@ -93,22 +93,22 @@
93
93
  ".env.example"
94
94
  ],
95
95
  "dependencies": {
96
- "@anthropic-ai/claude-agent-sdk": "^0.3.288",
97
- "@anthropic-ai/sdk": "^0.131.0",
98
- "@github/copilot-sdk": "^1.0.16",
96
+ "@anthropic-ai/claude-agent-sdk": "^0.3.293",
97
+ "@anthropic-ai/sdk": "^0.132.0",
98
+ "@github/copilot-sdk": "^1.0.17",
99
99
  "@google/genai": "^2.27.0",
100
100
  "@lydell/node-pty": "1.2.0-beta.15",
101
101
  "@mistralai/mistralai": "^2.7.0",
102
- "@modelcontextprotocol/sdk": "^1.32.0",
103
- "@openai/codex-sdk": "^0.160.0",
102
+ "@modelcontextprotocol/sdk": "^1.32.1",
103
+ "@openai/codex-sdk": "^0.161.0",
104
104
  "cors": "^2.8.6",
105
- "dotenv": "^18.0.5",
105
+ "dotenv": "^18.0.6",
106
106
  "express": "^5.2.1",
107
107
  "lru-cache": "^11.5.3",
108
- "nanoid": "^6.0.1",
109
- "openai": "^7.27.0",
108
+ "nanoid": "^6.0.2",
109
+ "openai": "^7.30.0",
110
110
  "p-limit": "^7.3.3",
111
- "vite": "^8.3.2"
111
+ "vite": "^8.3.3"
112
112
  },
113
113
  "devDependencies": {
114
114
  "@vitest/coverage-v8": "^5.0.3",
package/src/config.js CHANGED
@@ -241,6 +241,18 @@ const CONFIG_SCHEMA = {
241
241
  description:
242
242
  'Optional OpenRouter attribution title, sent as the canonical X-OpenRouter-Title header',
243
243
  },
244
+ // Cloudflare Workers AI (Clef decision models for the decide tool)
245
+ CLOUDFLARE_ACCOUNT_ID: {
246
+ type: 'string',
247
+ required: false,
248
+ description: 'Cloudflare account ID for Workers AI (decide tool: Clef)',
249
+ },
250
+ CLOUDFLARE_API_TOKEN: {
251
+ type: 'string',
252
+ required: false,
253
+ secret: true,
254
+ description: 'Cloudflare API token with Workers AI access (decide tool: Clef)',
255
+ },
244
256
  OPENROUTER_DYNAMIC_MODELS: {
245
257
  type: 'boolean',
246
258
  default: false,
@@ -688,6 +700,9 @@ export async function loadConfig() {
688
700
  // API-key providers require keys; SDK-based providers (codex, claude, gemini-cli, copilot)
689
701
  // work via subscription auth — check if their packages are actually installed
690
702
  const availableKeys = Object.keys(config.apiKeys);
703
+ if (config.providers.cloudflareaccountid && config.providers.cloudflareapitoken) {
704
+ availableKeys.push('cloudflare');
705
+ }
691
706
  const hasVertexAI =
692
707
  config.providers.googlegenaiusevertexai &&
693
708
  config.providers.googlecloudproject &&
@@ -721,7 +736,7 @@ export async function loadConfig() {
721
736
 
722
737
  if (availableKeys.length === 0 && !hasVertexAI && !hasSdkProvider) {
723
738
  errors.push(
724
- '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, ABLITERATION_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).',
739
+ '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, ABLITERATION_API_KEY, TYPESAFE_API_KEY, or CLOUDFLARE_ACCOUNT_ID with CLOUDFLARE_API_TOKEN. Alternatively, configure Google Vertex AI or use an SDK-based provider (codex, claude, copilot) or the Antigravity CLI (gemini-cli).',
725
740
  );
726
741
  }
727
742
 
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Decision wire formats
3
+ *
4
+ * The `decide` tool speaks System One: a `state`, a map of typed questions
5
+ * (`noul` / `choice` / `score`), and a map of answers. Each format turns that
6
+ * into one host's request body and maps the host's response back to System One
7
+ * answer shapes, so the tool formats every host's answers the same way.
8
+ *
9
+ * Every format returns `{ model, answers, usage, id }` from `parse`.
10
+ * `request(model, state, questions, images)` receives images as
11
+ * `{ mimeType, base64 }`; `images: true` marks formats that can send them.
12
+ */
13
+
14
+ import { DecisionError } from './http.js';
15
+
16
+ function asText(value) {
17
+ return typeof value === 'string' ? value : JSON.stringify(value);
18
+ }
19
+
20
+ function dataUrl(image) {
21
+ return `data:${image.mimeType};base64,${image.base64}`;
22
+ }
23
+
24
+ /**
25
+ * A score level given as `{ label, description? }` rather than a single
26
+ * description. OpenAI's levels take that pair natively; System One only takes
27
+ * one description per level.
28
+ */
29
+ export function isLabeledLevel(level) {
30
+ return level !== null && typeof level === 'object' && !Array.isArray(level) && 'label' in level;
31
+ }
32
+
33
+ function flattenLevel(level) {
34
+ if (!isLabeledLevel(level)) return level;
35
+ return level.description === undefined ? level.label : `${level.label}: ${asText(level.description)}`;
36
+ }
37
+
38
+ /** Rewrite labeled score levels into System One's one-description form. */
39
+ function toSystemOneQuestions(questions) {
40
+ return Object.fromEntries(
41
+ Object.entries(questions).map(([name, question]) =>
42
+ question.type === 'score' && question.criteria.some(isLabeledLevel)
43
+ ? [name, { ...question, criteria: question.criteria.map(flattenLevel) }]
44
+ : [name, question],
45
+ ),
46
+ );
47
+ }
48
+
49
+ /**
50
+ * TypeSafe and OpenRouter serve System One natively. Jev is text only, and
51
+ * OpenRouter's image routing for other decision models is unverified, so this
52
+ * format sends no images.
53
+ */
54
+ export const systemOneFormat = {
55
+ images: false,
56
+ request: (model, state, questions) => ({ model, state, questions: toSystemOneQuestions(questions) }),
57
+ parse: (body) => body,
58
+ };
59
+
60
+ // Cloudflare rejects requests outside these bounds; checking first turns a
61
+ // nested validation dump into a clear message.
62
+ const CLEF_MAX_QUESTIONS = 64;
63
+ const CLEF_QUESTION_ID = /^[A-Za-z0-9_.-]{1,100}$/;
64
+ const CLEF_MAX_IMAGE_BYTES = 4 * 1024 * 1024;
65
+ const CLEF_MAX_TOTAL_IMAGE_BYTES = 8 * 1024 * 1024;
66
+ // Workers AI rejects a request whose estimated size exceeds the context
67
+ // window before running it, estimating one token per four characters of the
68
+ // body including base64 image data, though far fewer tokens are billed.
69
+ const CLEF_CONTEXT_TOKENS = 65536;
70
+ const CLEF_CHARS_PER_ESTIMATED_TOKEN = 4;
71
+
72
+ function decodedBytes(image) {
73
+ return Math.floor((image.base64.length * 3) / 4);
74
+ }
75
+
76
+ function checkClefImages(images, state, questions) {
77
+ const sizes = images.map(decodedBytes);
78
+ const tooLarge = sizes.findIndex((bytes) => bytes > CLEF_MAX_IMAGE_BYTES);
79
+ if (tooLarge !== -1) {
80
+ throw new DecisionError(`Clef accepts images up to 4 MiB each; image ${tooLarge + 1} is ${(sizes[tooLarge] / 1048576).toFixed(1)} MiB.`, { terminal: true });
81
+ }
82
+ if (sizes.reduce((a, b) => a + b, 0) > CLEF_MAX_TOTAL_IMAGE_BYTES) {
83
+ throw new DecisionError('Clef accepts at most 8 MiB of images per call.', { terminal: true });
84
+ }
85
+ const chars = images.reduce((n, image) => n + dataUrl(image).length, 0) + asText(state ?? '').length + JSON.stringify(questions).length;
86
+ const estimate = Math.ceil(chars / CLEF_CHARS_PER_ESTIMATED_TOKEN);
87
+ if (estimate > CLEF_CONTEXT_TOKENS) {
88
+ throw new DecisionError(
89
+ `Cloudflare estimates this request at ~${estimate} tokens (4 base64 characters per token), over Clef's ${CLEF_CONTEXT_TOKENS}-token window. ` +
90
+ 'Downscale or recompress the images (about 180 KB in total fits), or use gpt-6-luna.',
91
+ { terminal: true },
92
+ );
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Cloudflare Workers AI runs Clef with the System One request and answer
98
+ * shapes, wrapped in its `{ result, success, errors }` envelope. Clef adds up
99
+ * to four embedded images; `state` stays required, but may be empty when the
100
+ * images are the whole input.
101
+ */
102
+ export const cloudflareFormat = {
103
+ images: true,
104
+ request(model, state, questions, images = []) {
105
+ const ids = Object.keys(questions);
106
+ if (ids.length > CLEF_MAX_QUESTIONS) {
107
+ throw new DecisionError(`Clef accepts at most ${CLEF_MAX_QUESTIONS} questions per call (got ${ids.length}).`, { terminal: true });
108
+ }
109
+ const bad = ids.filter((id) => !CLEF_QUESTION_ID.test(id));
110
+ if (bad.length) {
111
+ throw new DecisionError(
112
+ `Clef question names may use only letters, digits, "_", ".", and "-" (max 100 characters): ${bad.join(', ')}.`,
113
+ { terminal: true },
114
+ );
115
+ }
116
+ const body = { model, state: state ?? '', questions: toSystemOneQuestions(questions) };
117
+ if (images.length === 0) return body;
118
+ checkClefImages(images, state, questions);
119
+ return { ...body, images: images.map(dataUrl) };
120
+ },
121
+ parse(body) {
122
+ if (body?.success === false) {
123
+ const detail = (body.errors || []).map((e) => e?.message || JSON.stringify(e)).join('; ');
124
+ throw new DecisionError(`Cloudflare reported failure: ${detail || 'no details'}`, { retryable: true });
125
+ }
126
+ return body?.result ?? body;
127
+ },
128
+ };
129
+
130
+ /**
131
+ * OpenAI's predicates take no criteria, so a noul question's true/false
132
+ * descriptions become part of its instructions.
133
+ */
134
+ function predicateInstructions(question) {
135
+ const instructions = asText(question.instructions);
136
+ if (!question.criteria) return instructions;
137
+ return `${instructions}\n\nAnswer true if: ${asText(question.criteria.true)}\nAnswer false if: ${asText(question.criteria.false)}`;
138
+ }
139
+
140
+ function toOpenAIQuestion(name, question) {
141
+ if (question.type === 'noul') {
142
+ return { type: 'predicate', name, instructions: predicateInstructions(question) };
143
+ }
144
+ if (question.type === 'choice') {
145
+ return {
146
+ type: 'choice',
147
+ name,
148
+ instructions: asText(question.instructions),
149
+ choices: Object.entries(question.criteria).map(([value, description]) =>
150
+ description === null || description === undefined ? { value } : { value, description: asText(description) },
151
+ ),
152
+ };
153
+ }
154
+ return {
155
+ type: 'score',
156
+ name,
157
+ instructions: asText(question.instructions),
158
+ levels: question.criteria.map((level) => {
159
+ if (!isLabeledLevel(level)) return { label: asText(level) };
160
+ return level.description === undefined
161
+ ? { label: level.label }
162
+ : { label: level.label, description: asText(level.description) };
163
+ }),
164
+ };
165
+ }
166
+
167
+ function fromOpenAIAnswer(answer) {
168
+ const { type, name: _name, ...rest } = answer;
169
+ if (type === 'predicate') {
170
+ return { type: 'noul', noul: answer.probability };
171
+ }
172
+ if (type === 'choice') {
173
+ return {
174
+ type: 'choice',
175
+ choice: answer.choice,
176
+ probabilities: Object.fromEntries((answer.probabilities || []).map((p) => [p.value, p.probability])),
177
+ confidence: answer.confidence,
178
+ };
179
+ }
180
+ if (type === 'score') {
181
+ const levels = answer.probabilities || [];
182
+ return {
183
+ type: 'score',
184
+ score: answer.score,
185
+ legend: Object.fromEntries(levels.map((p) => [String(p.value), p.label])),
186
+ probabilities: Object.fromEntries(levels.map((p) => [String(p.value), p.probability])),
187
+ confidence: answer.confidence,
188
+ };
189
+ }
190
+ return { type, ...rest };
191
+ }
192
+
193
+ /**
194
+ * OpenAI's Decisions API (`POST /v1/decisions`) asks the same three kinds of
195
+ * question in its own schema: string `input` instead of `state`, a question
196
+ * array with `name`, `predicate` for `noul`, `choices` and `levels` arrays,
197
+ * and answers as an array with probability lists. It only accepts strings for
198
+ * `input` and `instructions`, so structured values are sent as JSON text.
199
+ * Images turn `input` into one user message of text and `input_image` parts.
200
+ */
201
+ export const openaiFormat = {
202
+ images: true,
203
+ request(model, state, questions, images = []) {
204
+ const input = images.length === 0
205
+ ? asText(state)
206
+ : [{
207
+ role: 'user',
208
+ content: [
209
+ ...(state === undefined ? [] : [{ type: 'input_text', text: asText(state) }]),
210
+ ...images.map((image) => ({ type: 'input_image', image_url: dataUrl(image) })),
211
+ ],
212
+ }];
213
+ return {
214
+ model,
215
+ input,
216
+ questions: Object.entries(questions).map(([name, question]) => toOpenAIQuestion(name, question)),
217
+ };
218
+ },
219
+ parse(body) {
220
+ if (!Array.isArray(body?.answers)) return body;
221
+ const answers = Object.fromEntries(body.answers.map((answer) => [answer.name, fromOpenAIAnswer(answer)]));
222
+ return {
223
+ model: body.model,
224
+ answers,
225
+ usage: body.usage ? { input_tokens: body.usage.input_tokens, output_tokens: body.usage.output_tokens } : undefined,
226
+ ...(body.id ? { id: body.id } : {}),
227
+ };
228
+ },
229
+ };