fix: schema issues w/zod and mcp tool bridge
This commit is contained in:
parent
7bdbb444e6
commit
800c211582
@ -214,15 +214,6 @@ export function createCcSdkAgentRuntime(context?: CcSdkAgentRuntimeContext): Age
|
|||||||
? `${params.provider ?? "anthropic"}/${params.model}`
|
? `${params.provider ?? "anthropic"}/${params.model}`
|
||||||
: (ccsdkOpts.modelTiers?.sonnet ?? context?.ccsdkConfig?.models?.sonnet ?? "default");
|
: (ccsdkOpts.modelTiers?.sonnet ?? context?.ccsdkConfig?.models?.sonnet ?? "default");
|
||||||
|
|
||||||
log.debug("Starting CCSDK Agent session", {
|
|
||||||
sessionId: params.sessionId,
|
|
||||||
runId: params.runId,
|
|
||||||
provider: providerConfig.name,
|
|
||||||
model: effectiveModel,
|
|
||||||
agentId,
|
|
||||||
thinkLevel: params.thinkLevel,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load conversation history from session file if not provided
|
// Load conversation history from session file if not provided
|
||||||
let conversationHistory = context?.conversationHistory;
|
let conversationHistory = context?.conversationHistory;
|
||||||
if (!conversationHistory && params.sessionFile) {
|
if (!conversationHistory && params.sessionFile) {
|
||||||
@ -275,12 +266,6 @@ export function createCcSdkAgentRuntime(context?: CcSdkAgentRuntimeContext): Age
|
|||||||
// Combine built-in tools with client tools
|
// Combine built-in tools with client tools
|
||||||
const tools: AnyAgentTool[] = [...builtInTools, ...clientToolsConverted];
|
const tools: AnyAgentTool[] = [...builtInTools, ...clientToolsConverted];
|
||||||
|
|
||||||
log.debug("Built tools for CCSDK run", {
|
|
||||||
builtInCount: builtInTools.length,
|
|
||||||
clientToolCount: clientToolsConverted.length,
|
|
||||||
totalCount: tools.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Resolve CCSDK config directory for this agent's session storage.
|
// Resolve CCSDK config directory for this agent's session storage.
|
||||||
// This ensures Moltbot sessions are stored separately from CLI sessions.
|
// This ensures Moltbot sessions are stored separately from CLI sessions.
|
||||||
const ccsdkConfigDir = resolveCcSdkConfigDirForAgent(agentId);
|
const ccsdkConfigDir = resolveCcSdkConfigDirForAgent(agentId);
|
||||||
|
|||||||
@ -630,12 +630,12 @@ export async function runSdkAgent(params: SdkRunnerParams): Promise<SdkRunnerRes
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(
|
// log.debug(
|
||||||
`Bridged ${bridgeResult.toolCount} tools to MCP server "${mcpServerName}"` +
|
// `Exposed ${bridgeResult.toolCount} tools to MCP server "${mcpServerName}"` +
|
||||||
(bridgeResult.skippedTools.length > 0
|
// (bridgeResult.skippedTools.length > 0
|
||||||
? ` (skipped: ${bridgeResult.skippedTools.join(", ")})`
|
// ? ` (skipped: ${bridgeResult.skippedTools.join(", ")})`
|
||||||
: ""),
|
// : ""),
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|||||||
@ -119,7 +119,8 @@ describe("tool-bridge", () => {
|
|||||||
expect(typeof zodSchema.parse).toBe("function");
|
expect(typeof zodSchema.parse).toBe("function");
|
||||||
expect(typeof zodSchema.safeParse).toBe("function");
|
expect(typeof zodSchema.safeParse).toBe("function");
|
||||||
expect(typeof zodSchema.safeParseAsync).toBe("function");
|
expect(typeof zodSchema.safeParseAsync).toBe("function");
|
||||||
expect(zodSchema._def.typeName).toBe("ZodObject");
|
// Zod v4 Mini uses _zod instead of _def
|
||||||
|
expect((zodSchema as any)._zod).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("parse returns data for valid input", () => {
|
it("parse returns data for valid input", () => {
|
||||||
@ -645,4 +646,182 @@ describe("tool-bridge", () => {
|
|||||||
expect(result.skippedTools).toContain("bad_tool");
|
expect(result.skippedTools).toContain("bad_tool");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("tool call error handling", () => {
|
||||||
|
const mockExtra = { signal: new AbortController().signal };
|
||||||
|
|
||||||
|
it("includes tool name in error message when tool throws", async () => {
|
||||||
|
const mockExecute = vi.fn().mockRejectedValue(new Error("Database connection failed"));
|
||||||
|
|
||||||
|
const tool = {
|
||||||
|
name: "database-query",
|
||||||
|
execute: mockExecute,
|
||||||
|
} as unknown as AnyAgentTool;
|
||||||
|
|
||||||
|
const handler = wrapToolHandler(tool);
|
||||||
|
const result = await handler({ query: "SELECT *" }, mockExtra);
|
||||||
|
|
||||||
|
expect(result.isError).toBe(true);
|
||||||
|
expect(result.content[0].type).toBe("text");
|
||||||
|
expect((result.content[0] as { text: string }).text).toContain("database_query");
|
||||||
|
expect((result.content[0] as { text: string }).text).toContain("Database connection failed");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles non-Error thrown values", async () => {
|
||||||
|
const mockExecute = vi.fn().mockRejectedValue("string error message");
|
||||||
|
|
||||||
|
const tool = {
|
||||||
|
name: "string-error-tool",
|
||||||
|
execute: mockExecute,
|
||||||
|
} as unknown as AnyAgentTool;
|
||||||
|
|
||||||
|
const handler = wrapToolHandler(tool);
|
||||||
|
const result = await handler({}, mockExtra);
|
||||||
|
|
||||||
|
expect(result.isError).toBe(true);
|
||||||
|
expect((result.content[0] as { text: string }).text).toContain("string error message");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles undefined/null thrown values", async () => {
|
||||||
|
const mockExecute = vi.fn().mockRejectedValue(undefined);
|
||||||
|
|
||||||
|
const tool = {
|
||||||
|
name: "undefined-error-tool",
|
||||||
|
execute: mockExecute,
|
||||||
|
} as unknown as AnyAgentTool;
|
||||||
|
|
||||||
|
const handler = wrapToolHandler(tool);
|
||||||
|
const result = await handler({}, mockExtra);
|
||||||
|
|
||||||
|
expect(result.isError).toBe(true);
|
||||||
|
expect(result.content.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts tool_error blocks from result to MCP error format", () => {
|
||||||
|
const result: AgentToolResult<unknown> = {
|
||||||
|
content: [
|
||||||
|
{ type: "tool_error", error: "Validation failed: missing required field 'name'" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mcpResult = convertToolResult(result);
|
||||||
|
|
||||||
|
expect(mcpResult.isError).toBe(true);
|
||||||
|
expect(mcpResult.content).toHaveLength(1);
|
||||||
|
expect((mcpResult.content[0] as { text: string }).text).toBe(
|
||||||
|
"Validation failed: missing required field 'name'",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("preserves error details from multiple error blocks", () => {
|
||||||
|
const result: AgentToolResult<unknown> = {
|
||||||
|
content: [
|
||||||
|
{ type: "tool_error", error: "Error 1: Invalid input" },
|
||||||
|
{ type: "tool_error", error: "Error 2: Permission denied" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mcpResult = convertToolResult(result);
|
||||||
|
|
||||||
|
expect(mcpResult.isError).toBe(true);
|
||||||
|
expect(mcpResult.content).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("marks result as error when any block has error field", () => {
|
||||||
|
const result: AgentToolResult<unknown> = {
|
||||||
|
content: [
|
||||||
|
{ type: "text", text: "Partial success" },
|
||||||
|
{ type: "warning", error: "But something went wrong" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mcpResult = convertToolResult(result);
|
||||||
|
|
||||||
|
expect(mcpResult.isError).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Zod schema conversion", () => {
|
||||||
|
it("converts TypeBox string property to Zod string", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
name: Type.String(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
// Should successfully parse valid input
|
||||||
|
const parseResult = zodSchema.safeParse({ name: "test" });
|
||||||
|
expect(parseResult.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts TypeBox number property to Zod number", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
count: Type.Number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
const parseResult = zodSchema.safeParse({ count: 42 });
|
||||||
|
expect(parseResult.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts TypeBox boolean property to Zod boolean", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
active: Type.Boolean(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
const parseResult = zodSchema.safeParse({ active: true });
|
||||||
|
expect(parseResult.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts TypeBox array property to Zod array", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
items: Type.Array(Type.String()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
const parseResult = zodSchema.safeParse({ items: ["a", "b", "c"] });
|
||||||
|
expect(parseResult.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts TypeBox optional property to Zod optional", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
required: Type.String(),
|
||||||
|
optional: Type.Optional(Type.String()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
// Should work without optional field
|
||||||
|
const parseResult = zodSchema.safeParse({ required: "value" });
|
||||||
|
expect(parseResult.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects invalid input with Zod validation errors", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
name: Type.String(),
|
||||||
|
count: Type.Number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
// Pass wrong types
|
||||||
|
const parseResult = zodSchema.safeParse({ name: 123, count: "not a number" });
|
||||||
|
expect(parseResult.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has _zod property for MCP SDK compatibility", () => {
|
||||||
|
const typeboxSchema = Type.Object({
|
||||||
|
value: Type.String(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const zodSchema = createZodCompatibleSchema(typeboxSchema);
|
||||||
|
|
||||||
|
// Zod v4 Mini schemas have _zod property that MCP SDK checks
|
||||||
|
expect((zodSchema as any)._zod).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||||
import type { TSchema } from "@sinclair/typebox";
|
import type { TSchema } from "@sinclair/typebox";
|
||||||
import { Value } from "@sinclair/typebox/value";
|
import { Value } from "@sinclair/typebox/value";
|
||||||
|
import * as z from "zod/v4-mini";
|
||||||
|
|
||||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||||
import { normalizeToolName } from "../tool-policy.js";
|
import { normalizeToolName } from "../tool-policy.js";
|
||||||
@ -36,85 +37,136 @@ import type {
|
|||||||
const log = createSubsystemLogger("agents/claude-agent-sdk/tool-bridge");
|
const log = createSubsystemLogger("agents/claude-agent-sdk/tool-bridge");
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Schema conversion: TypeBox → Zod-compatible schema
|
// Schema conversion: TypeBox → Zod v4 Mini schema
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Use z.ZodMiniType as the base type for all Zod Mini schemas
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: Zod Mini type system is complex; any allows proper schema composition
|
||||||
|
type ZodMiniSchema = z.ZodMiniType<any, any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for Zod-compatible schema that the MCP SDK expects.
|
* Convert a TypeBox property schema to a Zod v4 Mini schema.
|
||||||
* The SDK checks for `parse` and `safeParse` methods to identify valid schemas.
|
* This recursive converter handles common TypeBox/JSON Schema types.
|
||||||
*/
|
*/
|
||||||
export interface ZodCompatibleSchema {
|
function convertTypeBoxPropertyToZod(propSchema: TSchema): ZodMiniSchema {
|
||||||
/** Synchronous parse - throws on validation failure */
|
const type = (propSchema as { type?: string }).type;
|
||||||
parse(data: unknown): unknown;
|
|
||||||
/** Safe synchronous parse - returns success/error result */
|
// Handle based on JSON Schema type
|
||||||
safeParse(data: unknown): { success: true; data: unknown } | { success: false; error: unknown };
|
switch (type) {
|
||||||
/** Async version of safeParse (used by MCP SDK) */
|
case "string": {
|
||||||
safeParseAsync(
|
return z.string();
|
||||||
data: unknown,
|
}
|
||||||
): Promise<{ success: true; data: unknown } | { success: false; error: unknown }>;
|
case "number":
|
||||||
/** Mark this as a Zod-like schema instance (SDK checks for _def or _zod) */
|
case "integer": {
|
||||||
_def: { typeName: string; description?: string };
|
return z.number();
|
||||||
|
}
|
||||||
|
case "boolean": {
|
||||||
|
return z.boolean();
|
||||||
|
}
|
||||||
|
case "array": {
|
||||||
|
const items = (propSchema as { items?: TSchema }).items;
|
||||||
|
const itemSchema = items ? convertTypeBoxPropertyToZod(items) : z.unknown();
|
||||||
|
return z.array(itemSchema);
|
||||||
|
}
|
||||||
|
case "object": {
|
||||||
|
// Nested object - recursively convert
|
||||||
|
const nestedProps = (propSchema as { properties?: Record<string, TSchema> }).properties;
|
||||||
|
const nestedRequired = (propSchema as { required?: string[] }).required ?? [];
|
||||||
|
if (nestedProps) {
|
||||||
|
const shape = buildZodShapeFromTypeBox(nestedProps, nestedRequired);
|
||||||
|
return z.object(shape);
|
||||||
|
}
|
||||||
|
// Object without defined properties - use passthrough
|
||||||
|
return z.record(z.string(), z.unknown());
|
||||||
|
}
|
||||||
|
case "null": {
|
||||||
|
return z.null();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Check for enum/const
|
||||||
|
const enumValues = (propSchema as { enum?: string[] }).enum;
|
||||||
|
if (enumValues && Array.isArray(enumValues)) {
|
||||||
|
// Create a union of literals for enum
|
||||||
|
if (enumValues.length === 1) {
|
||||||
|
return z.literal(enumValues[0]);
|
||||||
|
}
|
||||||
|
const literals = enumValues.map((v) => z.literal(v));
|
||||||
|
// TypeScript requires explicit tuple type assertion for z.union
|
||||||
|
return z.union(literals as unknown as [ZodMiniSchema, ZodMiniSchema, ...ZodMiniSchema[]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const constValue = (propSchema as { const?: unknown }).const;
|
||||||
|
if (constValue !== undefined) {
|
||||||
|
return z.literal(constValue as string | number | boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to unknown for unrecognized types
|
||||||
|
return z.unknown();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap a TypeBox schema to make it Zod-compatible for the MCP SDK.
|
* Build a Zod shape object from TypeBox properties.
|
||||||
*
|
|
||||||
* The MCP SDK's McpServer.tool() checks `isZodTypeLike()` which requires:
|
|
||||||
* - `parse` method (function)
|
|
||||||
* - `safeParse` method (function)
|
|
||||||
*
|
|
||||||
* This wrapper uses TypeBox's Value module to perform validation.
|
|
||||||
*/
|
*/
|
||||||
export function createZodCompatibleSchema(typeboxSchema: TSchema): ZodCompatibleSchema {
|
function buildZodShapeFromTypeBox(
|
||||||
return {
|
properties: Record<string, TSchema>,
|
||||||
parse(data: unknown): unknown {
|
requiredProps: string[],
|
||||||
// Decode handles coercion (string→number, etc.) and throws on failure
|
): Record<string, ZodMiniSchema> {
|
||||||
return Value.Decode(typeboxSchema, data);
|
const requiredSet = new Set(requiredProps);
|
||||||
},
|
const shape: Record<string, ZodMiniSchema> = {};
|
||||||
|
|
||||||
safeParse(
|
for (const [propName, propSchema] of Object.entries(properties)) {
|
||||||
data: unknown,
|
let zodProp = convertTypeBoxPropertyToZod(propSchema);
|
||||||
): { success: true; data: unknown } | { success: false; error: unknown } {
|
|
||||||
try {
|
|
||||||
// First check if valid
|
|
||||||
if (!Value.Check(typeboxSchema, data)) {
|
|
||||||
// Get the first validation error for a useful message
|
|
||||||
const errors = [...Value.Errors(typeboxSchema, data)];
|
|
||||||
const firstError = errors[0];
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
message: firstError?.message ?? "Validation failed",
|
|
||||||
path: firstError?.path ?? "",
|
|
||||||
issues: errors.map((e) => ({ message: e.message, path: e.path })),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Decode to apply any transformations
|
|
||||||
const decoded = Value.Decode(typeboxSchema, data);
|
|
||||||
return { success: true, data: decoded };
|
|
||||||
} catch (err) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: err instanceof Error ? { message: err.message } : { message: String(err) },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async safeParseAsync(
|
// Wrap in optional if not required
|
||||||
data: unknown,
|
if (!requiredSet.has(propName)) {
|
||||||
): Promise<{ success: true; data: unknown } | { success: false; error: unknown }> {
|
zodProp = z.optional(zodProp);
|
||||||
return this.safeParse(data);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Mimic Zod v3's internal structure so isZodSchemaInstance returns true
|
|
||||||
_def: {
|
|
||||||
typeName: "ZodObject",
|
|
||||||
description: (typeboxSchema as { description?: string }).description,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shape[propName] = zodProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a TypeBox object schema to a Zod v4 Mini object schema.
|
||||||
|
*
|
||||||
|
* The MCP SDK natively supports Zod v4 Mini schemas. By converting TypeBox
|
||||||
|
* to real Zod schemas, we get proper JSON Schema generation for Claude
|
||||||
|
* and validation that matches what the SDK expects.
|
||||||
|
*/
|
||||||
|
export function convertTypeBoxToZod(typeboxSchema: TSchema): ZodMiniSchema {
|
||||||
|
const properties = (typeboxSchema as { properties?: Record<string, TSchema> }).properties;
|
||||||
|
const requiredProps = (typeboxSchema as { required?: string[] }).required ?? [];
|
||||||
|
|
||||||
|
if (!properties || typeof properties !== "object") {
|
||||||
|
// No properties defined - return a permissive object schema
|
||||||
|
return z.object({});
|
||||||
|
}
|
||||||
|
|
||||||
|
const shape = buildZodShapeFromTypeBox(properties, requiredProps);
|
||||||
|
return z.object(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Zod v4 Mini schema from a tool's TypeBox parameters.
|
||||||
|
* Returns undefined if the tool has no parameters.
|
||||||
|
*/
|
||||||
|
export function extractZodSchema(tool: AnyAgentTool): ZodMiniSchema | undefined {
|
||||||
|
const schema = tool.parameters as TSchema | undefined;
|
||||||
|
if (!schema || typeof schema !== "object") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return convertTypeBoxToZod(schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy exports for backward compatibility
|
||||||
|
export type ZodCompatibleSchema = ZodMiniSchema;
|
||||||
|
export const createZodCompatibleSchema = convertTypeBoxToZod;
|
||||||
|
export const extractZodCompatibleSchema = extractZodSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a clean JSON Schema object from a TypeBox schema.
|
* Extract a clean JSON Schema object from a TypeBox schema.
|
||||||
*
|
*
|
||||||
@ -136,20 +188,6 @@ export function extractJsonSchema(tool: AnyAgentTool): Record<string, unknown> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Zod-compatible schema from a tool's TypeBox parameters.
|
|
||||||
* Falls back to a permissive schema if the tool has no parameters.
|
|
||||||
*/
|
|
||||||
export function extractZodCompatibleSchema(tool: AnyAgentTool): ZodCompatibleSchema | undefined {
|
|
||||||
const schema = tool.parameters as TSchema | undefined;
|
|
||||||
if (!schema || typeof schema !== "object") {
|
|
||||||
// Return undefined to indicate no input schema
|
|
||||||
// The MCP SDK will then call handler(extra) instead of handler(args, extra)
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return createZodCompatibleSchema(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Result conversion: AgentToolResult → McpCallToolResult
|
// Result conversion: AgentToolResult → McpCallToolResult
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user