@jaypie/mcp 0.8.184 → 0.8.186
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/dist/assets.js +3 -3
- package/package.json +1 -1
- package/release-notes/jaypie/1.2.120.md +9 -0
- package/release-notes/jaypie/1.2.121.md +9 -0
- package/release-notes/llm/1.3.61.md +28 -0
- package/release-notes/llm/1.3.62.md +22 -0
- package/release-notes/mcp/0.8.185.md +10 -0
- package/release-notes/mcp/0.8.186.md +10 -0
- package/release-notes/testkit/1.2.83.md +9 -0
- package/skills/llm.md +56 -35
package/dist/assets.js
CHANGED
|
@@ -55,11 +55,11 @@ function getMcpAssetPaths() {
|
|
|
55
55
|
*/
|
|
56
56
|
function getMcpBuildInfo() {
|
|
57
57
|
return {
|
|
58
|
-
commit: "
|
|
58
|
+
commit: "5f9c6bfe"
|
|
59
59
|
,
|
|
60
|
-
version: "0.8.
|
|
60
|
+
version: "0.8.186"
|
|
61
61
|
,
|
|
62
|
-
versionString: "@jaypie/mcp@0.8.
|
|
62
|
+
versionString: "@jaypie/mcp@0.8.186#5f9c6bfe"
|
|
63
63
|
,
|
|
64
64
|
};
|
|
65
65
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.3.61
|
|
3
|
+
date: 2026-10-07
|
|
4
|
+
summary: modelOptions sets provider options per model for every attempt in a chain; providerOptions and send are deprecated
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
### `modelOptions` (issue #614)
|
|
10
|
+
|
|
11
|
+
`providerOptions` reached the primary only, so a fallback ran on defaults unless the caller rebuilt the chain with options on every entry.
|
|
12
|
+
|
|
13
|
+
- `modelOptions` (`LlmModelOptions`) on `operate` and `stream` keys provider options by exact model id, `MODEL` catalog key, provider (or the `gemini` alias), or `default`. Each attempt (primary, fallback, linger pass) resolves it against its own model.
|
|
14
|
+
- Every matching key merges, least to most specific: `default` → provider → catalog key → exact id. Objects merge deeply; arrays and scalars replace.
|
|
15
|
+
- First-class `effort`, `temperature`, and `format` still win over resolved options.
|
|
16
|
+
- When `modelOptions` is set, `providerOptions` (call and chain entry) is ignored.
|
|
17
|
+
- `LlmExchangeRequest.providerOptions` records the options each attempt sent.
|
|
18
|
+
- `resolveModelOptions({ model, modelOptions, provider? })` returns `{ keys, model, options, provider }`.
|
|
19
|
+
|
|
20
|
+
### Deprecations
|
|
21
|
+
|
|
22
|
+
- `providerOptions` on `LlmOperateOptions` and `LlmFallbackConfig` is deprecated and removed in 2.0. Use `modelOptions`.
|
|
23
|
+
- `Llm.send` (instance and static) is deprecated and receives no new features. Use `operate`.
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
|
|
27
|
+
- `modelOptions.spec.ts` covers merge order, deep merge, array replacement, the provider alias, ignoring `providerOptions`, and the issue's example through the OpenAI and Google adapters.
|
|
28
|
+
- `OperateLoop` and `StreamLoop` specs cover resolution and the exchange record; `Llm` specs cover `modelOptions` reaching every fallback and the linger pass.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.3.62
|
|
3
|
+
date: 2026-10-08
|
|
4
|
+
summary: Anthropic format schemas with more than 16 nullable or union-typed parameters answer through the structured_output tool path
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
### Union parameter limit (issue #616)
|
|
10
|
+
|
|
11
|
+
Anthropic rejects native structured output (`output_config.format`) when a schema carries more than 16 union-typed parameters, and every `.nullable()` field is one. The 400 ("Schemas contains too many parameters with union types") matched none of the patterns that trigger the `structured_output` tool fallback, so `operate` failed, and an Anthropic fallback in a chain could not serve such a schema.
|
|
12
|
+
|
|
13
|
+
- `AnthropicAdapter.buildRequest` counts the schema's union-typed parameters (`anyOf` or a `type` array, at any depth) and sends a schema over 16 straight to the tool path, avoiding the rejected round trip.
|
|
14
|
+
- The union-limit 400 is retried through the tool path for the request that received it.
|
|
15
|
+
- Neither path demotes the model. The limit belongs to the schema, so the same model keeps native `output_config.format` for smaller schemas.
|
|
16
|
+
- Both log at debug.
|
|
17
|
+
- Citations and deprecated-field 400s still propagate unchanged.
|
|
18
|
+
|
|
19
|
+
## Testing
|
|
20
|
+
|
|
21
|
+
- `AnthropicAdapter.spec.ts` covers the retry, the model staying native afterward, the count at 16 and 17, `type` arrays and nested unions, and the debug log on each path.
|
|
22
|
+
- Verified live on `claude-sonnet-5-5` and `claude-opus-5-5`: the API accepts 16 union-typed parameters natively and rejects 17.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 0.8.185
|
|
3
|
+
date: 2026-10-07
|
|
4
|
+
summary: Release notes for @jaypie/llm 1.3.61, testkit 1.2.83, and jaypie 1.2.120; llm skill documents modelOptions and the providerOptions and send deprecations
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- **Release notes**: `@jaypie/llm` 1.3.61, `@jaypie/testkit` 1.2.83, and `jaypie` 1.2.120
|
|
10
|
+
- **`llm` skill**: `modelOptions`, `resolveModelOptions`, and the deprecation of `providerOptions` and `send()`
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 0.8.186
|
|
3
|
+
date: 2026-10-08
|
|
4
|
+
summary: Release notes for @jaypie/llm 1.3.62 and jaypie 1.2.121; llm skill documents Anthropic's union parameter limit
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- **Release notes**: `@jaypie/llm` 1.3.62 and `jaypie` 1.2.121
|
|
10
|
+
- **`llm` skill**: Anthropic's limit of 16 union-typed parameters on native structured output
|
package/skills/llm.md
CHANGED
|
@@ -138,9 +138,11 @@ Completions `length`, Anthropic `max_tokens`, and OpenAI `max_output_tokens`
|
|
|
138
138
|
all read `max_tokens`). The provider's own value rides
|
|
139
139
|
`exchange.response.stopReason`.
|
|
140
140
|
|
|
141
|
-
### send() - Simple Completions
|
|
141
|
+
### send() - Simple Completions (deprecated)
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
`send()` is deprecated and receives no new features (fallback, `effort`,
|
|
144
|
+
`modelOptions`, tools). Prefer `operate()`, with `format` for structured
|
|
145
|
+
output. For single-shot text completions:
|
|
144
146
|
|
|
145
147
|
```typescript
|
|
146
148
|
const response = await Llm.send("Explain REST APIs", {
|
|
@@ -408,6 +410,8 @@ const result = await Llm.operate(
|
|
|
408
410
|
);
|
|
409
411
|
```
|
|
410
412
|
|
|
413
|
+
Anthropic's native structured output accepts at most 16 union-typed parameters per schema, and every `.nullable()` field counts as one. A larger schema is answered through a `structured_output` tool call instead and still returns parsed JSON in `content`.
|
|
414
|
+
|
|
411
415
|
### Bare JSON Schema
|
|
412
416
|
|
|
413
417
|
`format` also duck-types a bare JSON Schema object node — `{ type: "object", properties: {...} }` — without needing the OpenAI-style `{ type: "json_schema", ... }` envelope. `required` is honored.
|
|
@@ -859,23 +863,41 @@ An incomplete `content` is the raw partial text, a string even when `format` was
|
|
|
859
863
|
|
|
860
864
|
A `format` request answered with **no content** (zero output tokens, an empty candidate, a blocked prompt) settles incomplete with `error.detail` `Model returned no content: <stop reason>`, logged at `warn`, and `stopReason: "other"`. An adapter that supports the corrective turn tries it once first. A chain moves past an empty response like any cut-off, and an earlier partial is kept over a later empty one. When nothing but empty responses come back, `operate` throws `LlmIncompleteError` whatever `incomplete` says: there is no partial to return (issue #608).
|
|
861
865
|
|
|
862
|
-
###
|
|
866
|
+
### Model Options in a Chain
|
|
867
|
+
|
|
868
|
+
`modelOptions` sets provider-specific request fields per model. Every attempt (primary, each fallback, and the linger pass) resolves the map against its own model, so options shaped for one provider (OpenAI `reasoning`, Gemini `thinkingConfig`) reach every model they fit and no other. Keys match the way [effort maps](#per-model-effort) do: exact model id, `MODEL` catalog key, provider (or alias such as `gemini`), `default`.
|
|
863
869
|
|
|
864
|
-
|
|
870
|
+
Every matching key merges, least to most specific: `default` → provider → catalog key → exact id. Objects merge deeply; arrays and scalars replace. First-class fields (`effort`, `temperature`, `format`) still win.
|
|
865
871
|
|
|
866
872
|
```typescript
|
|
867
|
-
|
|
873
|
+
await llm.operate(input, {
|
|
874
|
+
model: LLM.MODEL.SOL,
|
|
868
875
|
fallback: [
|
|
869
|
-
{
|
|
870
|
-
|
|
871
|
-
model: LLM.MODEL.GEMINI_FLASH,
|
|
872
|
-
providerOptions: { thinkingConfig: { thinkingBudget: 1024 } },
|
|
873
|
-
},
|
|
876
|
+
{ provider: "google", model: LLM.MODEL.GEMINI_FLASH },
|
|
877
|
+
{ provider: "openai", model: "gpt-6-mini" },
|
|
874
878
|
],
|
|
875
|
-
|
|
879
|
+
effort: { gemini_flash: "low", default: "medium" },
|
|
880
|
+
modelOptions: {
|
|
881
|
+
openai: { reasoning: { summary: "detailed" } },
|
|
882
|
+
gemini_flash: { thinkingConfig: { includeThoughts: true } },
|
|
883
|
+
},
|
|
876
884
|
});
|
|
885
|
+
// gpt-6-sol, gpt-6-mini: reasoning { summary: "detailed", effort: "medium" }
|
|
886
|
+
// gemini-3.8-flash: thinkingConfig { includeThoughts: true, thinkingLevel: "LOW" }
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
The exchange records the merged options each attempt sent (`LlmExchangeRequest.providerOptions`). `resolveModelOptions` returns them for one model, with the keys that matched in merge order:
|
|
890
|
+
|
|
891
|
+
```typescript
|
|
892
|
+
import { resolveModelOptions } from "@jaypie/llm";
|
|
893
|
+
|
|
894
|
+
resolveModelOptions({ model: LLM.MODEL.SOL, modelOptions });
|
|
895
|
+
// → { keys: ["openai"], model: "gpt-6-sol", provider: "openai",
|
|
896
|
+
// options: { reasoning: { summary: "detailed" } } }
|
|
877
897
|
```
|
|
878
898
|
|
|
899
|
+
`providerOptions` is deprecated and removed in 2.0. Until then, per-call `providerOptions` reach the primary only (including its linger pass) and a fallback receives only the `providerOptions` on its own entry. When `modelOptions` is set, `providerOptions` (call and chain entry) is ignored.
|
|
900
|
+
|
|
879
901
|
### Attempt Timeout
|
|
880
902
|
|
|
881
903
|
`timeout` sets a per-attempt deadline in milliseconds. Without it, a provider that accepts the connection but never answers holds the chain until the runtime gives up (undici waits 300 s for response headers). A stalled attempt is aborted and throws `LlmTimeoutError` (a `LlmTransientError`, status 504, with `timeoutMs`):
|
|
@@ -1066,10 +1088,10 @@ budget runs out. Those errors carry `error.reason` (`LlmResponseErrorReason`)
|
|
|
1066
1088
|
because status alone cannot identify them: an exhausted turn budget is a 429,
|
|
1067
1089
|
exactly like a provider rate limit.
|
|
1068
1090
|
|
|
1069
|
-
| `reason` | Status | Meaning
|
|
1070
|
-
| ------------- | ------ |
|
|
1071
|
-
| `max_turns` | 429 | The model asked for another tool call after `turns` ran out. Nothing failed; the run did not converge.
|
|
1072
|
-
| `tool_errors` | 502 | Tool execution failed six times in a row and the loop stopped.
|
|
1091
|
+
| `reason` | Status | Meaning |
|
|
1092
|
+
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1093
|
+
| `max_turns` | 429 | The model asked for another tool call after `turns` ran out. Nothing failed; the run did not converge. |
|
|
1094
|
+
| `tool_errors` | 502 | Tool execution failed six times in a row and the loop stopped. |
|
|
1073
1095
|
| `incomplete` | 502 | The provider cut the model off before it finished (an output token ceiling, a content filter). `content` holds the partial text; `error.detail` names the provider's reason and `stopReason` normalizes it. A fallback chain moves past it. With `format`, empty content settles here too and throws once the chain is spent. |
|
|
1074
1096
|
|
|
1075
1097
|
```typescript
|
|
@@ -1132,7 +1154,8 @@ interface LlmOperateOptions {
|
|
|
1132
1154
|
hooks?: LlmHooks; // Lifecycle callbacks
|
|
1133
1155
|
instructions?: string; // Additional instructions
|
|
1134
1156
|
model?: string; // Model override
|
|
1135
|
-
|
|
1157
|
+
modelOptions?: LlmModelOptions; // Provider-specific request fields per model, every attempt
|
|
1158
|
+
providerOptions?: JsonObject; // Deprecated: primary only; ignored when modelOptions is set
|
|
1136
1159
|
system?: string; // System prompt
|
|
1137
1160
|
temperature?: number; // Sampling temperature (0-2)
|
|
1138
1161
|
tools?: LlmTool[] | Toolkit; // Available tools
|
|
@@ -1145,7 +1168,7 @@ interface LlmFallbackConfig {
|
|
|
1145
1168
|
model?: string; // Model to use (optional, uses provider default)
|
|
1146
1169
|
apiKey?: string; // API key (optional, uses environment variable)
|
|
1147
1170
|
effort?: LlmEffort; // Effort for this entry only, replacing the call's effort
|
|
1148
|
-
providerOptions?: JsonObject; //
|
|
1171
|
+
providerOptions?: JsonObject; // Deprecated: this entry only; ignored when modelOptions is set
|
|
1149
1172
|
timeout?: number | false; // Per-attempt deadline for this entry only
|
|
1150
1173
|
}
|
|
1151
1174
|
```
|
|
@@ -1275,9 +1298,10 @@ OpenRouter path). Comparing `native` across levels shows which are distinct on
|
|
|
1275
1298
|
a model: `lowest` and `low` on Claude both send `{ effort: "low" }`, so one of
|
|
1276
1299
|
the two is redundant in an effort sweep.
|
|
1277
1300
|
|
|
1278
|
-
First-class `effort` takes precedence over a raw `
|
|
1279
|
-
(same convention as `temperature`). For control the neutral
|
|
1280
|
-
express (an exact token budget, OpenAI `none`), use
|
|
1301
|
+
First-class `effort` takes precedence over a raw `reasoning` in
|
|
1302
|
+
`modelOptions` (same convention as `temperature`). For control the neutral
|
|
1303
|
+
scale does not express (an exact token budget, OpenAI `none`), use
|
|
1304
|
+
`modelOptions` directly.
|
|
1281
1305
|
|
|
1282
1306
|
## Prompt Caching
|
|
1283
1307
|
|
|
@@ -1307,9 +1331,9 @@ tokens surface as `cacheRead`/`cacheWrite` on usage (with `cacheWriteTtl`
|
|
|
1307
1331
|
splitting writes by TTL on Anthropic and Bedrock), in the exchange envelope
|
|
1308
1332
|
`usageTotals`, and in the report tally.
|
|
1309
1333
|
|
|
1310
|
-
##
|
|
1334
|
+
## Model Options and Output Limits
|
|
1311
1335
|
|
|
1312
|
-
`
|
|
1336
|
+
`modelOptions` passes provider-specific request fields straight through to
|
|
1313
1337
|
the underlying API: Anthropic merges them into the Messages request body;
|
|
1314
1338
|
Google merges them into the generation config.
|
|
1315
1339
|
|
|
@@ -1324,19 +1348,16 @@ truncate:
|
|
|
1324
1348
|
- **Streaming** (`stream()`): the model maximum — e.g., 128,000 for current
|
|
1325
1349
|
Claude models (64,000 for Haiku), 65,536 for Gemini 2.5/3.x
|
|
1326
1350
|
|
|
1327
|
-
Override per
|
|
1351
|
+
Override per model with `modelOptions`:
|
|
1328
1352
|
|
|
1329
1353
|
```typescript
|
|
1330
|
-
// Anthropic: max_tokens
|
|
1331
1354
|
await Llm.operate(input, {
|
|
1332
1355
|
model: LLM.MODEL.SONNET,
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
// Google: maxOutputTokens
|
|
1337
|
-
|
|
1338
|
-
model: LLM.MODEL.GEMINI_PRO,
|
|
1339
|
-
providerOptions: { maxOutputTokens: 32000 },
|
|
1356
|
+
fallback: [{ provider: "google", model: LLM.MODEL.GEMINI_PRO }],
|
|
1357
|
+
modelOptions: {
|
|
1358
|
+
anthropic: { max_tokens: 32000 }, // Anthropic: max_tokens
|
|
1359
|
+
google: { maxOutputTokens: 32000 }, // Google: maxOutputTokens
|
|
1360
|
+
},
|
|
1340
1361
|
});
|
|
1341
1362
|
```
|
|
1342
1363
|
|
|
@@ -1345,13 +1366,13 @@ A chain that mixes them with Anthropic, Google, or Mistral therefore mixes
|
|
|
1345
1366
|
output budgets: a Gemini primary resolves to 16,384 non-streaming while an
|
|
1346
1367
|
OpenAI fallback sends no ceiling, so swapping the primary moves the effective
|
|
1347
1368
|
budget with nothing at the call site changing. Reasoning tokens count against
|
|
1348
|
-
the ceiling. Set `
|
|
1349
|
-
|
|
1369
|
+
the ceiling. Set `maxOutputTokens` (Google) or `max_tokens` (Anthropic,
|
|
1370
|
+
Mistral) in `modelOptions` to pin it per model.
|
|
1350
1371
|
Mistral is capped (32,768 streaming / 16,384 non-streaming) despite publishing
|
|
1351
1372
|
no low ceiling: a Mistral model can degenerate into restating its answer when
|
|
1352
1373
|
`format` and tools are combined, and an uncapped completion turns that into a
|
|
1353
|
-
multi-minute request. Override with `
|
|
1354
|
-
OpenRouter varies by routed model; pass `max_tokens` via `
|
|
1374
|
+
multi-minute request. Override with `modelOptions: { mistral: { max_tokens } }`.
|
|
1375
|
+
OpenRouter varies by routed model; pass `max_tokens` via `modelOptions`
|
|
1355
1376
|
when needed. A truncated response settles `status: "incomplete"` with
|
|
1356
1377
|
`stopReason: "max_tokens"` and moves a fallback chain to its next entry;
|
|
1357
1378
|
raise the limit or switch to `stream()`.
|