@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.
@@ -0,0 +1,94 @@
1
+ import {
2
+ ListResourcesRequestSchema,
3
+ ReadResourceRequestSchema,
4
+ } from '@modelcontextprotocol/sdk/types.js';
5
+ import { StateStore } from '../state-store.js';
6
+
7
+ export function registerResourceHandlers(server: any, stateStore: StateStore) {
8
+ // List available resources
9
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
10
+ return {
11
+ resources: [
12
+ {
13
+ uri: 'aiready://project/summary',
14
+ name: 'AIReady Project Summary',
15
+ description: 'Quick top-level AI-readiness summary.',
16
+ mimeType: 'text/markdown',
17
+ },
18
+ {
19
+ uri: 'aiready://project/issues',
20
+ name: 'AIReady Critical Issues',
21
+ description: 'List of top 10 critical readiness issues.',
22
+ mimeType: 'application/json',
23
+ },
24
+ {
25
+ uri: 'aiready://project/graph',
26
+ name: 'AIReady Codebase Graph',
27
+ description: 'Force-directed graph data for visualization.',
28
+ mimeType: 'application/json',
29
+ },
30
+ {
31
+ uri: 'aiready://project/roadmap',
32
+ name: 'AIReady Readiness Roadmap',
33
+ description: 'A prioritized plan to reach elite readiness.',
34
+ mimeType: 'text/markdown',
35
+ },
36
+ ],
37
+ };
38
+ });
39
+
40
+ // Read resource content
41
+ server.setRequestHandler(ReadResourceRequestSchema, async (request: any) => {
42
+ const { uri } = request.params;
43
+
44
+ if (uri === 'aiready://project/summary') {
45
+ return {
46
+ contents: [
47
+ {
48
+ uri,
49
+ mimeType: 'text/markdown',
50
+ text: stateStore.getSummaryMarkdown(),
51
+ },
52
+ ],
53
+ };
54
+ }
55
+
56
+ if (uri === 'aiready://project/issues') {
57
+ return {
58
+ contents: [
59
+ {
60
+ uri,
61
+ mimeType: 'application/json',
62
+ text: stateStore.getIssuesJson(),
63
+ },
64
+ ],
65
+ };
66
+ }
67
+
68
+ if (uri === 'aiready://project/graph') {
69
+ return {
70
+ contents: [
71
+ {
72
+ uri,
73
+ mimeType: 'application/json',
74
+ text: stateStore.getGraphJson(),
75
+ },
76
+ ],
77
+ };
78
+ }
79
+
80
+ if (uri === 'aiready://project/roadmap') {
81
+ return {
82
+ contents: [
83
+ {
84
+ uri,
85
+ mimeType: 'text/markdown',
86
+ text: stateStore.getRoadmapMarkdown(),
87
+ },
88
+ ],
89
+ };
90
+ }
91
+
92
+ throw new Error(`Resource not found: ${uri}`);
93
+ });
94
+ }
@@ -0,0 +1,111 @@
1
+ import { UnifiedReport } from '@aiready/core';
2
+
3
+ export class StateStore {
4
+ private lastResults: UnifiedReport | null = null;
5
+ private lastScanTimestamp: string | null = null;
6
+
7
+ updateLastResults(results: UnifiedReport) {
8
+ this.lastResults = results;
9
+ this.lastScanTimestamp = new Date().toISOString();
10
+ }
11
+
12
+ getLastResults(): UnifiedReport | null {
13
+ return this.lastResults;
14
+ }
15
+
16
+ getSummaryMarkdown(): string {
17
+ if (!this.lastResults) {
18
+ return '# AIReady Summary\n\nNo scan has been run yet. Run an AIReady scan tool to see results here.';
19
+ }
20
+
21
+ const { score, summary } = this.lastResults;
22
+ const grade = this.calculateGrade(score);
23
+
24
+ return `# AIReady Summary
25
+
26
+ Project Score: **${score}/100 (${grade})**
27
+ Last Scan: ${this.lastScanTimestamp}
28
+
29
+ ## Issue Breakdown
30
+ - Critical: ${summary.criticalIssues}
31
+ - Major: ${summary.majorIssues}
32
+ - Total Issues: ${summary.totalIssues}
33
+ - Files Analyzed: ${summary.totalFiles}
34
+
35
+ Run the \`aiready-mcp\` tool for a detailed analysis.`;
36
+ }
37
+
38
+ getIssuesJson(): string {
39
+ if (!this.lastResults) {
40
+ return JSON.stringify({
41
+ message: 'No issues found. Please run a scan first.',
42
+ });
43
+ }
44
+
45
+ // Return top 10 issues
46
+ const topIssues = this.lastResults.issues.slice(0, 10);
47
+ return JSON.stringify(topIssues, null, 2);
48
+ }
49
+
50
+ getGraphJson(): string {
51
+ if (!this.lastResults || !this.lastResults.metadata.graph) {
52
+ return JSON.stringify({
53
+ message:
54
+ 'Graph data not available. Run a scan with graph analysis enabled.',
55
+ });
56
+ }
57
+ return JSON.stringify(this.lastResults.metadata.graph, null, 2);
58
+ }
59
+
60
+ getRoadmapMarkdown(): string {
61
+ if (!this.lastResults) {
62
+ return '# AIReady Roadmap\n\nNo scan has been run yet. Run an AIReady scan to generate a prioritized roadmap.';
63
+ }
64
+
65
+ const { score, issues } = this.lastResults;
66
+ const grade = this.calculateGrade(score);
67
+
68
+ let roadmap = `# AIReady Roadmap\n\n`;
69
+ roadmap += `Current Score: **${score}/100 (${grade})**\n\n`;
70
+
71
+ roadmap += `## Phase 1: High-Impact Fixes (Readiness Score 90+)\n`;
72
+ const criticalIssues = issues
73
+ .filter((i: any) => i.severity === 'critical')
74
+ .slice(0, 3);
75
+ if (criticalIssues.length > 0) {
76
+ criticalIssues.forEach((i: any) => {
77
+ roadmap += ` - [ ] **Fix ${i.type}** in \`${i.location.file}:L${i.location.line}\`: ${i.message}\n`;
78
+ });
79
+ } else {
80
+ roadmap += ` - ✅ All critical issues resolved!\n`;
81
+ }
82
+
83
+ roadmap += `\n## Phase 2: Structural Optimization (Efficiency)\n`;
84
+ const majorIssues = issues
85
+ .filter((i: any) => i.severity === 'major')
86
+ .slice(0, 3);
87
+ if (majorIssues.length > 0) {
88
+ majorIssues.forEach((i: any) => {
89
+ roadmap += ` - [ ] **Address ${i.type}**: ${i.message} (\`${i.location.file}\`)\n`;
90
+ });
91
+ } else {
92
+ roadmap += ` - ✅ All major structural issues resolved!\n`;
93
+ }
94
+
95
+ roadmap += `\n## Phase 3: Continuous AI Excellence\n`;
96
+ roadmap += ` - [ ] Implement \`mcp-server\` in CI/CD pipeline.\n`;
97
+ roadmap += ` - [ ] Achieve consistency score > 95 across all naming patterns.\n`;
98
+
99
+ return roadmap;
100
+ }
101
+
102
+ private calculateGrade(score: number): string {
103
+ if (score >= 90) return 'A';
104
+ if (score >= 80) return 'B';
105
+ if (score >= 70) return 'C';
106
+ if (score >= 60) return 'D';
107
+ return 'F';
108
+ }
109
+ }
110
+
111
+ export const stateStore = new StateStore();
@@ -0,0 +1,167 @@
1
+ import { z } from 'zod';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ // Resolve skills path: go up to src, then to package root, then to monorepo root, then into skills
9
+ // Actually, it's easier to use a relative path if they are in the same monorepo
10
+ const SKILLS_AGENTS_MD_PATH = path.resolve(
11
+ __dirname,
12
+ '../../skills/aiready-best-practices/AGENTS.md'
13
+ );
14
+
15
+ export const BestPracticesArgsSchema = z.object({
16
+ category: z
17
+ .string()
18
+ .describe(
19
+ 'Category of best practices (e.g., patterns, context, consistency, signal, grounding)'
20
+ ),
21
+ });
22
+
23
+ export const ComplianceArgsSchema = z.object({
24
+ file_path: z.string().describe('Absolute path to the file to check'),
25
+ });
26
+
27
+ export async function handleGetBestPractices(
28
+ args: z.infer<typeof BestPracticesArgsSchema>
29
+ ) {
30
+ const { category } = args;
31
+
32
+ try {
33
+ const content = await fs.promises.readFile(SKILLS_AGENTS_MD_PATH, 'utf-8');
34
+
35
+ // Simple parsing to extract the relevant section based on ## <Number>. <Category>
36
+ // We'll search for the heading that starts with the number and contains the category name in parentheses
37
+ // e.g., "## 1. Pattern Detection (patterns)"
38
+ const lines = content.split('\n');
39
+ let inSection = false;
40
+ const sectionContent: string[] = [];
41
+
42
+ for (const line of lines) {
43
+ if (
44
+ line.startsWith('## ') &&
45
+ line.toLowerCase().includes(`(${category.toLowerCase()})`)
46
+ ) {
47
+ inSection = true;
48
+ sectionContent.push(line);
49
+ continue;
50
+ }
51
+
52
+ if (
53
+ inSection &&
54
+ line.startsWith('## ') &&
55
+ !line.toLowerCase().includes(`(${category.toLowerCase()})`)
56
+ ) {
57
+ break; // Next main section
58
+ }
59
+
60
+ if (inSection) {
61
+ sectionContent.push(line);
62
+ }
63
+ }
64
+
65
+ if (sectionContent.length === 0) {
66
+ return {
67
+ content: [
68
+ {
69
+ type: 'text',
70
+ text: `Category "${category}" not found in AIReady Best Practices.`,
71
+ },
72
+ ],
73
+ isError: true,
74
+ };
75
+ }
76
+
77
+ return {
78
+ content: [{ type: 'text', text: sectionContent.join('\n') }],
79
+ };
80
+ } catch (error: any) {
81
+ return {
82
+ content: [
83
+ {
84
+ type: 'text',
85
+ text: `Error reading best practices: ${error.message}`,
86
+ },
87
+ ],
88
+ isError: true,
89
+ };
90
+ }
91
+ }
92
+
93
+ export async function handleCheckCompliance(
94
+ args: z.infer<typeof ComplianceArgsSchema>
95
+ ) {
96
+ const { file_path } = args;
97
+
98
+ try {
99
+ const content = await fs.promises.readFile(file_path, 'utf-8');
100
+ const issues: string[] = [];
101
+
102
+ // Lightweight checks based on AGENTS.md rules
103
+
104
+ // 1. File Length (>500 lines)
105
+ const lineCount = content.split('\n').length;
106
+ if (lineCount > 500) {
107
+ issues.push(
108
+ `⚠️ **Context Optimization (2.3)**: File has ${lineCount} lines. Large files waste context. Consider splitting into smaller modules.`
109
+ );
110
+ }
111
+
112
+ // 2. Boolean Trap Parameters
113
+ if (
114
+ content.includes('true, false') ||
115
+ content.includes('false, true') ||
116
+ content.includes('true, true')
117
+ ) {
118
+ issues.push(
119
+ `🚩 **AI Signal Clarity (4.1)**: Detected potential positional boolean traps. Prefer named options objects for clarity.`
120
+ );
121
+ }
122
+
123
+ // 3. Magic Literals
124
+ const magicNumberRegex = /[^A-Z_a-z][0-9]{2,}[^A-Z_a-z0-9]/g;
125
+ if (magicNumberRegex.test(content)) {
126
+ issues.push(
127
+ `🚩 **AI Signal Clarity (4.3)**: Detected potential magic literals (raw numbers). Use named constants/enums for business rules.`
128
+ );
129
+ }
130
+
131
+ // 4. Entropy
132
+ const entropyRegex = /\b(data|info|handle|obj|item)\b/gi;
133
+ const matches = content.match(entropyRegex);
134
+ if (matches && matches.length > 5) {
135
+ issues.push(
136
+ `🚩 **AI Signal Clarity (4.2)**: High-entropy names detected (${[...new Set(matches.map((m) => m.toLowerCase()))].join(', ')}). Use specific domain names instead.`
137
+ );
138
+ }
139
+
140
+ if (issues.length === 0) {
141
+ return {
142
+ content: [
143
+ {
144
+ type: 'text',
145
+ text: `✅ File "${path.basename(file_path)}" is compliant with lightweight AIReady Best Practices.`,
146
+ },
147
+ ],
148
+ };
149
+ }
150
+
151
+ return {
152
+ content: [
153
+ {
154
+ type: 'text',
155
+ text: `AIReady Compliance Report for "${path.basename(file_path)}":\n\n${issues.join('\n')}`,
156
+ },
157
+ ],
158
+ };
159
+ } catch (error: any) {
160
+ return {
161
+ content: [
162
+ { type: 'text', text: `Error checking compliance: ${error.message}` },
163
+ ],
164
+ isError: true,
165
+ };
166
+ }
167
+ }
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ export const ContextBudgetArgsSchema = z.object({
6
+ file_path: z.string().describe('Absolute path to the file to analyze'),
7
+ });
8
+
9
+ export interface TokenEstimation {
10
+ tokens: number;
11
+ budgetPercentage: number;
12
+ tier: string;
13
+ }
14
+
15
+ export async function handleAnalyzeContextBudget(
16
+ args: z.infer<typeof ContextBudgetArgsSchema>
17
+ ) {
18
+ const { file_path } = args;
19
+
20
+ try {
21
+ const content = await fs.promises.readFile(file_path, 'utf-8');
22
+
23
+ // Token estimation (approx 4 chars/token)
24
+ const charCount = content.length;
25
+ const tokens = Math.ceil(charCount / 4);
26
+
27
+ // Tiers according to common context windows
28
+ const tiers = [
29
+ { name: '8k (GPT-4/Turbo)', threshold: 8000 },
30
+ { name: '32k (GPT-4o/Mini)', threshold: 32000 },
31
+ { name: '128k (GPT-4o/Claude 3)', threshold: 128000 },
32
+ { name: '200k (Claude 3.5 Sonnet)', threshold: 200000 },
33
+ { name: '1M (Gemini 1.5 Pro)', threshold: 1000000 },
34
+ ];
35
+
36
+ let result = `# Context Budget for "${path.basename(file_path)}"\n\n`;
37
+ result += `**Estimated Tokens:** ${tokens.toLocaleString()}\n\n`;
38
+
39
+ result += `### Context Usage By Tier\n`;
40
+ tiers.forEach((tier) => {
41
+ const percentage = (tokens / tier.threshold) * 100;
42
+ const barLength = Math.min(Math.ceil(percentage / 5), 20);
43
+ const bar =
44
+ '█'.repeat(barLength) + '░'.repeat(Math.max(0, 20 - barLength));
45
+ result += ` - **${tier.name}:** ${bar} ${percentage.toFixed(1)}%\n`;
46
+ });
47
+
48
+ result += `\n### Recommendations\n`;
49
+
50
+ if (tokens > 5000) {
51
+ result += `⚠️ **High Context Usage:** This file alone takes up a significant chunk of smaller context windows. Consider refactoring to extract logical sub-modules.\n`;
52
+ } else {
53
+ result += `✅ **Optimal Size:** This file is well-sized for AI context windows.\n`;
54
+ }
55
+
56
+ // Check for large imports
57
+ const importCount = (content.match(/^import /gm) || []).length;
58
+ if (importCount > 20) {
59
+ result += `⚠️ **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.\n`;
60
+ }
61
+
62
+ return {
63
+ content: [{ type: 'text', text: result }],
64
+ };
65
+ } catch (error: any) {
66
+ return {
67
+ content: [
68
+ {
69
+ type: 'text',
70
+ text: `Error analyzing context budget: ${error.message}`,
71
+ },
72
+ ],
73
+ isError: true,
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,236 @@
1
+ import { z } from 'zod';
2
+ import { ToolRegistry, ToolName } from '@aiready/core';
3
+ import {
4
+ handleGetBestPractices,
5
+ handleCheckCompliance,
6
+ BestPracticesArgsSchema,
7
+ ComplianceArgsSchema,
8
+ } from './best-practices.js';
9
+ import {
10
+ handleAnalyzeContextBudget,
11
+ ContextBudgetArgsSchema,
12
+ } from './context-budget.js';
13
+
14
+ /**
15
+ * Zod schemas for tool arguments
16
+ */
17
+ export const AnalysisArgsSchema = z.object({
18
+ path: z.string().describe('Path to the directory to analyze'),
19
+ summary_only: z
20
+ .boolean()
21
+ .optional()
22
+ .describe(
23
+ 'If true, returns only the summary and skips the detailed issue list. Best for large projects to save context.'
24
+ ),
25
+ });
26
+
27
+ export 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
+ export {
34
+ BestPracticesArgsSchema,
35
+ ComplianceArgsSchema,
36
+ ContextBudgetArgsSchema,
37
+ };
38
+
39
+ /**
40
+ * Mapping between tool names and @aiready/ package names.
41
+ * Used for dynamic registration on-demand to minimize initial context budget.
42
+ */
43
+ export const TOOL_PACKAGE_MAP: Record<string, string> = {
44
+ [ToolName.PatternDetect]: '@aiready/pattern-detect',
45
+ [ToolName.ContextAnalyzer]: '@aiready/context-analyzer',
46
+ [ToolName.NamingConsistency]: '@aiready/consistency',
47
+ [ToolName.AiSignalClarity]: '@aiready/ai-signal-clarity',
48
+ [ToolName.AgentGrounding]: '@aiready/agent-grounding',
49
+ [ToolName.TestabilityIndex]: '@aiready/testability',
50
+ [ToolName.DocDrift]: '@aiready/doc-drift',
51
+ [ToolName.DependencyHealth]: '@aiready/deps',
52
+ [ToolName.ChangeAmplification]: '@aiready/change-amplification',
53
+ [ToolName.ContractEnforcement]: '@aiready/contract-enforcement',
54
+ // New tools from core
55
+ [ToolName.CognitiveLoad]: '@aiready/cognitive-load',
56
+ [ToolName.PatternEntropy]: '@aiready/pattern-entropy',
57
+ [ToolName.ConceptCohesion]: '@aiready/concept-cohesion',
58
+ [ToolName.SemanticDistance]: '@aiready/semantic-distance',
59
+ // Aliases
60
+ patterns: '@aiready/pattern-detect',
61
+ duplicates: '@aiready/pattern-detect',
62
+ context: '@aiready/context-analyzer',
63
+ fragmentation: '@aiready/context-analyzer',
64
+ consistency: '@aiready/consistency',
65
+ 'ai-signal': '@aiready/ai-signal-clarity',
66
+ grounding: '@aiready/agent-grounding',
67
+ testability: '@aiready/testability',
68
+ 'deps-health': '@aiready/deps',
69
+ 'change-amp': '@aiready/change-amplification',
70
+ 'contract-enforce': '@aiready/contract-enforcement',
71
+ };
72
+
73
+ /**
74
+ * List of tools to advertise to the client
75
+ */
76
+ export const ADVERTISED_TOOLS = [
77
+ ToolName.PatternDetect,
78
+ ToolName.ContextAnalyzer,
79
+ ToolName.NamingConsistency,
80
+ ToolName.AiSignalClarity,
81
+ ToolName.AgentGrounding,
82
+ ToolName.TestabilityIndex,
83
+ ToolName.DocDrift,
84
+ ToolName.DependencyHealth,
85
+ ToolName.ChangeAmplification,
86
+ ToolName.ContractEnforcement,
87
+ ToolName.CognitiveLoad,
88
+ ToolName.PatternEntropy,
89
+ ToolName.ConceptCohesion,
90
+ ToolName.SemanticDistance,
91
+ 'get_best_practices',
92
+ 'check_best_practice_compliance',
93
+ 'analyze_context_budget',
94
+ ];
95
+
96
+ export {
97
+ handleGetBestPractices,
98
+ handleCheckCompliance,
99
+ handleAnalyzeContextBudget,
100
+ };
101
+
102
+ export async function handleAnalysis(
103
+ name: string,
104
+ args: any,
105
+ stateStore?: any
106
+ ) {
107
+ const parsedArgs = AnalysisArgsSchema.safeParse(args);
108
+ if (!parsedArgs.success) {
109
+ throw new Error(
110
+ `Invalid arguments for ${name}: ${parsedArgs.error.message}`
111
+ );
112
+ }
113
+ const { path: rootDir, summary_only } = parsedArgs.data;
114
+
115
+ let provider = ToolRegistry.find(name);
116
+
117
+ // Dynamic loading if not already registered
118
+ if (!provider) {
119
+ const packageName =
120
+ TOOL_PACKAGE_MAP[name] ??
121
+ (name.startsWith('@aiready/') ? name : `@aiready/${name}`);
122
+
123
+ try {
124
+ console.error(
125
+ `[MCP] Dynamically loading ${packageName} for tool ${name}`
126
+ );
127
+ await import(packageName);
128
+ provider = ToolRegistry.find(name);
129
+ } catch (importError: unknown) {
130
+ const importErrorMessage =
131
+ importError instanceof Error
132
+ ? importError.message
133
+ : String(importError);
134
+ const error = new Error(
135
+ `Tool ${name} not found and failed to load package ${packageName}: ${importErrorMessage}`
136
+ );
137
+ (error as any).cause = importError;
138
+ throw error;
139
+ }
140
+ }
141
+
142
+ if (!provider) {
143
+ throw new Error(`Tool ${name} not found after attempting to load`);
144
+ }
145
+
146
+ console.error(
147
+ `[MCP] Executing ${name} on ${rootDir}${summary_only ? ' (summary only)' : ''}`
148
+ );
149
+
150
+ const results = await provider.analyze({
151
+ rootDir,
152
+ });
153
+
154
+ // Update state store if provided
155
+ if (stateStore) {
156
+ stateStore.updateLastResults(results);
157
+ }
158
+
159
+ // Format results for the agent
160
+ if (summary_only) {
161
+ const summary = results.summary;
162
+ return {
163
+ summary: `## Issue Breakdown
164
+ - Critical: ${summary.criticalIssues}
165
+ - Major: ${summary.majorIssues}
166
+ - Total Issues: ${summary.totalIssues}
167
+ - Files Analyzed: ${summary.totalFiles}`,
168
+ metadata: results.metadata,
169
+ notice:
170
+ 'Detailed issues were omitted (summary_only: true). Run without summary_only for full details.',
171
+ };
172
+ }
173
+
174
+ return results;
175
+ }
176
+
177
+ export async function handleRemediation(
178
+ args: z.infer<typeof RemediationArgsSchema>
179
+ ) {
180
+ const apiKey = process.env.AIREADY_API_KEY;
181
+ const serverUrl =
182
+ process.env.AIREADY_PLATFORM_URL || 'https://platform.getaiready.dev';
183
+
184
+ if (!apiKey) {
185
+ throw new Error(
186
+ 'AIREADY_API_KEY is not set. Remediation requires an active subscription.'
187
+ );
188
+ }
189
+
190
+ console.error(`[MCP] Requesting remediation for ${args.issue_id}...`);
191
+
192
+ try {
193
+ const response = await fetch(`${serverUrl}/api/v1/remediate`, {
194
+ method: 'POST',
195
+ headers: {
196
+ 'Content-Type': 'application/json',
197
+ 'X-API-KEY': apiKey,
198
+ },
199
+ body: JSON.stringify({
200
+ issueId: args.issue_id,
201
+ filePath: args.file_path,
202
+ context: args.context,
203
+ agent: 'mcp-server',
204
+ }),
205
+ });
206
+
207
+ if (!response.ok) {
208
+ const errorData = await response.json().catch(() => ({}));
209
+ throw new Error(
210
+ `Platform Error: ${errorData.message || response.statusText}`
211
+ );
212
+ }
213
+
214
+ const data = await response.json();
215
+
216
+ return {
217
+ content: [
218
+ {
219
+ type: 'text',
220
+ text: `Recommended Fix (Diff):\n\n${data.diff}\n\nRationale:\n${data.rationale}`,
221
+ },
222
+ ],
223
+ };
224
+ } catch (error: unknown) {
225
+ const errorMessage = error instanceof Error ? error.message : String(error);
226
+ return {
227
+ content: [
228
+ {
229
+ type: 'text',
230
+ text: `Failed to get remediation: ${errorMessage}. Please visit the dashboard to fix manually.`,
231
+ },
232
+ ],
233
+ isError: true,
234
+ };
235
+ }
236
+ }
package/tsconfig.json CHANGED
@@ -2,7 +2,8 @@
2
2
  "extends": "../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
4
  "outDir": "dist",
5
- "rootDir": "src"
5
+ "rootDir": "src",
6
+ "ignoreDeprecations": "6.0"
6
7
  },
7
8
  "include": ["src/**/*"]
8
9
  }