@codebakers/cli 1.1.5 → 1.1.6

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 (44) hide show
  1. package/dist/commands/doctor.d.ts +8 -0
  2. package/dist/commands/doctor.js +218 -0
  3. package/dist/commands/init.d.ts +4 -0
  4. package/dist/commands/init.js +772 -0
  5. package/dist/commands/install-hook.d.ts +12 -0
  6. package/dist/commands/install-hook.js +193 -0
  7. package/dist/commands/install.d.ts +1 -0
  8. package/dist/commands/install.js +81 -0
  9. package/dist/commands/login.d.ts +1 -0
  10. package/dist/commands/login.js +54 -0
  11. package/dist/commands/mcp-config.d.ts +6 -0
  12. package/dist/commands/mcp-config.js +209 -0
  13. package/dist/commands/serve.d.ts +1 -0
  14. package/dist/commands/serve.js +26 -0
  15. package/dist/commands/setup.d.ts +1 -0
  16. package/dist/commands/setup.js +92 -0
  17. package/dist/commands/status.d.ts +1 -0
  18. package/dist/commands/status.js +49 -0
  19. package/dist/commands/uninstall.d.ts +1 -0
  20. package/dist/commands/uninstall.js +50 -0
  21. package/dist/config.d.ts +5 -0
  22. package/dist/config.js +33 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +71 -1075
  25. package/dist/mcp/server.d.ts +2 -0
  26. package/dist/mcp/server.js +544 -0
  27. package/package.json +16 -38
  28. package/src/commands/doctor.ts +231 -0
  29. package/src/commands/init.ts +827 -0
  30. package/src/commands/install-hook.ts +207 -0
  31. package/src/commands/install.ts +94 -0
  32. package/src/commands/login.ts +56 -0
  33. package/src/commands/mcp-config.ts +235 -0
  34. package/src/commands/serve.ts +23 -0
  35. package/src/commands/setup.ts +104 -0
  36. package/src/commands/status.ts +48 -0
  37. package/src/commands/uninstall.ts +49 -0
  38. package/src/config.ts +34 -0
  39. package/src/index.ts +87 -0
  40. package/src/mcp/server.ts +617 -0
  41. package/tsconfig.json +16 -0
  42. package/README.md +0 -89
  43. package/dist/chunk-7CKLRE2H.js +0 -36
  44. package/dist/config-R2H6JKGW.js +0 -16
