File size: 3,070 Bytes
b9a3ef2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | // ---------------------------------------------------------------------------
// Tool Registry -- MCP-like typed tool system
// ---------------------------------------------------------------------------
import fs from 'fs';
import path from 'path';
export interface ToolParams {
url?: string;
prompt?: string;
input?: string;
raw?: string;
captures?: string[];
[key: string]: unknown;
}
export interface ToolResult {
transcript?: string;
downloadUrl?: string;
filename?: string;
method?: string;
message?: string;
mock?: boolean;
error?: string;
[key: string]: unknown;
}
export type ProgressEmitter = (message: string) => void;
export interface ToolDefinition {
name: string;
description: string;
syntax: string;
pattern: RegExp;
mock: boolean;
execute: (params: ToolParams, emitProgress: ProgressEmitter) => Promise<ToolResult>;
}
export interface ToolSummary {
name: string;
description: string;
syntax: string;
mock: boolean;
}
export interface ToolMatch {
tool: ToolDefinition;
params: ToolParams;
}
export class ToolRegistry {
private tools = new Map<string, ToolDefinition>();
register(tool: ToolDefinition): void {
if (!tool.name || !tool.execute) {
throw new Error('Tool must have a name and execute function.');
}
this.tools.set(tool.name, tool);
console.log(` [ToolRegistry] Registered: ${tool.name}`);
}
listTools(): ToolSummary[] {
return Array.from(this.tools.values()).map((t) => ({
name: t.name,
description: t.description,
syntax: t.syntax,
mock: t.mock,
}));
}
getTool(name: string): ToolDefinition | null {
return this.tools.get(name) ?? null;
}
matchTool(message: string): ToolMatch | null {
for (const tool of this.tools.values()) {
if (!tool.pattern) continue;
const match = message.match(tool.pattern);
if (match) {
return {
tool,
params: { ...match.groups, raw: message, captures: match.slice(1) },
};
}
}
return null;
}
}
export function setupToolRegistry(): ToolRegistry {
const registry = new ToolRegistry();
const toolsDir = path.join(__dirname, 'tools');
if (fs.existsSync(toolsDir)) {
const files = fs.readdirSync(toolsDir).filter((f) => f.endsWith('.ts') || f.endsWith('.js'));
for (const file of files) {
try {
const mod = require(path.join(toolsDir, file));
if (typeof mod.register === 'function') {
mod.register(registry);
}
} catch (err: any) {
console.error(` [ToolRegistry] Failed to load ${file}: ${err.message}`);
}
}
}
return registry;
}
|