@stackguide/mcp-server 1.0.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 (48) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +453 -0
  3. package/data/knowledge/python-django/architecture/architecture-patterns.md +201 -0
  4. package/data/knowledge/python-django/common-issues/common-issues.md +181 -0
  5. package/data/knowledge/python-django/patterns/drf-patterns.md +133 -0
  6. package/data/knowledge/react-node/architecture/node-architecture.md +257 -0
  7. package/data/knowledge/react-node/common-issues/common-issues.md +262 -0
  8. package/data/knowledge/react-node/patterns/react-patterns.md +244 -0
  9. package/data/rules/python-django/best-practices/django-best-practices.md +120 -0
  10. package/data/rules/python-django/coding-standards/django-standards.md +104 -0
  11. package/data/rules/python-django/security/security-guidelines.md +146 -0
  12. package/data/rules/react-node/best-practices/react-best-practices.md +195 -0
  13. package/data/rules/react-node/coding-standards/node-standards.md +192 -0
  14. package/data/rules/react-node/coding-standards/react-standards.md +155 -0
  15. package/data/rules/react-node/security/security-guidelines.md +228 -0
  16. package/dist/config/persistence.d.ts +15 -0
  17. package/dist/config/persistence.d.ts.map +1 -0
  18. package/dist/config/persistence.js +171 -0
  19. package/dist/config/persistence.js.map +1 -0
  20. package/dist/config/types.d.ts +47 -0
  21. package/dist/config/types.d.ts.map +1 -0
  22. package/dist/config/types.js +116 -0
  23. package/dist/config/types.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +1799 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/resources/knowledgeProvider.d.ts +10 -0
  29. package/dist/resources/knowledgeProvider.d.ts.map +1 -0
  30. package/dist/resources/knowledgeProvider.js +130 -0
  31. package/dist/resources/knowledgeProvider.js.map +1 -0
  32. package/dist/resources/rulesProvider.d.ts +10 -0
  33. package/dist/resources/rulesProvider.d.ts.map +1 -0
  34. package/dist/resources/rulesProvider.js +135 -0
  35. package/dist/resources/rulesProvider.js.map +1 -0
  36. package/dist/services/cursorDirectory.d.ts +55 -0
  37. package/dist/services/cursorDirectory.d.ts.map +1 -0
  38. package/dist/services/cursorDirectory.js +367 -0
  39. package/dist/services/cursorDirectory.js.map +1 -0
  40. package/dist/services/ruleManager.d.ts +18 -0
  41. package/dist/services/ruleManager.d.ts.map +1 -0
  42. package/dist/services/ruleManager.js +382 -0
  43. package/dist/services/ruleManager.js.map +1 -0
  44. package/dist/services/webDocumentation.d.ts +41 -0
  45. package/dist/services/webDocumentation.d.ts.map +1 -0
  46. package/dist/services/webDocumentation.js +237 -0
  47. package/dist/services/webDocumentation.js.map +1 -0
  48. package/package.json +46 -0
