@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/dist/index.js CHANGED
@@ -5,24 +5,213 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import {
7
7
  CallToolRequestSchema,
8
- ListToolsRequestSchema,
9
- ListResourcesRequestSchema,
10
- ReadResourceRequestSchema,
11
- ListPromptsRequestSchema,
12
- GetPromptRequestSchema
8
+ ListToolsRequestSchema
13
9
  } from "@modelcontextprotocol/sdk/types.js";
10
+
11
+ // src/tools/index.ts
12
+ import { z as z3 } from "zod";
14
13
  import { ToolRegistry, ToolName } from "@aiready/core";
14
+
15
+ // src/tools/best-practices.ts
15
16
  import { z } from "zod";
16
- var AnalysisArgsSchema = z.object({
17
- path: z.string().describe("Path to the directory to analyze"),
18
- summary_only: z.boolean().optional().describe(
17
+ import fs from "fs";
18
+ import path from "path";
19
+ import { fileURLToPath } from "url";
20
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+ var SKILLS_AGENTS_MD_PATH = path.resolve(
22
+ __dirname,
23
+ "../../skills/aiready-best-practices/AGENTS.md"
24
+ );
25
+ var BestPracticesArgsSchema = z.object({
26
+ category: z.string().describe(
27
+ "Category of best practices (e.g., patterns, context, consistency, signal, grounding)"
28
+ )
29
+ });
30
+ var ComplianceArgsSchema = z.object({
31
+ file_path: z.string().describe("Absolute path to the file to check")
32
+ });
33
+ async function handleGetBestPractices(args) {
34
+ const { category } = args;
35
+ try {
36
+ const content = await fs.promises.readFile(SKILLS_AGENTS_MD_PATH, "utf-8");
37
+ const lines = content.split("\n");
38
+ let inSection = false;
39
+ const sectionContent = [];
40
+ for (const line of lines) {
41
+ if (line.startsWith("## ") && line.toLowerCase().includes(`(${category.toLowerCase()})`)) {
42
+ inSection = true;
43
+ sectionContent.push(line);
44
+ continue;
45
+ }
46
+ if (inSection && line.startsWith("## ") && !line.toLowerCase().includes(`(${category.toLowerCase()})`)) {
47
+ break;
48
+ }
49
+ if (inSection) {
50
+ sectionContent.push(line);
51
+ }
52
+ }
53
+ if (sectionContent.length === 0) {
54
+ return {
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: `Category "${category}" not found in AIReady Best Practices.`
59
+ }
60
+ ],
61
+ isError: true
62
+ };
63
+ }
64
+ return {
65
+ content: [{ type: "text", text: sectionContent.join("\n") }]
66
+ };
67
+ } catch (error) {
68
+ return {
69
+ content: [
70
+ {
71
+ type: "text",
72
+ text: `Error reading best practices: ${error.message}`
73
+ }
74
+ ],
75
+ isError: true
76
+ };
77
+ }
78
+ }
79
+ async function handleCheckCompliance(args) {
80
+ const { file_path } = args;
81
+ try {
82
+ const content = await fs.promises.readFile(file_path, "utf-8");
83
+ const issues = [];
84
+ const lineCount = content.split("\n").length;
85
+ if (lineCount > 500) {
86
+ issues.push(
87
+ `\u26A0\uFE0F **Context Optimization (2.3)**: File has ${lineCount} lines. Large files waste context. Consider splitting into smaller modules.`
88
+ );
89
+ }
90
+ if (content.includes("true, false") || content.includes("false, true") || content.includes("true, true")) {
91
+ issues.push(
92
+ `\u{1F6A9} **AI Signal Clarity (4.1)**: Detected potential positional boolean traps. Prefer named options objects for clarity.`
93
+ );
94
+ }
95
+ const magicNumberRegex = /[^A-Z_a-z][0-9]{2,}[^A-Z_a-z0-9]/g;
96
+ if (magicNumberRegex.test(content)) {
97
+ issues.push(
98
+ `\u{1F6A9} **AI Signal Clarity (4.3)**: Detected potential magic literals (raw numbers). Use named constants/enums for business rules.`
99
+ );
100
+ }
101
+ const entropyRegex = /\b(data|info|handle|obj|item)\b/gi;
102
+ const matches = content.match(entropyRegex);
103
+ if (matches && matches.length > 5) {
104
+ issues.push(
105
+ `\u{1F6A9} **AI Signal Clarity (4.2)**: High-entropy names detected (${[...new Set(matches.map((m) => m.toLowerCase()))].join(", ")}). Use specific domain names instead.`
106
+ );
107
+ }
108
+ if (issues.length === 0) {
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: `\u2705 File "${path.basename(file_path)}" is compliant with lightweight AIReady Best Practices.`
114
+ }
115
+ ]
116
+ };
117
+ }
118
+ return {
119
+ content: [
120
+ {
121
+ type: "text",
122
+ text: `AIReady Compliance Report for "${path.basename(file_path)}":
123
+
124
+ ${issues.join("\n")}`
125
+ }
126
+ ]
127
+ };
128
+ } catch (error) {
129
+ return {
130
+ content: [
131
+ { type: "text", text: `Error checking compliance: ${error.message}` }
132
+ ],
133
+ isError: true
134
+ };
135
+ }
136
+ }
137
+
138
+ // src/tools/context-budget.ts
139
+ import { z as z2 } from "zod";
140
+ import fs2 from "fs";
141
+ import path2 from "path";
142
+ var ContextBudgetArgsSchema = z2.object({
143
+ file_path: z2.string().describe("Absolute path to the file to analyze")
144
+ });
145
+ async function handleAnalyzeContextBudget(args) {
146
+ const { file_path } = args;
147
+ try {
148
+ const content = await fs2.promises.readFile(file_path, "utf-8");
149
+ const charCount = content.length;
150
+ const tokens = Math.ceil(charCount / 4);
151
+ const tiers = [
152
+ { name: "8k (GPT-4/Turbo)", threshold: 8e3 },
153
+ { name: "32k (GPT-4o/Mini)", threshold: 32e3 },
154
+ { name: "128k (GPT-4o/Claude 3)", threshold: 128e3 },
155
+ { name: "200k (Claude 3.5 Sonnet)", threshold: 2e5 },
156
+ { name: "1M (Gemini 1.5 Pro)", threshold: 1e6 }
157
+ ];
158
+ let result = `# Context Budget for "${path2.basename(file_path)}"
159
+
160
+ `;
161
+ result += `**Estimated Tokens:** ${tokens.toLocaleString()}
162
+
163
+ `;
164
+ result += `### Context Usage By Tier
165
+ `;
166
+ tiers.forEach((tier) => {
167
+ const percentage = tokens / tier.threshold * 100;
168
+ const barLength = Math.min(Math.ceil(percentage / 5), 20);
169
+ const bar = "\u2588".repeat(barLength) + "\u2591".repeat(Math.max(0, 20 - barLength));
170
+ result += ` - **${tier.name}:** ${bar} ${percentage.toFixed(1)}%
171
+ `;
172
+ });
173
+ result += `
174
+ ### Recommendations
175
+ `;
176
+ if (tokens > 5e3) {
177
+ result += `\u26A0\uFE0F **High Context Usage:** This file alone takes up a significant chunk of smaller context windows. Consider refactoring to extract logical sub-modules.
178
+ `;
179
+ } else {
180
+ result += `\u2705 **Optimal Size:** This file is well-sized for AI context windows.
181
+ `;
182
+ }
183
+ const importCount = (content.match(/^import /gm) || []).length;
184
+ if (importCount > 20) {
185
+ result += `\u26A0\uFE0F **High Dependency Load:** ${importCount} imports found. AI will need to load many dependent files, multiplying the context budget needed. Use barrel exports (index.ts) to flatten the dependency tree.
186
+ `;
187
+ }
188
+ return {
189
+ content: [{ type: "text", text: result }]
190
+ };
191
+ } catch (error) {
192
+ return {
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: `Error analyzing context budget: ${error.message}`
197
+ }
198
+ ],
199
+ isError: true
200
+ };
201
+ }
202
+ }
203
+
204
+ // src/tools/index.ts
205
+ var AnalysisArgsSchema = z3.object({
206
+ path: z3.string().describe("Path to the directory to analyze"),
207
+ summary_only: z3.boolean().optional().describe(
19
208
  "If true, returns only the summary and skips the detailed issue list. Best for large projects to save context."
20
209
  )
21
210
  });
