Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion packages/sdk/generated/domain-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,38 @@
"each": true
}
]
}
},
"errors": [
{
"name": "ValidationError",
"match": "VALIDATION_ERROR",
"schema": {
"type": "object",
"properties": {
"details": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": { "type": "string" },
"errorCode": { "type": "string" }
}
}
}
}
}
},
{
"name": "RateLimitError",
"match": "RATE_LIMITED",
"schema": {
"type": "object",
"properties": {
"retry_after_ms": { "type": "number" }
}
}
}
]
},
{
"tool": "edit_screens",
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/generated/src/designsystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/
import { type StitchToolClientSpec } from "../../src/spec/client.js";
import { StitchError } from "../../src/spec/errors.js";
Expand Down
4 changes: 2 additions & 2 deletions packages/sdk/generated/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/
export { Stitch } from "./stitch.js";
export { Project } from "./project.js";
Expand All @@ -16,4 +16,4 @@ export {
type ToolPropertySchema,
} from "./tool-definitions.js";
export type * from "./types.generated.js";
export type * from "./responses.generated.js";
export * from "./responses.generated.js";
30 changes: 29 additions & 1 deletion packages/sdk/generated/src/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/
import { type StitchToolClientSpec } from "../../src/spec/client.js";
import { StitchError } from "../../src/spec/errors.js";
Expand Down Expand Up @@ -41,6 +41,10 @@ import {
} from "./types.generated.js";
import {
GenerateScreenFromTextResponse,
ValidationError,
ValidationErrorSchema,
RateLimitError,
RateLimitErrorSchema,
ListScreensResponse,
GetScreenResponse,
CreateDesignSystemResponse,
Expand Down Expand Up @@ -162,6 +166,30 @@ export class Project {
deviceType: options?.deviceType ?? "DESKTOP",
modelId: options?.modelId,
},
{
ValidationError: {
schema: ValidationErrorSchema,
match: "VALIDATION_ERROR",
create: (data: any, raw: any) =>
new ValidationError(
"Tool Call Failed [generate_screen_from_text]: " +
(ValidationErrorSchema.description || "VALIDATION_ERROR"),
data,
raw,
),
},
RateLimitError: {
schema: RateLimitErrorSchema,
match: "RATE_LIMITED",
create: (data: any, raw: any) =>
new RateLimitError(
"Tool Call Failed [generate_screen_from_text]: " +
(RateLimitErrorSchema.description || "RATE_LIMITED"),
data,
raw,
),
},
},
);
const _screens = (
(raw.outputComponents || []).flatMap(
Expand Down
40 changes: 39 additions & 1 deletion packages/sdk/generated/src/responses.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,50 @@ import {
VariantOptions,
SelectedScreenInstance,
} from "./types.generated.js";
import { z } from "zod";
import { StitchError } from "../../src/spec/errors.js";

export const ValidationErrorSchema = z.object({
details: z.array(z.object({ field: z.string(), errorCode: z.string() })),
});
export type ValidationErrorPayload = z.infer<typeof ValidationErrorSchema>;

export interface ValidationError extends ValidationErrorPayload {}
export class ValidationError extends StitchError {
constructor(message: string, payload: ValidationErrorPayload, raw?: unknown) {
super({
code: "VALIDATION_ERROR" as any,
message,
recoverable: false,
raw,
});
this.name = "ValidationError";
Object.assign(this, payload);
}
}

export const RateLimitErrorSchema = z.object({ retry_after_ms: z.number() });
export type RateLimitErrorPayload = z.infer<typeof RateLimitErrorSchema>;

export interface RateLimitError extends RateLimitErrorPayload {}
export class RateLimitError extends StitchError {
constructor(message: string, payload: RateLimitErrorPayload, raw?: unknown) {
super({
code: "RATE_LIMITED" as any,
message,
recoverable: true,
raw,
});
this.name = "RateLimitError";
Object.assign(this, payload);
}
}
/**
* AUTO-GENERATED by scripts/generate-sdk.ts
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/

/** Response message for create_project. */
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/generated/src/screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/
import { type StitchToolClientSpec } from "../../src/spec/client.js";
import { StitchError } from "../../src/spec/errors.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/generated/src/stitch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/
import { type StitchToolClientSpec } from "../../src/spec/client.js";
import { StitchError } from "../../src/spec/errors.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/generated/src/tool-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/
/** JSON Schema property descriptor for a tool parameter. */
export interface ToolPropertySchema {
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/generated/src/types.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DO NOT EDIT — changes will be overwritten.

Source: tools-manifest.json (sha256:88ec3dfa066c...)
domain-map.json (sha256:a6177cc7e2f4...)
domain-map.json (sha256:46e20fc68c7b...)
*/

/** A component style token in a design system. */
Expand Down
10 changes: 5 additions & 5 deletions packages/sdk/generated/stitch-sdk.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"schemaVersion": 1,
"generated": {
"generatedAt": "2026-09-25T04:45:34.219Z",
"sourceHash": "sha256:aad79acad078a250f95d5e58767a9082c41be37d4f5565bf307c96f1cd654b38",
"generatedAt": "2026-09-25T04:57:37.343Z",
"sourceHash": "sha256:2cb353043fd99ef56818f90968eff1060ec95888bc7cf6a45d7b4a11e232eca2",
"manifestHash": "sha256:88ec3dfa066ce5060be236b0a5c0b5fb51671a7dd60d05556c028e6c4c8f0e1c",
"domainMapHash": "sha256:a6177cc7e2f44d2d536928d4e0ae7bd51ed2dd3393235787216b976498c3b1b2",
"domainMapHash": "sha256:46e20fc68c7bc123f2545680389e4a70792fb1df48959f6d19aa8110a829bb1d",
"fileCount": 8,
"repairedTools": [
"generate_screen_from_text",
Expand All @@ -13,8 +13,8 @@
]
},
"domainMap": {
"generatedAt": "2026-09-23T18:36:09.460Z",
"sourceHash": "sha256:a6177cc7e2f44d2d536928d4e0ae7bd51ed2dd3393235787216b976498c3b1b2",
"generatedAt": "2026-09-25T04:57:37.343Z",
"sourceHash": "sha256:46e20fc68c7bc123f2545680389e4a70792fb1df48959f6d19aa8110a829bb1d",
"manifestHash": "sha256:88ec3dfa066ce5060be236b0a5c0b5fb51671a7dd60d05556c028e6c4c8f0e1c",
"classCount": 4,
"bindingCount": 15
Expand Down
76 changes: 72 additions & 4 deletions packages/sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,67 @@ export class StitchToolClient implements StitchToolClientSpec {
};
}

private parseToolResponse<T>(result: any, name: string): T {
return parseToolResult<T>(result, name);
private parseToolResponse<T>(
result: any,
name: string,
errorMap?: Record<
string,
{ schema: any; match: string; create: (data: any, raw: any) => Error }
>,
): T {
if (result.isError) {
const errorText = (result.content as any[])
.map((c: any) => (c.type === "text" ? c.text : ""))
.join("");

let errorJson: any = null;
try {
errorJson = JSON.parse(errorText);
} catch (e) {}

if (errorMap) {
for (const [errName, errDef] of Object.entries(errorMap)) {
if (
errorText.includes(errDef.match) ||
(errorJson && JSON.stringify(errorJson).includes(errDef.match))
) {
if (errorJson) {
const parsed = errDef.schema.safeParse(errorJson);
if (parsed.success) {
throw errDef.create(parsed.data, result);
}
}
}
}
}

const code = classifyError({ text: errorText });

throw new StitchError({
code,
message: `Tool Call Failed [${name}]: ${errorText}`,
recoverable: isRecoverable(code),
toolName: name,
raw: result,
});
}

// Stitch specific parsing: Check structuredContent first, then JSON in text
const anyResult = result as any;
if (anyResult.structuredContent) return anyResult.structuredContent as T;

const textContent = (result.content as any[]).find(
(c: any) => c.type === "text",
);
if (textContent && textContent.type === "text") {
try {
return JSON.parse(textContent.text) as T;
} catch {
return textContent.text as unknown as T;
}
}

return anyResult as T;
}

async connect() {
Expand Down Expand Up @@ -390,7 +449,14 @@ export class StitchToolClient implements StitchToolClientSpec {
* with exponential backoff + full jitter, per `config.retry`. MCP text
* errors carry no Retry-After header, so nothing else is honored.
*/
async callTool<T>(name: string, args: Record<string, any>): Promise<T> {
async callTool<T>(
name: string,
args: Record<string, any>,
errorMap?: Record<
string,
{ schema: any; match: string; create: (data: any, raw: any) => Error }
>,
): Promise<T> {
this.assertNotClosed();
if (!this.isConnected) await this.connect();

Expand All @@ -416,7 +482,7 @@ export class StitchToolClient implements StitchToolClientSpec {
undefined,
{ timeout: this.config.timeout },
);
return this.parseToolResponse<T>(result, name);
return this.parseToolResponse<T>(result, name, errorMap);
} catch (rawErr) {
// Normalize transport HTTP / network errors first, so a real 429 or
// transient socket reset is classified and retry-eligible.
Expand Down Expand Up @@ -500,6 +566,8 @@ export class StitchToolClient implements StitchToolClientSpec {
code,
message: `HTTP ${response.status}: ${text || response.statusText}`,
recoverable: isRecoverable(code),
status: response.status,
raw: text,
});
}

Expand Down
10 changes: 9 additions & 1 deletion packages/sdk/src/spec/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,17 @@ export interface StitchToolClientSpec {
* Call a tool on the MCP server.
* @param name - Tool name
* @param args - Tool arguments
* @param errorMap - Optional mapping for typed error handling
* @returns Parsed tool result
*/
callTool: <T>(name: string, args: Record<string, unknown>) => Promise<T>;
callTool: <T>(
name: string,
args: Record<string, unknown>,
errorMap?: Record<
string,
{ schema: any; match: string; create: (data: any, raw: any) => Error }
>,
) => Promise<T>;

/**
* Get available tools from the server.
Expand Down
17 changes: 3 additions & 14 deletions packages/sdk/src/spec/errors.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { z } from "zod";

/**
Expand Down Expand Up @@ -46,6 +32,7 @@ export interface StitchErrorData {
toolName?: string;
/** Delay in milliseconds requested by the server before retrying. */
retryAfter?: number;
raw?: unknown;
}

/**
Expand All @@ -62,6 +49,7 @@ export class StitchError extends Error {
public readonly toolName?: string;
/** Delay in milliseconds requested by the server before retrying. */
public readonly retryAfter?: number;
public readonly raw?: unknown;

constructor(data: StitchErrorData) {
super(data.message);
Expand All @@ -72,6 +60,7 @@ export class StitchError extends Error {
this.status = data.status;
this.toolName = data.toolName;
this.retryAfter = data.retryAfter;
this.raw = data.raw;
}

/**
Expand Down
Loading
Loading