toolcraft 0.0.17 → 0.0.19

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.
Files changed (109) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +833 -124
  3. package/dist/error-report.d.ts +39 -0
  4. package/dist/error-report.js +330 -0
  5. package/dist/human-in-loop/approval-tasks.js +11 -8
  6. package/dist/human-in-loop/approvals-commands.js +21 -20
  7. package/dist/human-in-loop/default-provider.js +5 -3
  8. package/dist/human-in-loop/runner.js +45 -4
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +55 -35
  11. package/dist/json-schema-converter.d.ts +1 -0
  12. package/dist/json-schema-converter.js +102 -52
  13. package/dist/mcp-proxy.d.ts +1 -0
  14. package/dist/mcp-proxy.js +13 -6
  15. package/dist/mcp.d.ts +2 -0
  16. package/dist/mcp.js +131 -55
  17. package/dist/sdk.d.ts +4 -2
  18. package/dist/sdk.js +132 -48
  19. package/dist/source-snippet.d.ts +8 -0
  20. package/dist/source-snippet.js +42 -0
  21. package/dist/stack-trim.d.ts +4 -0
  22. package/dist/stack-trim.js +70 -0
  23. package/dist/suggest.d.ts +4 -0
  24. package/dist/suggest.js +46 -0
  25. package/dist/user-error.d.ts +3 -0
  26. package/dist/user-error.js +7 -1
  27. package/dist/validation-errors.d.ts +5 -0
  28. package/dist/validation-errors.js +18 -0
  29. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +3 -3
  30. package/node_modules/@poe-code/config-mutations/dist/mutations/template-mutation.d.ts +3 -3
  31. package/node_modules/@poe-code/config-mutations/dist/template/render.d.ts +0 -1
  32. package/node_modules/@poe-code/config-mutations/dist/template/render.js +2 -22
  33. package/node_modules/@poe-code/config-mutations/package.json +1 -4
  34. package/node_modules/@poe-code/design-system/dist/acp/components.js +15 -13
  35. package/node_modules/@poe-code/design-system/dist/components/color.d.ts +31 -0
  36. package/node_modules/@poe-code/design-system/dist/components/color.js +101 -0
  37. package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +1 -0
  38. package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +1 -1
  39. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +4 -0
  40. package/node_modules/@poe-code/design-system/dist/components/index.js +2 -0
  41. package/node_modules/@poe-code/design-system/dist/components/logger.js +2 -2
  42. package/node_modules/@poe-code/design-system/dist/components/symbols.js +3 -3
  43. package/node_modules/@poe-code/design-system/dist/components/table.js +191 -40
  44. package/node_modules/@poe-code/design-system/dist/components/template.d.ts +6 -0
  45. package/node_modules/@poe-code/design-system/dist/components/template.js +271 -0
  46. package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
  47. package/node_modules/@poe-code/design-system/dist/components/text.js +11 -3
  48. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +20 -13
  49. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +5 -0
  50. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +146 -12
  51. package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +31 -0
  52. package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
  53. package/node_modules/@poe-code/design-system/dist/explorer/actions.d.ts +16 -0
  54. package/node_modules/@poe-code/design-system/dist/explorer/actions.js +39 -0
  55. package/node_modules/@poe-code/design-system/dist/explorer/demo.d.ts +13 -0
  56. package/node_modules/@poe-code/design-system/dist/explorer/demo.js +297 -0
  57. package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +61 -0
  58. package/node_modules/@poe-code/design-system/dist/explorer/events.js +1 -0
  59. package/node_modules/@poe-code/design-system/dist/explorer/filter.d.ts +10 -0
  60. package/node_modules/@poe-code/design-system/dist/explorer/filter.js +95 -0
  61. package/node_modules/@poe-code/design-system/dist/explorer/index.d.ts +8 -0
  62. package/node_modules/@poe-code/design-system/dist/explorer/index.js +8 -0
  63. package/node_modules/@poe-code/design-system/dist/explorer/jobs.d.ts +7 -0
  64. package/node_modules/@poe-code/design-system/dist/explorer/jobs.js +59 -0
  65. package/node_modules/@poe-code/design-system/dist/explorer/keymap.d.ts +21 -0
  66. package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +363 -0
  67. package/node_modules/@poe-code/design-system/dist/explorer/layout.d.ts +20 -0
  68. package/node_modules/@poe-code/design-system/dist/explorer/layout.js +73 -0
  69. package/node_modules/@poe-code/design-system/dist/explorer/reducer.d.ts +9 -0
  70. package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +704 -0
  71. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.d.ts +4 -0
  72. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +96 -0
  73. package/node_modules/@poe-code/design-system/dist/explorer/render/footer.d.ts +4 -0
  74. package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +49 -0
  75. package/node_modules/@poe-code/design-system/dist/explorer/render/header.d.ts +4 -0
  76. package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +56 -0
  77. package/node_modules/@poe-code/design-system/dist/explorer/render/index.d.ts +8 -0
  78. package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +61 -0
  79. package/node_modules/@poe-code/design-system/dist/explorer/render/list.d.ts +4 -0
  80. package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +106 -0
  81. package/node_modules/@poe-code/design-system/dist/explorer/render/modal.d.ts +3 -0
  82. package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +91 -0
  83. package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.d.ts +8 -0
  84. package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.js +156 -0
  85. package/node_modules/@poe-code/design-system/dist/explorer/runtime.d.ts +2 -0
  86. package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +282 -0
  87. package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.d.ts +50 -0
  88. package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.js +101 -0
  89. package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +130 -0
  90. package/node_modules/@poe-code/design-system/dist/explorer/state.js +87 -0
  91. package/node_modules/@poe-code/design-system/dist/explorer/theme.d.ts +27 -0
  92. package/node_modules/@poe-code/design-system/dist/explorer/theme.js +97 -0
  93. package/node_modules/@poe-code/design-system/dist/index.d.ts +7 -0
  94. package/node_modules/@poe-code/design-system/dist/index.js +5 -0
  95. package/node_modules/@poe-code/design-system/dist/internal/color-support.d.ts +9 -0
  96. package/node_modules/@poe-code/design-system/dist/internal/color-support.js +12 -0
  97. package/node_modules/@poe-code/design-system/dist/prompts/index.js +2 -2
  98. package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +2 -2
  99. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -2
  100. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +4 -4
  101. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +5 -5
  102. package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +2 -2
  103. package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +3 -3
  104. package/node_modules/@poe-code/design-system/dist/static/menu.js +5 -5
  105. package/node_modules/@poe-code/design-system/dist/static/spinner.js +8 -8
  106. package/node_modules/@poe-code/design-system/dist/tokens/colors.js +29 -29
  107. package/node_modules/@poe-code/design-system/dist/tokens/typography.js +6 -6
  108. package/node_modules/@poe-code/design-system/package.json +6 -3
  109. package/package.json +6 -5
