diff --git a/app/docs/_content/architecture-rendering-output.mdx b/app/docs/_content/architecture-rendering-output.mdx index 322eef6..bb0ea79 100644 --- a/app/docs/_content/architecture-rendering-output.mdx +++ b/app/docs/_content/architecture-rendering-output.mdx @@ -37,7 +37,6 @@ stateDiagram-v2 Collecting --> Collecting: attach(image) Collecting --> StructuredReady: setStructuredOutput(output) StructuredReady --> StructuredReady: setNextSteps(steps, runtime) - Collecting --> ErrorTracked: error fragment emitted StructuredReady --> ErrorTracked: structured result didError=true ErrorTracked --> Finalizing: finalize() @@ -60,7 +59,7 @@ stateDiagram-v2 RuntimeBoundary --> [*] ``` -The render session is the collection point for the tool invocation. It receives fragments as work happens, stores attachments, stores the final structured output, stores next steps, tracks error state, and finalizes the transcript for the selected render strategy. +The render session is the collection point for the tool invocation. It receives fragments as work happens, stores attachments, stores the final structured output, stores next steps, derives error state from the structured result, and finalizes the transcript for the selected render strategy. ## Render strategy vs CLI output mode diff --git a/app/docs/_content/architecture-tool-lifecycle.mdx b/app/docs/_content/architecture-tool-lifecycle.mdx index 9094f02..ae57dee 100644 --- a/app/docs/_content/architecture-tool-lifecycle.mdx +++ b/app/docs/_content/architecture-tool-lifecycle.mdx @@ -60,8 +60,6 @@ interface ToolHandlerContext { | `nextSteps` | Provide explicit dynamic follow-ups when templates cannot express the result. | | `structuredOutput` | Set the final canonical result, schema ID, and schema version. | -The omitted progress flags are internal boundary controls. They exist in source today, but the public docs intentionally do not teach tool authors to branch on them. - ## Fragments versus structured output A handler produces two different things, and they serve different readers. Fragments are live progress and transcript material — log lines, build output, attachments — that callers and pipelines consume while the work is running. Structured output is the one final canonical result the handler sets at the end of the call. Fragments are never the final result; the structured output is what powers CLI `--output json`, MCP `structuredContent`, schema validation, fixtures, and stable integrations. diff --git a/app/docs/_content/mcp-protocol-support.mdx b/app/docs/_content/mcp-protocol-support.mdx index a1e6da1..6b8f022 100644 --- a/app/docs/_content/mcp-protocol-support.mdx +++ b/app/docs/_content/mcp-protocol-support.mdx @@ -51,7 +51,7 @@ The envelope wraps every tool's structured payload in the same shape, so consume } ``` -The envelope fields are documented in [Output Formats](/docs/output-formats). Canonical JSON schemas live in [`schemas/structured-output/`](https://github.com/getsentry/XcodeBuildMCP/tree/main/schemas/structured-output). Tools advertise their output schema in `tools/list`. +The envelope fields are documented in [Output Formats](/docs/output-formats). Canonical JSON schemas live in [`schemas/structured-output/`](https://github.com/getsentry/XcodeBuildMCP/tree/main/schemas/structured-output). Tools advertise their output schema in `tools/list` as an object-shaped union of the domain schema OR `xcodebuildmcp.output.error`, so clients can always branch on `schema` to determine whether a structured result is a normal domain payload or a generic error. The one exception is the [Xcode IDE bridge](/docs/xcode-ide): bridge tools forward calls to Xcode's own MCP service, so their output shape comes from Xcode and does not use the XcodeBuildMCP envelope. diff --git a/app/docs/_content/output-formats.mdx b/app/docs/_content/output-formats.mdx index fd35f48..282d572 100644 --- a/app/docs/_content/output-formats.mdx +++ b/app/docs/_content/output-formats.mdx @@ -60,9 +60,31 @@ The response has these fields: |-------|------|---------| | `schema` | `string` | Schema identifier, for example `xcodebuildmcp.output.build-result`. | | `schemaVersion` | `string` | Version of that schema contract. | -| `didError` | `boolean` | Whether the tool reported a domain error. | +| `didError` | `boolean` | Whether the final result is an error. | | `error` | `string \| null` | Human-readable error text when `didError` is true. | -| `data` | `object \| null` | Tool-specific data. | +| `data` | `object \| null` | Schema-specific payload. Domain schemas use tool-specific data; `xcodebuildmcp.output.error` uses generic error metadata. | + +Structured output is a tagged union. Consumers should branch on `schema`, then parse `data` according to that schema: + +```typescript +type XcodeBuildMCPOutput = + | { + schema: "xcodebuildmcp.output.simulator-list" + schemaVersion: "1" + didError: boolean + error: string | null + data: { simulators: Simulator[] } + } + | { + schema: "xcodebuildmcp.output.error" + schemaVersion: "1" + didError: true + error: string + data: { category: "runtime" | "validation" | "schema"; code: string } + } +``` + +For example, `list_sims` normally returns `xcodebuildmcp.output.simulator-list`, but a validation or runtime failure before simulator listing begins returns `xcodebuildmcp.output.error`. Do not assume a tool always returns only its domain schema; always check `schema` first. ```json { @@ -91,7 +113,22 @@ The response has these fields: } ``` -`data` can be `null` when a tool succeeds but has no structured payload. If a tool does not produce structured output for `--output json`, the CLI emits an error response with `schema: "xcodebuildmcp.output.error"`, `didError: true`, and exits with a non-zero status. +`data` can be `null` when a tool succeeds but has no structured payload. If a tool produces no structured output, or if a pre-domain failure occurs (validation, schema, or runtime error before the tool executes), the response uses the generic error branch and the CLI exits with a non-zero status: + +```json +{ + "schema": "xcodebuildmcp.output.error", + "schemaVersion": "1", + "didError": true, + "error": "Parameter validation failed: Invalid parameters...", + "data": { + "category": "validation", + "code": "PARAMETER_VALIDATION_FAILED" + } +} +``` + +Domain failures still use their domain schema. For example, an attempted build that fails returns `xcodebuildmcp.output.build-result` with `didError: true` and build diagnostics in `data`. ## `--output jsonl` @@ -133,7 +170,7 @@ Do not script against `raw`. The transcript is intentionally close to the underl ## Structured content for MCP clients -MCP mode always returns the normal `content` array. When a tool sets structured output, XcodeBuildMCP also attaches [`structuredContent`](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#structured-content) with the same shape as `--output json`. +MCP mode always returns the normal `content` array. When a tool sets structured output, XcodeBuildMCP also attaches [`structuredContent`](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#structured-content) with the same shape as `--output json`. Pre-domain failures — validation errors, schema mismatches, or runtime errors before the tool executes — also produce `structuredContent` using `schema: "xcodebuildmcp.output.error"`. ```json { @@ -178,7 +215,7 @@ MCP mode always returns the normal `content` array. When a tool sets structured } ``` -Tools that declare an [output schema](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#output-schema) also advertise it during MCP registration. Clients can use that schema to validate `structuredContent` or render typed UI. +Tools that declare an [output schema](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#output-schema) also advertise it during MCP registration. For tools with structured output, the advertised schema is a union of the tool's domain envelope and the generic `xcodebuildmcp.output.error` envelope. Clients can use that schema to validate `structuredContent` or render typed UI, but should still branch on the returned `schema` value at runtime. ## Response schema reference