toolcraft 0.0.148 → 0.0.150

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/README.md CHANGED
@@ -583,6 +583,7 @@ Toolcraft configuration is code-first. Use `defineCommand(config)` and `defineGr
583
583
  - `positional?: string[]` — parameter names mapped from CLI argv order.
584
584
  - `params: S.Object(...)` — input schema from `toolcraft-schema`.
585
585
  - `result?: S.Object(...)` — structured result schema for MCP `outputSchema`; must be a root object.
586
+ - `mcpResult?: (result) => Record<string, unknown>` — optional MCP-only projection applied before `result` validation; CLI and SDK still return the raw handler result. Requires `result`.
586
587
  - `secrets?: Record<string, { env: string; description?: string; optional?: boolean }>`
587
588
  - `scope?: Array<"cli" | "mcp" | "sdk">` — defaults to `["cli", "sdk"]`.
588
589
  - `confirm?: boolean` — deprecated CLI-only TTY confirmation; use `humanInLoop` instead. Cannot be combined with `humanInLoop`.
package/composition.json CHANGED
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.148",
116
+ "version": "0.0.150",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.148",
126
+ "version": "0.0.150",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.148",
116
+ "version": "0.0.150",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.148",
126
+ "version": "0.0.150",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
package/dist/index.d.ts CHANGED
@@ -123,6 +123,7 @@ export interface CommandConfig<TServices extends object, TParamsSchema extends O
123
123
  positional?: string[];
124
124
  params: TParamsSchema;
125
125
  result?: ObjectSchema<any>;
126
+ mcpResult?: (result: TResult) => Record<string, unknown>;
126
127
  secrets?: TSecrets;
127
128
  scope?: Scope[];
128
129
  confirm?: boolean;
@@ -140,7 +141,7 @@ export type StreamHandlerContext<TParamsSchema extends ObjectSchema<any>, TSecre
140
141
  status(event: StreamStatusEvent): void;
141
142
  refreshSecrets(): Promise<InferSecrets<TSecrets>>;
142
143
  };
143
- export interface StreamCommandConfig<TServices extends object, TParamsSchema extends ObjectSchema<any>, TSecrets extends SecretDeclarations | undefined, TEventSchema extends AnySchema> extends Omit<CommandConfig<TServices, TParamsSchema, TSecrets, AsyncIterable<Static<TEventSchema>>>, "handler" | "render" | "result" | "humanInLoop" | "confirm"> {
144
+ export interface StreamCommandConfig<TServices extends object, TParamsSchema extends ObjectSchema<any>, TSecrets extends SecretDeclarations | undefined, TEventSchema extends AnySchema> extends Omit<CommandConfig<TServices, TParamsSchema, TSecrets, AsyncIterable<Static<TEventSchema>>>, "handler" | "render" | "result" | "mcpResult" | "humanInLoop" | "confirm"> {
144
145
  event: TEventSchema;
145
146
  handler: (ctx: StreamHandlerContext<TParamsSchema, TSecrets, TServices>) => AsyncIterable<Static<TEventSchema>> | Promise<AsyncIterable<Static<TEventSchema>>>;
146
147
  render?: Renderers<Static<TEventSchema>>;
@@ -157,6 +158,7 @@ export interface Command<TServices extends object = EmptyServices, TParamsSchema
157
158
  positional: string[];
158
159
  params: TParamsSchema;
159
160
  result?: ObjectSchema<any>;
161
+ mcpResult?: (result: TResult) => Record<string, unknown>;
160
162
  stream?: StreamDefinition<any>;
161
163
  secrets: SecretDeclarations;
162
164
  scope: Scope[];
package/dist/index.js CHANGED
@@ -291,6 +291,9 @@ function resolveGroupScope(ownScope, inheritedScope) {
291
291
  return cloneScope(ownScope ?? inheritedScope);
292
292
  }
293
293
  function createBaseCommand(config) {
294
+ if (config.mcpResult !== undefined && config.result === undefined) {
295
+ throw new ToolcraftBugError(`Command "${config.name}" defines mcpResult without a result schema.`);
296
+ }
294
297
  const command = {
295
298
  kind: "command",
296
299
  name: config.name,
@@ -303,6 +306,7 @@ function createBaseCommand(config) {
303
306
  positional: [...(config.positional ?? [])],
304
307
  params: config.params,
305
308
  result: config.result,
309
+ mcpResult: config.mcpResult,
306
310
  stream: undefined,
307
311
  secrets: cloneSecrets(config.secrets),
308
312
  scope: resolveCommandScope(config.scope, undefined),
@@ -320,6 +324,7 @@ function createBaseCommand(config) {
320
324
  hidden: config.hidden ?? false,
321
325
  examples: cloneCommandExamples(config.examples),
322
326
  result: config.result,
327
+ mcpResult: config.mcpResult,
323
328
  humanInLoop: config.humanInLoop,
324
329
  secrets: cloneSecrets(config.secrets),
325
330
  requires: cloneRequires(config.requires),
@@ -376,6 +381,7 @@ function materializeCommand(command, inherited) {
376
381
  positional: [...command.positional],
377
382
  params: command.params,
378
383
  result: internal.result,
384
+ mcpResult: internal.mcpResult,
379
385
  stream: command.stream,
380
386
  secrets: mergeSecrets(inherited.secrets, internal.secrets),
381
387
  scope: resolveCommandScope(internal.scope, inherited.scope),
@@ -393,6 +399,7 @@ function materializeCommand(command, inherited) {
393
399
  hidden: internal.hidden,
394
400
  examples: cloneCommandExamples(internal.examples),
395
401
  result: internal.result,
402
+ mcpResult: internal.mcpResult,
396
403
  humanInLoop: internal.humanInLoop,
397
404
  secrets: cloneSecrets(internal.secrets),
398
405
  requires: cloneRequires(internal.requires),
package/dist/mcp.js CHANGED
@@ -892,7 +892,8 @@ function createResolvedMCPServer(root, options, runtime = {}) {
892
892
  return renderPendingApproval(result);
893
893
  }
894
894
  if (tool.resultSchema !== undefined) {
895
- const structuredContent = validateCommandResult(tool.resultSchema, result, casing);
895
+ const mcpResult = tool.command.mcpResult === undefined ? result : tool.command.mcpResult(result);
896
+ const structuredContent = validateCommandResult(tool.resultSchema, mcpResult, casing);
896
897
  return {
897
898
  content: [{ type: "text", text: JSON.stringify(structuredContent) }],
898
899
  structuredContent
@@ -33,6 +33,17 @@ const ignoredRoot = defineGroup({
33
33
  apiKey: params.APIKey,
34
34
  }),
35
35
  }),
36
+ defineCommand({
37
+ name: "json-payload",
38
+ scope: ["sdk"],
39
+ params: S.Object({
40
+ payload_json: S.Json(),
41
+ items_by_id: S.Record(S.Object({
42
+ display_name: S.String(),
43
+ })),
44
+ }),
45
+ handler: async ({ params }) => params,
46
+ }),
36
47
  defineCommand({
37
48
  name: "cli-only",
38
49
  scope: ["cli"],
@@ -116,6 +127,22 @@ const ignoredHttpServerResult = ignoredSdk.poeCode.generate.httpServer({
116
127
  void ignoredHttpServerResult.then((value) => {
117
128
  void value.apiKey;
118
129
  });
130
+ void ignoredSdk.poeCode.generate.jsonPayload({
131
+ payloadJson: {
132
+ file_upload: "upload-1",
133
+ nested_value: { preserve_this_key: true },
134
+ },
135
+ itemsById: {
136
+ "item-1": { displayName: "One" },
137
+ },
138
+ });
139
+ ignoredSdk.poeCode.generate.jsonPayload({
140
+ payloadJson: null,
141
+ itemsById: {
142
+ // @ts-expect-error declared fields inside arbitrary-key records are still camel-cased
143
+ "item-1": { display_name: "One" },
144
+ },
145
+ });
119
146
  const ignoredQueuedResult = ignoredSdk.poeCode.generate.queued({
120
147
  promptText: "hello",
121
148
  });
package/dist/sdk.d.ts CHANGED
@@ -30,7 +30,9 @@ type CapitalizeJoinCamelWords<TWords extends readonly string[]> = TWords extends
30
30
  ...infer TTail extends readonly string[]
31
31
  ] ? `${Capitalize<THead>}${CapitalizeJoinCamelWords<TTail>}` : "";
32
32
  type CamelCase<TValue extends string> = JoinCamelWords<SplitCamelWords<TValue>>;
33
- type Camelize<TValue> = TValue extends Primitive ? TValue : TValue extends readonly (infer TItem)[] ? Array<Camelize<TItem>> : TValue extends object ? {
33
+ type Camelize<TValue> = TValue extends Primitive ? TValue : TValue extends readonly (infer TItem)[] ? Array<Camelize<TItem>> : TValue extends object ? string extends keyof TValue ? {
34
+ [TKey in keyof TValue]: Camelize<TValue[TKey]>;
35
+ } : {
34
36
  [TKey in keyof TValue as TKey extends string ? CamelCase<TKey> : TKey]: Camelize<TValue[TKey]>;
35
37
  } : TValue;
36
38
  type SDKResult<TResult, THumanInLoopMode extends HumanInLoopMode | undefined> = [THumanInLoopMode] extends ["async"] ? ["async"] extends [THumanInLoopMode] ? HumanInLoopPending : TResult : TResult;
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.148",
11
+ "version": "0.0.150",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.148",
3
+ "version": "0.0.150",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.148",
3
+ "version": "0.0.150",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -158,7 +158,7 @@
158
158
  "yaml"
159
159
  ],
160
160
  "optionalDependencies": {
161
- "toolcraft-schema": "0.0.148",
161
+ "toolcraft-schema": "0.0.150",
162
162
  "toolcraft-design": "*",
163
163
  "@poe-code/frontmatter": "*",
164
164
  "@poe-code/agent-mcp-config": "*",