@aiready/mcp-server 0.6.1 → 0.6.2
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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-format-check.log +2 -2
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test$colon$coverage.log +132 -0
- package/.turbo/turbo-test.log +69 -44
- package/.turbo/turbo-type-check.log +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +598 -231
- package/package.json +19 -18
- package/src/__tests__/schema-sync.test.ts +54 -0
- package/src/__tests__/server.test.ts +28 -1
- package/src/index.ts +34 -327
- package/src/prompts/index.ts +76 -0
- package/src/resources/index.ts +94 -0
- package/src/state-store.ts +111 -0
- package/src/tools/best-practices.ts +167 -0
- package/src/tools/context-budget.ts +76 -0
- package/src/tools/index.ts +236 -0
- package/tsconfig.json +2 -1
- package/.smithery/shttp/manifest.json +0 -73
- package/.smithery/shttp/module.js +0 -270910
- package/.smithery/shttp/module.js.map +0 -7
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -6
- package/coverage/coverage-final.json +0 -1
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -101
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/mcp-server",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "The AIReady Model Context Protocol (MCP) Server for Agentic Readiness. Optimize codebases for AI agents like Cursor, Windsurf, and Claude directly via MCP.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,26 +10,26 @@
|
|
|
10
10
|
"mcp-server": "./dist/index.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
14
|
-
"chalk": "^5.
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
14
|
+
"chalk": "^5.6.2",
|
|
15
15
|
"zod": "^4.3.6",
|
|
16
|
-
"@aiready/agent-grounding": "0.14.
|
|
17
|
-
"@aiready/ai-signal-clarity": "0.14.
|
|
18
|
-
"@aiready/change-amplification": "0.14.
|
|
19
|
-
"@aiready/consistency": "0.21.
|
|
20
|
-
"@aiready/
|
|
21
|
-
"@aiready/
|
|
22
|
-
"@aiready/deps": "0.14.
|
|
23
|
-
"@aiready/
|
|
24
|
-
"@aiready/pattern-detect": "0.17.
|
|
16
|
+
"@aiready/agent-grounding": "0.14.17",
|
|
17
|
+
"@aiready/ai-signal-clarity": "0.14.19",
|
|
18
|
+
"@aiready/change-amplification": "0.14.17",
|
|
19
|
+
"@aiready/consistency": "0.21.17",
|
|
20
|
+
"@aiready/core": "0.24.20",
|
|
21
|
+
"@aiready/doc-drift": "0.14.17",
|
|
22
|
+
"@aiready/deps": "0.14.17",
|
|
23
|
+
"@aiready/context-analyzer": "0.22.17",
|
|
24
|
+
"@aiready/pattern-detect": "0.17.17",
|
|
25
25
|
"@aiready/testability": "0.7.17"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@types/node": "^24.
|
|
29
|
-
"esbuild": "^0.27.
|
|
30
|
-
"tsup": "^8.
|
|
31
|
-
"typescript": "^
|
|
32
|
-
"vitest": "^4.
|
|
28
|
+
"@types/node": "^24.12.2",
|
|
29
|
+
"esbuild": "^0.27.7",
|
|
30
|
+
"tsup": "^8.5.1",
|
|
31
|
+
"typescript": "^6.0.2",
|
|
32
|
+
"vitest": "^4.1.2"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"aiready",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"type-check": "tsc --noEmit",
|
|
58
58
|
"format-check": "prettier --check . --ignore-path ../../.prettierignore",
|
|
59
59
|
"format": "prettier --write . --ignore-path ../../.prettierignore",
|
|
60
|
-
"lint:fix": "eslint . --fix"
|
|
60
|
+
"lint:fix": "eslint . --fix",
|
|
61
|
+
"test:coverage": "vitest run --coverage"
|
|
61
62
|
}
|
|
62
63
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { ToolName } from '@aiready/core';
|
|
3
|
+
import {
|
|
4
|
+
ADVERTISED_TOOLS,
|
|
5
|
+
TOOL_PACKAGE_MAP,
|
|
6
|
+
handleAnalysis,
|
|
7
|
+
} from '../tools/index.js';
|
|
8
|
+
|
|
9
|
+
describe('MCP Schema Sync Validation', () => {
|
|
10
|
+
it('every advertised tool should have a package mapping or a custom handler', () => {
|
|
11
|
+
// 1. Spoke tools that use handleAnalysis
|
|
12
|
+
const spokeTools = ADVERTISED_TOOLS.filter(
|
|
13
|
+
(t) =>
|
|
14
|
+
![
|
|
15
|
+
'get_best_practices',
|
|
16
|
+
'check_best_practice_compliance',
|
|
17
|
+
'analyze_context_budget',
|
|
18
|
+
].includes(t)
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
for (const tool of spokeTools) {
|
|
22
|
+
expect(
|
|
23
|
+
TOOL_PACKAGE_MAP[tool],
|
|
24
|
+
`Tool "${tool}" is missing a mapping in TOOL_PACKAGE_MAP`
|
|
25
|
+
).toBeDefined();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('every package in TOOL_PACKAGE_MAP should be a valid @aiready package name', () => {
|
|
30
|
+
for (const [tool, pkg] of Object.entries(TOOL_PACKAGE_MAP)) {
|
|
31
|
+
expect(
|
|
32
|
+
pkg.startsWith('@aiready/'),
|
|
33
|
+
`Mapping for "${tool}" -> "${pkg}" must use a scoped @aiready package`
|
|
34
|
+
).toBe(true);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('handleAnalysis should correctly resolve and attempt to load packages from the map', async () => {
|
|
39
|
+
// Mock the dynamic import to verify it's called with the correct package
|
|
40
|
+
const importMock = vi.fn().mockRejectedValue(new Error('Stop here'));
|
|
41
|
+
vi.stubGlobal('import', importMock);
|
|
42
|
+
|
|
43
|
+
// Testing dynamic loading path
|
|
44
|
+
const toolName = ToolName.PatternDetect;
|
|
45
|
+
const expectedPackage = TOOL_PACKAGE_MAP[toolName];
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await handleAnalysis(toolName, { path: './test' });
|
|
49
|
+
} catch (e: any) {
|
|
50
|
+
// We expect it to fail at the import stage because of our mock
|
|
51
|
+
expect(e.message).toContain(`failed to load package ${expectedPackage}`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -142,7 +142,34 @@ describe('AIReady MCP Server Integration', () => {
|
|
|
142
142
|
});
|
|
143
143
|
expect(result.contents).toBeDefined();
|
|
144
144
|
expect(result.contents[0].uri).toBe('aiready://project/summary');
|
|
145
|
-
expect(result.contents[0].text).toContain('# AIReady Summary');
|
|
145
|
+
expect((result.contents[0] as any).text).toContain('# AIReady Summary');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should execute get_best_practices', async () => {
|
|
149
|
+
const result = await client.callTool({
|
|
150
|
+
name: 'get_best_practices',
|
|
151
|
+
arguments: { category: 'patterns' },
|
|
152
|
+
});
|
|
153
|
+
const typedResult = result as ToolCallResponse;
|
|
154
|
+
expect(typedResult.content[0].text).toContain('Pattern Detection');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should execute check_best_practice_compliance', async () => {
|
|
158
|
+
const result = await client.callTool({
|
|
159
|
+
name: 'check_best_practice_compliance',
|
|
160
|
+
arguments: { file_path: path.resolve(__dirname, '../index.ts') },
|
|
161
|
+
});
|
|
162
|
+
const typedResult = result as ToolCallResponse;
|
|
163
|
+
expect(typedResult.content[0].text).toContain('is compliant');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should execute analyze_context_budget', async () => {
|
|
167
|
+
const result = await client.callTool({
|
|
168
|
+
name: 'analyze_context_budget',
|
|
169
|
+
arguments: { file_path: path.resolve(__dirname, '../index.ts') },
|
|
170
|
+
});
|
|
171
|
+
const typedResult = result as ToolCallResponse;
|
|
172
|
+
expect(typedResult.content[0].text).toContain('Context Budget');
|
|
146
173
|
});
|
|
147
174
|
|
|
148
175
|
it('should list available prompts', async () => {
|
package/src/index.ts
CHANGED
|
@@ -3,68 +3,29 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import {
|
|
4
4
|
CallToolRequestSchema,
|
|
5
5
|
ListToolsRequestSchema,
|
|
6
|
-
ListResourcesRequestSchema,
|
|
7
|
-
ReadResourceRequestSchema,
|
|
8
|
-
ListPromptsRequestSchema,
|
|
9
|
-
GetPromptRequestSchema,
|
|
10
6
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const RemediationArgsSchema = z.object({
|
|
28
|
-
issue_id: z.string().describe('The unique ID of the issue to fix'),
|
|
29
|
-
file_path: z.string().describe('The path to the file containing the issue'),
|
|
30
|
-
context: z.string().describe('The content of the file or surrounding code'),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Mapping between tool names and @aiready/ package names.
|
|
35
|
-
* Used for dynamic registration on-demand to minimize initial context budget.
|
|
36
|
-
*/
|
|
37
|
-
const TOOL_PACKAGE_MAP: Record<string, string> = {
|
|
38
|
-
[ToolName.PatternDetect]: '@aiready/pattern-detect',
|
|
39
|
-
[ToolName.ContextAnalyzer]: '@aiready/context-analyzer',
|
|
40
|
-
[ToolName.NamingConsistency]: '@aiready/consistency',
|
|
41
|
-
[ToolName.AiSignalClarity]: '@aiready/ai-signal-clarity',
|
|
42
|
-
[ToolName.AgentGrounding]: '@aiready/agent-grounding',
|
|
43
|
-
[ToolName.TestabilityIndex]: '@aiready/testability',
|
|
44
|
-
[ToolName.DocDrift]: '@aiready/doc-drift',
|
|
45
|
-
[ToolName.DependencyHealth]: '@aiready/deps',
|
|
46
|
-
[ToolName.ChangeAmplification]: '@aiready/change-amplification',
|
|
47
|
-
[ToolName.ContractEnforcement]: '@aiready/contract-enforcement',
|
|
48
|
-
// Aliases
|
|
49
|
-
patterns: '@aiready/pattern-detect',
|
|
50
|
-
duplicates: '@aiready/pattern-detect',
|
|
51
|
-
context: '@aiready/context-analyzer',
|
|
52
|
-
fragmentation: '@aiready/context-analyzer',
|
|
53
|
-
consistency: '@aiready/consistency',
|
|
54
|
-
'ai-signal': '@aiready/ai-signal-clarity',
|
|
55
|
-
grounding: '@aiready/agent-grounding',
|
|
56
|
-
testability: '@aiready/testability',
|
|
57
|
-
'deps-health': '@aiready/deps',
|
|
58
|
-
'change-amp': '@aiready/change-amplification',
|
|
59
|
-
'contract-enforce': '@aiready/contract-enforcement',
|
|
60
|
-
};
|
|
7
|
+
import {
|
|
8
|
+
handleAnalysis,
|
|
9
|
+
handleRemediation,
|
|
10
|
+
ADVERTISED_TOOLS,
|
|
11
|
+
RemediationArgsSchema,
|
|
12
|
+
BestPracticesArgsSchema,
|
|
13
|
+
handleGetBestPractices,
|
|
14
|
+
ComplianceArgsSchema,
|
|
15
|
+
handleCheckCompliance,
|
|
16
|
+
ContextBudgetArgsSchema,
|
|
17
|
+
handleAnalyzeContextBudget,
|
|
18
|
+
} from './tools/index.js';
|
|
19
|
+
import { registerResourceHandlers } from './resources/index.js';
|
|
20
|
+
import { registerPromptHandlers } from './prompts/index.js';
|
|
21
|
+
import { stateStore } from './state-store.js';
|
|
61
22
|
|
|
62
23
|
/**
|
|
63
24
|
* AIReady MCP Server Implementation
|
|
64
25
|
*/
|
|
65
26
|
export class AIReadyMcpServer {
|
|
66
27
|
private server: Server;
|
|
67
|
-
private version: string = '0.
|
|
28
|
+
private version: string = '0.3.0';
|
|
68
29
|
|
|
69
30
|
constructor() {
|
|
70
31
|
this.server = new Server(
|
|
@@ -88,221 +49,17 @@ export class AIReadyMcpServer {
|
|
|
88
49
|
};
|
|
89
50
|
}
|
|
90
51
|
|
|
91
|
-
private async handleRemediation(args: z.infer<typeof RemediationArgsSchema>) {
|
|
92
|
-
const apiKey = process.env.AIREADY_API_KEY;
|
|
93
|
-
const serverUrl =
|
|
94
|
-
process.env.AIREADY_PLATFORM_URL || 'https://platform.getaiready.dev';
|
|
95
|
-
|
|
96
|
-
if (!apiKey) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
'AIREADY_API_KEY is not set. Remediation requires an active subscription.'
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.error(`[MCP] Requesting remediation for ${args.issue_id}...`);
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
const response = await fetch(`${serverUrl}/api/v1/remediate`, {
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: {
|
|
108
|
-
'Content-Type': 'application/json',
|
|
109
|
-
'X-API-KEY': apiKey,
|
|
110
|
-
},
|
|
111
|
-
body: JSON.stringify({
|
|
112
|
-
issueId: args.issue_id,
|
|
113
|
-
filePath: args.file_path,
|
|
114
|
-
context: args.context,
|
|
115
|
-
agent: 'mcp-server',
|
|
116
|
-
}),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
if (!response.ok) {
|
|
120
|
-
const errorData = await response.json().catch(() => ({}));
|
|
121
|
-
throw new Error(
|
|
122
|
-
`Platform Error: ${errorData.message || response.statusText}`
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const data = await response.json();
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
content: [
|
|
130
|
-
{
|
|
131
|
-
type: 'text',
|
|
132
|
-
text: `Recommended Fix (Diff):\n\n${data.diff}\n\nRationale:\n${data.rationale}`,
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
};
|
|
136
|
-
} catch (error: unknown) {
|
|
137
|
-
const errorMessage =
|
|
138
|
-
error instanceof Error ? error.message : String(error);
|
|
139
|
-
return {
|
|
140
|
-
content: [
|
|
141
|
-
{
|
|
142
|
-
type: 'text',
|
|
143
|
-
text: `Failed to get remediation: ${errorMessage}. Please visit the dashboard to fix manually.`,
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
isError: true,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
52
|
private setupHandlers() {
|
|
152
|
-
//
|
|
153
|
-
this.server
|
|
154
|
-
return {
|
|
155
|
-
resources: [
|
|
156
|
-
{
|
|
157
|
-
uri: 'aiready://project/summary',
|
|
158
|
-
name: 'AIReady Project Summary',
|
|
159
|
-
description: 'Quick top-level AI-readiness summary.',
|
|
160
|
-
mimeType: 'text/markdown',
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
uri: 'aiready://project/issues',
|
|
164
|
-
name: 'AIReady Critical Issues',
|
|
165
|
-
description: 'List of top 10 critical readiness issues.',
|
|
166
|
-
mimeType: 'application/json',
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
uri: 'aiready://project/graph',
|
|
170
|
-
name: 'AIReady Codebase Graph',
|
|
171
|
-
description: 'Force-directed graph data for visualization.',
|
|
172
|
-
mimeType: 'application/json',
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
};
|
|
176
|
-
});
|
|
53
|
+
// Register Resource Handlers
|
|
54
|
+
registerResourceHandlers(this.server, stateStore);
|
|
177
55
|
|
|
178
|
-
//
|
|
179
|
-
this.server
|
|
180
|
-
ReadResourceRequestSchema,
|
|
181
|
-
async (request) => {
|
|
182
|
-
const { uri } = request.params;
|
|
183
|
-
|
|
184
|
-
if (uri === 'aiready://project/summary') {
|
|
185
|
-
return {
|
|
186
|
-
contents: [
|
|
187
|
-
{
|
|
188
|
-
uri,
|
|
189
|
-
mimeType: 'text/markdown',
|
|
190
|
-
text: '# AIReady Summary\n\nProject: Current Directory\nScore: 84/100 (B)\n\nCritical Issues: 2\nMajor Issues: 14\n\nRun the `aiready-mcp` tool for full analysis.',
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
uri === 'aiready://project/issues' ||
|
|
198
|
-
uri === 'aiready://project/graph'
|
|
199
|
-
) {
|
|
200
|
-
return {
|
|
201
|
-
contents: [
|
|
202
|
-
{
|
|
203
|
-
uri,
|
|
204
|
-
mimeType: 'application/json',
|
|
205
|
-
text: JSON.stringify({
|
|
206
|
-
message: 'Resource content coming from latest scan...',
|
|
207
|
-
}),
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
throw new Error(`Resource not found: ${uri}`);
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// List available prompts
|
|
218
|
-
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
219
|
-
return {
|
|
220
|
-
prompts: [
|
|
221
|
-
{
|
|
222
|
-
name: 'analyze-project',
|
|
223
|
-
description:
|
|
224
|
-
'Audit the project for AI-readiness and suggest improvements.',
|
|
225
|
-
arguments: [
|
|
226
|
-
{
|
|
227
|
-
name: 'path',
|
|
228
|
-
description: 'Path/directory to analyze',
|
|
229
|
-
required: true,
|
|
230
|
-
},
|
|
231
|
-
],
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
name: 'remediate-issue',
|
|
235
|
-
description: 'Help the user fix a specific AIReady issue.',
|
|
236
|
-
arguments: [
|
|
237
|
-
{
|
|
238
|
-
name: 'issueId',
|
|
239
|
-
description: 'The unique ID of the issue to fix',
|
|
240
|
-
required: true,
|
|
241
|
-
},
|
|
242
|
-
],
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
};
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// Get prompt content
|
|
249
|
-
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
250
|
-
const { name, arguments: args } = request.params;
|
|
251
|
-
|
|
252
|
-
if (name === 'analyze-project') {
|
|
253
|
-
const path = args?.path || '.';
|
|
254
|
-
return {
|
|
255
|
-
description: 'Project audit instructions',
|
|
256
|
-
messages: [
|
|
257
|
-
{
|
|
258
|
-
role: 'user',
|
|
259
|
-
content: {
|
|
260
|
-
type: 'text',
|
|
261
|
-
text: `I want to audit the project at "${path}" for AI-readiness. Please use the AIReady tools to identify duplication patterns, context fragmentation, and naming inconsistencies. Then, provide a prioritized list of improvements to help me leverage AI agents more effectively.`,
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
],
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (name === 'remediate-issue') {
|
|
269
|
-
const issueId = args?.issueId;
|
|
270
|
-
return {
|
|
271
|
-
description: 'Issue remediation instructions',
|
|
272
|
-
messages: [
|
|
273
|
-
{
|
|
274
|
-
role: 'user',
|
|
275
|
-
content: {
|
|
276
|
-
type: 'text',
|
|
277
|
-
text: `I've identified an AIReady issue with ID: ${issueId}. Please use the \`get_remediation_diff\` tool to find a fix, explain the rationale behind the recommended change, and then help me apply it to the codebase.`,
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
],
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
throw new Error(`Prompt not found: ${name}`);
|
|
285
|
-
});
|
|
56
|
+
// Register Prompt Handlers
|
|
57
|
+
registerPromptHandlers(this.server);
|
|
286
58
|
|
|
287
59
|
// List available tools
|
|
288
60
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
289
|
-
// Define canonical tool names to advertise to the client
|
|
290
|
-
// These will be dynamically loaded on demand
|
|
291
|
-
const toolsToAdvertise = [
|
|
292
|
-
ToolName.PatternDetect,
|
|
293
|
-
ToolName.ContextAnalyzer,
|
|
294
|
-
ToolName.NamingConsistency,
|
|
295
|
-
ToolName.AiSignalClarity,
|
|
296
|
-
ToolName.AgentGrounding,
|
|
297
|
-
ToolName.TestabilityIndex,
|
|
298
|
-
ToolName.DocDrift,
|
|
299
|
-
ToolName.DependencyHealth,
|
|
300
|
-
ToolName.ChangeAmplification,
|
|
301
|
-
ToolName.ContractEnforcement,
|
|
302
|
-
];
|
|
303
|
-
|
|
304
61
|
const tools: any[] = [
|
|
305
|
-
...
|
|
62
|
+
...ADVERTISED_TOOLS.map((id) => ({
|
|
306
63
|
name: id,
|
|
307
64
|
description: `Scan the directory for ${id} issues to improve AI-readiness.`,
|
|
308
65
|
inputSchema: {
|
|
@@ -355,82 +112,32 @@ export class AIReadyMcpServer {
|
|
|
355
112
|
|
|
356
113
|
try {
|
|
357
114
|
if (name === 'get_remediation_diff') {
|
|
358
|
-
const parsedArgs = RemediationArgsSchema.
|
|
359
|
-
|
|
360
|
-
throw new Error(
|
|
361
|
-
`Invalid arguments for ${name}: ${parsedArgs.error.message}`
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
return await this.handleRemediation(parsedArgs.data);
|
|
115
|
+
const parsedArgs = RemediationArgsSchema.parse(args);
|
|
116
|
+
return await handleRemediation(parsedArgs);
|
|
365
117
|
}
|
|
366
118
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
`Invalid arguments for ${name}: ${parsedArgs.error.message}`
|
|
371
|
-
);
|
|
119
|
+
if (name === 'get_best_practices') {
|
|
120
|
+
const parsedArgs = BestPracticesArgsSchema.parse(args);
|
|
121
|
+
return await handleGetBestPractices(parsedArgs);
|
|
372
122
|
}
|
|
373
|
-
const { path: rootDir, summary_only } = parsedArgs.data;
|
|
374
123
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (!provider) {
|
|
379
|
-
const packageName =
|
|
380
|
-
TOOL_PACKAGE_MAP[name] ??
|
|
381
|
-
(name.startsWith('@aiready/') ? name : `@aiready/${name}`);
|
|
382
|
-
|
|
383
|
-
try {
|
|
384
|
-
console.error(
|
|
385
|
-
`[MCP] Dynamically loading ${packageName} for tool ${name}`
|
|
386
|
-
);
|
|
387
|
-
await import(packageName);
|
|
388
|
-
provider = ToolRegistry.find(name);
|
|
389
|
-
} catch (importError: unknown) {
|
|
390
|
-
const importErrorMessage =
|
|
391
|
-
importError instanceof Error
|
|
392
|
-
? importError.message
|
|
393
|
-
: String(importError);
|
|
394
|
-
console.error(
|
|
395
|
-
`[MCP] Failed to load tool package ${packageName}: ${importErrorMessage}`
|
|
396
|
-
);
|
|
397
|
-
const error = new Error(
|
|
398
|
-
`Tool ${name} not found and failed to load package ${packageName}: ${importErrorMessage}`
|
|
399
|
-
);
|
|
400
|
-
(error as { cause?: unknown }).cause = importError;
|
|
401
|
-
throw error;
|
|
402
|
-
}
|
|
124
|
+
if (name === 'check_best_practice_compliance') {
|
|
125
|
+
const parsedArgs = ComplianceArgsSchema.parse(args);
|
|
126
|
+
return await handleCheckCompliance(parsedArgs);
|
|
403
127
|
}
|
|
404
128
|
|
|
405
|
-
if (
|
|
406
|
-
|
|
129
|
+
if (name === 'analyze_context_budget') {
|
|
130
|
+
const parsedArgs = ContextBudgetArgsSchema.parse(args);
|
|
131
|
+
return await handleAnalyzeContextBudget(parsedArgs);
|
|
407
132
|
}
|
|
408
133
|
|
|
409
|
-
|
|
410
|
-
`[MCP] Executing ${name} on ${rootDir}${
|
|
411
|
-
summary_only ? ' (summary only)' : ''
|
|
412
|
-
}`
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
const results = await provider.analyze({
|
|
416
|
-
rootDir,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
// Format results for the agent
|
|
420
|
-
const responseData = summary_only
|
|
421
|
-
? {
|
|
422
|
-
summary: results.summary,
|
|
423
|
-
metadata: results.metadata,
|
|
424
|
-
notice:
|
|
425
|
-
'Detailed issues were omitted (summary_only: true). Run without summary_only for full details.',
|
|
426
|
-
}
|
|
427
|
-
: results;
|
|
134
|
+
const results = await handleAnalysis(name, args, stateStore);
|
|
428
135
|
|
|
429
136
|
return {
|
|
430
137
|
content: [
|
|
431
138
|
{
|
|
432
139
|
type: 'text',
|
|
433
|
-
text: JSON.stringify(
|
|
140
|
+
text: JSON.stringify(results, null, 2),
|
|
434
141
|
},
|
|
435
142
|
],
|
|
436
143
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ListPromptsRequestSchema,
|
|
3
|
+
GetPromptRequestSchema,
|
|
4
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
|
|
6
|
+
export function registerPromptHandlers(server: any) {
|
|
7
|
+
// List available prompts
|
|
8
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
9
|
+
return {
|
|
10
|
+
prompts: [
|
|
11
|
+
{
|
|
12
|
+
name: 'analyze-project',
|
|
13
|
+
description:
|
|
14
|
+
'Audit the project for AI-readiness and suggest improvements.',
|
|
15
|
+
arguments: [
|
|
16
|
+
{
|
|
17
|
+
name: 'path',
|
|
18
|
+
description: 'Path/directory to analyze',
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'remediate-issue',
|
|
25
|
+
description: 'Help the user fix a specific AIReady issue.',
|
|
26
|
+
arguments: [
|
|
27
|
+
{
|
|
28
|
+
name: 'issueId',
|
|
29
|
+
description: 'The unique ID of the issue to fix',
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Get prompt content
|
|
39
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request: any) => {
|
|
40
|
+
const { name, arguments: args } = request.params;
|
|
41
|
+
|
|
42
|
+
if (name === 'analyze-project') {
|
|
43
|
+
const path = args?.path || '.';
|
|
44
|
+
return {
|
|
45
|
+
description: 'Project audit instructions',
|
|
46
|
+
messages: [
|
|
47
|
+
{
|
|
48
|
+
role: 'user',
|
|
49
|
+
content: {
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `I want to audit the project at "${path}" for AI-readiness. Please use the AIReady tools to identify duplication patterns, context fragmentation, and naming inconsistencies. Then, provide a prioritized list of improvements to help me leverage AI agents more effectively.`,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (name === 'remediate-issue') {
|
|
59
|
+
const issueId = args?.issueId;
|
|
60
|
+
return {
|
|
61
|
+
description: 'Issue remediation instructions',
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
role: 'user',
|
|
65
|
+
content: {
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: `I've identified an AIReady issue with ID: ${issueId}. Please use the \`get_remediation_diff\` tool to find a fix, explain the rationale behind the recommended change, and then help me apply it to the codebase.`,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw new Error(`Prompt not found: ${name}`);
|
|
75
|
+
});
|
|
76
|
+
}
|