@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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +352 -0
  3. package/dist/auth/authManager.d.ts +83 -0
  4. package/dist/auth/authManager.d.ts.map +1 -0
  5. package/dist/auth/authManager.js +555 -0
  6. package/dist/auth/sessionCache.d.ts +5 -0
  7. package/dist/auth/sessionCache.d.ts.map +1 -0
  8. package/dist/auth/sessionCache.js +29 -0
  9. package/dist/auth/sessionStorage.d.ts +53 -0
  10. package/dist/auth/sessionStorage.d.ts.map +1 -0
  11. package/dist/auth/sessionStorage.js +234 -0
  12. package/dist/auth/types.d.ts +28 -0
  13. package/dist/auth/types.d.ts.map +1 -0
  14. package/dist/auth/types.js +1 -0
  15. package/dist/config.d.ts +65 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +74 -0
  18. package/dist/server.d.ts +3 -0
  19. package/dist/server.d.ts.map +1 -0
  20. package/dist/server.js +144 -0
  21. package/dist/setup-config.d.ts +4 -0
  22. package/dist/setup-config.d.ts.map +1 -0
  23. package/dist/setup-config.js +69 -0
  24. package/dist/tools/index.d.ts +361 -0
  25. package/dist/tools/index.d.ts.map +1 -0
  26. package/dist/tools/index.js +275 -0
  27. package/dist/tools/protected/aiFixDesignIssue.d.ts +17 -0
  28. package/dist/tools/protected/aiFixDesignIssue.d.ts.map +1 -0
  29. package/dist/tools/protected/aiFixDesignIssue.js +205 -0
  30. package/dist/tools/protected/aiFixQualityIssue.d.ts +17 -0
  31. package/dist/tools/protected/aiFixQualityIssue.d.ts.map +1 -0
  32. package/dist/tools/protected/aiFixQualityIssue.js +188 -0
  33. package/dist/tools/protected/aiFixSecurityIssue.d.ts +17 -0
  34. package/dist/tools/protected/aiFixSecurityIssue.d.ts.map +1 -0
  35. package/dist/tools/protected/aiFixSecurityIssue.js +205 -0
  36. package/dist/tools/protected/checkDesignFixes.d.ts +17 -0
  37. package/dist/tools/protected/checkDesignFixes.d.ts.map +1 -0
  38. package/dist/tools/protected/checkDesignFixes.js +199 -0
  39. package/dist/tools/protected/checkQualityFixes.d.ts +17 -0
  40. package/dist/tools/protected/checkQualityFixes.d.ts.map +1 -0
  41. package/dist/tools/protected/checkQualityFixes.js +199 -0
  42. package/dist/tools/protected/checkSecurityFixes.d.ts +17 -0
  43. package/dist/tools/protected/checkSecurityFixes.d.ts.map +1 -0
  44. package/dist/tools/protected/checkSecurityFixes.js +177 -0
  45. package/dist/tools/protected/listApis.d.ts +28 -0
  46. package/dist/tools/protected/listApis.d.ts.map +1 -0
  47. package/dist/tools/protected/listApis.js +102 -0
  48. package/dist/tools/protected/logout.d.ts +11 -0
  49. package/dist/tools/protected/logout.d.ts.map +1 -0
  50. package/dist/tools/protected/logout.js +22 -0
  51. package/dist/tools/protected/manageOrganizations.d.ts +26 -0
  52. package/dist/tools/protected/manageOrganizations.d.ts.map +1 -0
  53. package/dist/tools/protected/manageOrganizations.js +147 -0
  54. package/dist/tools/protected/runDesignTest.d.ts +21 -0
  55. package/dist/tools/protected/runDesignTest.d.ts.map +1 -0
  56. package/dist/tools/protected/runDesignTest.js +132 -0
  57. package/dist/tools/protected/runQualityTest.d.ts +21 -0
  58. package/dist/tools/protected/runQualityTest.d.ts.map +1 -0
  59. package/dist/tools/protected/runQualityTest.js +150 -0
  60. package/dist/tools/protected/runSecurityTest.d.ts +21 -0
  61. package/dist/tools/protected/runSecurityTest.d.ts.map +1 -0
  62. package/dist/tools/protected/runSecurityTest.js +107 -0
  63. package/dist/tools/protected/selectApi.d.ts +24 -0
  64. package/dist/tools/protected/selectApi.d.ts.map +1 -0
  65. package/dist/tools/protected/selectApi.js +172 -0
  66. package/dist/tools/protected/setup.d.ts +11 -0
  67. package/dist/tools/protected/setup.d.ts.map +1 -0
  68. package/dist/tools/protected/setup.js +151 -0
  69. package/dist/tools/protected/showDesignIssues.d.ts +38 -0
  70. package/dist/tools/protected/showDesignIssues.d.ts.map +1 -0
  71. package/dist/tools/protected/showDesignIssues.js +201 -0
  72. package/dist/tools/protected/showFixedIssues.d.ts +11 -0
  73. package/dist/tools/protected/showFixedIssues.d.ts.map +1 -0
  74. package/dist/tools/protected/showFixedIssues.js +36 -0
  75. package/dist/tools/protected/showQualityIssues.d.ts +33 -0
  76. package/dist/tools/protected/showQualityIssues.d.ts.map +1 -0
  77. package/dist/tools/protected/showQualityIssues.js +225 -0
  78. package/dist/tools/protected/showSecurityIssues.d.ts +47 -0
  79. package/dist/tools/protected/showSecurityIssues.d.ts.map +1 -0
  80. package/dist/tools/protected/showSecurityIssues.js +212 -0
  81. package/dist/tools/protected/summarizeIssues.d.ts +11 -0
  82. package/dist/tools/protected/summarizeIssues.d.ts.map +1 -0
  83. package/dist/tools/protected/summarizeIssues.js +161 -0
  84. package/dist/tools/protected/userInfo.d.ts +11 -0
  85. package/dist/tools/protected/userInfo.d.ts.map +1 -0
  86. package/dist/tools/protected/userInfo.js +21 -0
  87. package/dist/tools/protected/visionAiAppLearning.d.ts +37 -0
  88. package/dist/tools/protected/visionAiAppLearning.d.ts.map +1 -0
  89. package/dist/tools/protected/visionAiAppLearning.js +122 -0
  90. package/dist/tools/public/authStatus.d.ts +11 -0
  91. package/dist/tools/public/authStatus.d.ts.map +1 -0
  92. package/dist/tools/public/authStatus.js +78 -0
  93. package/dist/tools/public/login.d.ts +12 -0
  94. package/dist/tools/public/login.d.ts.map +1 -0
  95. package/dist/tools/public/login.js +230 -0
  96. package/dist/types/api.d.ts +12 -0
  97. package/dist/types/api.d.ts.map +1 -0
  98. package/dist/types/api.js +1 -0
  99. package/dist/utils/dockerRunner.d.ts +44 -0
  100. package/dist/utils/dockerRunner.d.ts.map +1 -0
  101. package/dist/utils/dockerRunner.js +300 -0
  102. package/dist/utils/formatters.d.ts +14 -0
  103. package/dist/utils/formatters.d.ts.map +1 -0
  104. package/dist/utils/formatters.js +510 -0
  105. package/dist/utils/promptBuilder.d.ts +4 -0
  106. package/dist/utils/promptBuilder.d.ts.map +1 -0
  107. package/dist/utils/promptBuilder.js +132 -0
  108. 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"}