/** * In-memory tool registry. * * Packages call `defineTool()` to create a typed ToolDef, then * `ToolRegistry.register()` to publish it. Transport adapters (OpenAPI, * MCP) consume the registry via `list()` / `get()`. * * There is no global singleton registry exported here — each server * instantiates its own registry so tests stay isolated. */ import type { ToolDef } from './types.js'; // --------------------------------------------------------------------------- // defineTool — identity helper that preserves full generic types // --------------------------------------------------------------------------- /** * Wrap a tool definition to get TypeScript inference on `I` and `O` while * returning a value that satisfies `ToolDef`. * * Usage: * ```ts * export const searchInbox = defineTool({ * name: 'search-inbox', * tier: 'ops', * requiredScope: 'gmail:self', * // ... * handler: async (input: SearchInboxInput, ctx) => { ... }, * }); * ``` */ export function defineTool(def: ToolDef): ToolDef { return def; } // --------------------------------------------------------------------------- // ToolRegistry // --------------------------------------------------------------------------- /** * A typed, in-memory registry of tool definitions. * * Tool names must be unique across a registry instance. Attempting to * register a duplicate name throws synchronously so misconfigurations are * caught at server startup, not at request time. */ export class ToolRegistry { // eslint-disable-next-line @typescript-eslint/no-explicit-any private readonly tools = new Map>(); /** * Register a tool. Throws if a tool with the same name is already registered. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any register(def: ToolDef): this { if (this.tools.has(def.name)) { throw new Error( `ToolRegistry: duplicate tool name "${def.name}". ` + 'Each tool name must be unique within a registry.', ); } this.tools.set(def.name, def); return this; } /** * Return all registered tool definitions in insertion order. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any list(): ToolDef[] { return [...this.tools.values()]; } /** * Look up a tool by name. Returns `undefined` if not found. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any get(name: string): ToolDef | undefined { return this.tools.get(name); } /** * The number of tools currently registered. */ get size(): number { return this.tools.size; } }