Problem
A Toolcraft command is shared by CLI, SDK, and MCP. MCP outputSchema requires an object-root structuredContent, but real APIs commonly return a root array, scalar success message, or empty 204/null response.
Today result: S.Object(...) validates the raw handler result. An application can wrap arrays/null into an object only by changing the shared handler, which silently changes CLI and SDK return values and leaves their inferred result type inaccurate. Omitting result avoids the break but leaves the MCP tool without an output schema.
Found while adding output schemas to Skylight Calendar: reward points return a root array, chore deletion may return a string, and multiple deletes may return an empty body.
Proposed API
Keep this optional on the existing command config:
defineCommand({
name: "list-points",
params: S.Object({}),
result: S.Object({ data: S.Array(Point) }),
mcpResult: (result) => ({ data: result }),
handler: async () => api.listPoints() // SDK/CLI still receive the raw array
})
mcpResult is invoked only by the MCP adapter after the handler succeeds and before result validation/casing. It is not invoked by CLI or SDK.
Acceptance criteria
- Optional
mcpResult?: (result: TResult) => unknown is preserved across command materialization.
- Defining
mcpResult without result fails early with a clear error.
- MCP validates/maps the projected value and returns matching
structuredContent plus JSON text fallback.
- CLI and SDK continue returning the original raw handler value.
- Mapper exceptions follow normal MCP error reporting.
- Commands that omit the mapper are unchanged.
- HTTP and stdio MCP behavior remains identical.
This is transport-specific representation, not business logic, and avoids forcing every user of a multi-surface Toolcraft command into MCP object-envelope semantics.
Problem
A Toolcraft command is shared by CLI, SDK, and MCP. MCP
outputSchemarequires an object-rootstructuredContent, but real APIs commonly return a root array, scalar success message, or empty 204/null response.Today
result: S.Object(...)validates the raw handler result. An application can wrap arrays/null into an object only by changing the shared handler, which silently changes CLI and SDK return values and leaves their inferred result type inaccurate. Omittingresultavoids the break but leaves the MCP tool without an output schema.Found while adding output schemas to Skylight Calendar: reward points return a root array, chore deletion may return a string, and multiple deletes may return an empty body.
Proposed API
Keep this optional on the existing command config:
mcpResultis invoked only by the MCP adapter after the handler succeeds and beforeresultvalidation/casing. It is not invoked by CLI or SDK.Acceptance criteria
mcpResult?: (result: TResult) => unknownis preserved across command materialization.mcpResultwithoutresultfails early with a clear error.structuredContentplus JSON text fallback.This is transport-specific representation, not business logic, and avoids forcing every user of a multi-surface Toolcraft command into MCP object-envelope semantics.