mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-02 07:13:17 +00:00
90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
|
|
/**
|
||
|
|
* 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<I, O>`.
|
||
|
|
*
|
||
|
|
* Usage:
|
||
|
|
* ```ts
|
||
|
|
* export const searchInbox = defineTool({
|
||
|
|
* name: 'search-inbox',
|
||
|
|
* tier: 'ops',
|
||
|
|
* requiredScope: 'gmail:self',
|
||
|
|
* // ...
|
||
|
|
* handler: async (input: SearchInboxInput, ctx) => { ... },
|
||
|
|
* });
|
||
|
|
* ```
|
||
|
|
*/
|
||
|
|
export function defineTool<I, O>(def: ToolDef<I, O>): ToolDef<I, O> {
|
||
|
|
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<string, ToolDef<any, any>>();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register a tool. Throws if a tool with the same name is already registered.
|
||
|
|
*/
|
||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
|
|
register<I, O>(def: ToolDef<I, O>): 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<any, any>[] {
|
||
|
|
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<any, any> | undefined {
|
||
|
|
return this.tools.get(name);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The number of tools currently registered.
|
||
|
|
*/
|
||
|
|
get size(): number {
|
||
|
|
return this.tools.size;
|
||
|
|
}
|
||
|
|
}
|