@@ -0,0 +1,617 @@
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 {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ ErrorCode,
9
+ McpError,
10
+ } from '@modelcontextprotocol/sdk/types.js';
11
+ import { getApiKey, getApiUrl } from '../config.js';
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+
15
+ // Pattern cache to avoid repeated API calls
16
+ const patternCache = new Map<string, { content: string; timestamp: number }>();
17
+ const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
18
+
19
+ // Project context type
20
+ interface ProjectContext {
21
+ projectName: string;
22
+ dependencies: string[];
23
+ devDependencies: string[];
24
+ folderStructure: string[];
25
+ hasAuth: boolean;
26
+ hasDatabase: boolean;
27
+ hasPayments: boolean;
28
+ uiLibrary: string | null;
29
+ schemaPath: string | null;
30
+ componentsPath: string | null;
31
+ existingComponents: string[];
32
+ existingServices: string[];
33
+ existingApiRoutes: string[];
34
+ codebakersState: Record<string, unknown> | null;
35
+ }
36
+
37
+ // API response type for optimize-prompt
38
+ interface OptimizeResponse {
39
+ optimizedPrompt: string;
40
+ featureName: string;
41
+ patterns: string[];
42
+ method: string;
43
+ hasContext: boolean;
44
+ }
45
+
46
+ class CodeBakersServer {
47
+ private server: Server;
48
+ private apiKey: string | null;
49
+ private apiUrl: string;
50
+
51
+ constructor() {
52
+ this.apiKey = getApiKey();
53
+ this.apiUrl = getApiUrl();
54
+
55
+ this.server = new Server(
56
+ {
57
+ name: 'codebakers',
58
+ version: '1.0.0',
59
+ },
60
+ {
61
+ capabilities: {
62
+ tools: {},
63
+ },
64
+ }
65
+ );
66
+
67
+ this.setupHandlers();
68
+ }
69
+
70
+ private gatherProjectContext(): ProjectContext {
71
+ const cwd = process.cwd();
72
+ const context: ProjectContext = {
73
+ projectName: 'Unknown',
74
+ dependencies: [],
75
+ devDependencies: [],
76
+ folderStructure: [],
77
+ hasAuth: false,
78
+ hasDatabase: false,
79
+ hasPayments: false,
80
+ uiLibrary: null,
81
+ schemaPath: null,
82
+ componentsPath: null,
83
+ existingComponents: [],
84
+ existingServices: [],
85
+ existingApiRoutes: [],
86
+ codebakersState: null,
87
+ };
88
+
89
+ // Read package.json
90
+ try {
91
+ const pkgPath = path.join(cwd, 'package.json');
92
+ if (fs.existsSync(pkgPath)) {
93
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
94
+ context.projectName = pkg.name || 'Unknown';
95
+ context.dependencies = Object.keys(pkg.dependencies || {});
96
+ context.devDependencies = Object.keys(pkg.devDependencies || {});
97
+
98
+ // Detect libraries
99
+ const allDeps = [...context.dependencies, ...context.devDependencies];
100
+ context.hasAuth = allDeps.some(d => d.includes('supabase') || d.includes('next-auth') || d.includes('clerk'));
101
+ context.hasDatabase = allDeps.some(d => d.includes('drizzle') || d.includes('prisma') || d.includes('postgres'));
102
+ context.hasPayments = allDeps.some(d => d.includes('stripe') || d.includes('paypal'));
103
+
104
+ if (allDeps.includes('@radix-ui/react-dialog') || allDeps.some(d => d.includes('shadcn'))) {
105
+ context.uiLibrary = 'shadcn/ui';
106
+ } else if (allDeps.includes('@chakra-ui/react')) {
107
+ context.uiLibrary = 'Chakra UI';
108
+ } else if (allDeps.includes('@mui/material')) {
109
+ context.uiLibrary = 'Material UI';
110
+ }
111
+ }
112
+ } catch {
113
+ // Ignore package.json errors
114
+ }
115
+
116
+ // Read .codebakers.json state
117
+ try {
118
+ const statePath = path.join(cwd, '.codebakers.json');
119
+ if (fs.existsSync(statePath)) {
120
+ context.codebakersState = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
121
+ }
122
+ } catch {
123
+ // Ignore state file errors
124
+ }
125
+
126
+ // Scan folder structure
127
+ const scanDir = (dir: string, prefix = ''): string[] => {
128
+ const results: string[] = [];
129
+ try {
130
+ if (!fs.existsSync(dir)) return results;
131
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
132
+ for (const entry of entries) {
133
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
134
+ const fullPath = path.join(prefix, entry.name);
135
+ if (entry.isDirectory()) {
136
+ results.push(fullPath + '/');
137
+ // Only go 2 levels deep
138
+ if (prefix.split('/').length < 2) {
139
+ results.push(...scanDir(path.join(dir, entry.name), fullPath));
140
+ }
141
+ }
142
+ }
143
+ } catch {
144
+ // Ignore scan errors
145
+ }
146
+ return results;
147
+ };
148
+
149
+ context.folderStructure = scanDir(cwd);
150
+
151
+ // Find schema path
152
+ const schemaPaths = [
153
+ 'src/db/schema.ts',
154
+ 'src/lib/db/schema.ts',
155
+ 'db/schema.ts',
156
+ 'prisma/schema.prisma',
157
+ 'drizzle/schema.ts',
158
+ ];
159
+ for (const schemaPath of schemaPaths) {
160
+ if (fs.existsSync(path.join(cwd, schemaPath))) {
161
+ context.schemaPath = schemaPath;
162
+ break;
163
+ }
164
+ }
165
+
166
+ // Find components path and list components
167
+ const componentPaths = ['src/components', 'components', 'app/components'];
168
+ for (const compPath of componentPaths) {
169
+ const fullPath = path.join(cwd, compPath);
170
+ if (fs.existsSync(fullPath)) {
171
+ context.componentsPath = compPath;
172
+ try {
173
+ const scanComponents = (dir: string, prefix = ''): string[] => {
174
+ const comps: string[] = [];
175
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
176
+ for (const entry of entries) {
177
+ if (entry.name.startsWith('.')) continue;
178
+ if (entry.isDirectory()) {
179
+ comps.push(...scanComponents(path.join(dir, entry.name), entry.name + '/'));
180
+ } else if (entry.name.endsWith('.tsx') || entry.name.endsWith('.jsx')) {
181
+ comps.push(prefix + entry.name.replace(/\.(tsx|jsx)$/, ''));
182
+ }
183
+ }
184
+ return comps;
185
+ };
186
+ context.existingComponents = scanComponents(fullPath).slice(0, 50); // Limit to 50
187
+ } catch {
188
+ // Ignore component scan errors
189
+ }
190
+ break;
191
+ }
192
+ }
193
+
194
+ // Find services
195
+ const servicePaths = ['src/services', 'src/lib/services', 'services'];
196
+ for (const servPath of servicePaths) {
197
+ const fullPath = path.join(cwd, servPath);
198
+ if (fs.existsSync(fullPath)) {
199
+ try {
200
+ const entries = fs.readdirSync(fullPath);
201
+ context.existingServices = entries
202
+ .filter(e => e.endsWith('.ts') || e.endsWith('.js'))
203
+ .map(e => e.replace(/\.(ts|js)$/, ''))
204
+ .slice(0, 20);
205
+ } catch {
206
+ // Ignore service scan errors
207
+ }
208
+ break;
209
+ }
210
+ }
211
+
212
+ // Find API routes
213
+ const apiPaths = ['src/app/api', 'app/api', 'pages/api'];
214
+ for (const apiPath of apiPaths) {
215
+ const fullPath = path.join(cwd, apiPath);
216
+ if (fs.existsSync(fullPath)) {
217
+ try {
218
+ const scanApiRoutes = (dir: string, prefix = ''): string[] => {
219
+ const routes: string[] = [];
220
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
221
+ for (const entry of entries) {
222
+ if (entry.name.startsWith('.')) continue;
223
+ if (entry.isDirectory()) {
224
+ routes.push(...scanApiRoutes(path.join(dir, entry.name), prefix + '/' + entry.name));
225
+ } else if (entry.name === 'route.ts' || entry.name === 'route.js') {
226
+ routes.push(prefix || '/');
227
+ }
228
+ }
229
+ return routes;
230
+ };
231
+ context.existingApiRoutes = scanApiRoutes(fullPath).slice(0, 30);
232
+ } catch {
233
+ // Ignore API route scan errors
234
+ }
235
+ break;
236
+ }
237
+ }
238
+
239
+ return context;
240
+ }
241
+
242
+ private formatContextForPrompt(context: ProjectContext): string {
243
+ const lines: string[] = [];
244
+
245
+ lines.push(`Project: ${context.projectName}`);
246
+
247
+ if (context.uiLibrary) {
248
+ lines.push(`UI Library: ${context.uiLibrary}`);
249
+ }
250
+
251
+ if (context.schemaPath) {
252
+ lines.push(`Database Schema: ${context.schemaPath}`);
253
+ }
254
+
255
+ if (context.componentsPath && context.existingComponents.length > 0) {
256
+ lines.push(`Components Path: ${context.componentsPath}`);
257
+ lines.push(`Existing Components: ${context.existingComponents.slice(0, 20).join(', ')}`);
258
+ }
259
+
260
+ if (context.existingServices.length > 0) {
261
+ lines.push(`Existing Services: ${context.existingServices.join(', ')}`);
262
+ }
263
+
264
+ if (context.existingApiRoutes.length > 0) {
265
+ lines.push(`Existing API Routes: ${context.existingApiRoutes.join(', ')}`);
266
+ }
267
+
268
+ const features: string[] = [];
269
+ if (context.hasAuth) features.push('auth');
270
+ if (context.hasDatabase) features.push('database');
271
+ if (context.hasPayments) features.push('payments');
272
+ if (features.length > 0) {
273
+ lines.push(`Has: ${features.join(', ')}`);
274
+ }
275
+
276
+ const relevantDeps = context.dependencies.filter(d =>
277
+ ['next', 'react', 'drizzle-orm', 'stripe', '@supabase/supabase-js', 'zod', 'react-hook-form', 'tailwindcss'].some(rd => d.includes(rd))
278
+ );
279
+ if (relevantDeps.length > 0) {
280
+ lines.push(`Key Dependencies: ${relevantDeps.join(', ')}`);
281
+ }
282
+
283
+ return lines.join('\n');
284
+ }
285
+
286
+ private setupHandlers(): void {
287
+ // List available tools
288
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
289
+ tools: [
290
+ {
291
+ name: 'optimize_and_build',
292
+ description:
293
+ 'ALWAYS USE THIS FIRST for any coding request. Takes a simple user request, uses AI to analyze intent and detect relevant patterns, optimizes it into a production-ready prompt, and returns everything needed to build the feature correctly. No keyword matching - AI understands what you actually want to build.',
294
+ inputSchema: {
295
+ type: 'object' as const,
296
+ properties: {
297
+ request: {
298
+ type: 'string',
299
+ description: 'The user\'s original request (e.g., "add login", "create checkout", "zoom animation on image")',
300
+ },
301
+ },
302
+ required: ['request'],
303
+ },
304
+ },
305
+ {
306
+ name: 'get_pattern',
307
+ description:
308
+ 'Fetch a single CodeBakers pattern module by name. Use optimize_and_build instead for automatic pattern detection.',
309
+ inputSchema: {
310
+ type: 'object' as const,
311
+ properties: {
312
+ pattern: {
313
+ type: 'string',
314
+ description:
315
+ 'Pattern name (e.g., "00-core", "01-database", "02-auth", "03-api", "04-frontend")',
316
+ },
317
+ },
318
+ required: ['pattern'],
319
+ },
320
+ },
321
+ {
322
+ name: 'list_patterns',
323
+ description:
324
+ 'List all available CodeBakers pattern modules.',
325
+ inputSchema: {
326
+ type: 'object' as const,
327
+ properties: {},
328
+ },
329
+ },
330
+ {
331
+ name: 'get_patterns',
332
+ description:
333
+ 'Fetch multiple CodeBakers patterns at once. Use optimize_and_build instead for automatic pattern detection.',
334
+ inputSchema: {
335
+ type: 'object' as const,
336
+ properties: {
337
+ patterns: {
338
+ type: 'array',
339
+ items: { type: 'string' },
340
+ description: 'Array of pattern names to fetch (max 5)',
341
+ },
342
+ },
343
+ required: ['patterns'],
344
+ },
345
+ },
346
+ ],
347
+ }));
348
+
349
+ // Handle tool calls
350
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
351
+ if (!this.apiKey) {
352
+ throw new McpError(
353
+ ErrorCode.InvalidRequest,
354
+ 'Not logged in. Run `codebakers login` first.'
355
+ );
356
+ }
357
+
358
+ const { name, arguments: args } = request.params;
359
+
360
+ switch (name) {
361
+ case 'optimize_and_build':
362
+ return this.handleOptimizeAndBuild(args as { request: string });
363
+
364
+ case 'get_pattern':
365
+ return this.handleGetPattern(args as { pattern: string });
366
+
367
+ case 'list_patterns':
368
+ return this.handleListPatterns();
369
+
370
+ case 'get_patterns':
371
+ return this.handleGetPatterns(args as { patterns: string[] });
372
+
373
+ default:
374
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
375
+ }
376
+ });
377
+ }
378
+
379
+ private async handleOptimizeAndBuild(args: { request: string }) {
380
+ const { request: userRequest } = args;
381
+
382
+ // Step 1: Gather project context
383
+ const context = this.gatherProjectContext();
384
+ const contextSummary = this.formatContextForPrompt(context);
385
+
386
+ // Step 2: Call API to optimize the prompt with context
387
+ // The API uses AI to analyze intent and detect patterns (no keyword matching)
388
+ const optimizeResponse = await fetch(`${this.apiUrl}/api/optimize-prompt`, {
389
+ method: 'POST',
390
+ headers: {
391
+ 'Content-Type': 'application/json',
392
+ Authorization: `Bearer ${this.apiKey}`,
393
+ },
394
+ body: JSON.stringify({
395
+ prompt: userRequest,
396
+ context: {
397
+ summary: contextSummary,
398
+ projectName: context.projectName,
399
+ uiLibrary: context.uiLibrary,
400
+ schemaPath: context.schemaPath,
401
+ componentsPath: context.componentsPath,
402
+ existingComponents: context.existingComponents.slice(0, 30),
403
+ existingServices: context.existingServices,
404
+ existingApiRoutes: context.existingApiRoutes,
405
+ hasAuth: context.hasAuth,
406
+ hasDatabase: context.hasDatabase,
407
+ hasPayments: context.hasPayments,
408
+ dependencies: context.dependencies.slice(0, 30),
409
+ },
410
+ }),
411
+ });
412
+
413
+ // Default values if API fails
414
+ let optimizedPrompt = userRequest;
415
+ let detectedFeature = 'Feature';
416
+ let patterns = ['00-core', '04-frontend']; // Default fallback
417
+
418
+ if (optimizeResponse.ok) {
419
+ const optimizeData: OptimizeResponse = await optimizeResponse.json();
420
+ optimizedPrompt = optimizeData.optimizedPrompt || userRequest;
421
+ detectedFeature = optimizeData.featureName || 'Feature';
422
+ // Use AI-detected patterns from the API (no local keyword matching)
423
+ patterns = optimizeData.patterns || ['00-core', '04-frontend'];
424
+ }
425
+
426
+ // Step 3: Fetch all relevant patterns (as detected by AI)
427
+ const patternResult = await this.fetchPatterns(patterns);
428
+
429
+ // Step 4: Build the response showing the optimization with context
430
+ const patternContent = Object.entries(patternResult.patterns || {})
431
+ .map(([name, text]) => `## ${name}\n\n${text}`)
432
+ .join('\n\n---\n\n');
433
+
434
+ const response = `# 🪄 Prompt Optimizer (AI-Powered Intent Analysis)
435
+
436
+ ## Your Request
437
+ "${userRequest}"
438
+
439
+ ## Project Context Detected
440
+ ${contextSummary}
441
+
442
+ ## AI Analysis
443
+ - **Detected Intent:** ${detectedFeature}
444
+ - **Relevant Patterns:** ${patterns.join(', ')}
445
+
446
+ ## Optimized Prompt (Production-Ready)
447
+ ${optimizedPrompt}
448
+
449
+ ---
450
+
451
+ # Pattern Documentation
452
+
453
+ ${patternContent}
454
+
455
+ ---
456
+
457
+ **IMPORTANT:** Use the optimized prompt above as your guide. It is tailored to THIS project's structure, existing components, and conventions. The AI analyzed your intent (not just keywords) to select the right patterns. The prompt includes:
458
+ - References to existing components and services you should reuse
459
+ - The correct file paths for this project
460
+ - Production requirements (error handling, loading states, validation, tests)
461
+
462
+ Show the user what their simple request was expanded into, then proceed with the implementation following the patterns above.`;
463
+
464
+ return {
465
+ content: [
466
+ {
467
+ type: 'text' as const,
468
+ text: response,
469
+ },
470
+ ],
471
+ };
472
+ }
473
+
474
+ private async handleGetPattern(args: { pattern: string }) {
475
+ const { pattern } = args;
476
+
477
+ // Check cache first
478
+ const cached = patternCache.get(pattern);
479
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
480
+ return {
481
+ content: [
482
+ {
483
+ type: 'text' as const,
484
+ text: cached.content,
485
+ },
486
+ ],
487
+ };
488
+ }
489
+
490
+ // Fetch from API
491
+ const result = await this.fetchPatterns([pattern]);
492
+
493
+ if (result.patterns && result.patterns[pattern]) {
494
+ const content = result.patterns[pattern];
495
+
496
+ // Cache the result
497
+ patternCache.set(pattern, { content, timestamp: Date.now() });
498
+
499
+ return {
500
+ content: [
501
+ {
502
+ type: 'text' as const,
503
+ text: content,
504
+ },
505
+ ],
506
+ };
507
+ }
508
+
509
+ throw new McpError(
510
+ ErrorCode.InvalidRequest,
511
+ `Pattern "${pattern}" not found. Use list_patterns to see available patterns.`
512
+ );
513
+ }
514
+
515
+ private async handleListPatterns() {
516
+ const response = await fetch(`${this.apiUrl}/api/patterns`, {
517
+ method: 'GET',
518
+ headers: {
519
+ Authorization: `Bearer ${this.apiKey}`,
520
+ },
521
+ });
522
+
523
+ if (!response.ok) {
524
+ const error = await response.json().catch(() => ({}));
525
+ throw new McpError(
526
+ ErrorCode.InternalError,
527
+ error.error || 'Failed to fetch patterns'
528
+ );
529
+ }
530
+
531
+ const data = await response.json();
532
+ const patternList = data.patterns
533
+ .map((p: { name: string }) => `- ${p.name}`)
534
+ .join('\n');
535
+
536
+ return {
537
+ content: [
538
+ {
539
+ type: 'text' as const,
540
+ text: `Available CodeBakers Patterns (${data.total} total):\n\n${patternList}\n\nTip: Use optimize_and_build for automatic AI-powered pattern detection.`,
541
+ },
542
+ ],
543
+ };
544
+ }
545
+
546
+ private async handleGetPatterns(args: { patterns: string[] }) {
547
+ const { patterns } = args;
548
+
549
+ if (patterns.length > 5) {
550
+ throw new McpError(
551
+ ErrorCode.InvalidRequest,
552
+ 'Maximum 5 patterns per request'
553
+ );
554
+ }
555
+
556
+ const result = await this.fetchPatterns(patterns);
557
+
558
+ const content = Object.entries(result.patterns || {})
559
+ .map(([name, text]) => `## ${name}\n\n${text}`)
560
+ .join('\n\n---\n\n');
561
+
562
+ return {
563
+ content: [
564
+ {
565
+ type: 'text' as const,
566
+ text:
567
+ content ||
568
+ 'No patterns found. Use list_patterns to see available patterns.',
569
+ },
570
+ ],
571
+ };
572
+ }
573
+
574
+ private async fetchPatterns(patterns: string[]) {
575
+ const response = await fetch(`${this.apiUrl}/api/patterns`, {
576
+ method: 'POST',
577
+ headers: {
578
+ 'Content-Type': 'application/json',
579
+ Authorization: `Bearer ${this.apiKey}`,
580
+ },
581
+ body: JSON.stringify({ patterns }),
582
+ });
583
+
584
+ if (!response.ok) {
585
+ const error = await response.json().catch(() => ({}));
586
+ throw new McpError(
587
+ ErrorCode.InternalError,
588
+ error.error || 'Failed to fetch patterns'
589
+ );
590
+ }
591
+
592
+ return response.json();
593
+ }
594
+
595
+ async run(): Promise<void> {
596
+ const transport = new StdioServerTransport();
597
+ await this.server.connect(transport);
598
+
599
+ // Log to stderr so it doesn't interfere with stdio protocol
600
+ console.error('CodeBakers MCP server running on stdio');
601
+ }
602
+ }
603
+
604
+ // Export function for programmatic usage
605
+ export async function runServer(): Promise<void> {
606
+ const server = new CodeBakersServer();
607
+ await server.run();
608
+ }
609
+
610
+ // Run directly if executed as main module
611
+ const isMain = process.argv[1]?.includes('server');
612
+ if (isMain) {
613
+ runServer().catch((error) => {
614
+ console.error('Failed to start MCP server:', error);
615
+ process.exit(1);
616
+ });
617
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }