Skip to content

Commit b35bfa6

Browse files
ochafikclaude
andcommitted
fix: add .passthrough() to ToolSchema.outputSchema
Preserve JSON Schema properties like additionalProperties when parsing tool output schemas. This allows proper validation of structured content that uses additionalProperties: false. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 00162c4 commit b35bfa6

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

scripts/generate-schemas.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ const ARRAY_DEFAULT_FIELDS: Record<string, string[]> = {
9999
'ToolResultContentSchema': ['content'],
100100
};
101101

102+
/**
103+
* Fields that need .passthrough() to preserve unknown JSON Schema properties.
104+
* These are JSON Schema objects where extra properties like additionalProperties must be kept.
105+
*/
106+
const PASSTHROUGH_FIELDS: Record<string, string[]> = {
107+
'ToolSchema': ['outputSchema'],
108+
};
109+
102110
/**
103111
* Schemas that need .strict() added for stricter validation.
104112
*/
@@ -658,6 +666,7 @@ const AST_TRANSFORMS: Transform[] = [
658666
transformTypeofExpressions,
659667
transformIntegerRefinements,
660668
transformArrayDefaults,
669+
transformPassthroughFields,
661670
transformUnionToEnum,
662671
applyFieldOverrides,
663672
addStrictToSchemas,
@@ -685,6 +694,17 @@ function postProcess(content: string): string {
685694
// Run: npm run generate:schemas`,
686695
);
687696

697+
// Add .passthrough() to outputSchema to preserve JSON Schema properties like additionalProperties
698+
// Pattern matches: outputSchema: z.object({...}).optional()
699+
// We need to insert .passthrough() before .optional()
700+
// Note: ts-to-zod generates inline, so pattern is: outputSchema: z.object({...}).optional()
701+
const outputSchemaPattern = /(outputSchema:\s*z\.object\(\{[\s\S]*?\}\))(\.optional\(\))/g;
702+
const newContent = content.replace(outputSchemaPattern, '$1.passthrough()$2');
703+
if (newContent !== content) {
704+
content = newContent;
705+
console.log(' ✓ Added .passthrough() to ToolSchema.outputSchema');
706+
}
707+
688708
// AST-based transforms using ts-morph
689709
const project = new Project({ useInMemoryFileSystem: true });
690710
const sourceFile = project.createSourceFile('schemas.ts', content);
@@ -815,6 +835,14 @@ function transformArrayDefaults(sourceFile: SourceFile): void {
815835
}
816836
}
817837

838+
/**
839+
* Add .passthrough() to fields that need to preserve unknown JSON Schema properties.
840+
* This is done via text replacement in postProcess since the structure is complex.
841+
*/
842+
function transformPassthroughFields(_sourceFile: SourceFile): void {
843+
// This is handled in postProcess via text replacement for simplicity
844+
}
845+
818846
/**
819847
* Transform z.union([z.literal('a'), z.literal('b'), ...]) to z.enum(['a', 'b', ...])
820848
*

src/generated/sdk.schemas.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,7 @@ export const ToolSchema = BaseMetadataSchema.extend(IconsSchema.shape)
10411041
properties: z.record(z.string(), z.record(z.string(), z.any())).optional(),
10421042
required: z.array(z.string()).optional()
10431043
})
1044+
.passthrough()
10441045
.optional()
10451046
.describe(
10461047
'An optional JSON Schema object defining the structure of the tool\'s output returned in\nthe structuredContent field of a CallToolResult.\n\nDefaults to JSON Schema 2020-12 when no explicit $schema is provided.\nCurrently restricted to type: "object" at the root level.'

0 commit comments

Comments
 (0)