@stackkedjohn/mcp-factory-cli 0.1.0

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.
Files changed (60) hide show
  1. package/README.md +100 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +33 -0
  4. package/dist/commands/create.d.ts +4 -0
  5. package/dist/commands/create.js +56 -0
  6. package/dist/commands/install.d.ts +1 -0
  7. package/dist/commands/install.js +79 -0
  8. package/dist/commands/list.d.ts +1 -0
  9. package/dist/commands/list.js +24 -0
  10. package/dist/commands/validate.d.ts +1 -0
  11. package/dist/commands/validate.js +27 -0
  12. package/dist/generator/analyzer.d.ts +2 -0
  13. package/dist/generator/analyzer.js +14 -0
  14. package/dist/generator/engine.d.ts +10 -0
  15. package/dist/generator/engine.js +46 -0
  16. package/dist/parsers/ai-parser.d.ts +2 -0
  17. package/dist/parsers/ai-parser.js +7 -0
  18. package/dist/parsers/detector.d.ts +6 -0
  19. package/dist/parsers/detector.js +38 -0
  20. package/dist/parsers/openapi.d.ts +5 -0
  21. package/dist/parsers/openapi.js +205 -0
  22. package/dist/parsers/postman.d.ts +2 -0
  23. package/dist/parsers/postman.js +4 -0
  24. package/dist/registry/manager.d.ts +13 -0
  25. package/dist/registry/manager.js +43 -0
  26. package/dist/schema/api-schema.d.ts +77 -0
  27. package/dist/schema/api-schema.js +1 -0
  28. package/dist/utils/errors.d.ts +13 -0
  29. package/dist/utils/errors.js +26 -0
  30. package/dist/utils/logger.d.ts +7 -0
  31. package/dist/utils/logger.js +19 -0
  32. package/docs/plans/2026-02-02-mcp-factory-design.md +306 -0
  33. package/docs/plans/2026-02-02-mcp-factory-implementation.md +1866 -0
  34. package/package.json +48 -0
  35. package/src/cli.ts +41 -0
  36. package/src/commands/create.ts +65 -0
  37. package/src/commands/install.ts +92 -0
  38. package/src/commands/list.ts +28 -0
  39. package/src/commands/validate.ts +29 -0
  40. package/src/generator/analyzer.ts +20 -0
  41. package/src/generator/engine.ts +73 -0
  42. package/src/parsers/ai-parser.ts +10 -0
  43. package/src/parsers/detector.ts +49 -0
  44. package/src/parsers/openapi.ts +238 -0
  45. package/src/parsers/postman.ts +6 -0
  46. package/src/registry/manager.ts +62 -0
  47. package/src/schema/api-schema.ts +87 -0
  48. package/src/utils/errors.ts +27 -0
  49. package/src/utils/logger.ts +23 -0
  50. package/templates/README.md.hbs +40 -0
  51. package/templates/client.ts.hbs +45 -0
  52. package/templates/index.ts.hbs +36 -0
  53. package/templates/package.json.hbs +20 -0
  54. package/templates/test.ts.hbs +1 -0
  55. package/templates/tools.ts.hbs +38 -0
  56. package/templates/tsconfig.json.hbs +13 -0
  57. package/templates/types.ts.hbs +1 -0
  58. package/templates/validation.ts.hbs +1 -0
  59. package/test-fixtures/weather-api.json +49 -0
  60. package/tsconfig.json +17 -0
