stitchkit 0.101.2 → 0.101.3

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/CHANGELOG.md CHANGED
@@ -15,6 +15,17 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.101.3] — 2026-09-29
19
+
20
+ ### Fixed
21
+
22
+ - **`stitchkit/tools/connections`** — a discovered MCP command whose tool
23
+ answers with no content parts (a tool without an output contract) prints
24
+ `null` on the CLI and exits `0`. It printed the whole envelope — the
25
+ server's `_meta` and an empty `content` — as if that were the answer; a
26
+ failed call with no parts now fails as `UPSTREAM_TOOL_ERROR` without
27
+ relaying that envelope either.
28
+
18
29
  ## [0.101.2] — 2026-09-29
19
30
 
20
31
  ### Added
@@ -529,6 +529,8 @@ function unwrapMcpResult(result) {
529
529
  return result.structuredContent;
530
530
  if (!Array.isArray(result.content))
531
531
  return result;
532
+ if (result.content.length === 0)
533
+ return null;
532
534
  const only = result.content.length === 1 ? result.content[0] : undefined;
533
535
  if (!isRecord(only) || only.type !== "text" || typeof only.text !== "string")
534
536
  return result;
@@ -12,6 +12,10 @@
12
12
  * So the CLI unwraps, and only the CLI. The order is the server's own order of
13
13
  * preference: `structuredContent` is the answer when the server sent one; a lone
14
14
  * text part is the answer when it parses as JSON, and its text when it does not.
15
+ * No parts at all is no answer, and says so as JSON: `null`. The envelope around
16
+ * an empty `content` carries only protocol metadata, and `_meta` is never an
17
+ * answer. Not `undefined`: a discovered tool declares an output, and an absent
18
+ * value there is a broken handler, not an empty one.
15
19
  * Anything else — several parts, an image, audio — is passed through whole,
16
20
  * because picking one part out of many would be inventing an answer.
17
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-envelope.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAIjD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAaxD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,QAAQ,CAwB1E"}
1
+ {"version":3,"file":"mcp-envelope.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAIjD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAcxD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,QAAQ,CAwB1E"}
package/llms/cli.txt CHANGED
@@ -912,9 +912,11 @@ kind, because the handler's value is what gets printed, piped and aggregated:
912
912
  handed the envelope, `--count-by status` groups the *content parts* and answers
913
913
  `no record carries the field "status" — available: text, type`. So the CLI
914
914
  transport unwraps, and only it: `structuredContent` when the server sent one, a
915
- lone text part when it parses as JSON, its text when it does not. Several parts,
916
- an image or audio pass through whole — picking one of many would be inventing an
917
- answer.
915
+ lone text part when it parses as JSON, its text when it does not, and `null` when
916
+ there are no parts at all — a tool without an output contract answers that way,
917
+ and the `_meta` beside an empty `content` is protocol metadata, never the answer.
918
+ Several parts, an image or audio pass through whole — picking one of many would
919
+ be inventing an answer.
918
920
 
919
921
 
920
922
  ## Auth parity
package/llms-full.txt CHANGED
@@ -9067,9 +9067,11 @@ kind, because the handler's value is what gets printed, piped and aggregated:
9067
9067
  handed the envelope, `--count-by status` groups the *content parts* and answers
9068
9068
  `no record carries the field "status" — available: text, type`. So the CLI
9069
9069
  transport unwraps, and only it: `structuredContent` when the server sent one, a
9070
- lone text part when it parses as JSON, its text when it does not. Several parts,
9071
- an image or audio pass through whole — picking one of many would be inventing an
9072
- answer.
9070
+ lone text part when it parses as JSON, its text when it does not, and `null` when
9071
+ there are no parts at all — a tool without an output contract answers that way,
9072
+ and the `_meta` beside an empty `content` is protocol metadata, never the answer.
9073
+ Several parts, an image or audio pass through whole — picking one of many would
9074
+ be inventing an answer.
9073
9075
 
9074
9076
 
9075
9077
  ## Auth parity
package/llms.txt CHANGED
@@ -17,7 +17,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
17
17
  - **stitchkit/tools/contract**: [llms/tools-contract.txt](llms/tools-contract.txt) 0.2 KB
18
18
  - **stitchkit/tools/invoker**: [llms/tools-invoker.txt](llms/tools-invoker.txt) 0.2 KB
19
19
  - **stitchkit/tools/connections**: [llms/tools-connections.txt](llms/tools-connections.txt) 3.1 KB
20
- - **stitchkit/cli**: [llms/cli.txt](llms/cli.txt) 42.7 KB → [llms/cli.2.txt](llms/cli.2.txt) 8.1 KB
20
+ - **stitchkit/cli**: [llms/cli.txt](llms/cli.txt) 42.8 KB → [llms/cli.2.txt](llms/cli.2.txt) 8.1 KB
21
21
  - **stitchkit/remote**: [llms/remote.txt](llms/remote.txt) 1.0 KB
22
22
  - **stitchkit/contract**: [llms/contract.txt](llms/contract.txt) 31.9 KB
23
23
  - **stitchkit/primitives**: [llms/primitives.txt](llms/primitives.txt) 9.3 KB
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.101.2",
3
+ "version": "0.101.3",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",