diff --git a/packages/agent-runtime/src/__tests__/prompts-schema-handling.test.ts b/packages/agent-runtime/src/__tests__/prompts-schema-handling.test.ts index 999d45e0f8..60970db02d 100644 --- a/packages/agent-runtime/src/__tests__/prompts-schema-handling.test.ts +++ b/packages/agent-runtime/src/__tests__/prompts-schema-handling.test.ts @@ -1,5 +1,6 @@ import { TEST_AGENT_RUNTIME_IMPL } from '@codebuff/common/testing/impl/agent-runtime' import { describe, test, expect, mock } from 'bun:test' +import { convertJsonSchemaToZod } from 'zod-from-json-schema' import { z } from 'zod/v4' import { buildAgentToolInputSchema, buildAgentToolSet } from '../templates/prompts' @@ -172,6 +173,30 @@ describe('Schema handling error recovery', () => { expect(description).toContain('content') }) + test('buildToolDescription preserves MCP params when schema is represented as allOf', () => { + const mcpSchema = convertJsonSchemaToZod({ + type: 'object', + properties: { + name: { type: 'string' }, + }, + required: ['name'], + additionalProperties: false, + }) + + const description = buildToolDescription({ + toolName: 'greet__greet', + schema: mcpSchema, + description: 'Call greet', + endsAgentStep: true, + }) + + expect(description).toContain('greet__greet') + expect(description).toContain('Params: {') + expect(description).toContain('allOf') + expect(description).toContain('name') + expect(description).not.toContain('Params: None') + }) + test('getToolSet handles custom tools with problematic schemas', async () => { // Create a custom tool definition with a schema that can't be converted const customToolDefs = { diff --git a/packages/agent-runtime/src/tools/prompts.ts b/packages/agent-runtime/src/tools/prompts.ts index a191412996..c87aaf875d 100644 --- a/packages/agent-runtime/src/tools/prompts.ts +++ b/packages/agent-runtime/src/tools/prompts.ts @@ -53,6 +53,27 @@ function toJsonSchemaSafe(schema: z.ZodType): Record { } } +function hasMeaningfulJsonSchema(jsonSchema: Record): boolean { + const properties = jsonSchema.properties + if (properties && typeof properties === 'object' && Object.keys(properties).length > 0) { + return true + } + + for (const key of ['allOf', 'anyOf', 'oneOf']) { + const value = jsonSchema[key] + if (Array.isArray(value) && value.length > 0) { + return true + } + } + + const required = jsonSchema.required + if (Array.isArray(required) && required.length > 0) { + return true + } + + return false +} + function paramsSection(params: { schema: z.ZodType; endsAgentStep: boolean }) { const { schema, endsAgentStep } = params const safeSchema = ensureJsonSchemaCompatible(schema) @@ -68,7 +89,7 @@ function paramsSection(params: { schema: z.ZodType; endsAgentStep: boolean }) { const jsonSchema = toJsonSchemaSafe(schemaWithEndsAgentStepParam) delete jsonSchema.description delete jsonSchema['$schema'] - const paramsDescription = Object.keys(jsonSchema.properties ?? {}).length + const paramsDescription = hasMeaningfulJsonSchema(jsonSchema) ? JSON.stringify(jsonSchema, null, 2) : 'None'