@@ -0,0 +1,238 @@
1
+ import { APISchema, Endpoint, Parameter, SchemaType, AuthConfig, RequestBody, ResponseSchema, ErrorSchema } from '../schema/api-schema.js';
2
+
3
+ /**
4
+ * Parse OpenAPI 3.x or Swagger 2.0 specification into unified APISchema
5
+ */
6
+ export function parseOpenAPI(spec: any): APISchema {
7
+ const isSwagger2 = spec.swagger === '2.0';
8
+ const isOpenAPI3 = spec.openapi?.startsWith('3.');
9
+
10
+ if (!isSwagger2 && !isOpenAPI3) {
11
+ throw new Error('Unsupported spec version. Only OpenAPI 3.x and Swagger 2.0 are supported.');
12
+ }
13
+
14
+ return {
15
+ name: spec.info.title || 'API',
16
+ baseUrl: getBaseUrl(spec),
17
+ auth: detectAuth(spec),
18
+ endpoints: parseEndpoints(spec),
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Extract base URL from spec
24
+ */
25
+ function getBaseUrl(spec: any): string {
26
+ // OpenAPI 3.x
27
+ if (spec.servers && spec.servers.length > 0) {
28
+ return spec.servers[0].url;
29
+ }
30
+
31
+ // Swagger 2.0
32
+ if (spec.host) {
33
+ const scheme = spec.schemes?.[0] || 'https';
34
+ const basePath = spec.basePath || '';
35
+ return `${scheme}://${spec.host}${basePath}`;
36
+ }
37
+
38
+ return '';
39
+ }
40
+
41
+ /**
42
+ * Detect authentication configuration
43
+ */
44
+ function detectAuth(spec: any): AuthConfig {
45
+ const securitySchemes = spec.components?.securitySchemes || spec.securityDefinitions || {};
46
+ const firstScheme = Object.values(securitySchemes)[0] as any;
47
+
48
+ if (!firstScheme) {
49
+ return { type: 'none' };
50
+ }
51
+
52
+ switch (firstScheme.type) {
53
+ case 'apiKey':
54
+ return {
55
+ type: 'api-key',
56
+ location: firstScheme.in === 'header' ? 'header' : 'query',
57
+ name: firstScheme.name,
58
+ description: firstScheme.description,
59
+ };
60
+ case 'http':
61
+ if (firstScheme.scheme === 'bearer') {
62
+ return { type: 'bearer', description: firstScheme.description };
63
+ }
64
+ if (firstScheme.scheme === 'basic') {
65
+ return { type: 'basic', description: firstScheme.description };
66
+ }
67
+ return { type: 'none' };
68
+ case 'oauth2':
69
+ return { type: 'oauth', description: firstScheme.description };
70
+ default:
71
+ return { type: 'none' };
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Parse all endpoints from spec
77
+ */
78
+ function parseEndpoints(spec: any): Endpoint[] {
79
+ const endpoints: Endpoint[] = [];
80
+ const paths = spec.paths || {};
81
+
82
+ for (const [path, pathItem] of Object.entries(paths)) {
83
+ const methods = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'];
84
+
85
+ for (const method of methods) {
86
+ const operation = (pathItem as any)[method];
87
+ if (operation) {
88
+ endpoints.push(parseOperation(path, method, operation, pathItem as any));
89
+ }
90
+ }
91
+ }
92
+
93
+ return endpoints;
94
+ }
95
+
96
+ /**
97
+ * Parse a single operation into an Endpoint
98
+ */
99
+ function parseOperation(path: string, method: string, operation: any, pathItem: any): Endpoint {
100
+ const parameters: Parameter[] = [];
101
+
102
+ // Parse path-level parameters
103
+ if (pathItem.parameters) {
104
+ parameters.push(...parseParameters(pathItem.parameters));
105
+ }
106
+
107
+ // Parse operation-level parameters
108
+ if (operation.parameters) {
109
+ parameters.push(...parseParameters(operation.parameters));
110
+ }
111
+
112
+ // Parse request body (OpenAPI 3.x)
113
+ let requestBody: RequestBody | undefined;
114
+ if (operation.requestBody) {
115
+ const content = operation.requestBody.content || {};
116
+ const jsonContent = content['application/json'];
117
+ if (jsonContent?.schema) {
118
+ requestBody = {
119
+ description: operation.requestBody.description || '',
120
+ required: operation.requestBody.required || false,
121
+ contentType: 'application/json',
122
+ schema: parseSchema(jsonContent.schema),
123
+ };
124
+ }
125
+ }
126
+
127
+ // Parse response (use first successful response)
128
+ const responses = operation.responses || {};
129
+ const successCode = Object.keys(responses).find(code => code.startsWith('2')) || '200';
130
+ const successResponse = responses[successCode] || {};
131
+ const responseContent = successResponse.content?.['application/json'];
132
+
133
+ const response: ResponseSchema = {
134
+ statusCode: parseInt(successCode),
135
+ description: successResponse.description || '',
136
+ contentType: 'application/json',
137
+ schema: responseContent?.schema ? parseSchema(responseContent.schema) : { type: 'object' },
138
+ };
139
+
140
+ // Parse error responses
141
+ const errors: ErrorSchema[] = [];
142
+ for (const [code, resp] of Object.entries(responses)) {
143
+ if (!code.startsWith('2') && code !== 'default') {
144
+ const errorResp = resp as any;
145
+ const errorContent = errorResp.content?.['application/json'];
146
+ errors.push({
147
+ statusCode: parseInt(code),
148
+ description: errorResp.description || `Error ${code}`,
149
+ schema: errorContent?.schema ? parseSchema(errorContent.schema) : undefined,
150
+ });
151
+ }
152
+ }
153
+
154
+ return {
155
+ id: operation.operationId || `${method}_${path.replace(/\//g, '_')}`,
156
+ method: method.toUpperCase() as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
157
+ path,
158
+ description: operation.description || operation.summary || '',
159
+ parameters,
160
+ requestBody,
161
+ response,
162
+ errors,
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Parse parameters array
168
+ */
169
+ function parseParameters(params: any[]): Parameter[] {
170
+ return params.map((param) => {
171
+ // Handle $ref
172
+ if (param.$ref) {
173
+ // For simplicity, we'll skip resolving refs
174
+ return null;
175
+ }
176
+
177
+ // Only parse path, query, and header parameters (not body)
178
+ if (!['path', 'query', 'header'].includes(param.in)) {
179
+ return null;
180
+ }
181
+
182
+ const schema = param.schema || { type: param.type };
183
+
184
+ return {
185
+ name: param.name,
186
+ in: param.in as 'path' | 'query' | 'header',
187
+ required: param.required || false,
188
+ description: param.description || '',
189
+ schema: parseSchema(schema),
190
+ };
191
+ }).filter(Boolean) as Parameter[];
192
+ }
193
+
194
+ /**
195
+ * Parse JSON Schema into SchemaType
196
+ */
197
+ function parseSchema(schema: any): SchemaType {
198
+ if (!schema) {
199
+ return { type: 'string' };
200
+ }
201
+
202
+ // Handle basic types
203
+ if (schema.type === 'object' || schema.properties) {
204
+ const properties: Record<string, SchemaType> = {};
205
+ const required = schema.required || [];
206
+
207
+ for (const [key, value] of Object.entries(schema.properties || {})) {
208
+ properties[key] = parseSchema(value);
209
+ }
210
+
211
+ return {
212
+ type: 'object',
213
+ properties,
214
+ required,
215
+ };
216
+ }
217
+
218
+ if (schema.type === 'array') {
219
+ return {
220
+ type: 'array',
221
+ items: parseSchema(schema.items),
222
+ };
223
+ }
224
+
225
+ // Enum
226
+ if (schema.enum) {
227
+ return {
228
+ type: schema.type || 'string',
229
+ enum: schema.enum,
230
+ };
231
+ }
232
+
233
+ // Basic types with format
234
+ return {
235
+ type: schema.type || 'string',
236
+ format: schema.format,
237
+ };
238
+ }
@@ -0,0 +1,6 @@
1
+ import { APISchema } from '../schema/api-schema.js';
2
+ import { ParseError } from '../utils/errors.js';
3
+
4
+ export function parsePostman(collection: any): APISchema {
5
+ throw new ParseError('Postman collection parsing not yet implemented');
6
+ }
@@ -0,0 +1,62 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+
5
+ export interface RegistryEntry {
6
+ name: string;
7
+ path: string;
8
+ createdAt: string;
9
+ }
10
+
11
+ export interface Registry {
12
+ servers: RegistryEntry[];
13
+ }
14
+
15
+ const REGISTRY_DIR = path.join(os.homedir(), '.mcp-factory');
16
+ const REGISTRY_FILE = path.join(REGISTRY_DIR, 'registry.json');
17
+
18
+ async function ensureRegistry(): Promise<void> {
19
+ try {
20
+ await fs.access(REGISTRY_FILE);
21
+ } catch {
22
+ await fs.mkdir(REGISTRY_DIR, { recursive: true });
23
+ await fs.writeFile(REGISTRY_FILE, JSON.stringify({ servers: [] }, null, 2));
24
+ }
25
+ }
26
+
27
+ export async function loadRegistry(): Promise<Registry> {
28
+ await ensureRegistry();
29
+ const content = await fs.readFile(REGISTRY_FILE, 'utf-8');
30
+ return JSON.parse(content);
31
+ }
32
+
33
+ export async function saveRegistry(registry: Registry): Promise<void> {
34
+ await ensureRegistry();
35
+ await fs.writeFile(REGISTRY_FILE, JSON.stringify(registry, null, 2));
36
+ }
37
+
38
+ export async function addServer(name: string, serverPath: string): Promise<void> {
39
+ const registry = await loadRegistry();
40
+
41
+ // Remove existing entry if present
42
+ registry.servers = registry.servers.filter(s => s.name !== name);
43
+
44
+ // Add new entry
45
+ registry.servers.push({
46
+ name,
47
+ path: path.resolve(serverPath),
48
+ createdAt: new Date().toISOString(),
49
+ });
50
+
51
+ await saveRegistry(registry);
52
+ }
53
+
54
+ export async function getServer(name: string): Promise<RegistryEntry | undefined> {
55
+ const registry = await loadRegistry();
56
+ return registry.servers.find(s => s.name === name);
57
+ }
58
+
59
+ export async function listServers(): Promise<RegistryEntry[]> {
60
+ const registry = await loadRegistry();
61
+ return registry.servers;
62
+ }
@@ -0,0 +1,87 @@
1
+ export interface APISchema {
2
+ name: string;
3
+ baseUrl: string;
4
+ auth: AuthConfig;
5
+ endpoints: Endpoint[];
6
+ commonHeaders?: Record<string, string>;
7
+ rateLimit?: RateLimitConfig;
8
+ pagination?: PaginationConfig;
9
+ }
10
+
11
+ export interface AuthConfig {
12
+ type: 'api-key' | 'bearer' | 'oauth' | 'basic' | 'none';
13
+ location?: 'header' | 'query';
14
+ name?: string;
15
+ description?: string;
16
+ }
17
+
18
+ export interface Endpoint {
19
+ id: string;
20
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
21
+ path: string;
22
+ description: string;
23
+ parameters: Parameter[];
24
+ requestBody?: RequestBody;
25
+ response: ResponseSchema;
26
+ errors: ErrorSchema[];
27
+ }
28
+
29
+ export interface Parameter {
30
+ name: string;
31
+ in: 'path' | 'query' | 'header';
32
+ description?: string;
33
+ required: boolean;
34
+ schema: SchemaType;
35
+ }
36
+
37
+ export interface RequestBody {
38
+ description?: string;
39
+ required: boolean;
40
+ contentType: string;
41
+ schema: SchemaType;
42
+ }
43
+
44
+ export interface ResponseSchema {
45
+ statusCode: number;
46
+ description?: string;
47
+ contentType: string;
48
+ schema: SchemaType;
49
+ }
50
+
51
+ export interface ErrorSchema {
52
+ statusCode: number;
53
+ description: string;
54
+ schema?: SchemaType;
55
+ }
56
+
57
+ export interface SchemaType {
58
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array';
59
+ properties?: Record<string, SchemaType>;
60
+ items?: SchemaType;
61
+ required?: string[];
62
+ enum?: string[];
63
+ format?: string;
64
+ }
65
+
66
+ export interface RateLimitConfig {
67
+ strategy: 'header-based' | 'retry-after' | 'none';
68
+ headerName?: string;
69
+ requestsPerWindow?: number;
70
+ windowSeconds?: number;
71
+ }
72
+
73
+ export interface PaginationConfig {
74
+ style: 'cursor' | 'offset' | 'page' | 'link-header';
75
+ cursorParam?: string;
76
+ limitParam?: string;
77
+ offsetParam?: string;
78
+ pageParam?: string;
79
+ }
80
+
81
+ export interface DetectedPatterns {
82
+ authPattern: 'api-key' | 'bearer' | 'oauth' | 'basic' | 'none';
83
+ paginationStyle?: 'cursor' | 'offset' | 'page' | 'link-header';
84
+ rateLimitStrategy?: 'header-based' | 'retry-after' | 'none';
85
+ errorFormat: 'standard' | 'custom';
86
+ hasWebhooks: boolean;
87
+ }
@@ -0,0 +1,27 @@
1
+ export class MCPFactoryError extends Error {
2
+ constructor(message: string, public code: string) {
3
+ super(message);
4
+ this.name = 'MCPFactoryError';
5
+ }
6
+ }
7
+
8
+ export class ParseError extends MCPFactoryError {
9
+ constructor(message: string) {
10
+ super(message, 'PARSE_ERROR');
11
+ this.name = 'ParseError';
12
+ }
13
+ }
14
+
15
+ export class ValidationError extends MCPFactoryError {
16
+ constructor(message: string) {
17
+ super(message, 'VALIDATION_ERROR');
18
+ this.name = 'ValidationError';
19
+ }
20
+ }
21
+
22
+ export class GenerationError extends MCPFactoryError {
23
+ constructor(message: string) {
24
+ super(message, 'GENERATION_ERROR');
25
+ this.name = 'GenerationError';
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ export const logger = {
2
+ info: (message: string) => {
3
+ console.log(`ℹ ${message}`);
4
+ },
5
+
6
+ success: (message: string) => {
7
+ console.log(`✓ ${message}`);
8
+ },
9
+
10
+ error: (message: string) => {
11
+ console.error(`✗ ${message}`);
12
+ },
13
+
14
+ warn: (message: string) => {
15
+ console.warn(`⚠ ${message}`);
16
+ },
17
+
18
+ debug: (message: string) => {
19
+ if (process.env.DEBUG) {
20
+ console.log(`[DEBUG] ${message}`);
21
+ }
22
+ }
23
+ };
@@ -0,0 +1,40 @@
1
+ # {{name}} MCP Server
2
+
3
+ MCP server for the {{name}} API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ ```
11
+
12
+ ## Configuration
13
+
14
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "{{name}}": {
20
+ "command": "node",
21
+ "args": ["{{absolutePath}}/build/index.js"],
22
+ "env": {
23
+ {{#if (eq patterns.authPattern 'api-key')}}
24
+ "API_KEY": "your-api-key-here"
25
+ {{/if}}
26
+ {{#if (eq patterns.authPattern 'bearer')}}
27
+ "BEARER_TOKEN": "your-bearer-token-here"
28
+ {{/if}}
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ Available tools:
38
+ {{#each endpoints}}
39
+ - `{{id}}`: {{description}}
40
+ {{/each}}
@@ -0,0 +1,45 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ export interface APIClient {
4
+ baseUrl: string;
5
+ request: (method: string, path: string, params?: any) => Promise<any>;
6
+ }
7
+
8
+ export function createClient(baseUrl: string): APIClient {
9
+ return {
10
+ baseUrl,
11
+ async request(method: string, path: string, params?: any) {
12
+ const headers: Record<string, string> = {
13
+ 'Content-Type': 'application/json',
14
+ };
15
+
16
+ {{#if (eq patterns.authPattern 'api-key')}}
17
+ if (process.env.API_KEY) {
18
+ {{#if (eq auth.location 'header')}}
19
+ headers['{{auth.name}}'] = process.env.API_KEY;
20
+ {{/if}}
21
+ }
22
+ {{/if}}
23
+
24
+ {{#if (eq patterns.authPattern 'bearer')}}
25
+ if (process.env.BEARER_TOKEN) {
26
+ headers['Authorization'] = `Bearer ${process.env.BEARER_TOKEN}`;
27
+ }
28
+ {{/if}}
29
+
30
+ const url = new URL(path, baseUrl);
31
+
32
+ const response = await fetch(url.toString(), {
33
+ method,
34
+ headers,
35
+ body: params ? JSON.stringify(params) : undefined,
36
+ });
37
+
38
+ if (!response.ok) {
39
+ throw new Error(`API request failed: ${response.status} ${response.statusText}`);
40
+ }
41
+
42
+ return response.json();
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
6
+ import { createClient } from './client.js';
7
+ import { tools, handleToolCall } from './tools.js';
8
+
9
+ const server = new Server(
10
+ {
11
+ name: '{{name}}-mcp',
12
+ version: '1.0.0',
13
+ },
14
+ {
15
+ capabilities: {
16
+ tools: {},
17
+ },
18
+ }
19
+ );
20
+
21
+ const client = createClient('{{baseUrl}}');
22
+
23
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
24
+ tools,
25
+ }));
26
+
27
+ server.setRequestHandler(CallToolRequestSchema, async (request) =>
28
+ handleToolCall(request, client)
29
+ );
30
+
31
+ async function main() {
32
+ const transport = new StdioServerTransport();
33
+ await server.connect(transport);
34
+ }
35
+
36
+ main().catch(console.error);
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{name}}-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for {{name}} API",
5
+ "type": "module",
6
+ "main": "build/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "node --test test.ts"
10
+ },
11
+ "dependencies": {
12
+ "@modelcontextprotocol/sdk": "^1.0.0",
13
+ "zod": "^3.22.0",
14
+ "node-fetch": "^3.3.0"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.3.0",
18
+ "@types/node": "^20.0.0"
19
+ }
20
+ }
@@ -0,0 +1 @@
1
+ console.log('Tests not yet implemented');
@@ -0,0 +1,38 @@
1
+ import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';
2
+ import { APIClient } from './client.js';
3
+
4
+ export const tools = [
5
+ {{#each endpoints}}
6
+ {
7
+ name: '{{id}}',
8
+ description: '{{description}}',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ {{#each parameters}}
13
+ {{name}}: {
14
+ type: '{{schema.type}}',
15
+ {{#if description}}description: '{{description}}',{{/if}}
16
+ },
17
+ {{/each}}
18
+ },
19
+ required: [{{#each parameters}}{{#if required}}'{{name}}',{{/if}}{{/each}}],
20
+ },
21
+ },
22
+ {{/each}}
23
+ ];
24
+
25
+ export async function handleToolCall(request: CallToolRequest, client: APIClient) {
26
+ const { name, arguments: args } = request.params;
27
+
28
+ {{#each endpoints}}
29
+ if (name === '{{id}}') {
30
+ const result = await client.request('{{method}}', '{{path}}', args);
31
+ return {
32
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
33
+ };
34
+ }
35
+ {{/each}}
36
+
37
+ throw new Error(`Unknown tool: ${name}`);
38
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./build",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src/**/*"]
13
+ }
@@ -0,0 +1 @@
1
+ export {}; // Types generated from API schema
@@ -0,0 +1 @@
1
+ export {}; // Validation schemas