@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,38 @@
|
|
|
1
|
+
export declare const showDesignIssuesTool: {
|
|
2
|
+
readonly name: "show_design_issues";
|
|
3
|
+
readonly description: "šØ List design issues for selected APP ā filter by status and sort order (shows 20 at a time)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly limit: {
|
|
8
|
+
readonly type: "number";
|
|
9
|
+
readonly description: "Number of issues to fetch (default: 20, max: 100)";
|
|
10
|
+
};
|
|
11
|
+
readonly status: {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
readonly enum: readonly ["Open", "Dismissed"];
|
|
14
|
+
readonly description: "Filter by issue status (default: Open)";
|
|
15
|
+
};
|
|
16
|
+
readonly sort_by: {
|
|
17
|
+
readonly type: "string";
|
|
18
|
+
readonly description: "Field to sort by e.g. 'createdOn' (optional)";
|
|
19
|
+
};
|
|
20
|
+
readonly sort_order: {
|
|
21
|
+
readonly type: "string";
|
|
22
|
+
readonly enum: readonly ["ASC", "DESC"];
|
|
23
|
+
readonly description: "Sort direction (default: DESC)";
|
|
24
|
+
};
|
|
25
|
+
readonly search: {
|
|
26
|
+
readonly type: "string";
|
|
27
|
+
readonly description: "Search issues by keyword (optional)";
|
|
28
|
+
};
|
|
29
|
+
readonly cursor: {
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly description: "Pagination cursor for fetching next page (optional)";
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
readonly additionalProperties: false;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export declare function handleShowDesignIssues(authManager: any, args: any): Promise<any>;
|
|
38
|
+
//# sourceMappingURL=showDesignIssues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"showDesignIssues.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/showDesignIssues.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCvB,CAAC;AAEX,wBAAsB,sBAAsB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAiLtF"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
3
|
+
import { formatDesignIssues } from "../../utils/formatters.js";
|
|
4
|
+
export const showDesignIssuesTool = {
|
|
5
|
+
name: "show_design_issues",
|
|
6
|
+
description: "šØ List design issues for selected APP ā filter by status and sort order (shows 20 at a time)",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
limit: {
|
|
11
|
+
type: "number",
|
|
12
|
+
description: "Number of issues to fetch (default: 20, max: 100)"
|
|
13
|
+
},
|
|
14
|
+
status: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["Open", "Dismissed"],
|
|
17
|
+
description: "Filter by issue status (default: Open)"
|
|
18
|
+
},
|
|
19
|
+
sort_by: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Field to sort by e.g. 'createdOn' (optional)"
|
|
22
|
+
},
|
|
23
|
+
sort_order: {
|
|
24
|
+
type: "string",
|
|
25
|
+
enum: ["ASC", "DESC"],
|
|
26
|
+
description: "Sort direction (default: DESC)"
|
|
27
|
+
},
|
|
28
|
+
search: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Search issues by keyword (optional)"
|
|
31
|
+
},
|
|
32
|
+
cursor: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Pagination cursor for fetching next page (optional)"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export async function handleShowDesignIssues(authManager, args) {
|
|
41
|
+
try {
|
|
42
|
+
const requestedLimit = args.limit;
|
|
43
|
+
const search = args.search?.trim();
|
|
44
|
+
const cursor = args.cursor;
|
|
45
|
+
const status = args.status || 'Open';
|
|
46
|
+
const sortBy = args.sort_by || 'createdOn';
|
|
47
|
+
const sortOrder = args.sort_order || 'DESC';
|
|
48
|
+
// Default to 20 issues if no limit specified
|
|
49
|
+
const limit = requestedLimit ? Math.min(requestedLimit, 100) : 20; // Default 20, max 100 per request
|
|
50
|
+
// Make authenticated request
|
|
51
|
+
const userInfo = authManager.getUserInfo();
|
|
52
|
+
const accessToken = authManager.getAccessToken();
|
|
53
|
+
const tokenType = authManager.getTokenType();
|
|
54
|
+
const orgId = authManager.getSelectedOrgId();
|
|
55
|
+
if (!accessToken) {
|
|
56
|
+
throw new Error('No access token available');
|
|
57
|
+
}
|
|
58
|
+
if (!orgId) {
|
|
59
|
+
// Debug info for user
|
|
60
|
+
const orgs = authManager.getOrganizations();
|
|
61
|
+
const sessionInfo = authManager.session ? `Session exists with ${Object.keys(authManager.session).join(', ')}` : 'No session';
|
|
62
|
+
throw new Error(`No organization selected. Debug: ${sessionInfo}. Organizations: ${orgs.length}. Selected: ${authManager.session?.selectedOrgId || 'none'}`);
|
|
63
|
+
}
|
|
64
|
+
// Always require selected APP
|
|
65
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
66
|
+
const selectedAppData = authManager.getSelectedApiData();
|
|
67
|
+
const selectedAppIdentifier = authManager.getSelectedApiIdentifier();
|
|
68
|
+
if (!selectedAppId) {
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to view 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`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// First, get the app details using the catalog ID to get the design_test_appId
|
|
79
|
+
const catalogUrl = `${API_ENDPOINTS.API_CATALOG_SERVICE_IDS}/${selectedAppId}`;
|
|
80
|
+
let appDetails;
|
|
81
|
+
try {
|
|
82
|
+
const catalogResponse = await axios.get(catalogUrl, {
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
'X-Org-ID': orgId
|
|
87
|
+
},
|
|
88
|
+
timeout: 30000
|
|
89
|
+
});
|
|
90
|
+
if (catalogResponse.status !== 200 || !catalogResponse.data) {
|
|
91
|
+
throw new Error(`Failed to fetch app details with status ${catalogResponse.status}`);
|
|
92
|
+
}
|
|
93
|
+
appDetails = catalogResponse.data;
|
|
94
|
+
if (!appDetails.design_test_appId) {
|
|
95
|
+
throw new Error('No design_test_appId found for this app');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
100
|
+
return {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: `ā **Error fetching app details**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure the selected app has design testing enabled.`,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// Use the design_test_appId for fetching design issues
|
|
110
|
+
const designAppId = appDetails.design_test_appId;
|
|
111
|
+
// Store the design app ID for use in other tools (like runDesignTest)
|
|
112
|
+
authManager.setSelectedDesignAppId(designAppId);
|
|
113
|
+
// Build query params for the paginated org/issues endpoint
|
|
114
|
+
const params = new URLSearchParams();
|
|
115
|
+
params.set('app_id', designAppId);
|
|
116
|
+
params.set('limit', String(limit));
|
|
117
|
+
params.set('sortBy', sortBy);
|
|
118
|
+
params.set('sortOrder', sortOrder);
|
|
119
|
+
params.set('status', status);
|
|
120
|
+
if (cursor)
|
|
121
|
+
params.set('cursor', cursor);
|
|
122
|
+
if (search)
|
|
123
|
+
params.set('searchQuery', search);
|
|
124
|
+
const issuesUrl = `${API_ENDPOINTS.DESIGN_ISSUES_BY_APP}?${params.toString()}`;
|
|
125
|
+
console.error('š Debug: Fetching design issues');
|
|
126
|
+
const authHeaders = {
|
|
127
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
128
|
+
'Content-Type': 'application/json',
|
|
129
|
+
'X-Org-ID': orgId
|
|
130
|
+
};
|
|
131
|
+
let response;
|
|
132
|
+
try {
|
|
133
|
+
response = await axios.get(issuesUrl, { headers: authHeaders, timeout: 30000 });
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
if (cursor && err?.response?.status === 500) {
|
|
137
|
+
const retryParams = new URLSearchParams(params);
|
|
138
|
+
retryParams.delete('cursor');
|
|
139
|
+
response = await axios.get(`${API_ENDPOINTS.DESIGN_ISSUES_BY_APP}?${retryParams.toString()}`, { headers: authHeaders, timeout: 30000 });
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (response.status !== 200 || !response.data) {
|
|
146
|
+
throw new Error(`Request failed with status ${response.status}`);
|
|
147
|
+
}
|
|
148
|
+
const issues = response.data.issues || [];
|
|
149
|
+
const allIssues = issues;
|
|
150
|
+
let currentCursor = response.data.pageInfo?.nextCursor || null;
|
|
151
|
+
let hasMore = response.data.hasNextPage || false;
|
|
152
|
+
if (issues.length < limit) {
|
|
153
|
+
hasMore = false;
|
|
154
|
+
currentCursor = null;
|
|
155
|
+
}
|
|
156
|
+
const appDisplayName = selectedAppData?.label || selectedAppData?.api_name || appDetails?.api_name || selectedAppId || '';
|
|
157
|
+
const moreInfo = hasMore ? ' (more available)' : '';
|
|
158
|
+
const selectedOrg = authManager.getOrganizations().find((org) => org.org_id === orgId);
|
|
159
|
+
const orgName = selectedOrg?.orgDetails?.name || orgId || '';
|
|
160
|
+
const activeFilters = [];
|
|
161
|
+
if (status !== 'Open')
|
|
162
|
+
activeFilters.push(`status: ${status}`);
|
|
163
|
+
if (search)
|
|
164
|
+
activeFilters.push(`search: "${search}"`);
|
|
165
|
+
activeFilters.push(`sort: ${sortBy} ${sortOrder}`);
|
|
166
|
+
const filterInfo = `šØ **APP**: ${appDisplayName} | š¢ **Org**: ${orgName}\n` +
|
|
167
|
+
`š½ **Filters**: ${activeFilters.join(' | ')}\n` +
|
|
168
|
+
`š **Issues shown**: ${allIssues.length}${moreInfo}\n\n`;
|
|
169
|
+
const formattedText = formatDesignIssues(allIssues, userInfo, search, hasMore, currentCursor);
|
|
170
|
+
return {
|
|
171
|
+
content: [
|
|
172
|
+
{
|
|
173
|
+
type: "text",
|
|
174
|
+
text: filterInfo + formattedText,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
181
|
+
// Check if it's a 500 error specifically
|
|
182
|
+
if (errorMessage.includes('500')) {
|
|
183
|
+
return {
|
|
184
|
+
content: [
|
|
185
|
+
{
|
|
186
|
+
type: "text",
|
|
187
|
+
text: `ā **Server Error (500)**\n\nš“ **Error**: ${errorMessage}\n\nš” **Possible Causes**:\n⢠Invalid pagination cursor\n⢠Server temporarily unavailable\n⢠Rate limiting\n\nš **Try**:\n1. Run \`show_design_issues\` without cursor to get fresh results\n2. Wait a few minutes and try again\n3. Check if your authentication is still valid`,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: "text",
|
|
196
|
+
text: `ā **Error fetching design issues**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated and have access to design analysis data.\n\nš **Try**: Re-running the 'login' tool if the error persists.`,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const showFixedIssuesTool: {
|
|
2
|
+
readonly name: "show_fixed_issues";
|
|
3
|
+
readonly description: "š Show all AI-fixed security issues in current session (requires authentication)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {};
|
|
7
|
+
readonly additionalProperties: false;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare function handleShowFixedIssues(authManager: any): Promise<any>;
|
|
11
|
+
//# sourceMappingURL=showFixedIssues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"showFixedIssues.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/showFixedIssues.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB;;;;;;;;CAQtB,CAAC;AAEX,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CA2B1E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { formatFixedIssues } from "../../utils/formatters.js";
|
|
2
|
+
export const showFixedIssuesTool = {
|
|
3
|
+
name: "show_fixed_issues",
|
|
4
|
+
description: "š Show all AI-fixed security issues in current session (requires authentication)",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {},
|
|
8
|
+
additionalProperties: false,
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
export async function handleShowFixedIssues(authManager) {
|
|
12
|
+
try {
|
|
13
|
+
const fixedIssues = authManager.getFixedIssues();
|
|
14
|
+
const userInfo = authManager.getUserInfo();
|
|
15
|
+
const formattedText = formatFixedIssues(fixedIssues, userInfo);
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: "text",
|
|
20
|
+
text: formattedText,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: `ā **Error fetching fixed issues**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure you're authenticated and have a valid session.`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const showQualityIssuesTool: {
|
|
2
|
+
readonly name: "show_quality_issues";
|
|
3
|
+
readonly description: "š List quality/spec issues for selected APP ā filter by sort order (shows 20 at a time)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly limit: {
|
|
8
|
+
readonly type: "number";
|
|
9
|
+
readonly description: "Number of issues to fetch (default: 20, max: 100)";
|
|
10
|
+
};
|
|
11
|
+
readonly sort_by: {
|
|
12
|
+
readonly type: "string";
|
|
13
|
+
readonly description: "Field to sort by (optional)";
|
|
14
|
+
};
|
|
15
|
+
readonly sort_order: {
|
|
16
|
+
readonly type: "string";
|
|
17
|
+
readonly enum: readonly ["ASC", "DESC"];
|
|
18
|
+
readonly description: "Sort direction (default: DESC)";
|
|
19
|
+
};
|
|
20
|
+
readonly search: {
|
|
21
|
+
readonly type: "string";
|
|
22
|
+
readonly description: "Search issues by keyword (optional)";
|
|
23
|
+
};
|
|
24
|
+
readonly cursor: {
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
readonly description: "Page number for fetching next page (optional)";
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
readonly additionalProperties: false;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare function handleShowQualityIssues(authManager: any, args: any): Promise<any>;
|
|
33
|
+
//# sourceMappingURL=showQualityIssues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"showQualityIssues.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/showQualityIssues.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BxB,CAAC;AAEX,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAoNvF"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { API_ENDPOINTS } from "../../config.js";
|
|
3
|
+
import { formatQualityIssues } from "../../utils/formatters.js";
|
|
4
|
+
export const showQualityIssuesTool = {
|
|
5
|
+
name: "show_quality_issues",
|
|
6
|
+
description: "š List quality/spec issues for selected APP ā filter by sort order (shows 20 at a time)",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
limit: {
|
|
11
|
+
type: "number",
|
|
12
|
+
description: "Number of issues to fetch (default: 20, max: 100)"
|
|
13
|
+
},
|
|
14
|
+
sort_by: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Field to sort by (optional)"
|
|
17
|
+
},
|
|
18
|
+
sort_order: {
|
|
19
|
+
type: "string",
|
|
20
|
+
enum: ["ASC", "DESC"],
|
|
21
|
+
description: "Sort direction (default: DESC)"
|
|
22
|
+
},
|
|
23
|
+
search: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Search issues by keyword (optional)"
|
|
26
|
+
},
|
|
27
|
+
cursor: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Page number for fetching next page (optional)"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
export async function handleShowQualityIssues(authManager, args) {
|
|
36
|
+
try {
|
|
37
|
+
const requestedLimit = args.limit;
|
|
38
|
+
const search = args.search?.trim();
|
|
39
|
+
const cursor = args.cursor;
|
|
40
|
+
const sortBy = args.sort_by;
|
|
41
|
+
const sortOrder = args.sort_order || 'DESC';
|
|
42
|
+
// Default to 20 issues if no limit specified
|
|
43
|
+
const limit = requestedLimit ? Math.min(requestedLimit, 100) : 20; // Default 20, max 100 per request
|
|
44
|
+
// Make authenticated request
|
|
45
|
+
const userInfo = authManager.getUserInfo();
|
|
46
|
+
const accessToken = authManager.getAccessToken();
|
|
47
|
+
const tokenType = authManager.getTokenType();
|
|
48
|
+
const orgId = authManager.getSelectedOrgId();
|
|
49
|
+
if (!accessToken) {
|
|
50
|
+
throw new Error('No access token available');
|
|
51
|
+
}
|
|
52
|
+
if (!orgId) {
|
|
53
|
+
// Debug info for user
|
|
54
|
+
const orgs = authManager.getOrganizations();
|
|
55
|
+
const sessionInfo = authManager.session ? `Session exists with ${Object.keys(authManager.session).join(', ')}` : 'No session';
|
|
56
|
+
throw new Error(`No organization selected. Debug: ${sessionInfo}. Organizations: ${orgs.length}. Selected: ${authManager.session?.selectedOrgId || 'none'}`);
|
|
57
|
+
}
|
|
58
|
+
// Always require selected APP
|
|
59
|
+
const selectedAppId = authManager.getSelectedApiId();
|
|
60
|
+
const selectedAppData = authManager.getSelectedApiData();
|
|
61
|
+
const selectedAppIdentifier = authManager.getSelectedApiIdentifier();
|
|
62
|
+
if (!selectedAppId) {
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `ā **No APP Selected**\n\nšÆ **Required**: You must select an APP first to view 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`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// First, get the app details using the catalog ID to get the quality test app ID
|
|
73
|
+
const catalogUrl = `${API_ENDPOINTS.API_CATALOG_SERVICE_IDS}/${selectedAppId}`;
|
|
74
|
+
let appDetails;
|
|
75
|
+
try {
|
|
76
|
+
const catalogResponse = await axios.get(catalogUrl, {
|
|
77
|
+
headers: {
|
|
78
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
'X-Org-ID': orgId
|
|
81
|
+
},
|
|
82
|
+
timeout: 30000
|
|
83
|
+
});
|
|
84
|
+
if (catalogResponse.status !== 200 || !catalogResponse.data) {
|
|
85
|
+
throw new Error(`Failed to fetch app details with status ${catalogResponse.status}`);
|
|
86
|
+
}
|
|
87
|
+
appDetails = catalogResponse.data;
|
|
88
|
+
if (!appDetails.quality_test_appId) {
|
|
89
|
+
throw new Error('No quality_test_appId found for this app');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `ā **Error fetching app details**\n\nš“ **Error**: ${errorMessage}\n\nš” **Tip**: Make sure the selected app has quality_test_appId configured.`,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Use the quality_test_appId for fetching quality issues
|
|
104
|
+
const qualityAppId = appDetails.quality_test_appId;
|
|
105
|
+
// Store the quality app ID for use in other tools
|
|
106
|
+
authManager.setSelectedQualityAppId(qualityAppId);
|
|
107
|
+
// Fetch issues with pagination (default 20 at a time)
|
|
108
|
+
let allIssues = [];
|
|
109
|
+
let currentCursor = cursor;
|
|
110
|
+
let hasMore = true;
|
|
111
|
+
let totalFetched = 0;
|
|
112
|
+
let requestCount = 0;
|
|
113
|
+
// Quality endpoint uses page/pageSize pagination
|
|
114
|
+
const pageNumber = currentCursor ? (parseInt(currentCursor) || 1) : 1;
|
|
115
|
+
const params = new URLSearchParams();
|
|
116
|
+
params.set('page', String(pageNumber));
|
|
117
|
+
params.set('pageSize', String(limit));
|
|
118
|
+
params.set('sortOrder', sortOrder);
|
|
119
|
+
if (sortBy)
|
|
120
|
+
params.set('sortBy', sortBy);
|
|
121
|
+
if (search)
|
|
122
|
+
params.set('search', search);
|
|
123
|
+
let url = `${API_ENDPOINTS.QUALITY_ISSUES_BY_APP}/${qualityAppId}?${params.toString()}`;
|
|
124
|
+
console.error('š Debug: Making quality issues request');
|
|
125
|
+
console.error(' URL:', url);
|
|
126
|
+
console.error(' Cursor:', currentCursor);
|
|
127
|
+
console.error(' Limit:', limit);
|
|
128
|
+
// If we have a cursor but it might be invalid, try without it first
|
|
129
|
+
let response;
|
|
130
|
+
try {
|
|
131
|
+
response = await axios.get(url, {
|
|
132
|
+
headers: {
|
|
133
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
'X-Org-ID': orgId
|
|
136
|
+
},
|
|
137
|
+
timeout: 30000
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// If we get a 500 error with a cursor, try without the cursor
|
|
142
|
+
if (currentCursor && error instanceof Error && error.message.includes('500')) {
|
|
143
|
+
console.error('ā ļø 500 error with cursor, trying without cursor...');
|
|
144
|
+
const urlWithoutCursor = url.replace(`&cursor=${encodeURIComponent(currentCursor)}`, '');
|
|
145
|
+
response = await axios.get(urlWithoutCursor, {
|
|
146
|
+
headers: {
|
|
147
|
+
'Authorization': `${tokenType} ${accessToken}`,
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
'X-Org-ID': orgId
|
|
150
|
+
},
|
|
151
|
+
timeout: 30000
|
|
152
|
+
});
|
|
153
|
+
// Clear cursor since we're not using it
|
|
154
|
+
currentCursor = null;
|
|
155
|
+
hasMore = false;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
console.error('ā
Quality issues response received');
|
|
162
|
+
console.error(' Status:', response.status);
|
|
163
|
+
console.error(' Has data:', !!response.data);
|
|
164
|
+
console.error(' Items count:', response.data?.items?.length || 0);
|
|
165
|
+
console.error(' Has more:', response.data?.hasNextPage);
|
|
166
|
+
console.error(' Page:', response.data?.page);
|
|
167
|
+
console.error(' Total pages:', response.data?.totalPages);
|
|
168
|
+
console.error(' Sample issue:', JSON.stringify(response.data?.items?.[0], null, 2));
|
|
169
|
+
if (response.status !== 200 || !response.data) {
|
|
170
|
+
throw new Error(`Request failed with status ${response.status}`);
|
|
171
|
+
}
|
|
172
|
+
// Handle quality issues response structure - issues are in 'items' array
|
|
173
|
+
const issues = response.data.items || response.data.issues || response.data.data || [];
|
|
174
|
+
const totalCount = response.data.totalCount;
|
|
175
|
+
allIssues = allIssues.concat(issues);
|
|
176
|
+
// Use API-provided hasNextPage; fall back to totalPages comparison
|
|
177
|
+
hasMore = response.data.hasNextPage !== undefined
|
|
178
|
+
? response.data.hasNextPage
|
|
179
|
+
: (response.data.totalPages > (response.data.page ?? pageNumber));
|
|
180
|
+
currentCursor = hasMore ? String((response.data.page ?? pageNumber) + 1) : null;
|
|
181
|
+
const appDisplayName = selectedAppData?.label || selectedAppData?.api_name || appDetails?.api_name || selectedAppId || '';
|
|
182
|
+
const selectedOrg = authManager.getOrganizations().find((org) => org.org_id === orgId);
|
|
183
|
+
const orgName = selectedOrg?.orgDetails?.name || orgId || '';
|
|
184
|
+
const activeFilters = [];
|
|
185
|
+
if (search)
|
|
186
|
+
activeFilters.push(`search: "${search}"`);
|
|
187
|
+
activeFilters.push(`sort: ${sortBy || 'default'} ${sortOrder}`);
|
|
188
|
+
const totalNote = totalCount !== undefined ? ` of ${totalCount} total` : '';
|
|
189
|
+
const moreInfo = hasMore ? ' (more available)' : '';
|
|
190
|
+
const filterInfo = `š **APP**: ${appDisplayName} | š¢ **Org**: ${orgName}\n` +
|
|
191
|
+
`š½ **Filters**: ${activeFilters.join(' | ')}\n` +
|
|
192
|
+
`š **Issues shown**: ${allIssues.length}${totalNote}${moreInfo}\n\n`;
|
|
193
|
+
const formattedText = formatQualityIssues(allIssues, userInfo, search, hasMore, currentCursor);
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
type: "text",
|
|
198
|
+
text: filterInfo + formattedText,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
205
|
+
// Check if it's a 500 error specifically
|
|
206
|
+
if (errorMessage.includes('500')) {
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
type: "text",
|
|
211
|
+
text: `ā **Server Error (500)**\n\nš“ **Error**: ${errorMessage}\n\nš” **Possible Causes**:\n⢠Invalid pagination cursor\n⢠Server temporarily unavailable\n⢠Rate limiting\n\nš **Try**:\n1. Run \`show_quality_issues\` without cursor to get fresh results\n2. Wait a few minutes and try again\n3. Check if your authentication is still valid`,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: `ā **Error fetching quality issues**\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.`,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export declare const showSecurityIssuesTool: {
|
|
2
|
+
readonly name: "show_security_issues";
|
|
3
|
+
readonly description: "š List security issues for selected APP ā filter by severity, status, and sort order (shows 20 at a time)";
|
|
4
|
+
readonly inputSchema: {
|
|
5
|
+
readonly type: "object";
|
|
6
|
+
readonly properties: {
|
|
7
|
+
readonly limit: {
|
|
8
|
+
readonly type: "number";
|
|
9
|
+
readonly description: "Number of issues to fetch (default: 20, max: 100)";
|
|
10
|
+
};
|
|
11
|
+
readonly severities: {
|
|
12
|
+
readonly type: "array";
|
|
13
|
+
readonly items: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
readonly enum: readonly ["Low", "Medium", "High", "Critical"];
|
|
16
|
+
};
|
|
17
|
+
readonly description: "Filter by one or more severity levels e.g. [\"Critical\", \"High\"]";
|
|
18
|
+
};
|
|
19
|
+
readonly status: {
|
|
20
|
+
readonly type: "string";
|
|
21
|
+
readonly enum: readonly ["Open", "Dismissed"];
|
|
22
|
+
readonly description: "Filter by issue status (default: Open)";
|
|
23
|
+
};
|
|
24
|
+
readonly sort_by: {
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
readonly enum: readonly ["severity", "cvss", "method", "path", "type"];
|
|
27
|
+
readonly description: "Field to sort by (default: severity)";
|
|
28
|
+
};
|
|
29
|
+
readonly sort_order: {
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly enum: readonly ["ASC", "DESC"];
|
|
32
|
+
readonly description: "Sort direction (default: DESC ā highest severity first)";
|
|
33
|
+
};
|
|
34
|
+
readonly search: {
|
|
35
|
+
readonly type: "string";
|
|
36
|
+
readonly description: "Search issues by keyword (optional)";
|
|
37
|
+
};
|
|
38
|
+
readonly cursor: {
|
|
39
|
+
readonly type: "string";
|
|
40
|
+
readonly description: "Pagination cursor for fetching next page (optional)";
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
readonly additionalProperties: false;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
export declare function handleShowSecurityIssues(authManager: any, args: any): Promise<any>;
|
|
47
|
+
//# sourceMappingURL=showSecurityIssues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"showSecurityIssues.d.ts","sourceRoot":"","sources":["../../../src/tools/protected/showSecurityIssues.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCzB,CAAC;AAEX,wBAAsB,wBAAwB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAoLxF"}
|