import type { Tool, ToolSpec, ToolResult, ToolContext } from "./protocol" import { validateJsonSchema } from "./validation" export type { Tool, ToolSpec, ToolResult, ToolContext } export interface ToolDefinition { name: string description: string inputSchema: Record isReadOnly?: boolean isDestructive?: boolean run: (input: Record, ctx: ToolContext) => ToolResult | Promise } class ToolInstance implements Tool { private def: ToolDefinition constructor(def: ToolDefinition) { this.def = def } spec(): ToolSpec { return { name: this.def.name, description: this.def.description, inputSchema: this.def.inputSchema, isReadOnly: this.def.isReadOnly, isDestructive: this.def.isDestructive, } } run(input: Record, ctx: ToolContext): ToolResult | Promise { return this.def.run(input, ctx) } } export function createTool(def: ToolDefinition): Tool { return new ToolInstance(def) } export class ToolRegistry { private tools: Map = new Map() register(tool: Tool): void { const spec = tool.spec() const key = spec.name.toLowerCase() if (this.tools.has(key)) { throw new Error(`duplicate tool name: ${spec.name}`) } this.tools.set(key, tool) } listSpecs(): ToolSpec[] { return Array.from(this.tools.values()).map((t) => t.spec()) } get(name: string): Tool | undefined { return this.tools.get(name.toLowerCase()) } dispatch(name: string, input: Record, ctx: ToolContext): ToolResult { const tool = this.get(name) if (!tool) { return { name, output: { error: `unknown tool: ${name}` }, isError: true } } const spec = tool.spec() try { validateJsonSchema(input, spec.inputSchema) } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err) return { name: spec.name, output: { error: msg }, isError: true } } const result = tool.run(input, ctx) if (result instanceof Promise) { throw new Error("Async tools not supported in sync dispatch. Use dispatchAsync instead.") } return result } async dispatchAsync(name: string, input: Record, ctx: ToolContext): Promise { const tool = this.get(name) if (!tool) { return { name, output: { error: `unknown tool: ${name}` }, isError: true } } const spec = tool.spec() try { validateJsonSchema(input, spec.inputSchema) } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err) return { name: spec.name, output: { error: msg }, isError: true } } return tool.run(input, ctx) } }