22
- var RemediationArgsSchema = z.object({
23
- issue_id: z.string().describe("The unique ID of the issue to fix"),
24
- file_path: z.string().describe("The path to the file containing the issue"),
25
- context: z.string().describe("The content of the file or surrounding code")
211
+ var RemediationArgsSchema = z3.object({
212
+ issue_id: z3.string().describe("The unique ID of the issue to fix"),
213
+ file_path: z3.string().describe("The path to the file containing the issue"),
214
+ context: z3.string().describe("The content of the file or surrounding code")
26
215
  });
27
216
  var TOOL_PACKAGE_MAP = {
28
217
  [ToolName.PatternDetect]: "@aiready/pattern-detect",
@@ -35,6 +224,11 @@ var TOOL_PACKAGE_MAP = {
35
224
  [ToolName.DependencyHealth]: "@aiready/deps",
36
225
  [ToolName.ChangeAmplification]: "@aiready/change-amplification",
37
226
  [ToolName.ContractEnforcement]: "@aiready/contract-enforcement",
227
+ // New tools from core
228
+ [ToolName.CognitiveLoad]: "@aiready/cognitive-load",
229
+ [ToolName.PatternEntropy]: "@aiready/pattern-entropy",
230
+ [ToolName.ConceptCohesion]: "@aiready/concept-cohesion",
231
+ [ToolName.SemanticDistance]: "@aiready/semantic-distance",
38
232
  // Aliases
39
233
  patterns: "@aiready/pattern-detect",
40
234
  duplicates: "@aiready/pattern-detect",
@@ -48,216 +242,423 @@ var TOOL_PACKAGE_MAP = {
48
242
  "change-amp": "@aiready/change-amplification",
49
243
  "contract-enforce": "@aiready/contract-enforcement"
50
244
  };
51
- var AIReadyMcpServer = class {
52
- constructor() {
53
- this.version = "0.2.10";
54
- this.server = new Server(
55
- {
56
- name: "aiready-server",
57
- version: this.version
58
- },
59
- {
60
- capabilities: {
61
- tools: {},
62
- resources: {},
63
- prompts: {}
64
- }
65
- }
245
+ var ADVERTISED_TOOLS = [
246
+ ToolName.PatternDetect,
247
+ ToolName.ContextAnalyzer,
248
+ ToolName.NamingConsistency,
249
+ ToolName.AiSignalClarity,
250
+ ToolName.AgentGrounding,
251
+ ToolName.TestabilityIndex,
252
+ ToolName.DocDrift,
253
+ ToolName.DependencyHealth,
254
+ ToolName.ChangeAmplification,
255
+ ToolName.ContractEnforcement,
256
+ ToolName.CognitiveLoad,
257
+ ToolName.PatternEntropy,
258
+ ToolName.ConceptCohesion,
259
+ ToolName.SemanticDistance,
260
+ "get_best_practices",
261
+ "check_best_practice_compliance",
262
+ "analyze_context_budget"
263
+ ];
264
+ async function handleAnalysis(name, args, stateStore2) {
265
+ const parsedArgs = AnalysisArgsSchema.safeParse(args);
266
+ if (!parsedArgs.success) {
267
+ throw new Error(
268
+ `Invalid arguments for ${name}: ${parsedArgs.error.message}`
66
269
  );
67
- this.setupHandlers();
68
- this.server.onerror = (error) => {
69
- console.error("[MCP Error]", error);
270
+ }
271
+ const { path: rootDir, summary_only } = parsedArgs.data;
272
+ let provider = ToolRegistry.find(name);
273
+ if (!provider) {
274
+ const packageName = TOOL_PACKAGE_MAP[name] ?? (name.startsWith("@aiready/") ? name : `@aiready/${name}`);
275
+ try {
276
+ console.error(
277
+ `[MCP] Dynamically loading ${packageName} for tool ${name}`
278
+ );
279
+ await import(packageName);
280
+ provider = ToolRegistry.find(name);
281
+ } catch (importError) {
282
+ const importErrorMessage = importError instanceof Error ? importError.message : String(importError);
283
+ const error = new Error(
284
+ `Tool ${name} not found and failed to load package ${packageName}: ${importErrorMessage}`
285
+ );
286
+ error.cause = importError;
287
+ throw error;
288
+ }
289
+ }
290
+ if (!provider) {
291
+ throw new Error(`Tool ${name} not found after attempting to load`);
292
+ }
293
+ console.error(
294
+ `[MCP] Executing ${name} on ${rootDir}${summary_only ? " (summary only)" : ""}`
295
+ );
296
+ const results = await provider.analyze({
297
+ rootDir
298
+ });
299
+ if (stateStore2) {
300
+ stateStore2.updateLastResults(results);
301
+ }
302
+ if (summary_only) {
303
+ const summary = results.summary;
304
+ return {
305
+ summary: `## Issue Breakdown
306
+ - Critical: ${summary.criticalIssues}
307
+ - Major: ${summary.majorIssues}
308
+ - Total Issues: ${summary.totalIssues}
309
+ - Files Analyzed: ${summary.totalFiles}`,
310
+ metadata: results.metadata,
311
+ notice: "Detailed issues were omitted (summary_only: true). Run without summary_only for full details."
70
312
  };
71
313
  }
72
- async handleRemediation(args) {
73
- const apiKey = process.env.AIREADY_API_KEY;
74
- const serverUrl = process.env.AIREADY_PLATFORM_URL || "https://platform.getaiready.dev";
75
- if (!apiKey) {
314
+ return results;
315
+ }
316
+ async function handleRemediation(args) {
317
+ const apiKey = process.env.AIREADY_API_KEY;
318
+ const serverUrl = process.env.AIREADY_PLATFORM_URL || "https://platform.getaiready.dev";
319
+ if (!apiKey) {
320
+ throw new Error(
321
+ "AIREADY_API_KEY is not set. Remediation requires an active subscription."
322
+ );
323
+ }
324
+ console.error(`[MCP] Requesting remediation for ${args.issue_id}...`);
325
+ try {
326
+ const response = await fetch(`${serverUrl}/api/v1/remediate`, {
327
+ method: "POST",
328
+ headers: {
329
+ "Content-Type": "application/json",
330
+ "X-API-KEY": apiKey
331
+ },
332
+ body: JSON.stringify({
333
+ issueId: args.issue_id,
334
+ filePath: args.file_path,
335
+ context: args.context,
336
+ agent: "mcp-server"
337
+ })
338
+ });
339
+ if (!response.ok) {
340
+ const errorData = await response.json().catch(() => ({}));
76
341
  throw new Error(
77
- "AIREADY_API_KEY is not set. Remediation requires an active subscription."
342
+ `Platform Error: ${errorData.message || response.statusText}`
78
343
  );
79
344
  }
80
- console.error(`[MCP] Requesting remediation for ${args.issue_id}...`);
81
- try {
82
- const response = await fetch(`${serverUrl}/api/v1/remediate`, {
83
- method: "POST",
84
- headers: {
85
- "Content-Type": "application/json",
86
- "X-API-KEY": apiKey
87
- },
88
- body: JSON.stringify({
89
- issueId: args.issue_id,
90
- filePath: args.file_path,
91
- context: args.context,
92
- agent: "mcp-server"
93
- })
94
- });
95
- if (!response.ok) {
96
- const errorData = await response.json().catch(() => ({}));
97
- throw new Error(
98
- `Platform Error: ${errorData.message || response.statusText}`
99
- );
100
- }
101
- const data = await response.json();
102
- return {
103
- content: [
104
- {
105
- type: "text",
106
- text: `Recommended Fix (Diff):
345
+ const data = await response.json();
346
+ return {
347
+ content: [
348
+ {
349
+ type: "text",
350
+ text: `Recommended Fix (Diff):
107
351
 
108
352
  ${data.diff}
109
353
 
110
354
  Rationale:
111
355
  ${data.rationale}`
356
+ }
357
+ ]
358
+ };
359
+ } catch (error) {
360
+ const errorMessage = error instanceof Error ? error.message : String(error);
361
+ return {
362
+ content: [
363
+ {
364
+ type: "text",
365
+ text: `Failed to get remediation: ${errorMessage}. Please visit the dashboard to fix manually.`
366
+ }
367
+ ],
368
+ isError: true
369
+ };
370
+ }
371
+ }
372
+
373
+ // src/resources/index.ts
374
+ import {
375
+ ListResourcesRequestSchema,
376
+ ReadResourceRequestSchema
377
+ } from "@modelcontextprotocol/sdk/types.js";
378
+ function registerResourceHandlers(server, stateStore2) {
379
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
380
+ return {
381
+ resources: [
382
+ {
383
+ uri: "aiready://project/summary",
384
+ name: "AIReady Project Summary",
385
+ description: "Quick top-level AI-readiness summary.",
386
+ mimeType: "text/markdown"
387
+ },
388
+ {
389
+ uri: "aiready://project/issues",
390
+ name: "AIReady Critical Issues",
391
+ description: "List of top 10 critical readiness issues.",
392
+ mimeType: "application/json"
393
+ },
394
+ {
395
+ uri: "aiready://project/graph",
396
+ name: "AIReady Codebase Graph",
397
+ description: "Force-directed graph data for visualization.",
398
+ mimeType: "application/json"
399
+ },
400
+ {
401
+ uri: "aiready://project/roadmap",
402
+ name: "AIReady Readiness Roadmap",
403
+ description: "A prioritized plan to reach elite readiness.",
404
+ mimeType: "text/markdown"
405
+ }
406
+ ]
407
+ };
408
+ });
409
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
410
+ const { uri } = request.params;
411
+ if (uri === "aiready://project/summary") {
412
+ return {
413
+ contents: [
414
+ {
415
+ uri,
416
+ mimeType: "text/markdown",
417
+ text: stateStore2.getSummaryMarkdown()
112
418
  }
113
419
  ]
114
420
  };
115
- } catch (error) {
116
- const errorMessage = error instanceof Error ? error.message : String(error);
421
+ }
422
+ if (uri === "aiready://project/issues") {
117
423
  return {
118
- content: [
424
+ contents: [
119
425
  {
120
- type: "text",
121
- text: `Failed to get remediation: ${errorMessage}. Please visit the dashboard to fix manually.`
426
+ uri,
427
+ mimeType: "application/json",
428
+ text: stateStore2.getIssuesJson()
122
429
  }
123
- ],
124
- isError: true
430
+ ]
125
431
  };
126
432
  }
127
- }
128
- setupHandlers() {
129
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
433
+ if (uri === "aiready://project/graph") {
130
434
  return {
131
- resources: [
435
+ contents: [
132
436
  {
133
- uri: "aiready://project/summary",
134
- name: "AIReady Project Summary",
135
- description: "Quick top-level AI-readiness summary.",
136
- mimeType: "text/markdown"
137
- },
138
- {
139
- uri: "aiready://project/issues",
140
- name: "AIReady Critical Issues",
141
- description: "List of top 10 critical readiness issues.",
142
- mimeType: "application/json"
143
- },
144
- {
145
- uri: "aiready://project/graph",
146
- name: "AIReady Codebase Graph",
147
- description: "Force-directed graph data for visualization.",
148
- mimeType: "application/json"
437
+ uri,
438
+ mimeType: "application/json",
439
+ text: stateStore2.getGraphJson()
149
440
  }
150
441
  ]
151
442
  };
152
- });
153
- this.server.setRequestHandler(
154
- ReadResourceRequestSchema,
155
- async (request) => {
156
- const { uri } = request.params;
157
- if (uri === "aiready://project/summary") {
158
- return {
159
- contents: [
160
- {
161
- uri,
162
- mimeType: "text/markdown",
163
- 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."
164
- }
165
- ]
166
- };
167
- }
168
- if (uri === "aiready://project/issues" || uri === "aiready://project/graph") {
169
- return {
170
- contents: [
171
- {
172
- uri,
173
- mimeType: "application/json",
174
- text: JSON.stringify({
175
- message: "Resource content coming from latest scan..."
176
- })
177
- }
178
- ]
179
- };
180
- }
181
- throw new Error(`Resource not found: ${uri}`);
182
- }
183
- );
184
- this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
443
+ }
444
+ if (uri === "aiready://project/roadmap") {
185
445
  return {
186
- prompts: [
187
- {
188
- name: "analyze-project",
189
- description: "Audit the project for AI-readiness and suggest improvements.",
190
- arguments: [
191
- {
192
- name: "path",
193
- description: "Path/directory to analyze",
194
- required: true
195
- }
196
- ]
197
- },
446
+ contents: [
198
447
  {
199
- name: "remediate-issue",
200
- description: "Help the user fix a specific AIReady issue.",
201
- arguments: [
202
- {
203
- name: "issueId",
204
- description: "The unique ID of the issue to fix",
205
- required: true
206
- }
207
- ]
448
+ uri,
449
+ mimeType: "text/markdown",
450
+ text: stateStore2.getRoadmapMarkdown()
208
451
  }
209
452
  ]
210
453
  };
211
- });
212
- this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
213
- const { name, arguments: args } = request.params;
214
- if (name === "analyze-project") {
215
- const path = args?.path || ".";
216
- return {
217
- description: "Project audit instructions",
218
- messages: [
454
+ }
455
+ throw new Error(`Resource not found: ${uri}`);
456
+ });
457
+ }
458
+
459
+ // src/prompts/index.ts
460
+ import {
461
+ ListPromptsRequestSchema,
462
+ GetPromptRequestSchema
463
+ } from "@modelcontextprotocol/sdk/types.js";
464
+ function registerPromptHandlers(server) {
465
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
466
+ return {
467
+ prompts: [
468
+ {
469
+ name: "analyze-project",
470
+ description: "Audit the project for AI-readiness and suggest improvements.",
471
+ arguments: [
219
472
  {
220
- role: "user",
221
- content: {
222
- type: "text",
223
- 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.`
224
- }
473
+ name: "path",
474
+ description: "Path/directory to analyze",
475
+ required: true
225
476
  }
226
477
  ]
227
- };
228
- }
229
- if (name === "remediate-issue") {
230
- const issueId = args?.issueId;
231
- return {
232
- description: "Issue remediation instructions",
233
- messages: [
478
+ },
479
+ {
480
+ name: "remediate-issue",
481
+ description: "Help the user fix a specific AIReady issue.",
482
+ arguments: [
234
483
  {
235
- role: "user",
236
- content: {
237
- type: "text",
238
- 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.`
239
- }
484
+ name: "issueId",
485
+ description: "The unique ID of the issue to fix",
486
+ required: true
240
487
  }
241
488
  ]
242
- };
489
+ }
490
+ ]
491
+ };
492
+ });
493
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
494
+ const { name, arguments: args } = request.params;
495
+ if (name === "analyze-project") {
496
+ const path3 = args?.path || ".";
497
+ return {
498
+ description: "Project audit instructions",
499
+ messages: [
500
+ {
501
+ role: "user",
502
+ content: {
503
+ type: "text",
504
+ text: `I want to audit the project at "${path3}" 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.`
505
+ }
506
+ }
507
+ ]
508
+ };
509
+ }
510
+ if (name === "remediate-issue") {
511
+ const issueId = args?.issueId;
512
+ return {
513
+ description: "Issue remediation instructions",
514
+ messages: [
515
+ {
516
+ role: "user",
517
+ content: {
518
+ type: "text",
519
+ 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.`
520
+ }
521
+ }
522
+ ]
523
+ };
524
+ }
525
+ throw new Error(`Prompt not found: ${name}`);
526
+ });
527
+ }
528
+
529
+ // src/state-store.ts
530
+ var StateStore = class {
531
+ constructor() {
532
+ this.lastResults = null;
533
+ this.lastScanTimestamp = null;
534
+ }
535
+ updateLastResults(results) {
536
+ this.lastResults = results;
537
+ this.lastScanTimestamp = (/* @__PURE__ */ new Date()).toISOString();
538
+ }
539
+ getLastResults() {
540
+ return this.lastResults;
541
+ }
542
+ getSummaryMarkdown() {
543
+ if (!this.lastResults) {
544
+ return "# AIReady Summary\n\nNo scan has been run yet. Run an AIReady scan tool to see results here.";
545
+ }
546
+ const { score, summary } = this.lastResults;
547
+ const grade = this.calculateGrade(score);
548
+ return `# AIReady Summary
549
+
550
+ Project Score: **${score}/100 (${grade})**
551
+ Last Scan: ${this.lastScanTimestamp}
552
+
553
+ ## Issue Breakdown
554
+ - Critical: ${summary.criticalIssues}
555
+ - Major: ${summary.majorIssues}
556
+ - Total Issues: ${summary.totalIssues}
557
+ - Files Analyzed: ${summary.totalFiles}
558
+
559
+ Run the \`aiready-mcp\` tool for a detailed analysis.`;
560
+ }
561
+ getIssuesJson() {
562
+ if (!this.lastResults) {
563
+ return JSON.stringify({
564
+ message: "No issues found. Please run a scan first."
565
+ });
566
+ }
567
+ const topIssues = this.lastResults.issues.slice(0, 10);
568
+ return JSON.stringify(topIssues, null, 2);
569
+ }
570
+ getGraphJson() {
571
+ if (!this.lastResults || !this.lastResults.metadata.graph) {
572
+ return JSON.stringify({
573
+ message: "Graph data not available. Run a scan with graph analysis enabled."
574
+ });
575
+ }
576
+ return JSON.stringify(this.lastResults.metadata.graph, null, 2);
577
+ }
578
+ getRoadmapMarkdown() {
579
+ if (!this.lastResults) {
580
+ return "# AIReady Roadmap\n\nNo scan has been run yet. Run an AIReady scan to generate a prioritized roadmap.";
581
+ }
582
+ const { score, issues } = this.lastResults;
583
+ const grade = this.calculateGrade(score);
584
+ let roadmap = `# AIReady Roadmap
585
+
586
+ `;
587
+ roadmap += `Current Score: **${score}/100 (${grade})**
588
+
589
+ `;
590
+ roadmap += `## Phase 1: High-Impact Fixes (Readiness Score 90+)
591
+ `;
592
+ const criticalIssues = issues.filter((i) => i.severity === "critical").slice(0, 3);
593
+ if (criticalIssues.length > 0) {
594
+ criticalIssues.forEach((i) => {
595
+ roadmap += ` - [ ] **Fix ${i.type}** in \`${i.location.file}:L${i.location.line}\`: ${i.message}
596
+ `;
597
+ });
598
+ } else {
599
+ roadmap += ` - \u2705 All critical issues resolved!
600
+ `;
601
+ }
602
+ roadmap += `
603
+ ## Phase 2: Structural Optimization (Efficiency)
604
+ `;
605
+ const majorIssues = issues.filter((i) => i.severity === "major").slice(0, 3);
606
+ if (majorIssues.length > 0) {
607
+ majorIssues.forEach((i) => {
608
+ roadmap += ` - [ ] **Address ${i.type}**: ${i.message} (\`${i.location.file}\`)
609
+ `;
610
+ });
611
+ } else {
612
+ roadmap += ` - \u2705 All major structural issues resolved!
613
+ `;
614
+ }
615
+ roadmap += `
616
+ ## Phase 3: Continuous AI Excellence
617
+ `;
618
+ roadmap += ` - [ ] Implement \`mcp-server\` in CI/CD pipeline.
619
+ `;
620
+ roadmap += ` - [ ] Achieve consistency score > 95 across all naming patterns.
621
+ `;
622
+ return roadmap;
623
+ }
624
+ calculateGrade(score) {
625
+ if (score >= 90) return "A";
626
+ if (score >= 80) return "B";
627
+ if (score >= 70) return "C";
628
+ if (score >= 60) return "D";
629
+ return "F";
630
+ }
631
+ };
632
+ var stateStore = new StateStore();
633
+
634
+ // src/index.ts
635
+ var AIReadyMcpServer = class {
636
+ constructor() {
637
+ this.version = "0.3.0";
638
+ this.server = new Server(
639
+ {
640
+ name: "aiready-server",
641
+ version: this.version
642
+ },
643
+ {
644
+ capabilities: {
645
+ tools: {},
646
+ resources: {},
647
+ prompts: {}
648
+ }
243
649
  }
244
- throw new Error(`Prompt not found: ${name}`);
245
- });
650
+ );
651
+ this.setupHandlers();
652
+ this.server.onerror = (error) => {
653
+ console.error("[MCP Error]", error);
654
+ };
655
+ }
656
+ setupHandlers() {
657
+ registerResourceHandlers(this.server, stateStore);
658
+ registerPromptHandlers(this.server);
246
659
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
247
- const toolsToAdvertise = [
248
- ToolName.PatternDetect,
249
- ToolName.ContextAnalyzer,
250
- ToolName.NamingConsistency,
251
- ToolName.AiSignalClarity,
252
- ToolName.AgentGrounding,
253
- ToolName.TestabilityIndex,
254
- ToolName.DocDrift,
255
- ToolName.DependencyHealth,
256
- ToolName.ChangeAmplification,
257
- ToolName.ContractEnforcement
258
- ];
259
660
  const tools = [
260
- ...toolsToAdvertise.map((id) => ({
661
+ ...ADVERTISED_TOOLS.map((id) => ({
261
662
  name: id,
262
663
  description: `Scan the directory for ${id} issues to improve AI-readiness.`,
263
664
  inputSchema: {
@@ -304,61 +705,27 @@ ${data.rationale}`
304
705
  const { name, arguments: args } = request.params;
305
706
  try {
306
707
  if (name === "get_remediation_diff") {
307
- const parsedArgs2 = RemediationArgsSchema.safeParse(args);
308
- if (!parsedArgs2.success) {
309
- throw new Error(
310
- `Invalid arguments for ${name}: ${parsedArgs2.error.message}`
311
- );
312
- }
313
- return await this.handleRemediation(parsedArgs2.data);
708
+ const parsedArgs = RemediationArgsSchema.parse(args);
709
+ return await handleRemediation(parsedArgs);
314
710
  }
315
- const parsedArgs = AnalysisArgsSchema.safeParse(args);
316
- if (!parsedArgs.success) {
317
- throw new Error(
318
- `Invalid arguments for ${name}: ${parsedArgs.error.message}`
319
- );
711
+ if (name === "get_best_practices") {
712
+ const parsedArgs = BestPracticesArgsSchema.parse(args);
713
+ return await handleGetBestPractices(parsedArgs);
320
714
  }
321
- const { path: rootDir, summary_only } = parsedArgs.data;
322
- let provider = ToolRegistry.find(name);
323
- if (!provider) {
324
- const packageName = TOOL_PACKAGE_MAP[name] ?? (name.startsWith("@aiready/") ? name : `@aiready/${name}`);
325
- try {
326
- console.error(
327
- `[MCP] Dynamically loading ${packageName} for tool ${name}`
328
- );
329
- await import(packageName);
330
- provider = ToolRegistry.find(name);
331
- } catch (importError) {
332
- const importErrorMessage = importError instanceof Error ? importError.message : String(importError);
333
- console.error(
334
- `[MCP] Failed to load tool package ${packageName}: ${importErrorMessage}`
335
- );
336
- const error = new Error(
337
- `Tool ${name} not found and failed to load package ${packageName}: ${importErrorMessage}`
338
- );
339
- error.cause = importError;
340
- throw error;
341
- }
715
+ if (name === "check_best_practice_compliance") {
716
+ const parsedArgs = ComplianceArgsSchema.parse(args);
717
+ return await handleCheckCompliance(parsedArgs);
342
718
  }
343
- if (!provider) {
344
- throw new Error(`Tool ${name} not found after attempting to load`);
719
+ if (name === "analyze_context_budget") {
720
+ const parsedArgs = ContextBudgetArgsSchema.parse(args);
721
+ return await handleAnalyzeContextBudget(parsedArgs);
345
722
  }
346
- console.error(
347
- `[MCP] Executing ${name} on ${rootDir}${summary_only ? " (summary only)" : ""}`
348
- );
349
- const results = await provider.analyze({
350
- rootDir
351
- });
352
- const responseData = summary_only ? {
353
- summary: results.summary,
354
- metadata: results.metadata,
355
- notice: "Detailed issues were omitted (summary_only: true). Run without summary_only for full details."
356
- } : results;
723
+ const results = await handleAnalysis(name, args, stateStore);
357
724
  return {
358
725
  content: [
359
726
  {
360
727
  type: "text",
361
- text: JSON.stringify(responseData, null, 2)
728
+ text: JSON.stringify(results, null, 2)
362
729
  }
363
730
  ]
364
731
  };