@perfai/mcp 1.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +352 -0
- package/dist/auth/authManager.d.ts +83 -0
- package/dist/auth/authManager.d.ts.map +1 -0
- package/dist/auth/authManager.js +555 -0
- package/dist/auth/sessionCache.d.ts +5 -0
- package/dist/auth/sessionCache.d.ts.map +1 -0
- package/dist/auth/sessionCache.js +29 -0
- package/dist/auth/sessionStorage.d.ts +53 -0
- package/dist/auth/sessionStorage.d.ts.map +1 -0
- package/dist/auth/sessionStorage.js +234 -0
- package/dist/auth/types.d.ts +28 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +74 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +144 -0
- package/dist/setup-config.d.ts +4 -0
- package/dist/setup-config.d.ts.map +1 -0
- package/dist/setup-config.js +69 -0
- package/dist/tools/index.d.ts +361 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +275 -0
- package/dist/tools/protected/aiFixDesignIssue.d.ts +17 -0
- package/dist/tools/protected/aiFixDesignIssue.d.ts.map +1 -0
- package/dist/tools/protected/aiFixDesignIssue.js +205 -0
- package/dist/tools/protected/aiFixQualityIssue.d.ts +17 -0
- package/dist/tools/protected/aiFixQualityIssue.d.ts.map +1 -0
- package/dist/tools/protected/aiFixQualityIssue.js +188 -0
- package/dist/tools/protected/aiFixSecurityIssue.d.ts +17 -0
- package/dist/tools/protected/aiFixSecurityIssue.d.ts.map +1 -0
- package/dist/tools/protected/aiFixSecurityIssue.js +205 -0
- package/dist/tools/protected/checkDesignFixes.d.ts +17 -0
- package/dist/tools/protected/checkDesignFixes.d.ts.map +1 -0
- package/dist/tools/protected/checkDesignFixes.js +199 -0
- package/dist/tools/protected/checkQualityFixes.d.ts +17 -0
- package/dist/tools/protected/checkQualityFixes.d.ts.map +1 -0
- package/dist/tools/protected/checkQualityFixes.js +199 -0
- package/dist/tools/protected/checkSecurityFixes.d.ts +17 -0
- package/dist/tools/protected/checkSecurityFixes.d.ts.map +1 -0
- package/dist/tools/protected/checkSecurityFixes.js +177 -0
- package/dist/tools/protected/listApis.d.ts +28 -0
- package/dist/tools/protected/listApis.d.ts.map +1 -0
- package/dist/tools/protected/listApis.js +102 -0
- package/dist/tools/protected/logout.d.ts +11 -0
- package/dist/tools/protected/logout.d.ts.map +1 -0
- package/dist/tools/protected/logout.js +22 -0
- package/dist/tools/protected/manageOrganizations.d.ts +26 -0
- package/dist/tools/protected/manageOrganizations.d.ts.map +1 -0
- package/dist/tools/protected/manageOrganizations.js +147 -0
- package/dist/tools/protected/runDesignTest.d.ts +21 -0
- package/dist/tools/protected/runDesignTest.d.ts.map +1 -0
- package/dist/tools/protected/runDesignTest.js +132 -0
- package/dist/tools/protected/runQualityTest.d.ts +21 -0
- package/dist/tools/protected/runQualityTest.d.ts.map +1 -0
- package/dist/tools/protected/runQualityTest.js +150 -0
- package/dist/tools/protected/runSecurityTest.d.ts +21 -0
- package/dist/tools/protected/runSecurityTest.d.ts.map +1 -0
- package/dist/tools/protected/runSecurityTest.js +107 -0
- package/dist/tools/protected/selectApi.d.ts +24 -0
- package/dist/tools/protected/selectApi.d.ts.map +1 -0
- package/dist/tools/protected/selectApi.js +172 -0
- package/dist/tools/protected/setup.d.ts +11 -0
- package/dist/tools/protected/setup.d.ts.map +1 -0
- package/dist/tools/protected/setup.js +151 -0
- package/dist/tools/protected/showDesignIssues.d.ts +38 -0
- package/dist/tools/protected/showDesignIssues.d.ts.map +1 -0
- package/dist/tools/protected/showDesignIssues.js +201 -0
- package/dist/tools/protected/showFixedIssues.d.ts +11 -0
- package/dist/tools/protected/showFixedIssues.d.ts.map +1 -0
- package/dist/tools/protected/showFixedIssues.js +36 -0
- package/dist/tools/protected/showQualityIssues.d.ts +33 -0
- package/dist/tools/protected/showQualityIssues.d.ts.map +1 -0
- package/dist/tools/protected/showQualityIssues.js +225 -0
- package/dist/tools/protected/showSecurityIssues.d.ts +47 -0
- package/dist/tools/protected/showSecurityIssues.d.ts.map +1 -0
- package/dist/tools/protected/showSecurityIssues.js +212 -0
- package/dist/tools/protected/summarizeIssues.d.ts +11 -0
- package/dist/tools/protected/summarizeIssues.d.ts.map +1 -0
- package/dist/tools/protected/summarizeIssues.js +161 -0
- package/dist/tools/protected/userInfo.d.ts +11 -0
- package/dist/tools/protected/userInfo.d.ts.map +1 -0
- package/dist/tools/protected/userInfo.js +21 -0
- package/dist/tools/protected/visionAiAppLearning.d.ts +37 -0
- package/dist/tools/protected/visionAiAppLearning.d.ts.map +1 -0
- package/dist/tools/protected/visionAiAppLearning.js +122 -0
- package/dist/tools/public/authStatus.d.ts +11 -0
- package/dist/tools/public/authStatus.d.ts.map +1 -0
- package/dist/tools/public/authStatus.js +78 -0
- package/dist/tools/public/login.d.ts +12 -0
- package/dist/tools/public/login.d.ts.map +1 -0
- package/dist/tools/public/login.js +230 -0
- package/dist/types/api.d.ts +12 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +1 -0
- package/dist/utils/dockerRunner.d.ts +44 -0
- package/dist/utils/dockerRunner.d.ts.map +1 -0
- package/dist/utils/dockerRunner.js +300 -0
- package/dist/utils/formatters.d.ts +14 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +510 -0
- package/dist/utils/promptBuilder.d.ts +4 -0
- package/dist/utils/promptBuilder.d.ts.map +1 -0
- package/dist/utils/promptBuilder.js +132 -0
- package/package.json +67 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
3
|
+
import { buildAIFixDesignPrompt } from "../../utils/promptBuilder.js";
|
|
4
|
+
export const aiFixDesignIssueTool = {
|
|
5
|
+
name: "ai_fix_design_issue",
|
|
6
|
+
description: "šØ Generate AI-powered fix prompt for a design issue by sequence number or description (requires authentication)",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
issue_id: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "The design issue ID, sequence number (e.g., '14'), or descriptive text like 'Application Design Issues/Inconsistent Naming ⢠GET /users'"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
required: ["issue_id"],
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export async function handleAiFixDesignIssue(authManager, args) {
|
|
20
|
+
try {
|
|
21
|
+
const issueIdOrDescription = args.issue_id;
|
|
22
|
+
const accessToken = authManager.getAccessToken();
|
|
23
|
+
const tokenType = authManager.getTokenType();
|
|
24
|
+
const orgId = authManager.getSelectedOrgId();
|
|
25
|
+
if (!accessToken) {
|
|
26
|
+
throw new Error('No access token available');
|
|
27
|
+
}
|
|
28
|
+
if (!orgId) {
|
|
29
|
+
throw new Error('No organization selected. Please select an organization first using manage_organizations tool.');
|
|
30
|
+
}
|
|
31
|
+
if (!issueIdOrDescription) {
|
|
32
|
+
throw new Error('Issue ID or description is required');
|
|
33
|
+
}
|
|
34
|
+
// Get the selected APP and design app ID (same logic as security)
|
|
35
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
36
|
+
const selectedAppData = authManager.getSelectedApiData();
|
|
37
|
+
if (!selectedAppId) {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to fix its design issues.\n\nš **Steps**:\n1. Run \`list_apps\` to see available APPs\n2. Run \`select_app\` with an APP ID\n3. Then run this tool again`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// First, get the app details using the catalog ID to get the design app ID
|
|
48
|
+
const catalogUrl = `${API_ENDPOINTS.API_CATALOG_SERVICE_IDS}/${selectedAppId}`;
|
|
49
|
+
let appDetails;
|
|
50
|
+
try {
|
|
51
|
+
const catalogResponse = await axios.get(catalogUrl, {
|
|
52
|
+
headers: {
|
|
53
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Org-ID': orgId
|
|
56
|
+
},
|
|
57
|
+
timeout: 30000
|
|
58
|
+
});
|
|
59
|
+
if (catalogResponse.status !== 200 || !catalogResponse.data) {
|
|
60
|
+
throw new Error(`Failed to fetch app details with status ${catalogResponse.status}`);
|
|
61
|
+
}
|
|
62
|
+
appDetails = catalogResponse.data;
|
|
63
|
+
if (!appDetails.design_test_appId) {
|
|
64
|
+
throw new Error('No design_test_appId found for this app');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `ā **Error fetching app details**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure the selected app has design testing enabled.`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Use the design_test_appId for fetching design issues
|
|
79
|
+
const designAppId = appDetails.design_test_appId;
|
|
80
|
+
// Fetch design issues for the specific API
|
|
81
|
+
const url = `${API_ENDPOINTS.DESIGN_ISSUES_BY_APP}?app_id=${encodeURIComponent(designAppId)}&limit=100`;
|
|
82
|
+
const response = await axios.get(url, {
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
'X-Org-ID': orgId
|
|
87
|
+
},
|
|
88
|
+
timeout: 30000
|
|
89
|
+
});
|
|
90
|
+
if (response.status !== 200 || !response.data) {
|
|
91
|
+
throw new Error(`Failed to fetch design issues: ${response.status}`);
|
|
92
|
+
}
|
|
93
|
+
const issues = response.data.issues || [];
|
|
94
|
+
let targetIssue = null;
|
|
95
|
+
let issueId = '';
|
|
96
|
+
// Check if input is a sequence number (numeric string)
|
|
97
|
+
const sequenceNumber = parseInt(issueIdOrDescription);
|
|
98
|
+
if (!isNaN(sequenceNumber) && sequenceNumber > 0) {
|
|
99
|
+
// Input is a sequence number, get the issue by index (1-based)
|
|
100
|
+
const issueIndex = sequenceNumber - 1;
|
|
101
|
+
if (issueIndex >= 0 && issueIndex < issues.length) {
|
|
102
|
+
targetIssue = issues[issueIndex];
|
|
103
|
+
issueId = targetIssue._id;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: "text",
|
|
110
|
+
text: `ā **Invalid Sequence Number**\n\nš“ **Error**: Sequence number '${sequenceNumber}' is out of range\n\nš **Available range**: 1 to ${issues.length}\n\nš” **Tip**: Use show_design_issues to see the current list of issues with their sequence numbers.`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Try to find issue by exact ID first
|
|
118
|
+
targetIssue = issues.find((issue) => issue._id === issueIdOrDescription);
|
|
119
|
+
if (targetIssue) {
|
|
120
|
+
issueId = targetIssue._id;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Parse descriptive text to find matching issue
|
|
124
|
+
// Format: "Application Design Issues/Inconsistent Naming ⢠GET /users"
|
|
125
|
+
const descriptionLower = issueIdOrDescription.toLowerCase();
|
|
126
|
+
for (const issue of issues) {
|
|
127
|
+
const category = (issue.category || '').toLowerCase();
|
|
128
|
+
const method = (issue.method || 'multiple').toLowerCase();
|
|
129
|
+
const path = (issue.path || '').toLowerCase();
|
|
130
|
+
// Check if description matches this issue
|
|
131
|
+
const categoryMatch = category && descriptionLower.includes(category);
|
|
132
|
+
const methodMatch = method && descriptionLower.includes(method);
|
|
133
|
+
const pathMatch = path && descriptionLower.includes(path);
|
|
134
|
+
// Additional fuzzy matching for common patterns
|
|
135
|
+
const isApiDesign = descriptionLower.includes('api design');
|
|
136
|
+
const isInconsistent = descriptionLower.includes('inconsistent');
|
|
137
|
+
const isNaming = descriptionLower.includes('naming');
|
|
138
|
+
if ((categoryMatch && (methodMatch || pathMatch)) ||
|
|
139
|
+
(isApiDesign && isInconsistent && isNaming && pathMatch)) {
|
|
140
|
+
targetIssue = issue;
|
|
141
|
+
issueId = issue._id;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!targetIssue) {
|
|
148
|
+
// Try partial matching as last resort
|
|
149
|
+
const firstIssueOfType = issues.find((issue) => {
|
|
150
|
+
const category = (issue.category || '').toLowerCase();
|
|
151
|
+
return issueIdOrDescription.toLowerCase().includes(category) ||
|
|
152
|
+
category.includes(issueIdOrDescription.toLowerCase().split('ā¢')[0].trim());
|
|
153
|
+
});
|
|
154
|
+
if (firstIssueOfType) {
|
|
155
|
+
targetIssue = firstIssueOfType;
|
|
156
|
+
issueId = firstIssueOfType._id;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const availableIssues = issues.slice(0, 3).map((issue) => {
|
|
160
|
+
const method = issue.method || 'MULTIPLE';
|
|
161
|
+
const path = issue.path || '/unknown';
|
|
162
|
+
return `${issue.category || issue.label} ⢠${method} ${path}`;
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `ā **Issue Not Found**\n\nš“ **Error**: Could not find design issue matching: "${issueIdOrDescription}"\n\nš **Available Issues** (first 3):\n${availableIssues.map((issue) => `⢠${issue}`).join('\n')}\n\nš” **Tip**: Use show_design_issues tool to see all issues, or try a more specific description.`,
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check if issue is already fixed and show previous fix info, but allow re-fixing
|
|
175
|
+
const isRefix = authManager.isDesignIssueFixed(issueId);
|
|
176
|
+
if (isRefix) {
|
|
177
|
+
const fixedIssue = authManager.getFixedDesignIssues().find((fi) => fi.issueId === issueId);
|
|
178
|
+
console.error(`š Re-fixing design issue: ${issueId} (previously fixed at ${fixedIssue?.fixedAt.toLocaleString()})`);
|
|
179
|
+
}
|
|
180
|
+
// Generate AI fix prompt (matching extension logic)
|
|
181
|
+
const prompt = buildAIFixDesignPrompt(targetIssue, isRefix);
|
|
182
|
+
// Mark issue as fixed in session (like the extension does)
|
|
183
|
+
const fixedIssue = authManager.markDesignIssueAsFixed(issueId, targetIssue, prompt);
|
|
184
|
+
// Return the prompt directly for MCP to execute
|
|
185
|
+
return {
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: prompt,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: `ā **Error generating AI design fix**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated, have an organization selected, and the issue ID is valid.\n\nš **Try**: Using show_design_issues tool first to see available issues.`,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const aiFixQualityIssueTool: {
|
|
2
|
+
readonly name: "ai_fix_quality_issue";
|
|
3
|
+
readonly description: "š¤ Generate AI-powered fix prompt for a quality issue by sequence number or description (requires authentication)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly issue_id: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "The quality issue ID, sequence number (e.g., '14'), or descriptive text like 'Endpoint accessible without authentication [Authentication Flows] ⢠GET /store/inventory'";
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
readonly required: readonly ["issue_id"];
|
|
13
|
+
readonly additionalProperties: false;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function handleAiFixQualityIssue(authManager: any, args: any): Promise<any>;
|
|
17
|
+
//# sourceMappingURL=aiFixQualityIssue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiFixQualityIssue.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/aiFixQualityIssue.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;CAcxB,CAAC;AAEX,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CA8LvF"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
3
|
+
import { buildAIFixQualityPrompt } from "../../utils/promptBuilder.js";
|
|
4
|
+
export const aiFixQualityIssueTool = {
|
|
5
|
+
name: "ai_fix_quality_issue",
|
|
6
|
+
description: "š¤ Generate AI-powered fix prompt for a quality issue by sequence number or description (requires authentication)",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
issue_id: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "The quality issue ID, sequence number (e.g., '14'), or descriptive text like 'Endpoint accessible without authentication [Authentication Flows] ⢠GET /store/inventory'"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
required: ["issue_id"],
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export async function handleAiFixQualityIssue(authManager, args) {
|
|
20
|
+
try {
|
|
21
|
+
const issueIdOrDescription = args.issue_id;
|
|
22
|
+
const accessToken = authManager.getAccessToken();
|
|
23
|
+
const tokenType = authManager.getTokenType();
|
|
24
|
+
const orgId = authManager.getSelectedOrgId();
|
|
25
|
+
if (!accessToken) {
|
|
26
|
+
throw new Error('No access token available');
|
|
27
|
+
}
|
|
28
|
+
if (!orgId) {
|
|
29
|
+
throw new Error('No organization selected. Please select an organization first using manage_organizations tool.');
|
|
30
|
+
}
|
|
31
|
+
if (!issueIdOrDescription) {
|
|
32
|
+
throw new Error('Issue ID or description is required');
|
|
33
|
+
}
|
|
34
|
+
// Get the selected APP and quality app ID (same logic as show_quality_issues)
|
|
35
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
36
|
+
const selectedAppData = authManager.getSelectedApiData();
|
|
37
|
+
if (!selectedAppId) {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to fix its quality issues.\n\nš **Steps**:\n1. Run \`list_apps\` to see available APPs\n2. Run \`select_app\` with an APP ID\n3. Then run this tool again`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// First, get the app details using the catalog ID to get the quality_test_appId
|
|
48
|
+
const catalogUrl = `${API_ENDPOINTS.API_CATALOG_SERVICE_IDS}/${selectedAppId}`;
|
|
49
|
+
let appDetails;
|
|
50
|
+
try {
|
|
51
|
+
const catalogResponse = await axios.get(catalogUrl, {
|
|
52
|
+
headers: {
|
|
53
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Org-ID': orgId
|
|
56
|
+
},
|
|
57
|
+
timeout: 30000
|
|
58
|
+
});
|
|
59
|
+
if (catalogResponse.status !== 200 || !catalogResponse.data) {
|
|
60
|
+
throw new Error(`Failed to fetch app details with status ${catalogResponse.status}`);
|
|
61
|
+
}
|
|
62
|
+
appDetails = catalogResponse.data;
|
|
63
|
+
if (!appDetails.quality_test_appId) {
|
|
64
|
+
throw new Error('No quality_test_appId found for this app');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `ā **Error fetching app details**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure the selected app has quality testing enabled.`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Use the quality_test_appId for fetching quality issues
|
|
79
|
+
const qualityAppId = appDetails.quality_test_appId;
|
|
80
|
+
// Fetch quality issues for the specific API (using same endpoint as show_quality_issues)
|
|
81
|
+
const url = `${API_ENDPOINTS.QUALITY_ISSUES_BY_APP}/${qualityAppId}?page=1&pageSize=100`;
|
|
82
|
+
const response = await axios.get(url, {
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
'X-Org-ID': orgId
|
|
87
|
+
},
|
|
88
|
+
timeout: 30000
|
|
89
|
+
});
|
|
90
|
+
if (response.status !== 200 || !response.data) {
|
|
91
|
+
throw new Error(`Failed to fetch quality issues: ${response.status}`);
|
|
92
|
+
}
|
|
93
|
+
const issues = response.data.items || [];
|
|
94
|
+
let targetIssue = null;
|
|
95
|
+
let issueId = '';
|
|
96
|
+
// Check if input is a sequence number (numeric string)
|
|
97
|
+
const sequenceNumber = parseInt(issueIdOrDescription);
|
|
98
|
+
if (!isNaN(sequenceNumber) && sequenceNumber > 0) {
|
|
99
|
+
// Input is a sequence number, get the issue by index (1-based)
|
|
100
|
+
const issueIndex = sequenceNumber - 1;
|
|
101
|
+
if (issueIndex >= 0 && issueIndex < issues.length) {
|
|
102
|
+
targetIssue = issues[issueIndex];
|
|
103
|
+
issueId = targetIssue._id;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: "text",
|
|
110
|
+
text: `ā **Invalid Sequence Number**\n\nš“ **Error**: Sequence number '${sequenceNumber}' is out of range\n\nš **Available range**: 1 to ${issues.length}\n\nš” **Tip**: Use show_quality_issues to see the current list of issues with their sequence numbers.`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Try to find issue by exact ID first
|
|
118
|
+
targetIssue = issues.find((issue) => issue._id === issueIdOrDescription);
|
|
119
|
+
if (targetIssue) {
|
|
120
|
+
issueId = targetIssue._id;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Parse descriptive text to find matching issue
|
|
124
|
+
// Format: "Endpoint accessible without authentication [Authentication Flows] ⢠GET /store/inventory"
|
|
125
|
+
const descriptionLower = issueIdOrDescription.toLowerCase();
|
|
126
|
+
for (const issue of issues) {
|
|
127
|
+
const bugTitle = (issue.bug_title || '').toLowerCase();
|
|
128
|
+
const tags = (issue.tags || []).join(' ').toLowerCase();
|
|
129
|
+
const method = (issue.method || 'multiple').toLowerCase();
|
|
130
|
+
const path = (issue.path || '').toLowerCase();
|
|
131
|
+
// Check if description matches this issue
|
|
132
|
+
const bugTitleMatch = bugTitle && descriptionLower.includes(bugTitle);
|
|
133
|
+
const tagsMatch = tags && descriptionLower.includes(tags);
|
|
134
|
+
const methodMatch = method && descriptionLower.includes(method);
|
|
135
|
+
const pathMatch = path && descriptionLower.includes(path);
|
|
136
|
+
// If any key parts match, consider it a match
|
|
137
|
+
if (bugTitleMatch || (tagsMatch && methodMatch) || (methodMatch && pathMatch)) {
|
|
138
|
+
targetIssue = issue;
|
|
139
|
+
issueId = issue._id;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!targetIssue) {
|
|
144
|
+
return {
|
|
145
|
+
content: [
|
|
146
|
+
{
|
|
147
|
+
type: "text",
|
|
148
|
+
text: `ā **Issue Not Found**\n\nš“ **Error**: No quality issue found matching '${issueIdOrDescription}'\n\nš” **Tips**:\n⢠Use show_quality_issues to see available issues\n⢠Use sequence numbers (e.g., '1', '2', '3')\n⢠Use partial descriptions like 'authentication' or 'GET /store'`,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Generate AI fix prompt using the quality issue
|
|
156
|
+
const prompt = buildAIFixQualityPrompt(targetIssue);
|
|
157
|
+
// Store the fixed issue in the session
|
|
158
|
+
const fixedIssue = {
|
|
159
|
+
issueId: issueId,
|
|
160
|
+
issue: targetIssue,
|
|
161
|
+
fixedAt: new Date(),
|
|
162
|
+
prompt: prompt,
|
|
163
|
+
status: "ai-fixed"
|
|
164
|
+
};
|
|
165
|
+
// Add to fixed quality issues
|
|
166
|
+
authManager.addFixedQualityIssue(fixedIssue);
|
|
167
|
+
// Return the AI fix prompt
|
|
168
|
+
return {
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: `š¤ **AI Fix Generated for Quality Issue**\n\nšÆ **Issue**: ${targetIssue.bug_title || targetIssue.name}\nš **ID**: ${issueId}\nš **Endpoint**: ${targetIssue.method?.toUpperCase() || 'UNKNOWN'} ${targetIssue.path || '/unknown'}\nš·ļø **Tags**: ${targetIssue.tags?.join(', ') || 'None'}\n\n---\n\n**AI Fix Prompt:**\n\n${prompt}\n\n---\n\nā
**Status**: Issue marked as AI-fixed and added to fixed issues list\n\nš” **Next Steps**:\n⢠Review the generated fix prompt\n⢠Implement the suggested changes in your code\n⢠Run \`show_fixed_quality_issues\` to see all fixed issues\n⢠Run \`check_quality_fixes\` after implementing to verify fixes`,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: `ā **Error generating AI fix for quality issue**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated and have access to quality data.\n\nš **Try**: Re-running the 'login' tool if the error persists.`,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const aiFixSecurityIssueTool: {
|
|
2
|
+
readonly name: "ai_fix_security_issue";
|
|
3
|
+
readonly description: "š¤ Generate AI-powered fix prompt for a security issue by sequence number or description (requires authentication)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly issue_id: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "The security issue ID, sequence number (e.g., '14'), or descriptive text like 'System Design Issues/Enumerable Resource ID ⢠MULTIPLE /store'";
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
readonly required: readonly ["issue_id"];
|
|
13
|
+
readonly additionalProperties: false;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function handleAiFixSecurityIssue(authManager: any, args: any): Promise<any>;
|
|
17
|
+
//# sourceMappingURL=aiFixSecurityIssue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aiFixSecurityIssue.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/aiFixSecurityIssue.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;CAczB,CAAC;AAEX,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAgNxF"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
3
|
+
import { buildAIFixPrompt } from "../../utils/promptBuilder.js";
|
|
4
|
+
export const aiFixSecurityIssueTool = {
|
|
5
|
+
name: "ai_fix_security_issue",
|
|
6
|
+
description: "š¤ Generate AI-powered fix prompt for a security issue by sequence number or description (requires authentication)",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
issue_id: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "The security issue ID, sequence number (e.g., '14'), or descriptive text like 'System Design Issues/Enumerable Resource ID ⢠MULTIPLE /store'"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
required: ["issue_id"],
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export async function handleAiFixSecurityIssue(authManager, args) {
|
|
20
|
+
try {
|
|
21
|
+
const issueIdOrDescription = args.issue_id;
|
|
22
|
+
const accessToken = authManager.getAccessToken();
|
|
23
|
+
const tokenType = authManager.getTokenType();
|
|
24
|
+
const orgId = authManager.getSelectedOrgId();
|
|
25
|
+
if (!accessToken) {
|
|
26
|
+
throw new Error('No access token available');
|
|
27
|
+
}
|
|
28
|
+
if (!orgId) {
|
|
29
|
+
throw new Error('No organization selected. Please select an organization first using manage_organizations tool.');
|
|
30
|
+
}
|
|
31
|
+
if (!issueIdOrDescription) {
|
|
32
|
+
throw new Error('Issue ID or description is required');
|
|
33
|
+
}
|
|
34
|
+
// Get the selected APP and security app ID (same logic as show_security_issues)
|
|
35
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
36
|
+
const selectedAppData = authManager.getSelectedApiData();
|
|
37
|
+
if (!selectedAppId) {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to fix its security issues.\n\nš **Steps**:\n1. Run \`list_apps\` to see available APPs\n2. Run \`select_app\` with an APP ID\n3. Then run this tool again`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// First, get the app details using the catalog ID to get the security_privacy_test_appId
|
|
48
|
+
const catalogUrl = `${API_ENDPOINTS.API_CATALOG_SERVICE_IDS}/${selectedAppId}`;
|
|
49
|
+
let appDetails;
|
|
50
|
+
try {
|
|
51
|
+
const catalogResponse = await axios.get(catalogUrl, {
|
|
52
|
+
headers: {
|
|
53
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Org-ID': orgId
|
|
56
|
+
},
|
|
57
|
+
timeout: 30000
|
|
58
|
+
});
|
|
59
|
+
if (catalogResponse.status !== 200 || !catalogResponse.data) {
|
|
60
|
+
throw new Error(`Failed to fetch app details with status ${catalogResponse.status}`);
|
|
61
|
+
}
|
|
62
|
+
appDetails = catalogResponse.data;
|
|
63
|
+
if (!appDetails.security_privacy_test_appId) {
|
|
64
|
+
throw new Error('No security_privacy_test_appId found for this app');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `ā **Error fetching app details**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure the selected app has security testing enabled.`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Use the security_privacy_test_appId for fetching security issues
|
|
79
|
+
const securityAppId = appDetails.security_privacy_test_appId;
|
|
80
|
+
// Fetch security issues for the specific API (using same endpoint as show_security_issues)
|
|
81
|
+
const url = `${API_ENDPOINTS.SECURITY_ISSUES_BY_APP}?app_id=${encodeURIComponent(securityAppId)}&limit=100`;
|
|
82
|
+
const response = await axios.get(url, {
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
'X-Org-ID': orgId
|
|
87
|
+
},
|
|
88
|
+
timeout: 30000
|
|
89
|
+
});
|
|
90
|
+
if (response.status !== 200 || !response.data) {
|
|
91
|
+
throw new Error(`Failed to fetch security issues: ${response.status}`);
|
|
92
|
+
}
|
|
93
|
+
const issues = response.data.data || [];
|
|
94
|
+
let targetIssue = null;
|
|
95
|
+
let issueId = '';
|
|
96
|
+
// Check if input is a sequence number (numeric string)
|
|
97
|
+
const sequenceNumber = parseInt(issueIdOrDescription);
|
|
98
|
+
if (!isNaN(sequenceNumber) && sequenceNumber > 0) {
|
|
99
|
+
// Input is a sequence number, get the issue by index (1-based)
|
|
100
|
+
const issueIndex = sequenceNumber - 1;
|
|
101
|
+
if (issueIndex >= 0 && issueIndex < issues.length) {
|
|
102
|
+
targetIssue = issues[issueIndex];
|
|
103
|
+
issueId = targetIssue._id;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: "text",
|
|
110
|
+
text: `ā **Invalid Sequence Number**\n\nš“ **Error**: Sequence number '${sequenceNumber}' is out of range\n\nš **Available range**: 1 to ${issues.length}\n\nš” **Tip**: Use show_security_issues to see the current list of issues with their sequence numbers.`,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Try to find issue by exact ID first
|
|
118
|
+
targetIssue = issues.find((issue) => issue._id === issueIdOrDescription);
|
|
119
|
+
if (targetIssue) {
|
|
120
|
+
issueId = targetIssue._id;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Parse descriptive text to find matching issue
|
|
124
|
+
// Format: "System Design Issues/Enumerable Resource ID ⢠MULTIPLE /store"
|
|
125
|
+
const descriptionLower = issueIdOrDescription.toLowerCase();
|
|
126
|
+
for (const issue of issues) {
|
|
127
|
+
const label = (issue.label || '').toLowerCase();
|
|
128
|
+
const method = (issue.method || 'multiple').toLowerCase();
|
|
129
|
+
const path = (issue.path || '').toLowerCase();
|
|
130
|
+
// Check if description matches this issue
|
|
131
|
+
const labelMatch = label && descriptionLower.includes(label);
|
|
132
|
+
const methodMatch = method && descriptionLower.includes(method);
|
|
133
|
+
const pathMatch = path && descriptionLower.includes(path);
|
|
134
|
+
// Additional fuzzy matching for common patterns
|
|
135
|
+
const isSystemDesign = descriptionLower.includes('system design');
|
|
136
|
+
const isEnumerable = descriptionLower.includes('enumerable');
|
|
137
|
+
const isResourceId = descriptionLower.includes('resource id');
|
|
138
|
+
if ((labelMatch && (methodMatch || pathMatch)) ||
|
|
139
|
+
(isSystemDesign && isEnumerable && isResourceId && pathMatch)) {
|
|
140
|
+
targetIssue = issue;
|
|
141
|
+
issueId = issue._id;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!targetIssue) {
|
|
148
|
+
// Try partial matching as last resort
|
|
149
|
+
const firstIssueOfType = issues.find((issue) => {
|
|
150
|
+
const label = (issue.label || '').toLowerCase();
|
|
151
|
+
return issueIdOrDescription.toLowerCase().includes(label) ||
|
|
152
|
+
label.includes(issueIdOrDescription.toLowerCase().split('ā¢')[0].trim());
|
|
153
|
+
});
|
|
154
|
+
if (firstIssueOfType) {
|
|
155
|
+
targetIssue = firstIssueOfType;
|
|
156
|
+
issueId = firstIssueOfType._id;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const availableIssues = issues.slice(0, 3).map((issue) => {
|
|
160
|
+
const method = issue.method || 'MULTIPLE';
|
|
161
|
+
const path = issue.path || '/unknown';
|
|
162
|
+
return `${issue.label} ⢠${method} ${path}`;
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `ā **Issue Not Found**\n\nš“ **Error**: Could not find security issue matching: "${issueIdOrDescription}"\n\nš **Available Issues** (first 3):\n${availableIssues.map((issue) => `⢠${issue}`).join('\n')}\n\nš” **Tip**: Use show_security_issues tool to see all issues, or try a more specific description.`,
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Check if issue is already fixed and show previous fix info, but allow re-fixing
|
|
175
|
+
const isRefix = authManager.isIssueFixed(issueId);
|
|
176
|
+
if (isRefix) {
|
|
177
|
+
const fixedIssue = authManager.getFixedIssues().find((fi) => fi.issueId === issueId);
|
|
178
|
+
console.error(`š Re-fixing issue: ${issueId} (previously fixed at ${fixedIssue?.fixedAt.toLocaleString()})`);
|
|
179
|
+
}
|
|
180
|
+
// Generate AI fix prompt (matching extension logic)
|
|
181
|
+
const prompt = buildAIFixPrompt(targetIssue, isRefix);
|
|
182
|
+
// Mark issue as fixed in session (like the extension does)
|
|
183
|
+
const fixedIssue = authManager.markIssueAsFixed(issueId, targetIssue, prompt);
|
|
184
|
+
// Return the prompt directly for MCP to execute
|
|
185
|
+
return {
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: prompt,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: `ā **Error generating AI fix**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated, have an organization selected, and the issue ID is valid.\n\nš **Try**: Using show_security_issues tool first to see available issues.`,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const checkDesignFixesTool: {
|
|
2
|
+
readonly name: "check_design_fixes";
|
|
3
|
+
readonly description: "š Check which previously fixed design issues were actually resolved (auto-triggered after run_design_test)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly wait_seconds: {
|
|
8
|
+
readonly type: "number";
|
|
9
|
+
readonly description: "Wait time before checking issues (default: 15 seconds)";
|
|
10
|
+
readonly default: 15;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
readonly additionalProperties: false;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function handleCheckDesignFixes(authManager: any, args: any): Promise<any>;
|
|
17
|
+
//# sourceMappingURL=checkDesignFixes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkDesignFixes.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/checkDesignFixes.ts"],"names":[],"mappings":"AAkDA,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;CAcvB,CAAC;AAEX,wBAAsB,sBAAsB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CA8KtF"}
|