package/dist/index.js ADDED
@@ -0,0 +1,1799 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { SUPPORTED_PROJECTS } from './config/types.js';
6
+ import * as persistence from './config/persistence.js';
7
+ import * as rulesProvider from './resources/rulesProvider.js';
8
+ import * as knowledgeProvider from './resources/knowledgeProvider.js';
9
+ import * as ruleManager from './services/ruleManager.js';
10
+ import * as webDocs from './services/webDocumentation.js';
11
+ import * as cursorDirectory from './services/cursorDirectory.js';
12
+ // Server state
13
+ const serverState = {
14
+ activeProjectType: null,
15
+ activeConfiguration: null,
16
+ loadedRules: [],
17
+ loadedKnowledge: []
18
+ };
19
+ // Create MCP server
20
+ const server = new Server({
21
+ name: 'stackguide-mcp',
22
+ version: '1.0.0',
23
+ }, {
24
+ capabilities: {
25
+ tools: {},
26
+ resources: {},
27
+ prompts: {},
28
+ },
29
+ });
30
+ // ==================== TOOLS ====================
31
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
32
+ return {
33
+ tools: [
34
+ // Project Type Tools
35
+ {
36
+ name: 'list_project_types',
37
+ description: 'List all supported project types (Python/Django, React/Node, etc.)',
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {},
41
+ required: []
42
+ }
43
+ },
44
+ {
45
+ name: 'select_project_type',
46
+ description: 'Select and activate a project type to load its context, rules and knowledge',
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {
50
+ projectType: {
51
+ type: 'string',
52
+ description: 'The project type to select (e.g., python-django, react-node)',
53
+ enum: Object.keys(SUPPORTED_PROJECTS)
54
+ }
55
+ },
56
+ required: ['projectType']
57
+ }
58
+ },
59
+ {
60
+ name: 'get_current_context',
61
+ description: 'Get the currently active project context with loaded rules and knowledge',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {},
65
+ required: []
66
+ }
67
+ },
68
+ // Rules Tools
69
+ {
70
+ name: 'list_rules',
71
+ description: 'List all available rules for the current or specified project type',
72
+ inputSchema: {
73
+ type: 'object',
74
+ properties: {
75
+ projectType: {
76
+ type: 'string',
77
+ description: 'Project type (uses active project if not specified)',
78
+ enum: Object.keys(SUPPORTED_PROJECTS)
79
+ },
80
+ category: {
81
+ type: 'string',
82
+ description: 'Filter by category',
83
+ enum: ['coding-standards', 'best-practices', 'security', 'performance', 'architecture', 'testing', 'documentation', 'naming-conventions']
84
+ }
85
+ },
86
+ required: []
87
+ }
88
+ },
89
+ {
90
+ name: 'get_rule',
91
+ description: 'Get the full content of a specific rule by ID',
92
+ inputSchema: {
93
+ type: 'object',
94
+ properties: {
95
+ ruleId: {
96
+ type: 'string',
97
+ description: 'The ID of the rule to retrieve'
98
+ }
99
+ },
100
+ required: ['ruleId']
101
+ }
102
+ },
103
+ {
104
+ name: 'select_rules',
105
+ description: 'Select which rules to include in the active context',
106
+ inputSchema: {
107
+ type: 'object',
108
+ properties: {
109
+ ruleIds: {
110
+ type: 'array',
111
+ items: { type: 'string' },
112
+ description: 'Array of rule IDs to select'
113
+ }
114
+ },
115
+ required: ['ruleIds']
116
+ }
117
+ },
118
+ {
119
+ name: 'search_rules',
120
+ description: 'Search rules by keyword or term',
121
+ inputSchema: {
122
+ type: 'object',
123
+ properties: {
124
+ searchTerm: {
125
+ type: 'string',
126
+ description: 'Term to search for in rules'
127
+ },
128
+ projectType: {
129
+ type: 'string',
130
+ description: 'Project type to search in',
131
+ enum: Object.keys(SUPPORTED_PROJECTS)
132
+ }
133
+ },
134
+ required: ['searchTerm']
135
+ }
136
+ },
137
+ // Knowledge Tools
138
+ {
139
+ name: 'list_knowledge',
140
+ description: 'List all knowledge base files for the current or specified project type',
141
+ inputSchema: {
142
+ type: 'object',
143
+ properties: {
144
+ projectType: {
145
+ type: 'string',
146
+ description: 'Project type (uses active project if not specified)',
147
+ enum: Object.keys(SUPPORTED_PROJECTS)
148
+ },
149
+ category: {
150
+ type: 'string',
151
+ description: 'Filter by category',
152
+ enum: ['patterns', 'common-issues', 'architecture', 'snippets', 'workflows', 'troubleshooting']
153
+ }
154
+ },
155
+ required: []
156
+ }
157
+ },
158
+ {
159
+ name: 'get_knowledge',
160
+ description: 'Get the full content of a specific knowledge file by ID',
161
+ inputSchema: {
162
+ type: 'object',
163
+ properties: {
164
+ knowledgeId: {
165
+ type: 'string',
166
+ description: 'The ID of the knowledge file to retrieve'
167
+ }
168
+ },
169
+ required: ['knowledgeId']
170
+ }
171
+ },
172
+ {
173
+ name: 'select_knowledge',
174
+ description: 'Select which knowledge files to include in the active context',
175
+ inputSchema: {
176
+ type: 'object',
177
+ properties: {
178
+ knowledgeIds: {
179
+ type: 'array',
180
+ items: { type: 'string' },
181
+ description: 'Array of knowledge file IDs to select'
182
+ }
183
+ },
184
+ required: ['knowledgeIds']
185
+ }
186
+ },
187
+ {
188
+ name: 'search_knowledge',
189
+ description: 'Search knowledge base by keyword or term',
190
+ inputSchema: {
191
+ type: 'object',
192
+ properties: {
193
+ searchTerm: {
194
+ type: 'string',
195
+ description: 'Term to search for'
196
+ },
197
+ projectType: {
198
+ type: 'string',
199
+ description: 'Project type to search in',
200
+ enum: Object.keys(SUPPORTED_PROJECTS)
201
+ }
202
+ },
203
+ required: ['searchTerm']
204
+ }
205
+ },
206
+ // Configuration Tools
207
+ {
208
+ name: 'save_configuration',
209
+ description: 'Save the current context configuration for future use',
210
+ inputSchema: {
211
+ type: 'object',
212
+ properties: {
213
+ name: {
214
+ type: 'string',
215
+ description: 'Name for this configuration'
216
+ }
217
+ },
218
+ required: ['name']
219
+ }
220
+ },
221
+ {
222
+ name: 'load_configuration',
223
+ description: 'Load a previously saved configuration',
224
+ inputSchema: {
225
+ type: 'object',
226
+ properties: {
227
+ configurationId: {
228
+ type: 'string',
229
+ description: 'ID of the configuration to load'
230
+ }
231
+ },
232
+ required: ['configurationId']
233
+ }
234
+ },
235
+ {
236
+ name: 'list_configurations',
237
+ description: 'List all saved configurations',
238
+ inputSchema: {
239
+ type: 'object',
240
+ properties: {},
241
+ required: []
242
+ }
243
+ },
244
+ {
245
+ name: 'delete_configuration',
246
+ description: 'Delete a saved configuration',
247
+ inputSchema: {
248
+ type: 'object',
249
+ properties: {
250
+ configurationId: {
251
+ type: 'string',
252
+ description: 'ID of the configuration to delete'
253
+ }
254
+ },
255
+ required: ['configurationId']
256
+ }
257
+ },
258
+ {
259
+ name: 'export_configuration',
260
+ description: 'Export a configuration as JSON for sharing',
261
+ inputSchema: {
262
+ type: 'object',
263
+ properties: {
264
+ configurationId: {
265
+ type: 'string',
266
+ description: 'ID of the configuration to export'
267
+ }
268
+ },
269
+ required: ['configurationId']
270
+ }
271
+ },
272
+ {
273
+ name: 'import_configuration',
274
+ description: 'Import a configuration from JSON',
275
+ inputSchema: {
276
+ type: 'object',
277
+ properties: {
278
+ jsonConfig: {
279
+ type: 'string',
280
+ description: 'JSON string of the configuration to import'
281
+ }
282
+ },
283
+ required: ['jsonConfig']
284
+ }
285
+ },
286
+ // Context Tools
287
+ {
288
+ name: 'get_full_context',
289
+ description: 'Get the complete active context with all selected rules and knowledge combined',
290
+ inputSchema: {
291
+ type: 'object',
292
+ properties: {},
293
+ required: []
294
+ }
295
+ },
296
+ {
297
+ name: 'add_custom_rule',
298
+ description: 'Add a custom rule to the current configuration',
299
+ inputSchema: {
300
+ type: 'object',
301
+ properties: {
302
+ name: {
303
+ type: 'string',
304
+ description: 'Name of the custom rule'
305
+ },
306
+ category: {
307
+ type: 'string',
308
+ description: 'Category of the rule',
309
+ enum: ['coding-standards', 'best-practices', 'security', 'performance', 'architecture', 'testing', 'documentation', 'naming-conventions']
310
+ },
311
+ content: {
312
+ type: 'string',
313
+ description: 'The rule content in markdown format'
314
+ },
315
+ description: {
316
+ type: 'string',
317
+ description: 'Brief description of the rule'
318
+ }
319
+ },
320
+ required: ['name', 'category', 'content']
321
+ }
322
+ },
323
+ // ==================== DYNAMIC RULE MANAGEMENT ====================
324
+ {
325
+ name: 'create_rule',
326
+ description: 'Create a new custom rule for the specified project type. The rule will be persisted and available in future sessions.',
327
+ inputSchema: {
328
+ type: 'object',
329
+ properties: {
330
+ projectType: {
331
+ type: 'string',
332
+ description: 'Project type for this rule',
333
+ enum: Object.keys(SUPPORTED_PROJECTS)
334
+ },
335
+ name: {
336
+ type: 'string',
337
+ description: 'Name of the rule'
338
+ },
339
+ category: {
340
+ type: 'string',
341
+ description: 'Category of the rule',
342
+ enum: ['coding-standards', 'best-practices', 'security', 'performance', 'architecture', 'testing', 'documentation', 'naming-conventions']
343
+ },
344
+ content: {
345
+ type: 'string',
346
+ description: 'The rule content in markdown format'
347
+ },
348
+ description: {
349
+ type: 'string',
350
+ description: 'Brief description of the rule'
351
+ }
352
+ },
353
+ required: ['projectType', 'name', 'category', 'content']
354
+ }
355
+ },
356
+ {
357
+ name: 'create_rule_from_template',
358
+ description: 'Create a new rule using a predefined template (coding-standard, best-practice, security, architecture, testing)',
359
+ inputSchema: {
360
+ type: 'object',
361
+ properties: {
362
+ projectType: {
363
+ type: 'string',
364
+ description: 'Project type for this rule',
365
+ enum: Object.keys(SUPPORTED_PROJECTS)
366
+ },
367
+ templateId: {
368
+ type: 'string',
369
+ description: 'Template to use',
370
+ enum: ['coding-standard', 'best-practice', 'security', 'architecture', 'testing']
371
+ },
372
+ name: {
373
+ type: 'string',
374
+ description: 'Name for the new rule'
375
+ },
376
+ category: {
377
+ type: 'string',
378
+ description: 'Category of the rule',
379
+ enum: ['coding-standards', 'best-practices', 'security', 'performance', 'architecture', 'testing', 'documentation', 'naming-conventions']
380
+ },
381
+ description: {
382
+ type: 'string',
383
+ description: 'Description for the rule'
384
+ },
385
+ language: {
386
+ type: 'string',
387
+ description: 'Programming language for code examples (default: typescript)'
388
+ }
389
+ },
390
+ required: ['projectType', 'templateId', 'name', 'category', 'description']
391
+ }
392
+ },
393
+ {
394
+ name: 'list_rule_templates',
395
+ description: 'List all available rule templates',
396
+ inputSchema: {
397
+ type: 'object',
398
+ properties: {},
399
+ required: []
400
+ }
401
+ },
402
+ {
403
+ name: 'get_rule_template',
404
+ description: 'Get the content of a specific rule template',
405
+ inputSchema: {
406
+ type: 'object',
407
+ properties: {
408
+ templateId: {
409
+ type: 'string',
410
+ description: 'Template ID',
411
+ enum: ['coding-standard', 'best-practice', 'security', 'architecture', 'testing']
412
+ }
413
+ },
414
+ required: ['templateId']
415
+ }
416
+ },
417
+ {
418
+ name: 'update_rule',
419
+ description: 'Update an existing user-created rule',
420
+ inputSchema: {
421
+ type: 'object',
422
+ properties: {
423
+ ruleId: {
424
+ type: 'string',
425
+ description: 'ID of the rule to update (must be a user-created rule starting with "user-")'
426
+ },
427
+ name: {
428
+ type: 'string',
429
+ description: 'New name for the rule'
430
+ },
431
+ content: {
432
+ type: 'string',
433
+ description: 'New content for the rule'
434
+ },
435
+ description: {
436
+ type: 'string',
437
+ description: 'New description'
438
+ },
439
+ enabled: {
440
+ type: 'boolean',
441
+ description: 'Enable or disable the rule'
442
+ }
443
+ },
444
+ required: ['ruleId']
445
+ }
446
+ },
447
+ {
448
+ name: 'delete_rule',
449
+ description: 'Delete a user-created rule',
450
+ inputSchema: {
451
+ type: 'object',
452
+ properties: {
453
+ ruleId: {
454
+ type: 'string',
455
+ description: 'ID of the rule to delete (must be a user-created rule starting with "user-")'
456
+ }
457
+ },
458
+ required: ['ruleId']
459
+ }
460
+ },
461
+ {
462
+ name: 'list_user_rules',
463
+ description: 'List all user-created rules for a project type',
464
+ inputSchema: {
465
+ type: 'object',
466
+ properties: {
467
+ projectType: {
468
+ type: 'string',
469
+ description: 'Project type to list rules for',
470
+ enum: Object.keys(SUPPORTED_PROJECTS)
471
+ }
472
+ },
473
+ required: ['projectType']
474
+ }
475
+ },
476
+ {
477
+ name: 'export_user_rules',
478
+ description: 'Export all user-created rules as JSON for backup or sharing',
479
+ inputSchema: {
480
+ type: 'object',
481
+ properties: {},
482
+ required: []
483
+ }
484
+ },
485
+ {
486
+ name: 'import_user_rules',
487
+ description: 'Import user rules from JSON',
488
+ inputSchema: {
489
+ type: 'object',
490
+ properties: {
491
+ jsonRules: {
492
+ type: 'string',
493
+ description: 'JSON string containing rules to import'
494
+ }
495
+ },
496
+ required: ['jsonRules']
497
+ }
498
+ },
499
+ // ==================== WEB DOCUMENTATION ====================
500
+ {
501
+ name: 'fetch_web_docs',
502
+ description: 'Fetch documentation from a web URL and add it to the knowledge base',
503
+ inputSchema: {
504
+ type: 'object',
505
+ properties: {
506
+ url: {
507
+ type: 'string',
508
+ description: 'URL of the documentation to fetch'
509
+ },
510
+ projectType: {
511
+ type: 'string',
512
+ description: 'Associate with a project type',
513
+ enum: Object.keys(SUPPORTED_PROJECTS)
514
+ },
515
+ category: {
516
+ type: 'string',
517
+ description: 'Category for the documentation'
518
+ },
519
+ tags: {
520
+ type: 'array',
521
+ items: { type: 'string' },
522
+ description: 'Tags for easy searching'
523
+ }
524
+ },
525
+ required: ['url']
526
+ }
527
+ },
528
+ {
529
+ name: 'fetch_multiple_docs',
530
+ description: 'Fetch documentation from multiple URLs at once',
531
+ inputSchema: {
532
+ type: 'object',
533
+ properties: {
534
+ urls: {
535
+ type: 'array',
536
+ items: { type: 'string' },
537
+ description: 'List of URLs to fetch'
538
+ },
539
+ projectType: {
540
+ type: 'string',
541
+ description: 'Associate with a project type',
542
+ enum: Object.keys(SUPPORTED_PROJECTS)
543
+ }
544
+ },
545
+ required: ['urls']
546
+ }
547
+ },
548
+ {
549
+ name: 'get_web_doc',
550
+ description: 'Get the content of a previously fetched web document',
551
+ inputSchema: {
552
+ type: 'object',
553
+ properties: {
554
+ idOrUrl: {
555
+ type: 'string',
556
+ description: 'ID or URL of the document'
557
+ }
558
+ },
559
+ required: ['idOrUrl']
560
+ }
561
+ },
562
+ {
563
+ name: 'search_web_docs',
564
+ description: 'Search through fetched web documentation',
565
+ inputSchema: {
566
+ type: 'object',
567
+ properties: {
568
+ query: {
569
+ type: 'string',
570
+ description: 'Search query'
571
+ }
572
+ },
573
+ required: ['query']
574
+ }
575
+ },
576
+ {
577
+ name: 'list_web_docs',
578
+ description: 'List all fetched web documentation',
579
+ inputSchema: {
580
+ type: 'object',
581
+ properties: {},
582
+ required: []
583
+ }
584
+ },
585
+ {
586
+ name: 'get_suggested_docs',
587
+ description: 'Get suggested documentation URLs for a project type',
588
+ inputSchema: {
589
+ type: 'object',
590
+ properties: {
591
+ projectType: {
592
+ type: 'string',
593
+ description: 'Project type',
594
+ enum: Object.keys(SUPPORTED_PROJECTS)
595
+ }
596
+ },
597
+ required: ['projectType']
598
+ }
599
+ },
600
+ {
601
+ name: 'remove_web_doc',
602
+ description: 'Remove a fetched web document from cache',
603
+ inputSchema: {
604
+ type: 'object',
605
+ properties: {
606
+ idOrUrl: {
607
+ type: 'string',
608
+ description: 'ID or URL of the document to remove'
609
+ }
610
+ },
611
+ required: ['idOrUrl']
612
+ }
613
+ },
614
+ // ==================== CURSOR DIRECTORY INTEGRATION ====================
615
+ {
616
+ name: 'browse_cursor_directory',
617
+ description: 'Browse rules from cursor.directory by category. Available categories include: typescript, python, react, next.js, vue, django, fastapi, etc.',
618
+ inputSchema: {
619
+ type: 'object',
620
+ properties: {
621
+ category: {
622
+ type: 'string',
623
+ description: 'Category to browse (e.g., typescript, python, react, next.js, vue, django)'
624
+ }
625
+ },
626
+ required: ['category']
627
+ }
628
+ },
629
+ {
630
+ name: 'search_cursor_directory',
631
+ description: 'Search for rules on cursor.directory',
632
+ inputSchema: {
633
+ type: 'object',
634
+ properties: {
635
+ query: {
636
+ type: 'string',
637
+ description: 'Search query (e.g., "react hooks", "python fastapi", "typescript best practices")'
638
+ }
639
+ },
640
+ required: ['query']
641
+ }
642
+ },
643
+ {
644
+ name: 'get_cursor_directory_rule',
645
+ description: 'Get a specific rule from cursor.directory by its slug',
646
+ inputSchema: {
647
+ type: 'object',
648
+ properties: {
649
+ slug: {
650
+ type: 'string',
651
+ description: 'Rule slug from cursor.directory URL (e.g., "nextjs-react-typescript-cursor-rules")'
652
+ },
653
+ category: {
654
+ type: 'string',
655
+ description: 'Category of the rule'
656
+ }
657
+ },
658
+ required: ['slug']
659
+ }
660
+ },
661
+ {
662
+ name: 'list_cursor_directory_categories',
663
+ description: 'List all available categories on cursor.directory',
664
+ inputSchema: {
665
+ type: 'object',
666
+ properties: {},
667
+ required: []
668
+ }
669
+ },
670
+ {
671
+ name: 'get_popular_cursor_rules',
672
+ description: 'Get popular/featured rules from cursor.directory',
673
+ inputSchema: {
674
+ type: 'object',
675
+ properties: {},
676
+ required: []
677
+ }
678
+ },
679
+ {
680
+ name: 'import_cursor_directory_rule',
681
+ description: 'Import a rule from cursor.directory into your local rules collection',
682
+ inputSchema: {
683
+ type: 'object',
684
+ properties: {
685
+ slug: {
686
+ type: 'string',
687
+ description: 'Rule slug from cursor.directory'
688
+ },
689
+ projectType: {
690
+ type: 'string',
691
+ description: 'Project type to import the rule into',
692
+ enum: Object.keys(SUPPORTED_PROJECTS)
693
+ },
694
+ category: {
695
+ type: 'string',
696
+ description: 'Local category for the imported rule',
697
+ enum: ['coding-standards', 'best-practices', 'security', 'performance', 'architecture', 'testing']
698
+ }
699
+ },
700
+ required: ['slug', 'projectType']
701
+ }
702
+ }
703
+ ]
704
+ };
705
+ });
706
+ // Handler para ejecutar tools
707
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
708
+ const { name, arguments: args } = request.params;
709
+ try {
710
+ switch (name) {
711
+ // Project Type Tools
712
+ case 'list_project_types': {
713
+ const projects = Object.values(SUPPORTED_PROJECTS).map(p => ({
714
+ type: p.type,
715
+ name: p.name,
716
+ description: p.description,
717
+ languages: p.languages,
718
+ frameworks: p.frameworks
719
+ }));
720
+ return { content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }] };
721
+ }
722
+ case 'select_project_type': {
723
+ const projectType = args.projectType;
724
+ const project = SUPPORTED_PROJECTS[projectType];
725
+ if (!project) {
726
+ return { content: [{ type: 'text', text: `Error: Unknown project type "${projectType}"` }] };
727
+ }
728
+ serverState.activeProjectType = projectType;
729
+ serverState.loadedRules = rulesProvider.getRulesForProject(projectType);
730
+ serverState.loadedKnowledge = knowledgeProvider.getKnowledgeForProject(projectType);
731
+ return {
732
+ content: [{
733
+ type: 'text',
734
+ text: JSON.stringify({
735
+ success: true,
736
+ message: `Project type "${project.name}" activated`,
737
+ rulesLoaded: serverState.loadedRules.length,
738
+ knowledgeLoaded: serverState.loadedKnowledge.length
739
+ }, null, 2)
740
+ }]
741
+ };
742
+ }
743
+ case 'get_current_context': {
744
+ return {
745
+ content: [{
746
+ type: 'text',
747
+ text: JSON.stringify({
748
+ activeProjectType: serverState.activeProjectType,
749
+ activeConfiguration: serverState.activeConfiguration,
750
+ loadedRulesCount: serverState.loadedRules.length,
751
+ loadedKnowledgeCount: serverState.loadedKnowledge.length,
752
+ rules: serverState.loadedRules.map(r => ({ id: r.id, name: r.name, category: r.category })),
753
+ knowledge: serverState.loadedKnowledge.map(k => ({ id: k.id, name: k.name, category: k.category }))
754
+ }, null, 2)
755
+ }]
756
+ };
757
+ }
758
+ // Rules Tools
759
+ case 'list_rules': {
760
+ const { projectType, category } = args;
761
+ const pt = projectType || serverState.activeProjectType;
762
+ if (!pt) {
763
+ return { content: [{ type: 'text', text: 'Error: No project type selected. Use select_project_type first.' }] };
764
+ }
765
+ let rules = rulesProvider.getRulesForProject(pt);
766
+ if (category) {
767
+ rules = rules.filter(r => r.category === category);
768
+ }
769
+ return {
770
+ content: [{
771
+ type: 'text',
772
+ text: JSON.stringify(rules.map(r => ({
773
+ id: r.id,
774
+ name: r.name,
775
+ category: r.category,
776
+ description: r.description,
777
+ enabled: r.enabled
778
+ })), null, 2)
779
+ }]
780
+ };
781
+ }
782
+ case 'get_rule': {
783
+ const { ruleId } = args;
784
+ const rule = rulesProvider.getRuleById(ruleId);
785
+ if (!rule) {
786
+ return { content: [{ type: 'text', text: `Error: Rule "${ruleId}" not found` }] };
787
+ }
788
+ return { content: [{ type: 'text', text: rule.content }] };
789
+ }
790
+ case 'select_rules': {
791
+ const { ruleIds } = args;
792
+ if (serverState.activeConfiguration) {
793
+ persistence.updateSelectedRules(serverState.activeConfiguration.id, ruleIds);
794
+ serverState.activeConfiguration.selectedRules = ruleIds;
795
+ }
796
+ return {
797
+ content: [{
798
+ type: 'text',
799
+ text: JSON.stringify({ success: true, selectedRules: ruleIds }, null, 2)
800
+ }]
801
+ };
802
+ }
803
+ case 'search_rules': {
804
+ const { searchTerm, projectType } = args;
805
+ const pt = projectType || serverState.activeProjectType;
806
+ if (!pt) {
807
+ return { content: [{ type: 'text', text: 'Error: No project type selected.' }] };
808
+ }
809
+ const results = rulesProvider.searchRules(pt, searchTerm);
810
+ return {
811
+ content: [{
812
+ type: 'text',
813
+ text: JSON.stringify(results.map(r => ({
814
+ id: r.id,
815
+ name: r.name,
816
+ category: r.category,
817
+ description: r.description
818
+ })), null, 2)
819
+ }]
820
+ };
821
+ }
822
+ // Knowledge Tools
823
+ case 'list_knowledge': {
824
+ const { projectType, category } = args;
825
+ const pt = projectType || serverState.activeProjectType;
826
+ if (!pt) {
827
+ return { content: [{ type: 'text', text: 'Error: No project type selected.' }] };
828
+ }
829
+ let knowledge = knowledgeProvider.getKnowledgeForProject(pt);
830
+ if (category) {
831
+ knowledge = knowledge.filter(k => k.category === category);
832
+ }
833
+ return {
834
+ content: [{
835
+ type: 'text',
836
+ text: JSON.stringify(knowledge.map(k => ({
837
+ id: k.id,
838
+ name: k.name,
839
+ category: k.category,
840
+ description: k.description
841
+ })), null, 2)
842
+ }]
843
+ };
844
+ }
845
+ case 'get_knowledge': {
846
+ const { knowledgeId } = args;
847
+ const knowledge = knowledgeProvider.getKnowledgeById(knowledgeId);
848
+ if (!knowledge) {
849
+ return { content: [{ type: 'text', text: `Error: Knowledge "${knowledgeId}" not found` }] };
850
+ }
851
+ return { content: [{ type: 'text', text: knowledge.content }] };
852
+ }
853
+ case 'select_knowledge': {
854
+ const { knowledgeIds } = args;
855
+ if (serverState.activeConfiguration) {
856
+ persistence.updateSelectedKnowledge(serverState.activeConfiguration.id, knowledgeIds);
857
+ serverState.activeConfiguration.selectedKnowledge = knowledgeIds;
858
+ }
859
+ return {
860
+ content: [{
861
+ type: 'text',
862
+ text: JSON.stringify({ success: true, selectedKnowledge: knowledgeIds }, null, 2)
863
+ }]
864
+ };
865
+ }
866
+ case 'search_knowledge': {
867
+ const { searchTerm, projectType } = args;
868
+ const pt = projectType || serverState.activeProjectType;
869
+ if (!pt) {
870
+ return { content: [{ type: 'text', text: 'Error: No project type selected.' }] };
871
+ }
872
+ const results = knowledgeProvider.searchKnowledge(pt, searchTerm);
873
+ return {
874
+ content: [{
875
+ type: 'text',
876
+ text: JSON.stringify(results.map(k => ({
877
+ id: k.id,
878
+ name: k.name,
879
+ category: k.category,
880
+ description: k.description
881
+ })), null, 2)
882
+ }]
883
+ };
884
+ }
885
+ // Configuration Tools
886
+ case 'save_configuration': {
887
+ const { name: configName } = args;
888
+ if (!serverState.activeProjectType) {
889
+ return { content: [{ type: 'text', text: 'Error: No project type selected.' }] };
890
+ }
891
+ const selectedRules = serverState.activeConfiguration?.selectedRules || [];
892
+ const selectedKnowledge = serverState.activeConfiguration?.selectedKnowledge || [];
893
+ const config = persistence.createConfiguration(configName, serverState.activeProjectType, selectedRules, selectedKnowledge);
894
+ serverState.activeConfiguration = config;
895
+ return {
896
+ content: [{
897
+ type: 'text',
898
+ text: JSON.stringify({
899
+ success: true,
900
+ message: `Configuration "${configName}" saved`,
901
+ configurationId: config.id
902
+ }, null, 2)
903
+ }]
904
+ };
905
+ }
906
+ case 'load_configuration': {
907
+ const { configurationId } = args;
908
+ const config = persistence.setActiveConfiguration(configurationId);
909
+ if (!config) {
910
+ return { content: [{ type: 'text', text: `Error: Configuration "${configurationId}" not found` }] };
911
+ }
912
+ serverState.activeConfiguration = config;
913
+ serverState.activeProjectType = config.projectType;
914
+ serverState.loadedRules = rulesProvider.getRulesForProject(config.projectType);
915
+ serverState.loadedKnowledge = knowledgeProvider.getKnowledgeForProject(config.projectType);
916
+ return {
917
+ content: [{
918
+ type: 'text',
919
+ text: JSON.stringify({
920
+ success: true,
921
+ message: `Configuration "${config.name}" loaded`,
922
+ projectType: config.projectType,
923
+ selectedRules: config.selectedRules.length,
924
+ selectedKnowledge: config.selectedKnowledge.length
925
+ }, null, 2)
926
+ }]
927
+ };
928
+ }
929
+ case 'list_configurations': {
930
+ const configs = persistence.getAllConfigurations();
931
+ const active = persistence.getActiveConfiguration();
932
+ return {
933
+ content: [{
934
+ type: 'text',
935
+ text: JSON.stringify({
936
+ activeConfigurationId: active?.id || null,
937
+ configurations: configs.map(c => ({
938
+ id: c.id,
939
+ name: c.name,
940
+ projectType: c.projectType,
941
+ selectedRules: c.selectedRules.length,
942
+ selectedKnowledge: c.selectedKnowledge.length,
943
+ updatedAt: c.updatedAt
944
+ }))
945
+ }, null, 2)
946
+ }]
947
+ };
948
+ }
949
+ case 'delete_configuration': {
950
+ const { configurationId } = args;
951
+ const deleted = persistence.deleteConfiguration(configurationId);
952
+ return {
953
+ content: [{
954
+ type: 'text',
955
+ text: JSON.stringify({
956
+ success: deleted,
957
+ message: deleted ? 'Configuration deleted' : 'Configuration not found'
958
+ }, null, 2)
959
+ }]
960
+ };
961
+ }
962
+ case 'export_configuration': {
963
+ const { configurationId } = args;
964
+ const exported = persistence.exportConfiguration(configurationId);
965
+ if (!exported) {
966
+ return { content: [{ type: 'text', text: 'Error: Configuration not found' }] };
967
+ }
968
+ return { content: [{ type: 'text', text: exported }] };
969
+ }
970
+ case 'import_configuration': {
971
+ const { jsonConfig } = args;
972
+ const imported = persistence.importConfiguration(jsonConfig);
973
+ if (!imported) {
974
+ return { content: [{ type: 'text', text: 'Error: Invalid configuration JSON' }] };
975
+ }
976
+ return {
977
+ content: [{
978
+ type: 'text',
979
+ text: JSON.stringify({
980
+ success: true,
981
+ message: 'Configuration imported successfully',
982
+ configurationId: imported.id,
983
+ name: imported.name
984
+ }, null, 2)
985
+ }]
986
+ };
987
+ }
988
+ // Context Tools
989
+ case 'get_full_context': {
990
+ if (!serverState.activeProjectType) {
991
+ return { content: [{ type: 'text', text: 'Error: No project type selected.' }] };
992
+ }
993
+ const selectedRules = serverState.activeConfiguration?.selectedRules || [];
994
+ const selectedKnowledge = serverState.activeConfiguration?.selectedKnowledge || [];
995
+ const rulesContent = rulesProvider.getCombinedRulesContent(selectedRules);
996
+ const knowledgeContent = knowledgeProvider.getCombinedKnowledgeContent(selectedKnowledge);
997
+ const fullContext = `# Project Context: ${SUPPORTED_PROJECTS[serverState.activeProjectType].name}
998
+
999
+ ## Rules and Guidelines
1000
+
1001
+ ${rulesContent || 'No rules selected.'}
1002
+
1003
+ ---
1004
+
1005
+ ## Knowledge Base
1006
+
1007
+ ${knowledgeContent || 'No knowledge files selected.'}
1008
+ `;
1009
+ return { content: [{ type: 'text', text: fullContext }] };
1010
+ }
1011
+ case 'add_custom_rule': {
1012
+ const { name: ruleName, category, content, description } = args;
1013
+ if (!serverState.activeConfiguration) {
1014
+ return { content: [{ type: 'text', text: 'Error: No active configuration. Save a configuration first.' }] };
1015
+ }
1016
+ const rule = persistence.addCustomRule(serverState.activeConfiguration.id, {
1017
+ name: ruleName,
1018
+ category: category,
1019
+ content,
1020
+ description: description || '',
1021
+ enabled: true,
1022
+ priority: 50
1023
+ });
1024
+ if (!rule) {
1025
+ return { content: [{ type: 'text', text: 'Error: Could not add custom rule' }] };
1026
+ }
1027
+ return {
1028
+ content: [{
1029
+ type: 'text',
1030
+ text: JSON.stringify({
1031
+ success: true,
1032
+ message: 'Custom rule added',
1033
+ ruleId: rule.id
1034
+ }, null, 2)
1035
+ }]
1036
+ };
1037
+ }
1038
+ // ==================== DYNAMIC RULE MANAGEMENT ====================
1039
+ case 'create_rule': {
1040
+ const { projectType, name: ruleName, category, content, description } = args;
1041
+ const rule = ruleManager.createUserRule(projectType, category, ruleName, content, description || '');
1042
+ return {
1043
+ content: [{
1044
+ type: 'text',
1045
+ text: JSON.stringify({
1046
+ success: true,
1047
+ message: `Rule "${ruleName}" created successfully`,
1048
+ ruleId: rule.id,
1049
+ projectType,
1050
+ category
1051
+ }, null, 2)
1052
+ }]
1053
+ };
1054
+ }
1055
+ case 'create_rule_from_template': {
1056
+ const { projectType, templateId, name: ruleName, category, description, language } = args;
1057
+ const rule = ruleManager.createRuleFromTemplate(projectType, category, templateId, ruleName, description, language || 'typescript');
1058
+ if (!rule) {
1059
+ return { content: [{ type: 'text', text: `Error: Template "${templateId}" not found` }] };
1060
+ }
1061
+ return {
1062
+ content: [{
1063
+ type: 'text',
1064
+ text: JSON.stringify({
1065
+ success: true,
1066
+ message: `Rule "${ruleName}" created from template "${templateId}"`,
1067
+ ruleId: rule.id,
1068
+ hint: 'Use get_rule to view and customize the generated content'
1069
+ }, null, 2)
1070
+ }]
1071
+ };
1072
+ }
1073
+ case 'list_rule_templates': {
1074
+ const templates = ruleManager.listTemplates();
1075
+ return {
1076
+ content: [{
1077
+ type: 'text',
1078
+ text: JSON.stringify({
1079
+ templates,
1080
+ usage: 'Use create_rule_from_template with templateId to create a new rule'
1081
+ }, null, 2)
1082
+ }]
1083
+ };
1084
+ }
1085
+ case 'get_rule_template': {
1086
+ const { templateId } = args;
1087
+ const content = ruleManager.getTemplateContent(templateId);
1088
+ if (!content) {
1089
+ return { content: [{ type: 'text', text: `Error: Template "${templateId}" not found` }] };
1090
+ }
1091
+ return { content: [{ type: 'text', text: content }] };
1092
+ }
1093
+ case 'update_rule': {
1094
+ const { ruleId, ...updates } = args;
1095
+ const updated = ruleManager.updateUserRule(ruleId, updates);
1096
+ if (!updated) {
1097
+ return { content: [{ type: 'text', text: `Error: Rule "${ruleId}" not found or is not a user-created rule` }] };
1098
+ }
1099
+ return {
1100
+ content: [{
1101
+ type: 'text',
1102
+ text: JSON.stringify({
1103
+ success: true,
1104
+ message: 'Rule updated successfully',
1105
+ rule: {
1106
+ id: updated.id,
1107
+ name: updated.name,
1108
+ enabled: updated.enabled
1109
+ }
1110
+ }, null, 2)
1111
+ }]
1112
+ };
1113
+ }
1114
+ case 'delete_rule': {
1115
+ const { ruleId } = args;
1116
+ const deleted = ruleManager.deleteUserRule(ruleId);
1117
+ if (!deleted) {
1118
+ return { content: [{ type: 'text', text: `Error: Rule "${ruleId}" not found or is not a user-created rule` }] };
1119
+ }
1120
+ return {
1121
+ content: [{
1122
+ type: 'text',
1123
+ text: JSON.stringify({
1124
+ success: true,
1125
+ message: 'Rule deleted successfully'
1126
+ }, null, 2)
1127
+ }]
1128
+ };
1129
+ }
1130
+ case 'list_user_rules': {
1131
+ const { projectType } = args;
1132
+ const rules = ruleManager.getUserRules(projectType);
1133
+ return {
1134
+ content: [{
1135
+ type: 'text',
1136
+ text: JSON.stringify({
1137
+ projectType,
1138
+ count: rules.length,
1139
+ rules: rules.map(r => ({
1140
+ id: r.id,
1141
+ name: r.name,
1142
+ category: r.category,
1143
+ description: r.description,
1144
+ enabled: r.enabled
1145
+ }))
1146
+ }, null, 2)
1147
+ }]
1148
+ };
1149
+ }
1150
+ case 'export_user_rules': {
1151
+ const exported = ruleManager.exportAllUserRules();
1152
+ return { content: [{ type: 'text', text: exported }] };
1153
+ }
1154
+ case 'import_user_rules': {
1155
+ const { jsonRules } = args;
1156
+ const count = ruleManager.importUserRules(jsonRules);
1157
+ return {
1158
+ content: [{
1159
+ type: 'text',
1160
+ text: JSON.stringify({
1161
+ success: count > 0,
1162
+ message: count > 0 ? `${count} rules imported successfully` : 'No rules imported (invalid JSON or empty)',
1163
+ importedCount: count
1164
+ }, null, 2)
1165
+ }]
1166
+ };
1167
+ }
1168
+ // ==================== WEB DOCUMENTATION ====================
1169
+ case 'fetch_web_docs': {
1170
+ const { url, projectType, category, tags } = args;
1171
+ try {
1172
+ const doc = await webDocs.fetchWebDocumentation(url, { projectType, category, tags });
1173
+ return {
1174
+ content: [{
1175
+ type: 'text',
1176
+ text: JSON.stringify({
1177
+ success: true,
1178
+ message: 'Documentation fetched successfully',
1179
+ document: {
1180
+ id: doc.id,
1181
+ title: doc.title,
1182
+ url: doc.url,
1183
+ summary: doc.summary,
1184
+ contentLength: doc.content.length,
1185
+ fetchedAt: doc.fetchedAt
1186
+ },
1187
+ hint: 'Use get_web_doc to retrieve the full content'
1188
+ }, null, 2)
1189
+ }]
1190
+ };
1191
+ }
1192
+ catch (error) {
1193
+ return {
1194
+ content: [{
1195
+ type: 'text',
1196
+ text: `Error fetching documentation: ${error instanceof Error ? error.message : String(error)}`
1197
+ }]
1198
+ };
1199
+ }
1200
+ }
1201
+ case 'fetch_multiple_docs': {
1202
+ const { urls, projectType } = args;
1203
+ const results = await webDocs.fetchMultipleDocuments(urls, { projectType });
1204
+ return {
1205
+ content: [{
1206
+ type: 'text',
1207
+ text: JSON.stringify({
1208
+ success: results.successful.length > 0,
1209
+ fetched: results.successful.length,
1210
+ failed: results.failed.length,
1211
+ documents: results.successful.map(d => ({
1212
+ id: d.id,
1213
+ title: d.title,
1214
+ url: d.url
1215
+ })),
1216
+ errors: results.failed
1217
+ }, null, 2)
1218
+ }]
1219
+ };
1220
+ }
1221
+ case 'get_web_doc': {
1222
+ const { idOrUrl } = args;
1223
+ let doc = webDocs.getWebDocumentByUrl(idOrUrl) || webDocs.getWebDocumentById(idOrUrl);
1224
+ if (!doc) {
1225
+ return { content: [{ type: 'text', text: `Error: Document "${idOrUrl}" not found. Use fetch_web_docs first.` }] };
1226
+ }
1227
+ return {
1228
+ content: [{
1229
+ type: 'text',
1230
+ text: `# ${doc.title}\n\n**Source:** ${doc.url}\n**Fetched:** ${doc.fetchedAt}\n\n---\n\n${doc.content}`
1231
+ }]
1232
+ };
1233
+ }
1234
+ case 'search_web_docs': {
1235
+ const { query } = args;
1236
+ const results = webDocs.searchWebDocuments(query);
1237
+ return {
1238
+ content: [{
1239
+ type: 'text',
1240
+ text: JSON.stringify({
1241
+ query,
1242
+ resultsCount: results.length,
1243
+ results: results.map(d => ({
1244
+ id: d.id,
1245
+ title: d.title,
1246
+ url: d.url,
1247
+ summary: d.summary.substring(0, 200) + '...'
1248
+ }))
1249
+ }, null, 2)
1250
+ }]
1251
+ };
1252
+ }
1253
+ case 'list_web_docs': {
1254
+ const docs = webDocs.listCachedDocuments();
1255
+ return {
1256
+ content: [{
1257
+ type: 'text',
1258
+ text: JSON.stringify({
1259
+ count: docs.length,
1260
+ documents: docs.map(d => ({
1261
+ id: d.id,
1262
+ title: d.title,
1263
+ url: d.url,
1264
+ projectType: d.projectType,
1265
+ fetchedAt: d.fetchedAt
1266
+ }))
1267
+ }, null, 2)
1268
+ }]
1269
+ };
1270
+ }
1271
+ case 'get_suggested_docs': {
1272
+ const { projectType } = args;
1273
+ const suggestions = webDocs.getSuggestedDocs(projectType);
1274
+ return {
1275
+ content: [{
1276
+ type: 'text',
1277
+ text: JSON.stringify({
1278
+ projectType,
1279
+ suggestions,
1280
+ hint: 'Use fetch_web_docs or fetch_multiple_docs to load these documents'
1281
+ }, null, 2)
1282
+ }]
1283
+ };
1284
+ }
1285
+ case 'remove_web_doc': {
1286
+ const { idOrUrl } = args;
1287
+ const removed = webDocs.removeFromCache(idOrUrl);
1288
+ return {
1289
+ content: [{
1290
+ type: 'text',
1291
+ text: JSON.stringify({
1292
+ success: removed,
1293
+ message: removed ? 'Document removed from cache' : 'Document not found'
1294
+ }, null, 2)
1295
+ }]
1296
+ };
1297
+ }
1298
+ // ==================== CURSOR DIRECTORY TOOLS ====================
1299
+ case 'browse_cursor_directory': {
1300
+ const { category } = args;
1301
+ const rules = await cursorDirectory.browseCursorDirectoryCategory(category);
1302
+ return {
1303
+ content: [{
1304
+ type: 'text',
1305
+ text: JSON.stringify({
1306
+ category,
1307
+ count: rules.length,
1308
+ rules: rules.map(r => ({
1309
+ slug: r.slug,
1310
+ title: r.title,
1311
+ description: r.description,
1312
+ tags: r.tags,
1313
+ url: r.url
1314
+ })),
1315
+ hint: 'Use get_cursor_directory_rule with a slug to see the full content'
1316
+ }, null, 2)
1317
+ }]
1318
+ };
1319
+ }
1320
+ case 'search_cursor_directory': {
1321
+ const { query } = args;
1322
+ const results = await cursorDirectory.searchCursorDirectory(query);
1323
+ return {
1324
+ content: [{
1325
+ type: 'text',
1326
+ text: JSON.stringify({
1327
+ query,
1328
+ count: results.length,
1329
+ results: results.map(r => ({
1330
+ slug: r.slug,
1331
+ title: r.title,
1332
+ description: r.description,
1333
+ category: r.category,
1334
+ tags: r.tags,
1335
+ url: r.url
1336
+ }))
1337
+ }, null, 2)
1338
+ }]
1339
+ };
1340
+ }
1341
+ case 'get_cursor_directory_rule': {
1342
+ const { slug, category = 'general' } = args;
1343
+ const rule = await cursorDirectory.fetchCursorDirectoryRule(slug, category);
1344
+ if (!rule) {
1345
+ return { content: [{ type: 'text', text: `Error: Could not fetch rule "${slug}" from cursor.directory` }] };
1346
+ }
1347
+ return {
1348
+ content: [{
1349
+ type: 'text',
1350
+ text: `# ${rule.title}\n\n**Source:** ${rule.url}\n**Category:** ${rule.category}\n**Tags:** ${rule.tags.join(', ')}\n\n---\n\n${rule.content}`
1351
+ }]
1352
+ };
1353
+ }
1354
+ case 'list_cursor_directory_categories': {
1355
+ const categories = cursorDirectory.getCursorDirectoryCategories();
1356
+ return {
1357
+ content: [{
1358
+ type: 'text',
1359
+ text: JSON.stringify({
1360
+ count: categories.length,
1361
+ categories,
1362
+ hint: 'Use browse_cursor_directory with a category to see available rules'
1363
+ }, null, 2)
1364
+ }]
1365
+ };
1366
+ }
1367
+ case 'get_popular_cursor_rules': {
1368
+ const rules = await cursorDirectory.getPopularCursorDirectoryRules();
1369
+ return {
1370
+ content: [{
1371
+ type: 'text',
1372
+ text: JSON.stringify({
1373
+ count: rules.length,
1374
+ rules: rules.map(r => ({
1375
+ slug: r.slug,
1376
+ title: r.title,
1377
+ description: r.description,
1378
+ category: r.category,
1379
+ tags: r.tags,
1380
+ url: r.url
1381
+ })),
1382
+ hint: 'Use get_cursor_directory_rule with a slug to see full content, or import_cursor_directory_rule to import'
1383
+ }, null, 2)
1384
+ }]
1385
+ };
1386
+ }
1387
+ case 'import_cursor_directory_rule': {
1388
+ const { slug, projectType, category = 'best-practices' } = args;
1389
+ // Fetch the rule from cursor.directory
1390
+ const cursorRule = await cursorDirectory.fetchCursorDirectoryRule(slug, category);
1391
+ if (!cursorRule) {
1392
+ return { content: [{ type: 'text', text: `Error: Could not fetch rule "${slug}" from cursor.directory` }] };
1393
+ }
1394
+ // Format the content for import
1395
+ const formattedContent = cursorDirectory.formatRuleForImport(cursorRule);
1396
+ // Create a local user rule
1397
+ const userRule = ruleManager.createUserRule(projectType, category, `cursor-${slug}`, formattedContent, cursorRule.description);
1398
+ return {
1399
+ content: [{
1400
+ type: 'text',
1401
+ text: JSON.stringify({
1402
+ success: true,
1403
+ message: `Rule "${cursorRule.title}" imported successfully`,
1404
+ rule: {
1405
+ id: userRule.id,
1406
+ name: userRule.name,
1407
+ category: userRule.category,
1408
+ source: cursorRule.url
1409
+ },
1410
+ hint: 'The rule is now available in your local rules. Use list_user_rules to see all imported rules.'
1411
+ }, null, 2)
1412
+ }]
1413
+ };
1414
+ }
1415
+ default:
1416
+ return { content: [{ type: 'text', text: `Unknown tool: ${name}` }] };
1417
+ }
1418
+ }
1419
+ catch (error) {
1420
+ return {
1421
+ content: [{
1422
+ type: 'text',
1423
+ text: `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`
1424
+ }]
1425
+ };
1426
+ }
1427
+ });
1428
+ // ==================== RESOURCES ====================
1429
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1430
+ const resources = [];
1431
+ // Add resources for each project type with data
1432
+ const projectsWithRules = rulesProvider.getProjectTypesWithRules();
1433
+ const projectsWithKnowledge = knowledgeProvider.getProjectTypesWithKnowledge();
1434
+ for (const pt of projectsWithRules) {
1435
+ resources.push({
1436
+ uri: `rules://${pt}/all`,
1437
+ name: `${SUPPORTED_PROJECTS[pt]?.name || pt} - All Rules`,
1438
+ description: `All coding rules for ${pt} projects`,
1439
+ mimeType: 'text/markdown'
1440
+ });
1441
+ // Add user rules as resources
1442
+ const userRules = ruleManager.getUserRules(pt);
1443
+ if (userRules.length > 0) {
1444
+ resources.push({
1445
+ uri: `user-rules://${pt}/all`,
1446
+ name: `${SUPPORTED_PROJECTS[pt]?.name || pt} - User Rules`,
1447
+ description: `User-created rules for ${pt} projects`,
1448
+ mimeType: 'text/markdown'
1449
+ });
1450
+ }
1451
+ }
1452
+ for (const pt of projectsWithKnowledge) {
1453
+ resources.push({
1454
+ uri: `knowledge://${pt}/all`,
1455
+ name: `${SUPPORTED_PROJECTS[pt]?.name || pt} - Knowledge Base`,
1456
+ description: `Knowledge base for ${pt} projects`,
1457
+ mimeType: 'text/markdown'
1458
+ });
1459
+ }
1460
+ // Add cached web documents
1461
+ const webDocsList = webDocs.listCachedDocuments();
1462
+ for (const doc of webDocsList) {
1463
+ resources.push({
1464
+ uri: `web-doc://${doc.id}`,
1465
+ name: `Web: ${doc.title}`,
1466
+ description: `Fetched from ${doc.url}`,
1467
+ mimeType: 'text/markdown'
1468
+ });
1469
+ }
1470
+ // Active context resource
1471
+ resources.push({
1472
+ uri: 'context://active',
1473
+ name: 'Active Context',
1474
+ description: 'The currently active project context with selected rules and knowledge',
1475
+ mimeType: 'text/markdown'
1476
+ });
1477
+ // Recurso de templates
1478
+ resources.push({
1479
+ uri: 'templates://rules',
1480
+ name: 'Rule Templates',
1481
+ description: 'Available templates for creating new rules',
1482
+ mimeType: 'text/markdown'
1483
+ });
1484
+ return { resources };
1485
+ });
1486
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1487
+ const { uri } = request.params;
1488
+ try {
1489
+ if (uri.startsWith('rules://')) {
1490
+ const parts = uri.replace('rules://', '').split('/');
1491
+ const projectType = parts[0];
1492
+ const rules = rulesProvider.getRulesForProject(projectType);
1493
+ const content = rules.map(r => `# ${r.name}\n\n${r.content}`).join('\n\n---\n\n');
1494
+ return {
1495
+ contents: [{
1496
+ uri,
1497
+ mimeType: 'text/markdown',
1498
+ text: content || 'No rules available for this project type.'
1499
+ }]
1500
+ };
1501
+ }
1502
+ if (uri.startsWith('user-rules://')) {
1503
+ const parts = uri.replace('user-rules://', '').split('/');
1504
+ const projectType = parts[0];
1505
+ const rules = ruleManager.getUserRules(projectType);
1506
+ const content = rules.map(r => `# ${r.name}\n\n**Category:** ${r.category}\n**Description:** ${r.description}\n\n${r.content}`).join('\n\n---\n\n');
1507
+ return {
1508
+ contents: [{
1509
+ uri,
1510
+ mimeType: 'text/markdown',
1511
+ text: content || 'No user rules available for this project type.'
1512
+ }]
1513
+ };
1514
+ }
1515
+ if (uri.startsWith('web-doc://')) {
1516
+ const docId = uri.replace('web-doc://', '');
1517
+ const doc = webDocs.getWebDocumentById(docId);
1518
+ if (!doc) {
1519
+ return {
1520
+ contents: [{
1521
+ uri,
1522
+ mimeType: 'text/plain',
1523
+ text: 'Web document not found in cache.'
1524
+ }]
1525
+ };
1526
+ }
1527
+ return {
1528
+ contents: [{
1529
+ uri,
1530
+ mimeType: 'text/markdown',
1531
+ text: `# ${doc.title}\n\n**Source:** ${doc.url}\n**Fetched:** ${doc.fetchedAt}\n\n---\n\n${doc.content}`
1532
+ }]
1533
+ };
1534
+ }
1535
+ if (uri === 'templates://rules') {
1536
+ const templates = ruleManager.listTemplates();
1537
+ let content = '# Available Rule Templates\n\n';
1538
+ for (const t of templates) {
1539
+ content += `## ${t.name}\n\nTemplate ID: \`${t.id}\`\n\n`;
1540
+ const templateContent = ruleManager.getTemplateContent(t.id);
1541
+ if (templateContent) {
1542
+ content += '```markdown\n' + templateContent + '\n```\n\n---\n\n';
1543
+ }
1544
+ }
1545
+ return {
1546
+ contents: [{
1547
+ uri,
1548
+ mimeType: 'text/markdown',
1549
+ text: content
1550
+ }]
1551
+ };
1552
+ }
1553
+ if (uri.startsWith('knowledge://')) {
1554
+ const parts = uri.replace('knowledge://', '').split('/');
1555
+ const projectType = parts[0];
1556
+ const knowledge = knowledgeProvider.getKnowledgeForProject(projectType);
1557
+ const content = knowledge.map(k => `# ${k.name}\n\n${k.content}`).join('\n\n---\n\n');
1558
+ return {
1559
+ contents: [{
1560
+ uri,
1561
+ mimeType: 'text/markdown',
1562
+ text: content || 'No knowledge available for this project type.'
1563
+ }]
1564
+ };
1565
+ }
1566
+ if (uri === 'context://active') {
1567
+ if (!serverState.activeProjectType) {
1568
+ return {
1569
+ contents: [{
1570
+ uri,
1571
+ mimeType: 'text/markdown',
1572
+ text: 'No active context. Use select_project_type tool to activate a project type.'
1573
+ }]
1574
+ };
1575
+ }
1576
+ const selectedRules = serverState.activeConfiguration?.selectedRules || [];
1577
+ const selectedKnowledge = serverState.activeConfiguration?.selectedKnowledge || [];
1578
+ const rulesContent = rulesProvider.getCombinedRulesContent(selectedRules);
1579
+ const knowledgeContent = knowledgeProvider.getCombinedKnowledgeContent(selectedKnowledge);
1580
+ // Also include web docs and user rules
1581
+ const userRules = ruleManager.getUserRules(serverState.activeProjectType);
1582
+ const userRulesContent = userRules.length > 0
1583
+ ? userRules.map(r => `### ${r.name}\n\n${r.content}`).join('\n\n')
1584
+ : '';
1585
+ const webDocsList = webDocs.listCachedDocuments().filter(d => d.projectType === serverState.activeProjectType);
1586
+ const webDocsContent = webDocsList.length > 0
1587
+ ? webDocsList.map(d => {
1588
+ const fullDoc = webDocs.getWebDocumentById(d.id);
1589
+ return `### ${d.title}\n\n${fullDoc?.content || d.summary}`;
1590
+ }).join('\n\n')
1591
+ : '';
1592
+ const fullContext = `# Active Context: ${SUPPORTED_PROJECTS[serverState.activeProjectType].name}
1593
+
1594
+ ## Selected Rules
1595
+ ${rulesContent || 'No rules selected.'}
1596
+
1597
+ ## User Rules
1598
+ ${userRulesContent || 'No user rules.'}
1599
+
1600
+ ## Selected Knowledge
1601
+ ${knowledgeContent || 'No knowledge selected.'}
1602
+
1603
+ ## Web Documentation
1604
+ ${webDocsContent || 'No web documentation loaded.'}
1605
+ `;
1606
+ return {
1607
+ contents: [{
1608
+ uri,
1609
+ mimeType: 'text/markdown',
1610
+ text: fullContext
1611
+ }]
1612
+ };
1613
+ }
1614
+ return {
1615
+ contents: [{
1616
+ uri,
1617
+ mimeType: 'text/plain',
1618
+ text: 'Resource not found'
1619
+ }]
1620
+ };
1621
+ }
1622
+ catch (error) {
1623
+ return {
1624
+ contents: [{
1625
+ uri,
1626
+ mimeType: 'text/plain',
1627
+ text: `Error reading resource: ${error instanceof Error ? error.message : String(error)}`
1628
+ }]
1629
+ };
1630
+ }
1631
+ });
1632
+ // ==================== PROMPTS ====================
1633
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
1634
+ return {
1635
+ prompts: [
1636
+ {
1637
+ name: 'setup_project',
1638
+ description: 'Initialize context for a new project',
1639
+ arguments: [
1640
+ {
1641
+ name: 'projectType',
1642
+ description: 'Type of project to set up',
1643
+ required: true
1644
+ }
1645
+ ]
1646
+ },
1647
+ {
1648
+ name: 'code_review',
1649
+ description: 'Review code following the active rules and best practices',
1650
+ arguments: [
1651
+ {
1652
+ name: 'code',
1653
+ description: 'Code to review',
1654
+ required: true
1655
+ }
1656
+ ]
1657
+ },
1658
+ {
1659
+ name: 'apply_patterns',
1660
+ description: 'Apply architecture patterns from knowledge base',
1661
+ arguments: [
1662
+ {
1663
+ name: 'task',
1664
+ description: 'Task or feature to implement',
1665
+ required: true
1666
+ }
1667
+ ]
1668
+ }
1669
+ ]
1670
+ };
1671
+ });
1672
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1673
+ const { name, arguments: promptArgs } = request.params;
1674
+ switch (name) {
1675
+ case 'setup_project': {
1676
+ const projectType = promptArgs?.projectType;
1677
+ const project = SUPPORTED_PROJECTS[projectType];
1678
+ if (!project) {
1679
+ return {
1680
+ messages: [{
1681
+ role: 'user',
1682
+ content: { type: 'text', text: 'Unknown project type. Use list_project_types to see available options.' }
1683
+ }]
1684
+ };
1685
+ }
1686
+ return {
1687
+ messages: [{
1688
+ role: 'user',
1689
+ content: {
1690
+ type: 'text',
1691
+ text: `Set up development context for a ${project.name} project.
1692
+
1693
+ Project Details:
1694
+ - Languages: ${project.languages.join(', ')}
1695
+ - Frameworks: ${project.frameworks.join(', ')}
1696
+
1697
+ Please:
1698
+ 1. Use select_project_type tool with "${projectType}"
1699
+ 2. List available rules with list_rules
1700
+ 3. List knowledge base with list_knowledge
1701
+ 4. Select relevant rules and knowledge
1702
+ 5. Save the configuration for future use`
1703
+ }
1704
+ }]
1705
+ };
1706
+ }
1707
+ case 'code_review': {
1708
+ const code = promptArgs?.code || '';
1709
+ const projectName = serverState.activeProjectType
1710
+ ? SUPPORTED_PROJECTS[serverState.activeProjectType].name
1711
+ : 'the current project';
1712
+ const rules = serverState.loadedRules
1713
+ .filter(r => serverState.activeConfiguration?.selectedRules.includes(r.id))
1714
+ .map(r => `- ${r.name}: ${r.description}`)
1715
+ .join('\n');
1716
+ return {
1717
+ messages: [{
1718
+ role: 'user',
1719
+ content: {
1720
+ type: 'text',
1721
+ text: `Review the following code for ${projectName}.
1722
+
1723
+ Active Rules:
1724
+ ${rules || 'No specific rules selected. Using general best practices.'}
1725
+
1726
+ Code to Review:
1727
+ \`\`\`
1728
+ ${code}
1729
+ \`\`\`
1730
+
1731
+ Please analyze for:
1732
+ 1. Compliance with coding standards
1733
+ 2. Security issues
1734
+ 3. Performance concerns
1735
+ 4. Best practices
1736
+ 5. Suggested improvements`
1737
+ }
1738
+ }]
1739
+ };
1740
+ }
1741
+ case 'apply_patterns': {
1742
+ const task = promptArgs?.task || '';
1743
+ const projectName = serverState.activeProjectType
1744
+ ? SUPPORTED_PROJECTS[serverState.activeProjectType].name
1745
+ : 'the current project';
1746
+ const knowledge = serverState.loadedKnowledge
1747
+ .filter(k => serverState.activeConfiguration?.selectedKnowledge.includes(k.id))
1748
+ .map(k => `### ${k.name}\n${k.description}`)
1749
+ .join('\n\n');
1750
+ return {
1751
+ messages: [{
1752
+ role: 'user',
1753
+ content: {
1754
+ type: 'text',
1755
+ text: `Implement the following task for ${projectName} using established patterns.
1756
+
1757
+ Task: ${task}
1758
+
1759
+ Available Patterns and Knowledge:
1760
+ ${knowledge || 'No specific knowledge selected. Using general patterns.'}
1761
+
1762
+ Please:
1763
+ 1. Analyze the task requirements
1764
+ 2. Suggest appropriate patterns
1765
+ 3. Provide implementation guidance
1766
+ 4. Include code examples where helpful`
1767
+ }
1768
+ }]
1769
+ };
1770
+ }
1771
+ default:
1772
+ return {
1773
+ messages: [{
1774
+ role: 'user',
1775
+ content: { type: 'text', text: `Unknown prompt: ${name}` }
1776
+ }]
1777
+ };
1778
+ }
1779
+ });
1780
+ // ==================== MAIN ====================
1781
+ async function main() {
1782
+ // Load active configuration if exists
1783
+ const activeConfig = persistence.getActiveConfiguration();
1784
+ if (activeConfig) {
1785
+ serverState.activeConfiguration = activeConfig;
1786
+ serverState.activeProjectType = activeConfig.projectType;
1787
+ serverState.loadedRules = rulesProvider.getRulesForProject(activeConfig.projectType);
1788
+ serverState.loadedKnowledge = knowledgeProvider.getKnowledgeForProject(activeConfig.projectType);
1789
+ }
1790
+ // Start STDIO transport
1791
+ const transport = new StdioServerTransport();
1792
+ await server.connect(transport);
1793
+ console.error('StackGuide MCP Server started');
1794
+ }
1795
+ main().catch((error) => {
1796
+ console.error('Fatal error:', error);
1797
+ process.exit(1);
1798
+ });
1799
+ //# sourceMappingURL=index.js.map