Skip to content

Commit b46ac0d

Browse files
authored
fix: preserve MCP tool params when MCP schemas are rendered as allOf (#459)
1 parent 3a02b50 commit b46ac0d

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

packages/agent-runtime/src/__tests__/prompts-schema-handling.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TEST_AGENT_RUNTIME_IMPL } from '@codebuff/common/testing/impl/agent-runtime'
22
import { describe, test, expect, mock } from 'bun:test'
3+
import { convertJsonSchemaToZod } from 'zod-from-json-schema'
34
import { z } from 'zod/v4'
45

56
import { buildAgentToolInputSchema, buildAgentToolSet } from '../templates/prompts'
@@ -172,6 +173,30 @@ describe('Schema handling error recovery', () => {
172173
expect(description).toContain('content')
173174
})
174175

176+
test('buildToolDescription preserves MCP params when schema is represented as allOf', () => {
177+
const mcpSchema = convertJsonSchemaToZod({
178+
type: 'object',
179+
properties: {
180+
name: { type: 'string' },
181+
},
182+
required: ['name'],
183+
additionalProperties: false,
184+
})
185+
186+
const description = buildToolDescription({
187+
toolName: 'greet__greet',
188+
schema: mcpSchema,
189+
description: 'Call greet',
190+
endsAgentStep: true,
191+
})
192+
193+
expect(description).toContain('greet__greet')
194+
expect(description).toContain('Params: {')
195+
expect(description).toContain('allOf')
196+
expect(description).toContain('name')
197+
expect(description).not.toContain('Params: None')
198+
})
199+
175200
test('getToolSet handles custom tools with problematic schemas', async () => {
176201
// Create a custom tool definition with a schema that can't be converted
177202
const customToolDefs = {

packages/agent-runtime/src/tools/prompts.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@ function toJsonSchemaSafe(schema: z.ZodType): Record<string, unknown> {
5353
}
5454
}
5555

56+
function hasMeaningfulJsonSchema(jsonSchema: Record<string, unknown>): boolean {
57+
const properties = jsonSchema.properties
58+
if (properties && typeof properties === 'object' && Object.keys(properties).length > 0) {
59+
return true
60+
}
61+
62+
for (const key of ['allOf', 'anyOf', 'oneOf']) {
63+
const value = jsonSchema[key]
64+
if (Array.isArray(value) && value.length > 0) {
65+
return true
66+
}
67+
}
68+
69+
const required = jsonSchema.required
70+
if (Array.isArray(required) && required.length > 0) {
71+
return true
72+
}
73+
74+
return false
75+
}
76+
5677
function paramsSection(params: { schema: z.ZodType; endsAgentStep: boolean }) {
5778
const { schema, endsAgentStep } = params
5879
const safeSchema = ensureJsonSchemaCompatible(schema)
@@ -68,7 +89,7 @@ function paramsSection(params: { schema: z.ZodType; endsAgentStep: boolean }) {
6889
const jsonSchema = toJsonSchemaSafe(schemaWithEndsAgentStepParam)
6990
delete jsonSchema.description
7091
delete jsonSchema['$schema']
71-
const paramsDescription = Object.keys(jsonSchema.properties ?? {}).length
92+
const paramsDescription = hasMeaningfulJsonSchema(jsonSchema)
7293
? JSON.stringify(jsonSchema, null, 2)
7394
: 'None'
7495

0 commit comments

Comments
 (0)