SEP-1613: use.catchall() on inputSchema/outputSchema to support JSON Schema 2020-12 (#1135)
This commit is contained in:
parent
0c1cbf0d37
commit
41c6b35e07
3 changed files with 141 additions and 11 deletions
|
|
@ -6,6 +6,7 @@ import {
|
|||
PromptMessageSchema,
|
||||
CallToolResultSchema,
|
||||
CompleteRequestSchema,
|
||||
ToolSchema,
|
||||
ToolUseContentSchema,
|
||||
ToolResultContentSchema,
|
||||
ToolChoiceSchema,
|
||||
|
|
@ -319,6 +320,132 @@ describe('Types', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ToolSchema - JSON Schema 2020-12 support', () => {
|
||||
test('should accept inputSchema with $schema field', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
||||
type: 'object',
|
||||
properties: { name: { type: 'string' } }
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should accept inputSchema with additionalProperties', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { name: { type: 'string' } },
|
||||
additionalProperties: false
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should accept inputSchema with composition keywords', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
allOf: [{ properties: { a: { type: 'string' } } }, { properties: { b: { type: 'number' } } }]
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should accept inputSchema with $ref and $defs', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { user: { $ref: '#/$defs/User' } },
|
||||
$defs: {
|
||||
User: { type: 'object', properties: { name: { type: 'string' } } }
|
||||
}
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should accept inputSchema with metadata keywords', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
title: 'User Input',
|
||||
description: 'Input parameters for user creation',
|
||||
deprecated: false,
|
||||
examples: [{ name: 'John' }],
|
||||
properties: { name: { type: 'string' } }
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should accept outputSchema with full JSON Schema features', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: { type: 'object' },
|
||||
outputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
tags: { type: 'array' }
|
||||
},
|
||||
required: ['id'],
|
||||
additionalProperties: false,
|
||||
minProperties: 1
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
test('should still require type: object at root for inputSchema', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
type: 'string'
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
test('should still require type: object at root for outputSchema', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: { type: 'object' },
|
||||
outputSchema: {
|
||||
type: 'array'
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
test('should accept simple minimal schema (backward compatibility)', () => {
|
||||
const tool = {
|
||||
name: 'test',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { name: { type: 'string' } },
|
||||
required: ['name']
|
||||
}
|
||||
};
|
||||
const result = ToolSchema.safeParse(tool);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ToolUseContent', () => {
|
||||
test('should validate a tool call content', () => {
|
||||
const toolCall = {
|
||||
|
|
|
|||
24
src/types.ts
24
src/types.ts
|
|
@ -999,24 +999,28 @@ export const ToolSchema = z.object({
|
|||
*/
|
||||
description: z.string().optional(),
|
||||
/**
|
||||
* A JSON Schema object defining the expected parameters for the tool.
|
||||
* A JSON Schema 2020-12 object defining the expected parameters for the tool.
|
||||
* Must have type: 'object' at the root level per MCP spec.
|
||||
*/
|
||||
inputSchema: z.object({
|
||||
type: z.literal('object'),
|
||||
properties: z.record(z.string(), AssertObjectSchema).optional(),
|
||||
required: z.optional(z.array(z.string()))
|
||||
}),
|
||||
inputSchema: z
|
||||
.object({
|
||||
type: z.literal('object'),
|
||||
properties: z.record(z.string(), AssertObjectSchema).optional(),
|
||||
required: z.array(z.string()).optional()
|
||||
})
|
||||
.catchall(z.unknown()),
|
||||
/**
|
||||
* An optional JSON Schema object defining the structure of the tool's output returned in
|
||||
* the structuredContent field of a CallToolResult.
|
||||
* An optional JSON Schema 2020-12 object defining the structure of the tool's output
|
||||
* returned in the structuredContent field of a CallToolResult.
|
||||
* Must have type: 'object' at the root level per MCP spec.
|
||||
*/
|
||||
outputSchema: z
|
||||
.object({
|
||||
type: z.literal('object'),
|
||||
properties: z.record(z.string(), AssertObjectSchema).optional(),
|
||||
required: z.optional(z.array(z.string())),
|
||||
additionalProperties: z.optional(z.boolean())
|
||||
required: z.array(z.string()).optional()
|
||||
})
|
||||
.catchall(z.unknown())
|
||||
.optional(),
|
||||
/**
|
||||
* Optional additional tool information.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
* This provider uses @cfworker/json-schema for validation without code generation,
|
||||
* making it compatible with edge runtimes like Cloudflare Workers that restrict
|
||||
* eval and new Function.
|
||||
*
|
||||
*/
|
||||
|
||||
import { type Schema, Validator } from '@cfworker/json-schema';
|
||||
|
|
|
|||
Loading…
Reference in a new issue