codingbuddy-rules 4.5.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ai-rules/adapters/aider.md +374 -0
- package/.ai-rules/adapters/antigravity.md +6 -6
- package/.ai-rules/adapters/claude-code.md +68 -4
- package/.ai-rules/adapters/codex.md +5 -5
- package/.ai-rules/adapters/cursor.md +2 -2
- package/.ai-rules/adapters/kiro.md +8 -8
- package/.ai-rules/adapters/opencode.md +7 -7
- package/.ai-rules/adapters/q.md +2 -2
- package/.ai-rules/adapters/windsurf.md +395 -0
- package/.ai-rules/agents/README.md +66 -16
- package/.ai-rules/agents/accessibility-specialist.json +8 -1
- package/.ai-rules/agents/act-mode.json +8 -1
- package/.ai-rules/agents/agent-architect.json +14 -7
- package/.ai-rules/agents/ai-ml-engineer.json +7 -0
- package/.ai-rules/agents/architecture-specialist.json +7 -0
- package/.ai-rules/agents/auto-mode.json +10 -2
- package/.ai-rules/agents/backend-developer.json +7 -0
- package/.ai-rules/agents/code-quality-specialist.json +7 -0
- package/.ai-rules/agents/code-reviewer.json +86 -64
- package/.ai-rules/agents/data-engineer.json +14 -7
- package/.ai-rules/agents/data-scientist.json +16 -9
- package/.ai-rules/agents/devops-engineer.json +7 -0
- package/.ai-rules/agents/documentation-specialist.json +7 -0
- package/.ai-rules/agents/eval-mode.json +30 -19
- package/.ai-rules/agents/event-architecture-specialist.json +7 -0
- package/.ai-rules/agents/frontend-developer.json +7 -0
- package/.ai-rules/agents/i18n-specialist.json +8 -1
- package/.ai-rules/agents/integration-specialist.json +7 -0
- package/.ai-rules/agents/migration-specialist.json +7 -0
- package/.ai-rules/agents/mobile-developer.json +8 -10
- package/.ai-rules/agents/observability-specialist.json +7 -0
- package/.ai-rules/agents/parallel-orchestrator.json +352 -0
- package/.ai-rules/agents/performance-specialist.json +7 -0
- package/.ai-rules/agents/plan-mode.json +9 -1
- package/.ai-rules/agents/plan-reviewer.json +211 -0
- package/.ai-rules/agents/platform-engineer.json +7 -0
- package/.ai-rules/agents/security-engineer.json +15 -8
- package/.ai-rules/agents/security-specialist.json +8 -1
- package/.ai-rules/agents/seo-specialist.json +7 -0
- package/.ai-rules/agents/software-engineer.json +7 -0
- package/.ai-rules/agents/solution-architect.json +17 -10
- package/.ai-rules/agents/systems-developer.json +15 -8
- package/.ai-rules/agents/technical-planner.json +17 -10
- package/.ai-rules/agents/test-engineer.json +13 -6
- package/.ai-rules/agents/test-strategy-specialist.json +7 -0
- package/.ai-rules/agents/tooling-engineer.json +10 -3
- package/.ai-rules/agents/ui-ux-designer.json +7 -0
- package/.ai-rules/keyword-modes.json +4 -4
- package/.ai-rules/rules/clarification-guide.md +14 -14
- package/.ai-rules/rules/core.md +73 -0
- package/.ai-rules/rules/parallel-execution.md +217 -0
- package/.ai-rules/schemas/agent.schema.json +38 -0
- package/.ai-rules/skills/README.md +29 -1
- package/.ai-rules/skills/agent-design/SKILL.md +5 -0
- package/.ai-rules/skills/agent-design/examples/agent-template.json +55 -0
- package/.ai-rules/skills/agent-design/references/expertise-guidelines.md +112 -0
- package/.ai-rules/skills/agent-discussion/SKILL.md +199 -0
- package/.ai-rules/skills/agent-discussion-panel/SKILL.md +448 -0
- package/.ai-rules/skills/api-design/SKILL.md +5 -0
- package/.ai-rules/skills/api-design/examples/error-response.json +159 -0
- package/.ai-rules/skills/api-design/examples/openapi-template.yaml +393 -0
- package/.ai-rules/skills/build-fix/SKILL.md +234 -0
- package/.ai-rules/skills/code-explanation/SKILL.md +4 -0
- package/.ai-rules/skills/context-management/SKILL.md +1 -0
- package/.ai-rules/skills/cost-budget/SKILL.md +348 -0
- package/.ai-rules/skills/cross-repo-issues/SKILL.md +257 -0
- package/.ai-rules/skills/database-migration/SKILL.md +1 -0
- package/.ai-rules/skills/deepsearch/SKILL.md +214 -0
- package/.ai-rules/skills/deployment-checklist/SKILL.md +1 -0
- package/.ai-rules/skills/error-analysis/SKILL.md +1 -0
- package/.ai-rules/skills/finishing-a-development-branch/SKILL.md +281 -0
- package/.ai-rules/skills/frontend-design/SKILL.md +5 -0
- package/.ai-rules/skills/frontend-design/examples/component-template.tsx +203 -0
- package/.ai-rules/skills/frontend-design/references/css-patterns.md +243 -0
- package/.ai-rules/skills/git-master/SKILL.md +358 -0
- package/.ai-rules/skills/incident-response/SKILL.md +1 -0
- package/.ai-rules/skills/legacy-modernization/SKILL.md +1 -0
- package/.ai-rules/skills/mcp-builder/SKILL.md +7 -0
- package/.ai-rules/skills/mcp-builder/examples/resource-example.ts +233 -0
- package/.ai-rules/skills/mcp-builder/examples/tool-example.ts +198 -0
- package/.ai-rules/skills/mcp-builder/references/protocol-spec.md +215 -0
- package/.ai-rules/skills/onboard/SKILL.md +150 -0
- package/.ai-rules/skills/performance-optimization/SKILL.md +3 -0
- package/.ai-rules/skills/plan-and-review/SKILL.md +115 -0
- package/.ai-rules/skills/plan-to-issues/SKILL.md +318 -0
- package/.ai-rules/skills/pr-all-in-one/SKILL.md +15 -13
- package/.ai-rules/skills/pr-all-in-one/configuration-guide.md +7 -7
- package/.ai-rules/skills/pr-all-in-one/pr-templates.md +10 -10
- package/.ai-rules/skills/pr-review/SKILL.md +4 -0
- package/.ai-rules/skills/receiving-code-review/SKILL.md +347 -0
- package/.ai-rules/skills/refactoring/SKILL.md +1 -0
- package/.ai-rules/skills/requesting-code-review/SKILL.md +348 -0
- package/.ai-rules/skills/retrospective/SKILL.md +192 -0
- package/.ai-rules/skills/rule-authoring/SKILL.md +5 -0
- package/.ai-rules/skills/rule-authoring/examples/rule-template.md +142 -0
- package/.ai-rules/skills/rule-authoring/examples/trigger-patterns.md +126 -0
- package/.ai-rules/skills/security-audit/SKILL.md +4 -0
- package/.ai-rules/skills/ship/SKILL.md +242 -0
- package/.ai-rules/skills/skill-creator/SKILL.md +461 -0
- package/.ai-rules/skills/skill-creator/agents/analyzer.md +206 -0
- package/.ai-rules/skills/skill-creator/agents/comparator.md +167 -0
- package/.ai-rules/skills/skill-creator/agents/grader.md +152 -0
- package/.ai-rules/skills/skill-creator/assets/eval_review.html +568 -0
- package/.ai-rules/skills/skill-creator/assets/skill-template.md +43 -0
- package/.ai-rules/skills/skill-creator/eval-viewer/generate_review.py +496 -0
- package/.ai-rules/skills/skill-creator/references/frontmatter-guide.md +632 -0
- package/.ai-rules/skills/skill-creator/references/multi-tool-compat.md +480 -0
- package/.ai-rules/skills/skill-creator/references/schemas.md +784 -0
- package/.ai-rules/skills/skill-creator/scripts/aggregate_benchmark.py +302 -0
- package/.ai-rules/skills/skill-creator/scripts/init_skill.sh +196 -0
- package/.ai-rules/skills/skill-creator/scripts/run_loop.py +327 -0
- package/.ai-rules/skills/systematic-debugging/SKILL.md +1 -0
- package/.ai-rules/skills/tech-debt/SKILL.md +1 -0
- package/.ai-rules/skills/test-coverage-gate/SKILL.md +303 -0
- package/.ai-rules/skills/tmux-master/SKILL.md +491 -0
- package/.ai-rules/skills/using-git-worktrees/SKILL.md +368 -0
- package/.ai-rules/skills/verification-before-completion/SKILL.md +234 -0
- package/.ai-rules/skills/widget-slot-architecture/SKILL.md +6 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/parallel-route-setup.tsx +206 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/widget-component.tsx +250 -0
- package/.ai-rules/skills/writing-plans/SKILL.md +78 -0
- package/bin/cli.js +170 -0
- package/lib/init/detect-stack.js +162 -0
- package/lib/init/generate-config.js +31 -0
- package/lib/init/index.js +86 -0
- package/lib/init/prompt.js +60 -0
- package/lib/init/scaffold.js +67 -0
- package/lib/init/suggest-agent.js +57 -0
- package/package.json +10 -2
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Resource Implementation Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates a complete Resource handler following the MCP specification.
|
|
5
|
+
* Resources are read-only data exposed via URI that the AI can read.
|
|
6
|
+
*
|
|
7
|
+
* Key patterns:
|
|
8
|
+
* - URI-based addressing with scheme://path convention
|
|
9
|
+
* - Resource templates for dynamic URIs
|
|
10
|
+
* - List + Read separation
|
|
11
|
+
* - Proper MIME type handling
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Injectable } from '@nestjs/common';
|
|
15
|
+
|
|
16
|
+
// --- 1. Define Static Resources ---
|
|
17
|
+
|
|
18
|
+
export const staticResources = [
|
|
19
|
+
{
|
|
20
|
+
uri: 'config://project',
|
|
21
|
+
name: 'Project Configuration',
|
|
22
|
+
description: 'Current project settings including tech stack, conventions, and build config',
|
|
23
|
+
mimeType: 'application/json',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
uri: 'docs://changelog',
|
|
27
|
+
name: 'Changelog',
|
|
28
|
+
description: 'Project changelog with recent changes and version history',
|
|
29
|
+
mimeType: 'text/markdown',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// --- 2. Define Resource Templates (Dynamic URIs) ---
|
|
34
|
+
|
|
35
|
+
export const resourceTemplates = [
|
|
36
|
+
{
|
|
37
|
+
uriTemplate: 'rules://{ruleName}',
|
|
38
|
+
name: 'AI Rule',
|
|
39
|
+
description: 'Retrieve a specific AI coding rule by name (e.g., "core", "project")',
|
|
40
|
+
mimeType: 'text/markdown',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
uriTemplate: 'agents://{agentName}',
|
|
44
|
+
name: 'Agent Definition',
|
|
45
|
+
description: 'Retrieve a specialist agent definition by name (e.g., "solution-architect")',
|
|
46
|
+
mimeType: 'application/json',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
uriTemplate: 'skills://{skillName}',
|
|
50
|
+
name: 'Skill Content',
|
|
51
|
+
description: 'Retrieve a skill definition by name (e.g., "mcp-builder", "api-design")',
|
|
52
|
+
mimeType: 'text/markdown',
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// --- 3. Result Types ---
|
|
57
|
+
|
|
58
|
+
interface ResourceContent {
|
|
59
|
+
uri: string;
|
|
60
|
+
mimeType: string;
|
|
61
|
+
text: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- 4. Implement the Resource Handler ---
|
|
65
|
+
|
|
66
|
+
@Injectable()
|
|
67
|
+
export class ResourceHandler {
|
|
68
|
+
constructor(
|
|
69
|
+
private readonly rulesService: RulesService,
|
|
70
|
+
private readonly agentsService: AgentsService,
|
|
71
|
+
private readonly configService: ConfigService,
|
|
72
|
+
) {}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* List all available resources (static + dynamic discovered).
|
|
76
|
+
* Called by MCP client via resources/list.
|
|
77
|
+
*/
|
|
78
|
+
async listResources(): Promise<typeof staticResources> {
|
|
79
|
+
// Static resources are always available
|
|
80
|
+
const resources = [...staticResources];
|
|
81
|
+
|
|
82
|
+
// Dynamically discover available rules
|
|
83
|
+
const ruleNames = await this.rulesService.listRuleNames();
|
|
84
|
+
for (const name of ruleNames) {
|
|
85
|
+
resources.push({
|
|
86
|
+
uri: `rules://${name}`,
|
|
87
|
+
name: `Rule: ${name}`,
|
|
88
|
+
description: `AI coding rule: ${name}`,
|
|
89
|
+
mimeType: 'text/markdown',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return resources;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Read a specific resource by URI.
|
|
98
|
+
* Called by MCP client via resources/read.
|
|
99
|
+
*/
|
|
100
|
+
async readResource(uri: string): Promise<ResourceContent> {
|
|
101
|
+
// Parse the URI scheme and path
|
|
102
|
+
const match = uri.match(/^(\w+):\/\/(.+)$/);
|
|
103
|
+
if (!match) {
|
|
104
|
+
throw new ResourceNotFoundError(`Invalid resource URI format: ${uri}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const [, scheme, path] = match;
|
|
108
|
+
|
|
109
|
+
switch (scheme) {
|
|
110
|
+
case 'config':
|
|
111
|
+
return this.readConfig(uri, path);
|
|
112
|
+
case 'docs':
|
|
113
|
+
return this.readDocs(uri, path);
|
|
114
|
+
case 'rules':
|
|
115
|
+
return this.readRule(uri, path);
|
|
116
|
+
case 'agents':
|
|
117
|
+
return this.readAgent(uri, path);
|
|
118
|
+
case 'skills':
|
|
119
|
+
return this.readSkill(uri, path);
|
|
120
|
+
default:
|
|
121
|
+
throw new ResourceNotFoundError(`Unknown resource scheme: ${scheme}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private async readConfig(uri: string, path: string): Promise<ResourceContent> {
|
|
126
|
+
if (path !== 'project') {
|
|
127
|
+
throw new ResourceNotFoundError(`Unknown config resource: ${path}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const config = await this.configService.getProjectConfig();
|
|
131
|
+
return {
|
|
132
|
+
uri,
|
|
133
|
+
mimeType: 'application/json',
|
|
134
|
+
text: JSON.stringify(config, null, 2),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async readDocs(uri: string, path: string): Promise<ResourceContent> {
|
|
139
|
+
if (path !== 'changelog') {
|
|
140
|
+
throw new ResourceNotFoundError(`Unknown docs resource: ${path}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const changelog = await this.rulesService.readFile('CHANGELOG.md');
|
|
144
|
+
return {
|
|
145
|
+
uri,
|
|
146
|
+
mimeType: 'text/markdown',
|
|
147
|
+
text: changelog,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private async readRule(uri: string, ruleName: string): Promise<ResourceContent> {
|
|
152
|
+
const content = await this.rulesService.getRule(ruleName);
|
|
153
|
+
if (!content) {
|
|
154
|
+
throw new ResourceNotFoundError(`Rule not found: ${ruleName}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
uri,
|
|
159
|
+
mimeType: 'text/markdown',
|
|
160
|
+
text: content,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private async readAgent(uri: string, agentName: string): Promise<ResourceContent> {
|
|
165
|
+
const agent = await this.agentsService.getAgent(agentName);
|
|
166
|
+
if (!agent) {
|
|
167
|
+
throw new ResourceNotFoundError(`Agent not found: ${agentName}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
uri,
|
|
172
|
+
mimeType: 'application/json',
|
|
173
|
+
text: JSON.stringify(agent, null, 2),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private async readSkill(uri: string, skillName: string): Promise<ResourceContent> {
|
|
178
|
+
const content = await this.rulesService.getSkill(skillName);
|
|
179
|
+
if (!content) {
|
|
180
|
+
throw new ResourceNotFoundError(`Skill not found: ${skillName}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
uri,
|
|
185
|
+
mimeType: 'text/markdown',
|
|
186
|
+
text: content,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// --- 5. Register in MCP Service ---
|
|
192
|
+
|
|
193
|
+
/*
|
|
194
|
+
// In mcp.service.ts:
|
|
195
|
+
import { staticResources, resourceTemplates, ResourceHandler } from './resources/resource-handler';
|
|
196
|
+
|
|
197
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
198
|
+
resources: await this.resourceHandler.listResources(),
|
|
199
|
+
}));
|
|
200
|
+
|
|
201
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
202
|
+
resourceTemplates,
|
|
203
|
+
}));
|
|
204
|
+
|
|
205
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => ({
|
|
206
|
+
contents: [await this.resourceHandler.readResource(request.params.uri)],
|
|
207
|
+
}));
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
// --- Error Types ---
|
|
211
|
+
|
|
212
|
+
class ResourceNotFoundError extends Error {
|
|
213
|
+
constructor(message: string) {
|
|
214
|
+
super(message);
|
|
215
|
+
this.name = 'ResourceNotFoundError';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Placeholder interfaces for dependency injection
|
|
220
|
+
interface RulesService {
|
|
221
|
+
listRuleNames(): Promise<string[]>;
|
|
222
|
+
getRule(name: string): Promise<string | null>;
|
|
223
|
+
getSkill(name: string): Promise<string | null>;
|
|
224
|
+
readFile(path: string): Promise<string>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
interface AgentsService {
|
|
228
|
+
getAgent(name: string): Promise<Record<string, unknown> | null>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
interface ConfigService {
|
|
232
|
+
getProjectConfig(): Promise<Record<string, unknown>>;
|
|
233
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Implementation Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates a complete Tool handler following the MCP specification.
|
|
5
|
+
* Tools are functions the AI can call that may have side effects.
|
|
6
|
+
*
|
|
7
|
+
* Key patterns:
|
|
8
|
+
* - Schema-first design with JSON Schema validation
|
|
9
|
+
* - Structured error responses with isError flag
|
|
10
|
+
* - Input validation before processing
|
|
11
|
+
* - NestJS injectable service pattern
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Injectable } from '@nestjs/common';
|
|
15
|
+
|
|
16
|
+
// --- 1. Define the Tool Schema (JSON Schema) ---
|
|
17
|
+
|
|
18
|
+
export const fileAnalyzerToolSchema = {
|
|
19
|
+
name: 'analyze_file',
|
|
20
|
+
description:
|
|
21
|
+
'Analyze a source file for code quality metrics including complexity, ' +
|
|
22
|
+
'line count, and dependency analysis. Use when reviewing code quality ' +
|
|
23
|
+
'or planning refactoring.',
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object' as const,
|
|
26
|
+
properties: {
|
|
27
|
+
filePath: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Relative path to the file from project root (e.g., "src/utils/parser.ts")',
|
|
30
|
+
minLength: 1,
|
|
31
|
+
maxLength: 500,
|
|
32
|
+
},
|
|
33
|
+
metrics: {
|
|
34
|
+
type: 'array',
|
|
35
|
+
description: 'Specific metrics to analyze (default: all)',
|
|
36
|
+
items: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
enum: ['complexity', 'lines', 'dependencies', 'exports'],
|
|
39
|
+
},
|
|
40
|
+
default: ['complexity', 'lines', 'dependencies', 'exports'],
|
|
41
|
+
},
|
|
42
|
+
includeSource: {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
description: 'Whether to include the source code in the response (default: false)',
|
|
45
|
+
default: false,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['filePath'],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// --- 2. Define Result Types ---
|
|
53
|
+
|
|
54
|
+
interface ToolResult {
|
|
55
|
+
content: Array<{ type: 'text'; text: string }>;
|
|
56
|
+
isError?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface FileAnalysis {
|
|
60
|
+
filePath: string;
|
|
61
|
+
lineCount: number;
|
|
62
|
+
complexity: number;
|
|
63
|
+
dependencies: string[];
|
|
64
|
+
exports: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --- 3. Implement the Tool Handler ---
|
|
68
|
+
|
|
69
|
+
@Injectable()
|
|
70
|
+
export class FileAnalyzerHandler {
|
|
71
|
+
constructor(private readonly fileService: FileService) {}
|
|
72
|
+
|
|
73
|
+
async handle(params: {
|
|
74
|
+
filePath: string;
|
|
75
|
+
metrics?: string[];
|
|
76
|
+
includeSource?: boolean;
|
|
77
|
+
}): Promise<ToolResult> {
|
|
78
|
+
// Step 1: Validate input
|
|
79
|
+
if (!params.filePath?.trim()) {
|
|
80
|
+
return this.errorResult('filePath is required and cannot be empty');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (params.filePath.includes('..')) {
|
|
84
|
+
return this.errorResult('filePath cannot contain path traversal (..)');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Step 2: Check file exists
|
|
88
|
+
const exists = await this.fileService.exists(params.filePath);
|
|
89
|
+
if (!exists) {
|
|
90
|
+
return this.errorResult(`File not found: ${params.filePath}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Step 3: Perform analysis
|
|
94
|
+
try {
|
|
95
|
+
const analysis = await this.analyze(params.filePath, params.metrics);
|
|
96
|
+
const output = this.formatAnalysis(analysis, params.includeSource);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: 'text', text: output }],
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return this.errorResult(
|
|
103
|
+
`Analysis failed for ${params.filePath}: ${(error as Error).message}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async analyze(filePath: string, metrics?: string[]): Promise<FileAnalysis> {
|
|
109
|
+
const content = await this.fileService.read(filePath);
|
|
110
|
+
const lines = content.split('\n');
|
|
111
|
+
const selected = new Set(metrics ?? ['complexity', 'lines', 'dependencies', 'exports']);
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
filePath,
|
|
115
|
+
lineCount: selected.has('lines') ? lines.length : -1,
|
|
116
|
+
complexity: selected.has('complexity') ? this.calculateComplexity(lines) : -1,
|
|
117
|
+
dependencies: selected.has('dependencies') ? this.extractDependencies(lines) : [],
|
|
118
|
+
exports: selected.has('exports') ? this.extractExports(lines) : [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private calculateComplexity(lines: string[]): number {
|
|
123
|
+
const complexityKeywords = /\b(if|else|for|while|switch|case|catch|&&|\|\||\?)\b/g;
|
|
124
|
+
return lines.reduce((sum, line) => {
|
|
125
|
+
const matches = line.match(complexityKeywords);
|
|
126
|
+
return sum + (matches?.length ?? 0);
|
|
127
|
+
}, 1); // Base complexity of 1
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private extractDependencies(lines: string[]): string[] {
|
|
131
|
+
const importPattern = /^import\s+.*from\s+['"]([^'"]+)['"]/;
|
|
132
|
+
return lines
|
|
133
|
+
.map(line => line.match(importPattern)?.[1])
|
|
134
|
+
.filter((dep): dep is string => dep !== undefined);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private extractExports(lines: string[]): string[] {
|
|
138
|
+
const exportPattern =
|
|
139
|
+
/^export\s+(?:default\s+)?(?:class|function|const|interface|type|enum)\s+(\w+)/;
|
|
140
|
+
return lines
|
|
141
|
+
.map(line => line.match(exportPattern)?.[1])
|
|
142
|
+
.filter((name): name is string => name !== undefined);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private formatAnalysis(analysis: FileAnalysis, includeSource?: boolean): string {
|
|
146
|
+
const sections: string[] = [`## File Analysis: ${analysis.filePath}`, ''];
|
|
147
|
+
|
|
148
|
+
if (analysis.lineCount >= 0) {
|
|
149
|
+
sections.push(`**Lines:** ${analysis.lineCount}`);
|
|
150
|
+
}
|
|
151
|
+
if (analysis.complexity >= 0) {
|
|
152
|
+
sections.push(`**Cyclomatic Complexity:** ${analysis.complexity}`);
|
|
153
|
+
}
|
|
154
|
+
if (analysis.dependencies.length > 0) {
|
|
155
|
+
sections.push('', '**Dependencies:**');
|
|
156
|
+
analysis.dependencies.forEach(dep => sections.push(`- \`${dep}\``));
|
|
157
|
+
}
|
|
158
|
+
if (analysis.exports.length > 0) {
|
|
159
|
+
sections.push('', '**Exports:**');
|
|
160
|
+
analysis.exports.forEach(exp => sections.push(`- \`${exp}\``));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return sections.join('\n');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private errorResult(message: string): ToolResult {
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
169
|
+
isError: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// --- 4. Register in MCP Service ---
|
|
175
|
+
|
|
176
|
+
/*
|
|
177
|
+
// In mcp.service.ts:
|
|
178
|
+
import { fileAnalyzerToolSchema, FileAnalyzerHandler } from './tools/file-analyzer';
|
|
179
|
+
|
|
180
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
181
|
+
tools: [fileAnalyzerToolSchema],
|
|
182
|
+
}));
|
|
183
|
+
|
|
184
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
185
|
+
switch (request.params.name) {
|
|
186
|
+
case 'analyze_file':
|
|
187
|
+
return this.fileAnalyzerHandler.handle(request.params.arguments);
|
|
188
|
+
default:
|
|
189
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
// Placeholder for dependency injection
|
|
195
|
+
interface FileService {
|
|
196
|
+
exists(path: string): Promise<boolean>;
|
|
197
|
+
read(path: string): Promise<string>;
|
|
198
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# MCP Protocol Quick Reference
|
|
2
|
+
|
|
3
|
+
## Protocol Overview
|
|
4
|
+
|
|
5
|
+
The Model Context Protocol (MCP) uses JSON-RPC 2.0 over stdio or HTTP+SSE transport. Servers expose three capability types: **Tools**, **Resources**, and **Prompts**.
|
|
6
|
+
|
|
7
|
+
## JSON-RPC 2.0 Message Format
|
|
8
|
+
|
|
9
|
+
### Request
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"jsonrpc": "2.0",
|
|
14
|
+
"id": 1,
|
|
15
|
+
"method": "tools/call",
|
|
16
|
+
"params": {
|
|
17
|
+
"name": "search_rules",
|
|
18
|
+
"arguments": { "query": "TDD" }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Response (Success)
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"jsonrpc": "2.0",
|
|
28
|
+
"id": 1,
|
|
29
|
+
"result": {
|
|
30
|
+
"content": [{ "type": "text", "text": "..." }]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Response (Error)
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"jsonrpc": "2.0",
|
|
40
|
+
"id": 1,
|
|
41
|
+
"error": {
|
|
42
|
+
"code": -32601,
|
|
43
|
+
"message": "Method not found"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Capability Methods
|
|
49
|
+
|
|
50
|
+
### Tools
|
|
51
|
+
|
|
52
|
+
| Method | Direction | Description |
|
|
53
|
+
|--------|-----------|-------------|
|
|
54
|
+
| `tools/list` | Client → Server | List available tools with schemas |
|
|
55
|
+
| `tools/call` | Client → Server | Execute a tool by name with arguments |
|
|
56
|
+
|
|
57
|
+
**Tool Result Shape:**
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface ToolResult {
|
|
61
|
+
content: ContentBlock[]; // text, image, or resource content
|
|
62
|
+
isError?: boolean; // true if the tool encountered an error
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type ContentBlock =
|
|
66
|
+
| { type: 'text'; text: string }
|
|
67
|
+
| { type: 'image'; data: string; mimeType: string }
|
|
68
|
+
| { type: 'resource'; resource: ResourceContent };
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Resources
|
|
72
|
+
|
|
73
|
+
| Method | Direction | Description |
|
|
74
|
+
|--------|-----------|-------------|
|
|
75
|
+
| `resources/list` | Client → Server | List available resources |
|
|
76
|
+
| `resources/templates/list` | Client → Server | List URI templates for dynamic resources |
|
|
77
|
+
| `resources/read` | Client → Server | Read a resource by URI |
|
|
78
|
+
| `notifications/resources/list_changed` | Server → Client | Notify resource list changed |
|
|
79
|
+
|
|
80
|
+
**Resource Shape:**
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface Resource {
|
|
84
|
+
uri: string; // e.g., "rules://core"
|
|
85
|
+
name: string; // Human-readable name
|
|
86
|
+
description?: string; // What this resource contains
|
|
87
|
+
mimeType?: string; // e.g., "text/markdown", "application/json"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface ResourceContent {
|
|
91
|
+
uri: string;
|
|
92
|
+
mimeType?: string;
|
|
93
|
+
text?: string; // For text content
|
|
94
|
+
blob?: string; // For binary content (base64)
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Prompts
|
|
99
|
+
|
|
100
|
+
| Method | Direction | Description |
|
|
101
|
+
|--------|-----------|-------------|
|
|
102
|
+
| `prompts/list` | Client → Server | List available prompt templates |
|
|
103
|
+
| `prompts/get` | Client → Server | Get a prompt with arguments filled in |
|
|
104
|
+
|
|
105
|
+
**Prompt Shape:**
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
interface Prompt {
|
|
109
|
+
name: string;
|
|
110
|
+
description?: string;
|
|
111
|
+
arguments?: PromptArgument[];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface PromptArgument {
|
|
115
|
+
name: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
required?: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface GetPromptResult {
|
|
121
|
+
messages: Array<{
|
|
122
|
+
role: 'user' | 'assistant';
|
|
123
|
+
content: TextContent | ImageContent | EmbeddedResource;
|
|
124
|
+
}>;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Initialization Handshake
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Client Server
|
|
132
|
+
| |
|
|
133
|
+
|-- initialize ----------------->| (capabilities, clientInfo)
|
|
134
|
+
|<-- initialize result ----------| (capabilities, serverInfo)
|
|
135
|
+
|-- notifications/initialized -->| (confirm ready)
|
|
136
|
+
| |
|
|
137
|
+
|-- tools/list ----------------->| (discover tools)
|
|
138
|
+
|-- resources/list ------------->| (discover resources)
|
|
139
|
+
|-- prompts/list --------------->| (discover prompts)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Server Capabilities Declaration
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const serverCapabilities = {
|
|
146
|
+
tools: {}, // Server provides tools
|
|
147
|
+
resources: {
|
|
148
|
+
subscribe: true, // Supports resource subscriptions
|
|
149
|
+
listChanged: true, // Sends list_changed notifications
|
|
150
|
+
},
|
|
151
|
+
prompts: {
|
|
152
|
+
listChanged: true, // Sends list_changed notifications
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Transport Modes
|
|
158
|
+
|
|
159
|
+
### Stdio
|
|
160
|
+
|
|
161
|
+
- Server reads JSON-RPC from stdin, writes to stdout
|
|
162
|
+
- Logs MUST go to stderr (never stdout)
|
|
163
|
+
- One message per line (newline-delimited JSON)
|
|
164
|
+
- No HTTP overhead, fastest for local use
|
|
165
|
+
|
|
166
|
+
### HTTP + SSE
|
|
167
|
+
|
|
168
|
+
- Client opens SSE connection at `GET /sse`
|
|
169
|
+
- Server sends `endpoint` event with message URL
|
|
170
|
+
- Client sends JSON-RPC via `POST /messages`
|
|
171
|
+
- Server streams responses via SSE
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Client Server
|
|
175
|
+
| |
|
|
176
|
+
|-- GET /sse ----------------------->| (open SSE stream)
|
|
177
|
+
|<-- event: endpoint ---------------| (data: /messages?sessionId=abc)
|
|
178
|
+
| |
|
|
179
|
+
|-- POST /messages?sessionId=abc --->| (JSON-RPC request)
|
|
180
|
+
|<-- event: message ----------------| (JSON-RPC response via SSE)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Error Codes
|
|
184
|
+
|
|
185
|
+
| Code | Name | Description |
|
|
186
|
+
|------|------|-------------|
|
|
187
|
+
| `-32700` | Parse error | Invalid JSON |
|
|
188
|
+
| `-32600` | Invalid request | Missing required fields |
|
|
189
|
+
| `-32601` | Method not found | Unknown method |
|
|
190
|
+
| `-32602` | Invalid params | Wrong parameter types |
|
|
191
|
+
| `-32603` | Internal error | Server-side error |
|
|
192
|
+
|
|
193
|
+
## Input Schema Best Practices
|
|
194
|
+
|
|
195
|
+
- Always set `type: 'object'` at the top level
|
|
196
|
+
- Mark required parameters in the `required` array
|
|
197
|
+
- Add `description` to every property (LLMs use these to decide how to call)
|
|
198
|
+
- Use `minLength`, `maximum`, `enum` constraints where applicable
|
|
199
|
+
- Set sensible `default` values for optional parameters
|
|
200
|
+
|
|
201
|
+
## URI Scheme Conventions
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
scheme://authority/path
|
|
205
|
+
|
|
206
|
+
rules://core → Core rules
|
|
207
|
+
rules://augmented-coding → Augmented coding rules
|
|
208
|
+
agents://solution-architect → Agent definition
|
|
209
|
+
config://project → Project config
|
|
210
|
+
skills://mcp-builder → Skill content
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- Use lowercase, hyphen-separated paths
|
|
214
|
+
- Scheme should indicate the resource domain
|
|
215
|
+
- Path should be human-readable and predictable
|