import type { StandardSchemaV1 } from "@standard-schema/spec"; import type { FastMCP, Tool } from "fastmcp"; import { formatJsonValue, log } from "../utils/cli.ts"; import type { ToolContext } from "./server.ts"; // Tool is intentional: the tools array is a heterogeneous collection // where each tool has a different typed params schema. TypeScript's contravariance // rules make it impossible to express this without any in the generic position. 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 : JSON.stringify(data, null, 2); 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, }; }; 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; };