package/dist/mcp.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type SDKTransport, type Server as TinyServer } from "tiny-stdio-mcp-server";
2
2
  import type { Group } from "./index.js";
3
+ import { type ErrorReportsOption } from "./error-report.js";
3
4
  import { type HumanInLoopRuntimeOptions } from "./human-in-loop/index.js";
4
5
  type Casing = "snake" | "camel";
5
6
  type CmdkitServer = Omit<TinyServer, "connect"> & {
@@ -27,6 +28,7 @@ export interface RunMCPOptions<TServices extends object = Record<string, unknown
27
28
  */
28
29
  omitRootToolNamePrefix?: boolean;
29
30
  services?: TServices;
31
+ errorReports?: ErrorReportsOption;
30
32
  /**
31
33
  * Controls MCP input-schema key casing and accepted argument-key casing.
32
34
  *
package/dist/mcp.js CHANGED
@@ -1,14 +1,28 @@
1
1
  import { access, readFile, writeFile } from "node:fs/promises";
2
- import { createServer, JSON_RPC_ERROR_CODES, ToolError, } from "tiny-stdio-mcp-server";
2
+ import { createServer, JSON_RPC_ERROR_CODES, ToolError } from "tiny-stdio-mcp-server";
3
3
  import { toJsonSchema } from "toolcraft-schema";
4
- import { UserError, assertCommandRequirements, resolveCommandSecrets } from "./index.js";
4
+ import { ToolcraftBugError, UserError, assertCommandRequirements, resolveCommandSecrets } from "./index.js";
5
+ import { writeErrorReport } from "./error-report.js";
5
6
  import { mergeApprovalsGroup } from "./human-in-loop/approvals-commands.js";
6
- import { ApprovalDeclinedError, invokeWithHumanInLoop, } from "./human-in-loop/index.js";
7
+ import { ApprovalDeclinedError, invokeWithHumanInLoop } from "./human-in-loop/index.js";
7
8
  import { hasMcpProxyGroups, resolveMcpProxies } from "./mcp-proxy.js";
8
9
  import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
9
10
  import { findEntrypointPackageMetadata } from "./package-metadata.js";
10
11
  import { filterSchemaForScope } from "./schema-scope.js";
11
- const RESERVED_SERVICE_NAMES = new Set(["params", "secrets", "fetch", "fs", "env", "progress"]);
12
+ import { enableSourceMaps } from "./stack-trim.js";
13
+ import { suggest } from "./suggest.js";
14
+ import { throwValidationErrors } from "./validation-errors.js";
15
+ const RESERVED_SERVICE_NAMES = new Set([
16
+ "params",
17
+ "secrets",
18
+ "fetch",
19
+ "fs",
20
+ "env",
21
+ "progress",
22
+ "runtimeOptions",
23
+ "root"
24
+ ]);
25
+ const RESERVED_SERVICE_NAMES_MESSAGE = "Available reserved names: params, secrets, fetch, fs, env, progress, runtimeOptions, root.";
12
26
  function normalizeRoots(roots) {
13
27
  if (!Array.isArray(roots)) {
14
28
  return roots;
@@ -18,7 +32,7 @@ function normalizeRoots(roots) {
18
32
  name: "",
19
33
  aliases: [],
20
34
  secrets: {},
21
- children: roots,
35
+ children: roots
22
36
  };
23
37
  }
24
38
  function splitWords(value) {
@@ -39,7 +53,9 @@ function splitWords(value) {
39
53
  const isUppercase = char !== lower && char === upper;
40
54
  const previous = value[index - 1];
41
55
  const next = value[index + 1];
42
- const previousIsLowercase = previous !== undefined && previous === previous.toLowerCase() && previous !== previous.toUpperCase();
56
+ const previousIsLowercase = previous !== undefined &&
57
+ previous === previous.toLowerCase() &&
58
+ previous !== previous.toUpperCase();
43
59
  const nextIsLowercase = next !== undefined && next === next.toLowerCase() && next !== next.toUpperCase();
44
60
  if (isUppercase && current.length > 0 && (previousIsLowercase || nextIsLowercase)) {
45
61
  words.push(current.toLowerCase());
@@ -59,7 +75,7 @@ function formatSegment(segment, casing) {
59
75
  return words.join("_");
60
76
  }
61
77
  return words
62
- .map((word, index) => index === 0 ? word : `${word[0]?.toUpperCase() ?? ""}${word.slice(1)}`)
78
+ .map((word, index) => (index === 0 ? word : `${word[0]?.toUpperCase() ?? ""}${word.slice(1)}`))
63
79
  .join("");
64
80
  }
65
81
  function unwrapOptional(schema) {
@@ -88,42 +104,45 @@ function createFs() {
88
104
  catch {
89
105
  return false;
90
106
  }
91
- },
107
+ }
92
108
  };
93
109
  }
94
110
  function createEnv(values = process.env) {
95
111
  return {
96
112
  get(key) {
97
113
  return values[key];
98
- },
114
+ }
99
115
  };
100
116
  }
101
117
  function validateServices(services) {
102
118
  for (const name of Object.keys(services)) {
103
119
  if (RESERVED_SERVICE_NAMES.has(name)) {
104
- throw new Error(`Service name "${name}" is reserved. Choose a different name.`);
120
+ throw new Error(`Service name "${name}" is reserved. Choose a different name. ${RESERVED_SERVICE_NAMES_MESSAGE}`);
105
121
  }
106
122
  }
107
123
  }
124
+ function formatAvailableList(values) {
125
+ return `Available: ${[...values].sort().join(", ")}.`;
126
+ }
108
127
  function applySchemaCasing(schema, casing) {
109
128
  if (schema.type !== "object" || schema.properties === undefined) {
110
129
  if (schema.type === "array" && schema.items !== undefined) {
111
130
  return {
112
131
  ...schema,
113
- items: applySchemaCasing(schema.items, casing),
132
+ items: applySchemaCasing(schema.items, casing)
114
133
  };
115
134
  }
116
135
  return schema;
117
136
  }
118
137
  const properties = Object.fromEntries(Object.entries(schema.properties).map(([key, value]) => [
119
138
  formatSegment(key, casing),
120
- applySchemaCasing(value, casing),
139
+ applySchemaCasing(value, casing)
121
140
  ]));
122
141
  const required = schema.required?.map((key) => formatSegment(key, casing));
123
142
  return {
124
143
  ...schema,
125
144
  properties,
126
- ...(required === undefined ? {} : { required }),
145
+ ...(required === undefined ? {} : { required })
127
146
  };
128
147
  }
129
148
  function collectParamSummaries(schema, casing, path = [], inheritedOptional = false) {
@@ -175,14 +194,14 @@ function enumerateTools(root, casing, allowlist, omitRootToolNamePrefix) {
175
194
  return;
176
195
  }
177
196
  if (params === undefined || params.kind !== "object") {
178
- throw new Error(`Bug: command "${name}" must define an object params schema for MCP.`);
197
+ throw new ToolcraftBugError(`command "${name}" must define an object params schema for MCP.`);
179
198
  }
180
199
  tools.push({
181
200
  command: node,
182
201
  commandPath: [...commandPath, node.name].join("."),
183
202
  name,
184
203
  description: buildToolDescription(node.description, params, casing),
185
- inputSchema: applySchemaCasing(toJsonSchema(params), casing),
204
+ inputSchema: applySchemaCasing(toJsonSchema(params), casing)
186
205
  });
187
206
  return;
188
207
  }
@@ -212,13 +231,13 @@ function renderPendingApproval(pending) {
212
231
  content: [
213
232
  {
214
233
  type: "text",
215
- text: `Queued for human approval (id: ${pending.approvalId}). Track with \`toolcraft approvals show ${pending.approvalId}\`.`,
234
+ text: `Queued for human approval (id: ${pending.approvalId}). Track with \`toolcraft approvals show ${pending.approvalId}\`.`
216
235
  },
217
236
  {
218
237
  type: "text",
219
- text: JSON.stringify(pending),
220
- },
221
- ],
238
+ text: JSON.stringify(pending)
239
+ }
240
+ ]
222
241
  };
223
242
  }
224
243
  function renderDeclinedApproval(error) {
@@ -227,26 +246,45 @@ function renderDeclinedApproval(error) {
227
246
  content: [
228
247
  {
229
248
  type: "text",
230
- text: error.reason === undefined ? "Declined." : `Declined: ${error.reason}`,
249
+ text: error.reason === undefined ? "Declined." : `Declined: ${error.reason}`
231
250
  },
232
251
  {
233
252
  type: "text",
234
253
  text: JSON.stringify({
235
254
  outcome: "declined",
236
255
  reason: error.reason,
237
- commandPath: error.commandPath,
238
- }),
239
- },
240
- ],
256
+ commandPath: error.commandPath
257
+ })
258
+ }
259
+ ]
241
260
  };
242
261
  }
243
- function validateEnum(value, schema, label) {
244
- if (!schema.values.includes(value)) {
245
- throw new UserError(`Invalid value for "${label}". Expected one of: ${schema.values.map((candidate) => String(candidate)).join(", ")}.`);
246
- }
247
- return value;
262
+ function formatEnumError(value, schema, label) {
263
+ const suggestionLine = typeof value === "string"
264
+ ? formatEnumSuggestionLine(value, schema.values.map((candidate) => String(candidate)))
265
+ : " ";
266
+ return `Invalid value for "${label}".${suggestionLine}Expected one of: ${schema.values.map((candidate) => String(candidate)).join(", ")}, got ${describeReceived(value)}.`;
248
267
  }
249
- function validateSchemaValue(schema, value, casing, label) {
268
+ function formatEnumSuggestionLine(value, values) {
269
+ const suggestions = suggest(value, values);
270
+ return suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?\n` : " ";
271
+ }
272
+ function describeReceived(value) {
273
+ if (value === null)
274
+ return "null";
275
+ if (value === undefined)
276
+ return "missing";
277
+ if (Array.isArray(value))
278
+ return `array(${value.length})`;
279
+ if (typeof value === "object")
280
+ return "object";
281
+ if (typeof value === "string") {
282
+ const s = value.length > 40 ? `${value.slice(0, 40)}…` : value;
283
+ return `${JSON.stringify(s)}`;
284
+ }
285
+ return JSON.stringify(value);
286
+ }
287
+ function validateSchemaValue(schema, value, casing, label, errors) {
250
288
  const unwrappedSchema = unwrapOptional(schema);
251
289
  if (value === null && unwrappedSchema.nullable === true) {
252
290
  return null;
@@ -254,33 +292,53 @@ function validateSchemaValue(schema, value, casing, label) {
254
292
  switch (unwrappedSchema.kind) {
255
293
  case "string":
256
294
  if (typeof value !== "string") {
257
- throw new UserError(`Invalid value for "${label}". Expected a string.`);
295
+ errors.push({
296
+ path: label,
297
+ message: `Invalid value for "${label}". Expected a string, got ${describeReceived(value)}.`
298
+ });
258
299
  }
259
300
  return value;
260
301
  case "number":
261
302
  if (!isValidNumberSchemaValue(value, unwrappedSchema)) {
262
- throw new UserError(`Invalid value for "${label}". Expected ${getExpectedNumberDescription(unwrappedSchema)}.`);
303
+ errors.push({
304
+ path: label,
305
+ message: `Invalid value for "${label}". Expected ${getExpectedNumberDescription(unwrappedSchema)}, got ${describeReceived(value)}.`
306
+ });
263
307
  }
264
308
  return value;
265
309
  case "boolean":
266
310
  if (typeof value !== "boolean") {
267
- throw new UserError(`Invalid value for "${label}". Expected a boolean.`);
311
+ errors.push({
312
+ path: label,
313
+ message: `Invalid value for "${label}". Expected a boolean, got ${describeReceived(value)}.`
314
+ });
268
315
  }
269
316
  return value;
270
317
  case "enum":
271
- return validateEnum(value, unwrappedSchema, label);
318
+ if (!unwrappedSchema.values.includes(value)) {
319
+ errors.push({ path: label, message: formatEnumError(value, unwrappedSchema, label) });
320
+ }
321
+ return value;
272
322
  case "array":
273
323
  if (!Array.isArray(value)) {
274
- throw new UserError(`Invalid value for "${label}". Expected an array.`);
324
+ errors.push({
325
+ path: label,
326
+ message: `Invalid value for "${label}". Expected an array, got ${describeReceived(value)}.`
327
+ });
328
+ return value;
275
329
  }
276
- return value.map((item, index) => validateSchemaValue(unwrappedSchema.item, item, casing, `${label}[${index}]`));
330
+ return value.map((item, index) => validateSchemaValue(unwrappedSchema.item, item, casing, `${label}[${index}]`, errors));
277
331
  case "object":
278
- return validateObjectSchema(unwrappedSchema, value, casing, label);
332
+ return validateObjectSchema(unwrappedSchema, value, casing, label, errors);
279
333
  }
280
334
  }
281
- function validateObjectSchema(schema, value, casing, label) {
335
+ function validateObjectSchema(schema, value, casing, label, errors) {
282
336
  if (!isPlainObject(value)) {
283
- throw new UserError(`Invalid value for "${label}". Expected an object.`);
337
+ errors.push({
338
+ path: label,
339
+ message: `Invalid value for "${label}". Expected an object, got ${describeReceived(value)}.`
340
+ });
341
+ return {};
284
342
  }
285
343
  const result = {};
286
344
  const expectedKeys = new Map();
@@ -290,7 +348,10 @@ function validateObjectSchema(schema, value, casing, label) {
290
348
  for (const key of Object.keys(value)) {
291
349
  if (!expectedKeys.has(key)) {
292
350
  const fieldLabel = label.length === 0 ? key : `${label}.${key}`;
293
- throw new UserError(`Unexpected parameter "${fieldLabel}".`);
351
+ errors.push({
352
+ path: fieldLabel,
353
+ message: `Unexpected parameter "${fieldLabel}". ${formatAvailableList([...expectedKeys.keys()].map((expectedKey) => label.length === 0 ? expectedKey : `${label}.${expectedKey}`))}`
354
+ });
294
355
  }
295
356
  }
296
357
  for (const [inputKey, [outputKey, rawChildSchema]] of expectedKeys.entries()) {
@@ -305,14 +366,18 @@ function validateObjectSchema(schema, value, casing, label) {
305
366
  if (isOptional(rawChildSchema)) {
306
367
  continue;
307
368
  }
308
- throw new UserError(`Missing required parameter "${fieldLabel}".`);
369
+ errors.push({ path: fieldLabel, message: `Missing required parameter "${fieldLabel}".` });
370
+ continue;
309
371
  }
310
- result[outputKey] = validateSchemaValue(rawChildSchema, value[inputKey], casing, fieldLabel);
372
+ result[outputKey] = validateSchemaValue(rawChildSchema, value[inputKey], casing, fieldLabel, errors);
311
373
  }
312
374
  return result;
313
375
  }
314
376
  function validateToolArguments(schema, argumentsValue, casing) {
315
- return validateObjectSchema(schema, argumentsValue ?? {}, casing, "");
377
+ const errors = [];
378
+ const result = validateObjectSchema(schema, argumentsValue ?? {}, casing, "", errors);
379
+ throwValidationErrors(errors);
380
+ return result;
316
381
  }
317
382
  function isContentBlock(value) {
318
383
  if (!isPlainObject(value) || typeof value.type !== "string") {
@@ -330,9 +395,7 @@ function toToolContent(result) {
330
395
  if (Array.isArray(result)) {
331
396
  return result.flatMap((item) => toToolContent(item));
332
397
  }
333
- if (typeof result === "string" ||
334
- typeof result === "number" ||
335
- typeof result === "boolean") {
398
+ if (typeof result === "string" || typeof result === "number" || typeof result === "boolean") {
336
399
  return [{ type: "text", text: String(result) }];
337
400
  }
338
401
  if (result === null) {
@@ -362,7 +425,7 @@ function createResolvedMCPServer(root, options) {
362
425
  const servicesWithBuiltIns = {
363
426
  ...services,
364
427
  runtimeOptions,
365
- root,
428
+ root
366
429
  };
367
430
  validateServices(services);
368
431
  const tools = enumerateTools(root, casing, options.tools, options.omitRootToolNamePrefix ?? false);
@@ -370,8 +433,10 @@ function createResolvedMCPServer(root, options) {
370
433
  const server = createServer({ name: options.name, version });
371
434
  for (const tool of tools) {
372
435
  server.tool(tool.name, tool.description, tool.inputSchema, async (argumentsValue) => {
436
+ let params;
437
+ let secrets;
373
438
  try {
374
- const secrets = resolveCommandSecrets(tool.command);
439
+ secrets = resolveCommandSecrets(tool.command);
375
440
  const baseContext = {
376
441
  ...servicesWithBuiltIns,
377
442
  secrets,
@@ -380,13 +445,13 @@ function createResolvedMCPServer(root, options) {
380
445
  env: createEnv(),
381
446
  progress() {
382
447
  return undefined;
383
- },
448
+ }
384
449
  };
385
450
  await assertCommandRequirements(tool.command, { ...baseContext, params: undefined });
386
- const params = validateToolArguments(tool.command.params, argumentsValue, casing);
451
+ params = validateToolArguments(tool.command.params, argumentsValue, casing);
387
452
  const result = await invokeWithHumanInLoop(tool.command, {
388
453
  ...baseContext,
389
- params,
454
+ params
390
455
  }, runtimeOptions, tool.commandPath);
391
456
  if (isHumanInLoopPending(result)) {
392
457
  return renderPendingApproval(result);
@@ -397,6 +462,16 @@ function createResolvedMCPServer(root, options) {
397
462
  if (error instanceof ApprovalDeclinedError) {
398
463
  return renderDeclinedApproval(error);
399
464
  }
465
+ await writeErrorReport({
466
+ command: tool.command,
467
+ commandPath: tool.commandPath,
468
+ env: process.env,
469
+ error,
470
+ errorReports: options.errorReports,
471
+ params,
472
+ projectRoot: options.projectRoot,
473
+ secrets
474
+ });
400
475
  throw toToolError(error);
401
476
  }
402
477
  });
@@ -405,13 +480,13 @@ function createResolvedMCPServer(root, options) {
405
480
  ...server,
406
481
  connect(transport) {
407
482
  return server.connectSDK(transport);
408
- },
483
+ }
409
484
  };
410
485
  }
411
486
  function resolveMCPVersion(version) {
412
487
  const resolvedVersion = version ?? findEntrypointPackageMetadata(process.argv[1])?.version;
413
488
  if (resolvedVersion === undefined) {
414
- throw new Error("MCP version is required when no package.json version can be inferred from the entrypoint.");
489
+ throw new Error('MCP server version is required. Pass version: "x.y.z" to createMCPServer / runMCP, or run toolcraft from a project whose package.json defines "version".');
415
490
  }
416
491
  return resolvedVersion;
417
492
  }
@@ -430,14 +505,14 @@ function createDeferredMCPServer(root, options) {
430
505
  },
431
506
  connect(transport) {
432
507
  return resolveServer().then((server) => server.connect(transport));
433
- },
508
+ }
434
509
  }, {
435
510
  get(target, property, receiver) {
436
511
  if (property === "then") {
437
512
  return resolveServer().then.bind(resolveServer());
438
513
  }
439
514
  return Reflect.get(target, property, receiver);
440
- },
515
+ }
441
516
  });
442
517
  }
443
518
  export function createMCPServer(roots, options) {
@@ -448,6 +523,7 @@ export function createMCPServer(roots, options) {
448
523
  return createDeferredMCPServer(root, options);
449
524
  }
450
525
  export async function runMCP(roots, options) {
526
+ enableSourceMaps();
451
527
  const root = mergeApprovalsGroup(normalizeRoots(roots));
452
528
  await resolveMcpProxies(root, { projectRoot: options.projectRoot });
453
529
  const server = createResolvedMCPServer(root, options);
package/dist/sdk.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { ObjectSchema, Static } from "toolcraft-schema";
2
2
  import type { Group, Scope } from "./index.js";
3
+ import { type ErrorReportsOption } from "./error-report.js";
3
4
  import type { HumanInLoopPending, HumanInLoopRuntimeOptions } from "./human-in-loop/index.js";
4
5
  type ScopeInput = readonly Scope[] | undefined;
5
6
  type HumanInLoopMode = "sync" | "async";
@@ -12,8 +13,8 @@ type IncludesSDK<TScope> = TScope extends readonly Scope[] ? "sdk" extends TScop
12
13
  type EffectiveCommandHumanInLoopMode<TOwnHumanInLoopMode extends HumanInLoopModeInput, TInheritedHumanInLoopMode extends HumanInLoopMode | undefined> = TOwnHumanInLoopMode extends HumanInLoopMode ? TOwnHumanInLoopMode : TOwnHumanInLoopMode extends null ? undefined : TInheritedHumanInLoopMode;
13
14
  type EffectiveGroupHumanInLoopMode<TOwnHumanInLoopMode extends HumanInLoopModeInput, TInheritedHumanInLoopMode extends HumanInLoopMode | undefined> = TOwnHumanInLoopMode extends HumanInLoopMode ? TOwnHumanInLoopMode : TOwnHumanInLoopMode extends null ? undefined : TInheritedHumanInLoopMode;
14
15
  type Separator = "-" | "_" | " " | ".";
15
- type IsUppercase<TValue extends string> = TValue extends Uppercase<TValue> ? TValue extends Lowercase<TValue> ? false : true : false;
16
- type IsLowercase<TValue extends string> = TValue extends Lowercase<TValue> ? TValue extends Uppercase<TValue> ? false : true : false;
16
+ type IsUppercase<TValue extends string> = TValue extends Uppercase<TValue> ? (TValue extends Lowercase<TValue> ? false : true) : false;
17
+ type IsLowercase<TValue extends string> = TValue extends Lowercase<TValue> ? (TValue extends Uppercase<TValue> ? false : true) : false;
17
18
  type LastCharacter<TValue extends string> = TValue extends `${infer THead}${infer TTail}` ? TTail extends "" ? THead : LastCharacter<TTail> : never;
18
19
  type PushCurrentWord<TCurrent extends string, TWords extends readonly string[]> = TCurrent extends "" ? TWords : [...TWords, Lowercase<TCurrent>];
19
20
  type SplitCamelWords<TValue extends string, TCurrent extends string = "", TWords extends readonly string[] = []> = TValue extends `${infer TChar}${infer TRest}` ? TChar extends Separator ? SplitCamelWords<TRest, "", PushCurrentWord<TCurrent, TWords>> : IsUppercase<TChar> extends true ? TCurrent extends "" ? SplitCamelWords<TRest, TChar, TWords> : TRest extends `${infer TNext}${string}` ? IsLowercase<LastCharacter<TCurrent>> extends true ? SplitCamelWords<TRest, TChar, PushCurrentWord<TCurrent, TWords>> : IsLowercase<TNext> extends true ? SplitCamelWords<TRest, TChar, PushCurrentWord<TCurrent, TWords>> : SplitCamelWords<TRest, `${TCurrent}${TChar}`, TWords> : SplitCamelWords<TRest, `${TCurrent}${TChar}`, TWords> : SplitCamelWords<TRest, `${TCurrent}${TChar}`, TWords> : PushCurrentWord<TCurrent, TWords>;
@@ -64,6 +65,7 @@ export interface CreateSDKOptions<TServices extends object = Record<string, unkn
64
65
  casing?: "camel";
65
66
  humanInLoop?: HumanInLoopRuntimeOptions;
66
67
  projectRoot?: string;
68
+ errorReports?: ErrorReportsOption;
67
69
  }
68
70
  export declare function createSDK<TRootInfo, TServices extends object = Record<string, unknown>>(root: Group<any> & {
69
71
  readonly __agentKitGroupTypeInfo: TRootInfo;