Skip to content

Commit f6ffc37

Browse files
ochafikclaude
andcommitted
fix: add .default([]) to content arrays and fix test capabilities
- Add transformArrayDefaults to schema generation for backwards compatibility - CallToolResultSchema.content and ToolResultContentSchema.content now default to [] - Fix mcp.test.ts server capabilities to use correct tasks.requests.tools.call structure This fixes test failures caused by stricter schema validation requiring content arrays. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0ee2baf commit f6ffc37

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed

scripts/generate-schemas.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* - `z.record().and(z.object())` → `z.looseObject()`
1818
* - `jsonrpc: z.any()` → `z.literal("2.0")`
1919
* - Add `.int()` refinements to ProgressTokenSchema, RequestIdSchema
20+
* - Add `.default([])` to content arrays for backwards compatibility
2021
* - `z.union([z.literal("a"), ...])` → `z.enum(["a", ...])`
2122
* - Field-level validation overrides (datetime, startsWith, etc.)
2223
*
@@ -89,6 +90,15 @@ const FIELD_OVERRIDES: Record<string, Record<string, string>> = {
8990
*/
9091
const INTEGER_SCHEMAS = ['ProgressTokenSchema', 'RequestIdSchema'];
9192

93+
/**
94+
* Fields that need .default([]) for backwards compatibility.
95+
* The SDK historically made these optional with empty array defaults.
96+
*/
97+
const ARRAY_DEFAULT_FIELDS: Record<string, string[]> = {
98+
'CallToolResultSchema': ['content'],
99+
'ToolResultContentSchema': ['content'],
100+
};
101+
92102
/**
93103
* Schemas that need .strict() added for stricter validation.
94104
*/
@@ -616,6 +626,7 @@ const AST_TRANSFORMS: Transform[] = [
616626
transformRecordAndToLooseObject,
617627
transformTypeofExpressions,
618628
transformIntegerRefinements,
629+
transformArrayDefaults,
619630
transformUnionToEnum,
620631
applyFieldOverrides,
621632
addStrictToSchemas,
@@ -740,6 +751,39 @@ function transformIntegerRefinements(sourceFile: SourceFile): void {
740751
}
741752
}
742753

754+
/**
755+
* Add .default([]) to array fields for backwards compatibility.
756+
* The SDK historically made certain content arrays optional with empty defaults.
757+
*/
758+
function transformArrayDefaults(sourceFile: SourceFile): void {
759+
for (const [schemaName, fieldNames] of Object.entries(ARRAY_DEFAULT_FIELDS)) {
760+
const varDecl = sourceFile.getVariableDeclaration(schemaName);
761+
if (!varDecl) continue;
762+
763+
const initializer = varDecl.getInitializer();
764+
if (!initializer) continue;
765+
766+
// Find property assignments for the target fields
767+
initializer.forEachDescendant((node) => {
768+
if (!Node.isPropertyAssignment(node)) return;
769+
770+
const propName = node.getName();
771+
if (!fieldNames.includes(propName)) return;
772+
773+
// Get the initializer (the value assigned to the property)
774+
const propInit = node.getInitializer();
775+
if (!propInit) return;
776+
777+
const propText = propInit.getText();
778+
// Only add .default([]) if it's a z.array() and doesn't already have .default()
779+
if (propText.includes('z.array(') && !propText.includes('.default(')) {
780+
// Append .default([]) to the existing expression
781+
propInit.replaceWithText(propText + '.default([])');
782+
}
783+
});
784+
}
785+
}
786+
743787
/**
744788
* Transform z.union([z.literal('a'), z.literal('b'), ...]) to z.enum(['a', 'b', ...])
745789
*

src/generated/sdk.schemas.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,7 +2259,8 @@ export const ToolResultContentSchema = z
22592259
.array(ContentBlockSchema)
22602260
.describe(
22612261
'The unstructured result content of the tool use.\n\nThis has the same format as CallToolResult.content and can include text, images,\naudio, resource links, and embedded resources.'
2262-
),
2262+
)
2263+
.default([]),
22632264
/** @description An optional structured result object.
22642265
22652266
If the tool defined an outputSchema, this SHOULD conform to that schema. */
@@ -2389,7 +2390,10 @@ export const ListResourcesResultSchema = PaginatedResultSchema.extend({
23892390
*/
23902391
export const CallToolResultSchema = ResultSchema.extend({
23912392
/** @description A list of content objects that represent the unstructured result of the tool call. */
2392-
content: z.array(ContentBlockSchema).describe('A list of content objects that represent the unstructured result of the tool call.'),
2393+
content: z
2394+
.array(ContentBlockSchema)
2395+
.describe('A list of content objects that represent the unstructured result of the tool call.')
2396+
.default([]),
23932397
/** @description An optional JSON object that represents the structured result of the tool call. */
23942398
structuredContent: z
23952399
.record(z.string(), z.unknown())

test/server/mcp.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6456,8 +6456,11 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
64566456
capabilities: {
64576457
tools: {},
64586458
tasks: {
6459-
list: {},
6460-
cancel: {}
6459+
requests: {
6460+
tools: {
6461+
call: {}
6462+
}
6463+
}
64616464
}
64626465
},
64636466
taskStore

0 commit comments

Comments
 (0)