@auto-engineer/ai-gateway 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,499 @@
1
+ import { generateText, streamText, generateObject, streamObject } from 'ai';
2
+ import { openai } from '@ai-sdk/openai';
3
+ import { anthropic } from '@ai-sdk/anthropic';
4
+ import { google } from '@ai-sdk/google';
5
+ import { xai } from '@ai-sdk/xai';
6
+ import { configureAIProvider } from './config';
7
+ import { z } from 'zod';
8
+ import { getRegisteredToolsForAI } from './mcp-server';
9
+ import { startServer } from './mcp-server';
10
+
11
+ interface AIToolValidationError extends Error {
12
+ cause?: {
13
+ issues?: unknown[];
14
+ [key: string]: unknown;
15
+ };
16
+ issues?: unknown[];
17
+ errors?: unknown[];
18
+ zodIssues?: unknown[];
19
+ validationDetails?: {
20
+ cause?: {
21
+ issues?: unknown[];
22
+ };
23
+ issues?: unknown[];
24
+ errors?: unknown[];
25
+ };
26
+ schemaDescription?: string;
27
+ [key: string]: unknown;
28
+ }
29
+
30
+ export enum AIProvider {
31
+ OpenAI = 'openai',
32
+ Anthropic = 'anthropic',
33
+ Google = 'google',
34
+ XAI = 'xai',
35
+ }
36
+
37
+ export interface AIOptions {
38
+ model?: string;
39
+ temperature?: number;
40
+ maxTokens?: number;
41
+ streamCallback?: (token: string) => void;
42
+ includeTools?: boolean;
43
+ }
44
+
45
+ export interface StructuredAIOptions<T> extends Omit<AIOptions, 'streamCallback'> {
46
+ schema: z.ZodSchema<T>;
47
+ schemaName?: string;
48
+ schemaDescription?: string;
49
+ }
50
+
51
+ export interface StreamStructuredAIOptions<T> extends StructuredAIOptions<T> {
52
+ onPartialObject?: (partialObject: unknown) => void;
53
+ }
54
+
55
+ const defaultOptions: AIOptions = {
56
+ temperature: 0.7,
57
+ maxTokens: 1000,
58
+ };
59
+
60
+ function getDefaultModel(provider: AIProvider): string {
61
+ switch (provider) {
62
+ case AIProvider.OpenAI:
63
+ return 'gpt-4o-mini';
64
+ case AIProvider.Anthropic:
65
+ return 'claude-sonnet-4-20250514';
66
+ case AIProvider.Google:
67
+ return 'gemini-2.5-pro';
68
+ case AIProvider.XAI:
69
+ return 'grok-3';
70
+ default:
71
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
72
+ throw new Error(`Unknown provider: ${provider}`);
73
+ }
74
+ }
75
+
76
+ function getModel(provider: AIProvider, model?: string) {
77
+ const modelName = model ?? getDefaultModel(provider);
78
+
79
+ switch (provider) {
80
+ case AIProvider.OpenAI:
81
+ return openai(modelName);
82
+ case AIProvider.Anthropic:
83
+ return anthropic(modelName);
84
+ case AIProvider.Google:
85
+ return google(modelName);
86
+ case AIProvider.XAI:
87
+ return xai(modelName);
88
+ default:
89
+ throw new Error(`Unknown provider: ${provider as string}`);
90
+ }
91
+ }
92
+
93
+ export async function generateTextWithAI(
94
+ prompt: string,
95
+ provider: AIProvider,
96
+ options: AIOptions = {},
97
+ ): Promise<string> {
98
+ const finalOptions = { ...defaultOptions, ...options };
99
+ const model = finalOptions.model ?? getDefaultModel(provider);
100
+ const modelInstance = getModel(provider, model);
101
+
102
+ if (finalOptions.includeTools === true) {
103
+ await startServer();
104
+ const result = await generateTextWithToolsAI(prompt, provider, options);
105
+
106
+ return result.text;
107
+ }
108
+
109
+ const result = await generateText({
110
+ model: modelInstance,
111
+ prompt,
112
+ temperature: finalOptions.temperature,
113
+ maxTokens: finalOptions.maxTokens,
114
+ });
115
+
116
+ return result.text;
117
+ }
118
+
119
+ export async function* streamTextWithAI(
120
+ prompt: string,
121
+ provider: AIProvider,
122
+ options: AIOptions = {},
123
+ ): AsyncGenerator<string> {
124
+ const finalOptions = { ...defaultOptions, ...options };
125
+ const model = getModel(provider, finalOptions.model);
126
+
127
+ const stream = await streamText({
128
+ model,
129
+ prompt,
130
+ temperature: finalOptions.temperature,
131
+ maxTokens: finalOptions.maxTokens,
132
+ });
133
+
134
+ for await (const chunk of stream.textStream) {
135
+ yield chunk;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Generates text using streaming and collects the full response.
141
+ * Optionally calls a stream callback for each token if provided.
142
+ * Always returns the complete collected response.
143
+ */
144
+ export async function generateTextStreamingWithAI(
145
+ prompt: string,
146
+ provider: AIProvider,
147
+ options: AIOptions = {},
148
+ ): Promise<string> {
149
+ const finalOptions = { ...defaultOptions, ...options };
150
+ let collectedResult = '';
151
+
152
+ const stream = streamTextWithAI(prompt, provider, finalOptions);
153
+
154
+ for await (const token of stream) {
155
+ // Collect all tokens for the final result
156
+ collectedResult += token;
157
+
158
+ // Call the stream callback if provided
159
+ if (finalOptions.streamCallback) {
160
+ finalOptions.streamCallback(token);
161
+ }
162
+ }
163
+
164
+ return collectedResult;
165
+ }
166
+
167
+ export async function generateTextWithToolsAI(
168
+ prompt: string,
169
+ provider: AIProvider,
170
+ options: AIOptions = {},
171
+ ): Promise<{ text: string; toolCalls?: unknown[] }> {
172
+ const finalOptions = { ...defaultOptions, ...options };
173
+ const model = finalOptions.model ?? getDefaultModel(provider);
174
+ const modelInstance = getModel(provider, model);
175
+
176
+ const registeredTools = finalOptions.includeTools === true ? getRegisteredToolsForAI() : {};
177
+ console.log('registeredTools', registeredTools);
178
+ const hasTools = Object.keys(registeredTools).length > 0;
179
+ console.log('hasTools', hasTools);
180
+
181
+ // Build conversation messages
182
+ const messages: Array<{ role: 'user' | 'assistant'; content: string }> = [{ role: 'user', content: prompt }];
183
+
184
+ let finalResult = '';
185
+ const allToolCalls: unknown[] = [];
186
+ let attempts = 0;
187
+ const maxAttempts = 5;
188
+
189
+ while (attempts < maxAttempts) {
190
+ attempts++;
191
+
192
+ const opts = {
193
+ model: modelInstance,
194
+ messages,
195
+ temperature: finalOptions.temperature,
196
+ maxTokens: finalOptions.maxTokens,
197
+ ...(hasTools && {
198
+ tools: registeredTools,
199
+ toolChoice: 'auto' as const,
200
+ }),
201
+ };
202
+ console.log('opts', opts);
203
+ const result = await generateText(opts);
204
+ console.log('result', JSON.stringify(result, null, 2));
205
+
206
+ // Add assistant message to conversation
207
+ if (result.text) {
208
+ messages.push({ role: 'assistant', content: result.text });
209
+ finalResult = result.text;
210
+ }
211
+
212
+ // If there are tool calls, execute them and continue conversation
213
+ if (result.toolCalls !== undefined && result.toolCalls.length > 0) {
214
+ allToolCalls.push(...result.toolCalls);
215
+
216
+ // Execute tools and create a simple follow-up prompt
217
+ const toolResults = await executeToolCalls(result.toolCalls, registeredTools);
218
+
219
+ // Add the tool results as a user message and request a final response
220
+ messages.push({
221
+ role: 'user',
222
+ content: `${toolResults}Based on this product catalog data, please provide specific product recommendations for a soccer-loving daughter. Include product names, prices, and reasons why each item would be suitable.`,
223
+ });
224
+
225
+ // Continue the conversation to get AI's response to tool results
226
+ continue;
227
+ }
228
+
229
+ // If no tool calls, we're done
230
+ break;
231
+ }
232
+
233
+ return {
234
+ text: finalResult,
235
+ toolCalls: allToolCalls,
236
+ };
237
+ }
238
+
239
+ async function executeToolCalls(
240
+ toolCalls: unknown[],
241
+ registeredTools: Record<
242
+ string,
243
+ { execute?: (args: Record<string, unknown>) => Promise<string>; description?: string }
244
+ >,
245
+ ): Promise<string> {
246
+ let toolResults = '';
247
+
248
+ for (const toolCall of toolCalls) {
249
+ try {
250
+ const toolCallObj = toolCall as { toolName: string; args: Record<string, unknown> };
251
+ const tool = registeredTools[toolCallObj.toolName];
252
+ if (tool?.execute) {
253
+ const toolResult = await tool.execute(toolCallObj.args);
254
+ toolResults += `Tool ${toolCallObj.toolName} returned: ${String(toolResult)}\n\n`;
255
+ } else {
256
+ toolResults += `Error: Tool ${toolCallObj.toolName} not found or missing execute function\n\n`;
257
+ }
258
+ } catch (error) {
259
+ const toolCallObj = toolCall as { toolName: string };
260
+ console.error(`Tool execution error for ${toolCallObj.toolName}:`, error);
261
+ toolResults += `Error executing tool ${toolCallObj.toolName}: ${String(error)}\n\n`;
262
+ }
263
+ }
264
+
265
+ return toolResults;
266
+ }
267
+
268
+ export async function generateTextWithImageAI(
269
+ text: string,
270
+ imageBase64: string,
271
+ provider: AIProvider,
272
+ options: AIOptions = {},
273
+ ): Promise<string> {
274
+ const finalOptions = { ...defaultOptions, ...options };
275
+ const model = finalOptions.model ?? getDefaultModel(provider);
276
+ const modelInstance = getModel(provider, model);
277
+
278
+ if (provider !== AIProvider.OpenAI && provider !== AIProvider.XAI) {
279
+ throw new Error(`Provider ${provider} does not support image inputs`);
280
+ }
281
+
282
+ const result = await generateText({
283
+ model: modelInstance,
284
+ messages: [
285
+ {
286
+ role: 'user',
287
+ content: [
288
+ { type: 'text', text },
289
+ { type: 'image', image: imageBase64 },
290
+ ],
291
+ },
292
+ ],
293
+ temperature: finalOptions.temperature,
294
+ maxTokens: finalOptions.maxTokens,
295
+ });
296
+
297
+ return result.text;
298
+ }
299
+
300
+ export function getAvailableProviders(): AIProvider[] {
301
+ const config = configureAIProvider();
302
+ const providers: AIProvider[] = [];
303
+ if (config.openai != null) providers.push(AIProvider.OpenAI);
304
+ if (config.anthropic != null) providers.push(AIProvider.Anthropic);
305
+ if (config.google != null) providers.push(AIProvider.Google);
306
+ if (config.xai != null) providers.push(AIProvider.XAI);
307
+ return providers;
308
+ }
309
+
310
+ function getEnhancedPrompt(prompt: string, lastError: AIToolValidationError): string {
311
+ const errorDetails = lastError.zodIssues
312
+ ? JSON.stringify(lastError.zodIssues, null, 2)
313
+ : lastError.validationDetails?.cause?.issues
314
+ ? JSON.stringify(lastError.validationDetails.cause.issues, null, 2)
315
+ : lastError.message;
316
+
317
+ return `${prompt}\\n\\n⚠️ IMPORTANT: Your previous response failed validation with the following errors:\\n${errorDetails}\\n\\nPlease fix these errors and ensure your response EXACTLY matches the required schema structure.`;
318
+ }
319
+
320
+ function isSchemaError(error: Error): boolean {
321
+ return (
322
+ error.message.includes('response did not match schema') ||
323
+ error.message.includes('TypeValidationError') ||
324
+ error.name === 'AI_TypeValidationError'
325
+ );
326
+ }
327
+
328
+ function enhanceValidationError(error: AIToolValidationError): AIToolValidationError {
329
+ const enhancedError = new Error(error.message) as AIToolValidationError;
330
+ Object.assign(enhancedError, error);
331
+
332
+ if (error.cause !== undefined || error.issues !== undefined || error.errors !== undefined) {
333
+ enhancedError.validationDetails = {
334
+ cause: error.cause,
335
+ issues: error.issues,
336
+ errors: error.errors,
337
+ };
338
+ }
339
+
340
+ if (
341
+ error.message.includes('response did not match schema') &&
342
+ 'cause' in error &&
343
+ typeof error.cause === 'object' &&
344
+ error.cause !== null &&
345
+ 'issues' in error.cause
346
+ ) {
347
+ enhancedError.zodIssues = error.cause.issues as unknown[];
348
+ }
349
+ return enhancedError;
350
+ }
351
+
352
+ function handleFailedRequest(
353
+ lastError: AIToolValidationError | undefined,
354
+ maxRetries: number,
355
+ attempt: number,
356
+ ): { shouldRetry: boolean; enhancedError?: AIToolValidationError } {
357
+ if (!lastError || !isSchemaError(lastError) || attempt >= maxRetries - 1) {
358
+ return { shouldRetry: false, enhancedError: lastError };
359
+ }
360
+
361
+ console.log(`Schema validation failed on attempt ${attempt + 1}/${maxRetries}, retrying...`);
362
+ const enhancedError = enhanceValidationError(lastError);
363
+
364
+ return { shouldRetry: true, enhancedError };
365
+ }
366
+
367
+ export async function generateStructuredDataWithAI<T>(
368
+ prompt: string,
369
+ provider: AIProvider,
370
+ options: StructuredAIOptions<T>,
371
+ ): Promise<T> {
372
+ const maxSchemaRetries = 3;
373
+ let lastError: AIToolValidationError | undefined;
374
+
375
+ if (options.includeTools === true) await startServer();
376
+ const registeredTools = options.includeTools === true ? getRegisteredToolsForAI() : {};
377
+ console.log('registeredTools', registeredTools);
378
+ const hasTools = Object.keys(registeredTools).length > 0;
379
+ console.log('hasTools', hasTools);
380
+
381
+ for (let attempt = 0; attempt < maxSchemaRetries; attempt++) {
382
+ try {
383
+ const model = getModel(provider, options.model);
384
+
385
+ const enhancedPrompt = attempt > 0 && lastError ? getEnhancedPrompt(prompt, lastError) : prompt;
386
+ console.log('enhancedPrompt', enhancedPrompt);
387
+
388
+ const opts = {
389
+ model,
390
+ prompt: enhancedPrompt,
391
+ schema: options.schema,
392
+ schemaName: options.schemaName,
393
+ schemaDescription: options.schemaDescription,
394
+ temperature: options.temperature,
395
+ maxTokens: options.maxTokens,
396
+ ...(hasTools && {
397
+ tools: registeredTools,
398
+ toolChoice: 'auto' as const,
399
+ }),
400
+ };
401
+ console.log('opts', opts);
402
+ const result = await generateObject(opts);
403
+ console.log('result', JSON.stringify(result, null, 2));
404
+ return result.object;
405
+ } catch (error: unknown) {
406
+ lastError =
407
+ error instanceof Error
408
+ ? (error as AIToolValidationError)
409
+ : (new Error('An unknown error occurred') as AIToolValidationError);
410
+
411
+ const { shouldRetry, enhancedError } = handleFailedRequest(lastError, maxSchemaRetries, attempt);
412
+ lastError = enhancedError;
413
+
414
+ if (!shouldRetry) {
415
+ throw lastError;
416
+ }
417
+ }
418
+ }
419
+
420
+ // If we exhausted all retries, throw the last error
421
+ throw lastError;
422
+ }
423
+
424
+ export async function streamStructuredDataWithAI<T>(
425
+ prompt: string,
426
+ provider: AIProvider,
427
+ options: StreamStructuredAIOptions<T>,
428
+ ): Promise<T> {
429
+ const maxSchemaRetries = 3;
430
+ let lastError: AIToolValidationError | undefined;
431
+
432
+ for (let attempt = 0; attempt < maxSchemaRetries; attempt++) {
433
+ try {
434
+ const model = getModel(provider, options.model);
435
+
436
+ const enhancedPrompt = attempt > 0 && lastError ? getEnhancedPrompt(prompt, lastError) : prompt;
437
+
438
+ const result = streamObject({
439
+ model,
440
+ prompt: enhancedPrompt,
441
+ schema: options.schema,
442
+ schemaName: options.schemaName,
443
+ schemaDescription: options.schemaDescription,
444
+ temperature: options.temperature,
445
+ maxTokens: options.maxTokens,
446
+ });
447
+
448
+ // Stream partial objects if callback provided
449
+ if (options.onPartialObject) {
450
+ void (async () => {
451
+ try {
452
+ for await (const partialObject of result.partialObjectStream) {
453
+ options.onPartialObject?.(partialObject);
454
+ }
455
+ } catch (streamError) {
456
+ console.error('Error in partial object stream:', streamError);
457
+ }
458
+ })();
459
+ }
460
+
461
+ // Return the final complete object
462
+ return await result.object;
463
+ } catch (error: unknown) {
464
+ lastError =
465
+ error instanceof Error
466
+ ? (error as AIToolValidationError)
467
+ : (new Error('An unknown error occurred') as AIToolValidationError);
468
+
469
+ const { shouldRetry, enhancedError } = handleFailedRequest(lastError, maxSchemaRetries, attempt);
470
+ lastError = enhancedError;
471
+
472
+ if (!shouldRetry) {
473
+ throw lastError;
474
+ }
475
+ }
476
+ }
477
+
478
+ // If we exhausted all retries, throw the last error
479
+ throw lastError;
480
+ }
481
+
482
+ // Export zod for convenience
483
+ export { z };
484
+
485
+ // Export MCP server singleton functions
486
+ export {
487
+ server as mcpServer,
488
+ registerTool,
489
+ registerTools,
490
+ startServer,
491
+ isServerStarted,
492
+ getRegisteredTools,
493
+ getRegisteredToolsForAI,
494
+ getToolHandler,
495
+ getSchemaByName,
496
+ executeRegisteredTool,
497
+ type ToolHandler,
498
+ type RegisteredTool,
499
+ } from './mcp-server';
@@ -0,0 +1,203 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod';
4
+
5
+ interface ToolResult {
6
+ content: Array<{
7
+ type: 'text';
8
+ text: string;
9
+ }>;
10
+ isError?: boolean;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ interface ToolDescription {
15
+ title: string;
16
+ description: string;
17
+ inputSchema: Record<string, z.ZodSchema>;
18
+ schema?: z.ZodSchema;
19
+ schemaName?: string;
20
+ schemaDescription?: string;
21
+ }
22
+
23
+ type ToolHandler<T extends Record<string, unknown> = Record<string, unknown>> = (params: T) => Promise<ToolResult>;
24
+
25
+ interface RegisteredTool {
26
+ name: string;
27
+ description: ToolDescription;
28
+ handler: ToolHandler<Record<string, unknown>>;
29
+ aiSdkTool: {
30
+ parameters: z.ZodSchema;
31
+ description: string;
32
+ execute?: (args: Record<string, unknown>) => Promise<string>;
33
+ };
34
+ }
35
+
36
+ const server = new McpServer({
37
+ name: 'frontend-implementation',
38
+ version: '0.1.0',
39
+ });
40
+
41
+ const transport = new StdioServerTransport();
42
+
43
+ let isStarted = false;
44
+ const toolRegistry = new Map<string, RegisteredTool>();
45
+
46
+ async function cleanup() {
47
+ console.log('Cleaning up...');
48
+ await transport.close();
49
+ process.exit(0);
50
+ }
51
+
52
+ process.on('SIGTERM', () => {
53
+ void cleanup();
54
+ });
55
+ process.on('SIGINT', () => {
56
+ void cleanup();
57
+ });
58
+
59
+ export { server };
60
+
61
+ function createMcpHandler<T extends Record<string, unknown>>(
62
+ handler: ToolHandler<T>,
63
+ ): (args: Record<string, unknown>, extra: unknown) => Promise<ToolResult> {
64
+ return (args: Record<string, unknown>, _extra: unknown) => {
65
+ return handler(args as T);
66
+ };
67
+ }
68
+
69
+ function createAiSdkTool(description: ToolDescription, handler: ToolHandler) {
70
+ const parameterSchema =
71
+ Object.keys(description.inputSchema).length > 0 ? z.object(description.inputSchema) : z.object({}).passthrough();
72
+
73
+ return {
74
+ parameters: parameterSchema,
75
+ description: description.description,
76
+ execute: async (args: Record<string, unknown>) => {
77
+ const result = await handler(args);
78
+ const textOutput = result.content[0]?.text || '';
79
+
80
+ // If a schema is provided, parse and validate the JSON output
81
+ if (description.schema) {
82
+ try {
83
+ const parsed = JSON.parse(textOutput) as unknown;
84
+ description.schema.parse(parsed);
85
+ return textOutput; // Return original text output for consistency
86
+ } catch (parseError) {
87
+ console.warn(`Tool failed to parse/validate JSON output:`, parseError);
88
+ return textOutput; // Fallback to raw text
89
+ }
90
+ }
91
+
92
+ return textOutput;
93
+ },
94
+ ...(description.schema && { schema: description.schema }),
95
+ ...(description.schemaName != null && { schemaName: description.schemaName }),
96
+ ...(description.schemaDescription != null && { schemaDescription: description.schemaDescription }),
97
+ };
98
+ }
99
+
100
+ export function registerTool<T extends Record<string, unknown> = Record<string, unknown>>(
101
+ name: string,
102
+ description: ToolDescription,
103
+ handler: ToolHandler<T>,
104
+ ) {
105
+ console.log(`🔧 Registering MCP tool: ${name}`);
106
+
107
+ if (isStarted) {
108
+ throw new Error('Cannot register tools after server has started');
109
+ }
110
+
111
+ const mcpHandler = createMcpHandler(handler);
112
+ const aiSdkTool = createAiSdkTool(description, handler as ToolHandler<Record<string, unknown>>);
113
+
114
+ const registeredTool: RegisteredTool = {
115
+ name,
116
+ description,
117
+ handler: handler as ToolHandler<Record<string, unknown>>,
118
+ aiSdkTool,
119
+ };
120
+
121
+ toolRegistry.set(name, registeredTool);
122
+ server.registerTool(name, description, mcpHandler);
123
+ console.log(`✅ Tool ${name} registered successfully. Total tools: ${toolRegistry.size}`);
124
+ }
125
+
126
+ export function registerTools<T extends Record<string, unknown> = Record<string, unknown>>(
127
+ tools: Array<{
128
+ name: string;
129
+ description: ToolDescription;
130
+ handler: ToolHandler<T>;
131
+ }>,
132
+ ) {
133
+ if (isStarted) {
134
+ throw new Error('Cannot register tools after server has started');
135
+ }
136
+ tools.forEach((tool) => {
137
+ registerTool(tool.name, tool.description, tool.handler);
138
+ });
139
+ }
140
+
141
+ export async function startServer() {
142
+ if (isStarted) return;
143
+
144
+ await server.connect(transport);
145
+ isStarted = true;
146
+ }
147
+
148
+ export function isServerStarted() {
149
+ return isStarted;
150
+ }
151
+
152
+ export function getRegisteredTools(): RegisteredTool[] {
153
+ return Array.from(toolRegistry.values());
154
+ }
155
+
156
+ export function getRegisteredToolsForAI(): Record<
157
+ string,
158
+ {
159
+ parameters: z.ZodSchema;
160
+ description: string;
161
+ execute?: (args: Record<string, unknown>) => Promise<string>;
162
+ }
163
+ > {
164
+ console.log(`📋 Getting registered tools for AI. Registry size: ${toolRegistry.size}`);
165
+ const tools: Record<
166
+ string,
167
+ {
168
+ parameters: z.ZodSchema;
169
+ description: string;
170
+ execute?: (args: Record<string, unknown>) => Promise<string>;
171
+ }
172
+ > = {};
173
+ for (const tool of toolRegistry.values()) {
174
+ tools[tool.name] = tool.aiSdkTool;
175
+ console.log(` - Tool: ${tool.name}`);
176
+ }
177
+ console.log(`📊 Returning ${Object.keys(tools).length} tools for AI`);
178
+ return tools;
179
+ }
180
+
181
+ export function getToolHandler(name: string): ToolHandler | undefined {
182
+ return toolRegistry.get(name)?.handler;
183
+ }
184
+
185
+ export async function executeRegisteredTool(name: string, params: Record<string, unknown>): Promise<ToolResult> {
186
+ const tool = toolRegistry.get(name);
187
+ if (!tool) {
188
+ throw new Error(`Tool '${name}' not found`);
189
+ }
190
+ return await tool.handler(params);
191
+ }
192
+
193
+ export function getSchemaByName(schemaName: string): z.ZodSchema | undefined {
194
+ console.log();
195
+ for (const tool of toolRegistry.values()) {
196
+ if (tool.description?.schemaName === schemaName) {
197
+ return tool.description.schema;
198
+ }
199
+ }
200
+ return undefined;
201
+ }
202
+
203
+ export type { ToolResult, ToolDescription, ToolHandler, RegisteredTool };
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "outDir": "./dist",
6
+ "rootDir": "./src"
7
+ },
8
+ "include": ["src/**/*"],
9
+ "exclude": ["node_modules", "dist"]
10
+ }