* fix(mcp): sync required array with properties in tool schemas MCP servers can emit schemas where the required array contains keys not present in properties. This causes API 400 errors: "Extra required key 'X' supplied." - Add sanitizeSchemaRequired() to filter required arrays - Apply it to MCP tool inputJSONSchema before sending to API - Also fix filterSwarmFieldsFromSchema to update required after removing properties Fixes #525 * test: add MCP schema required sanitization test
106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import { expect, test } from 'bun:test'
|
|
import { z } from 'zod/v4'
|
|
import { getEmptyToolPermissionContext, type Tool, type Tools } from '../Tool.js'
|
|
import { SkillTool } from '../tools/SkillTool/SkillTool.js'
|
|
import { toolToAPISchema } from './api.js'
|
|
|
|
test('toolToAPISchema preserves provider-specific schema keywords in input_schema', async () => {
|
|
const schema = await toolToAPISchema(
|
|
{
|
|
name: 'WebFetch',
|
|
inputSchema: z.strictObject({}),
|
|
inputJSONSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
url: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
description: 'Public HTTP or HTTPS URL',
|
|
},
|
|
metadata: {
|
|
type: 'object',
|
|
propertyNames: {
|
|
pattern: '^[a-z]+$',
|
|
},
|
|
properties: {
|
|
callback: {
|
|
type: 'string',
|
|
format: 'uri-reference',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
prompt: async () => 'Fetch a URL',
|
|
} as unknown as Tool,
|
|
{
|
|
getToolPermissionContext: async () => getEmptyToolPermissionContext(),
|
|
tools: [] as unknown as Tools,
|
|
agents: [],
|
|
},
|
|
)
|
|
|
|
expect(schema).toMatchObject({
|
|
input_schema: {
|
|
type: 'object',
|
|
properties: {
|
|
url: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
description: 'Public HTTP or HTTPS URL',
|
|
},
|
|
metadata: {
|
|
type: 'object',
|
|
propertyNames: {
|
|
pattern: '^[a-z]+$',
|
|
},
|
|
properties: {
|
|
callback: {
|
|
type: 'string',
|
|
format: 'uri-reference',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
})
|
|
|
|
test('toolToAPISchema keeps skill required for SkillTool', async () => {
|
|
const schema = await toolToAPISchema(SkillTool, {
|
|
getToolPermissionContext: async () => getEmptyToolPermissionContext(),
|
|
tools: [] as unknown as Tools,
|
|
agents: [],
|
|
})
|
|
|
|
expect((schema as { input_schema: unknown }).input_schema).toMatchObject({
|
|
type: 'object',
|
|
required: ['skill'],
|
|
})
|
|
})
|
|
|
|
test('toolToAPISchema removes extra required keys not in properties (MCP schema sanitization)', async () => {
|
|
const schema = await toolToAPISchema(
|
|
{
|
|
name: 'mcp__test__create_object',
|
|
inputSchema: z.strictObject({}),
|
|
inputJSONSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
},
|
|
required: ['name', 'attributes'],
|
|
},
|
|
prompt: async () => 'Create an object',
|
|
} as unknown as Tool,
|
|
{
|
|
getToolPermissionContext: async () => getEmptyToolPermissionContext(),
|
|
tools: [] as unknown as Tools,
|
|
agents: [],
|
|
},
|
|
)
|
|
|
|
const inputSchema = (schema as { input_schema: { required?: string[] } }).input_schema
|
|
expect(inputSchema.required).toEqual(['name'])
|
|
})
|