@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.
- package/LICENSE +674 -0
- package/README.md +453 -0
- package/data/knowledge/python-django/architecture/architecture-patterns.md +201 -0
- package/data/knowledge/python-django/common-issues/common-issues.md +181 -0
- package/data/knowledge/python-django/patterns/drf-patterns.md +133 -0
- package/data/knowledge/react-node/architecture/node-architecture.md +257 -0
- package/data/knowledge/react-node/common-issues/common-issues.md +262 -0
- package/data/knowledge/react-node/patterns/react-patterns.md +244 -0
- package/data/rules/python-django/best-practices/django-best-practices.md +120 -0
- package/data/rules/python-django/coding-standards/django-standards.md +104 -0
- package/data/rules/python-django/security/security-guidelines.md +146 -0
- package/data/rules/react-node/best-practices/react-best-practices.md +195 -0
- package/data/rules/react-node/coding-standards/node-standards.md +192 -0
- package/data/rules/react-node/coding-standards/react-standards.md +155 -0
- package/data/rules/react-node/security/security-guidelines.md +228 -0
- package/dist/config/persistence.d.ts +15 -0
- package/dist/config/persistence.d.ts.map +1 -0
- package/dist/config/persistence.js +171 -0
- package/dist/config/persistence.js.map +1 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +116 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1799 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/knowledgeProvider.d.ts +10 -0
- package/dist/resources/knowledgeProvider.d.ts.map +1 -0
- package/dist/resources/knowledgeProvider.js +130 -0
- package/dist/resources/knowledgeProvider.js.map +1 -0
- package/dist/resources/rulesProvider.d.ts +10 -0
- package/dist/resources/rulesProvider.d.ts.map +1 -0
- package/dist/resources/rulesProvider.js +135 -0
- package/dist/resources/rulesProvider.js.map +1 -0
- package/dist/services/cursorDirectory.d.ts +55 -0
- package/dist/services/cursorDirectory.d.ts.map +1 -0
- package/dist/services/cursorDirectory.js +367 -0
- package/dist/services/cursorDirectory.js.map +1 -0
- package/dist/services/ruleManager.d.ts +18 -0
- package/dist/services/ruleManager.d.ts.map +1 -0
- package/dist/services/ruleManager.js +382 -0
- package/dist/services/ruleManager.js.map +1 -0
- package/dist/services/webDocumentation.d.ts +41 -0
- package/dist/services/webDocumentation.d.ts.map +1 -0
- package/dist/services/webDocumentation.js +237 -0
- package/dist/services/webDocumentation.js.map +1 -0
- 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
|