import type { StandardSchemaV1 } from "@standard-schema/spec"; import { encode as toonEncode } from "@toon-format/toon"; import type { FastMCP, Tool } from "fastmcp"; import { formatJsonValue, log } from "../utils/cli.ts"; import type { ToolContext } from "./server.ts"; export const tool = ( toolDef: Tool> ): Tool> => toolDef; export interface ToolResult { content: { type: "text"; text: string; }[]; isError?: boolean; } export const handleToolSuccess = (data: Record | string): ToolResult => { const text = typeof data === "string" ? data : toonEncode(data); return { content: [{ type: "text", text }], }; }; export const handleToolError = (error: unknown): ToolResult => { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error: ${errorMessage}`, }, ], isError: true, }; }; /** * Helper to wrap a tool execute function with error handling. * Captures ctx in closure so tools don't need to handle try/catch. * @param fn - the function to execute * @param toolName - optional tool name for error logging */ export const execute = | string>( fn: (params: T) => Promise, toolName?: string ) => { const _fn = async (params: T): Promise => { try { const result = await fn(params); return handleToolSuccess(result); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const prefix = toolName ? `[${toolName}]` : "tool"; log.info(`${prefix} error: ${errorMessage}`); log.debug(`${prefix} params: ${formatJsonValue(params)}`); return handleToolError(error); } }; return _fn; }; export const addTools = (_ctx: ToolContext, server: FastMCP, tools: Tool[]) => { for (const tool of tools) { server.addTool(tool); } return server; };