@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,234 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { createHash, createCipheriv, createDecipheriv, randomBytes } from 'crypto';
|
|
5
|
+
/**
|
|
6
|
+
* Secure session storage for MCP PerfAI server
|
|
7
|
+
* Handles persistent storage of authentication sessions with encryption
|
|
8
|
+
*/
|
|
9
|
+
export class SessionStorage {
|
|
10
|
+
storageDir;
|
|
11
|
+
sessionFile;
|
|
12
|
+
keyFile;
|
|
13
|
+
encryptionKey = null;
|
|
14
|
+
constructor(appName = 'mcp-perfai') {
|
|
15
|
+
// Use OS-specific config directory for npm package compatibility
|
|
16
|
+
this.storageDir = this.getConfigDirectory(appName);
|
|
17
|
+
this.sessionFile = path.join(this.storageDir, 'session.enc');
|
|
18
|
+
this.keyFile = path.join(this.storageDir, 'session.key');
|
|
19
|
+
this.ensureStorageDirectory();
|
|
20
|
+
this.initializeEncryptionKey();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get OS-specific configuration directory
|
|
24
|
+
*/
|
|
25
|
+
getConfigDirectory(appName) {
|
|
26
|
+
const platform = os.platform();
|
|
27
|
+
let configDir;
|
|
28
|
+
switch (platform) {
|
|
29
|
+
case 'win32':
|
|
30
|
+
configDir = path.join(os.homedir(), 'AppData', 'Local', appName);
|
|
31
|
+
break;
|
|
32
|
+
case 'darwin':
|
|
33
|
+
configDir = path.join(os.homedir(), 'Library', 'Application Support', appName);
|
|
34
|
+
break;
|
|
35
|
+
default: // linux and others
|
|
36
|
+
configDir = path.join(os.homedir(), '.config', appName);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
return configDir;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Ensure storage directory exists
|
|
43
|
+
*/
|
|
44
|
+
ensureStorageDirectory() {
|
|
45
|
+
try {
|
|
46
|
+
if (!fs.existsSync(this.storageDir)) {
|
|
47
|
+
fs.mkdirSync(this.storageDir, { recursive: true, mode: 0o700 });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error('Failed to create storage directory:', error);
|
|
52
|
+
throw new Error('Cannot initialize session storage');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Initialize or load encryption key
|
|
57
|
+
*/
|
|
58
|
+
initializeEncryptionKey() {
|
|
59
|
+
try {
|
|
60
|
+
if (fs.existsSync(this.keyFile)) {
|
|
61
|
+
// Load existing key
|
|
62
|
+
this.encryptionKey = fs.readFileSync(this.keyFile, 'utf8').trim();
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Generate new key
|
|
66
|
+
this.encryptionKey = randomBytes(32).toString('hex');
|
|
67
|
+
fs.writeFileSync(this.keyFile, this.encryptionKey, { mode: 0o600 });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error('Failed to initialize encryption key:', error);
|
|
72
|
+
// Fall back to in-memory key (less secure but functional)
|
|
73
|
+
this.encryptionKey = randomBytes(32).toString('hex');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Encrypt data using AES-256-CBC
|
|
78
|
+
*/
|
|
79
|
+
encrypt(data) {
|
|
80
|
+
try {
|
|
81
|
+
if (!this.encryptionKey) {
|
|
82
|
+
throw new Error('Encryption key not available');
|
|
83
|
+
}
|
|
84
|
+
const algorithm = 'aes-256-cbc';
|
|
85
|
+
const iv = randomBytes(16);
|
|
86
|
+
const key = createHash('sha256').update(this.encryptionKey).digest();
|
|
87
|
+
const cipher = createCipheriv(algorithm, key, iv);
|
|
88
|
+
let encrypted = cipher.update(data, 'utf8', 'hex');
|
|
89
|
+
encrypted += cipher.final('hex');
|
|
90
|
+
return iv.toString('hex') + ':' + encrypted;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error('Encryption failed:', error);
|
|
94
|
+
throw new Error('Failed to encrypt session data');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Decrypt data using AES-256-CBC
|
|
99
|
+
*/
|
|
100
|
+
decrypt(encryptedData) {
|
|
101
|
+
try {
|
|
102
|
+
if (!this.encryptionKey) {
|
|
103
|
+
throw new Error('Encryption key not available');
|
|
104
|
+
}
|
|
105
|
+
const algorithm = 'aes-256-cbc';
|
|
106
|
+
const parts = encryptedData.split(':');
|
|
107
|
+
if (parts.length !== 2) {
|
|
108
|
+
throw new Error('Invalid encrypted data format');
|
|
109
|
+
}
|
|
110
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
111
|
+
const encrypted = parts[1];
|
|
112
|
+
const key = createHash('sha256').update(this.encryptionKey).digest();
|
|
113
|
+
const decipher = createDecipheriv(algorithm, key, iv);
|
|
114
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
115
|
+
decrypted += decipher.final('utf8');
|
|
116
|
+
return decrypted;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error('Decryption failed:', error);
|
|
120
|
+
throw new Error('Failed to decrypt session data');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Save session to persistent storage
|
|
125
|
+
*/
|
|
126
|
+
async saveSession(session) {
|
|
127
|
+
try {
|
|
128
|
+
// Add metadata for validation
|
|
129
|
+
const sessionData = {
|
|
130
|
+
version: '1.0.0',
|
|
131
|
+
savedAt: Date.now(),
|
|
132
|
+
session: {
|
|
133
|
+
...session,
|
|
134
|
+
// Convert Date objects to timestamps for JSON serialization
|
|
135
|
+
fixedIssues: session.fixedIssues?.map(issue => ({
|
|
136
|
+
...issue,
|
|
137
|
+
fixedAt: issue.fixedAt instanceof Date ? issue.fixedAt.getTime() : issue.fixedAt
|
|
138
|
+
}))
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const jsonData = JSON.stringify(sessionData, null, 2);
|
|
142
|
+
const encryptedData = this.encrypt(jsonData);
|
|
143
|
+
fs.writeFileSync(this.sessionFile, encryptedData, { mode: 0o600 });
|
|
144
|
+
console.error('💾 Session saved successfully');
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error('❌ Failed to save session:', error);
|
|
148
|
+
// Don't throw - allow app to continue without persistence
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Load session from persistent storage
|
|
153
|
+
*/
|
|
154
|
+
async loadSession() {
|
|
155
|
+
try {
|
|
156
|
+
if (!fs.existsSync(this.sessionFile)) {
|
|
157
|
+
console.error('📂 No saved session found');
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const encryptedData = fs.readFileSync(this.sessionFile, 'utf8');
|
|
161
|
+
const jsonData = this.decrypt(encryptedData);
|
|
162
|
+
const sessionData = JSON.parse(jsonData);
|
|
163
|
+
// Validate session format and version
|
|
164
|
+
if (!sessionData.version || !sessionData.session) {
|
|
165
|
+
console.error('⚠️ Invalid session format, clearing storage');
|
|
166
|
+
this.clearSession();
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const session = sessionData.session;
|
|
170
|
+
// Check if session is expired
|
|
171
|
+
if (Date.now() >= session.expiresAt) {
|
|
172
|
+
console.error('⏰ Saved session has expired, clearing storage');
|
|
173
|
+
this.clearSession();
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
// Restore Date objects
|
|
177
|
+
if (session.fixedIssues) {
|
|
178
|
+
session.fixedIssues = session.fixedIssues.map(issue => ({
|
|
179
|
+
...issue,
|
|
180
|
+
fixedAt: new Date(issue.fixedAt)
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
const timeLeft = Math.round((session.expiresAt - Date.now()) / (1000 * 60 * 60));
|
|
184
|
+
console.error(`✅ Session loaded successfully, expires in ${timeLeft} hours`);
|
|
185
|
+
return session;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error('❌ Failed to load session:', error);
|
|
189
|
+
// Clear corrupted session
|
|
190
|
+
this.clearSession();
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Clear stored session
|
|
196
|
+
*/
|
|
197
|
+
clearSession() {
|
|
198
|
+
try {
|
|
199
|
+
if (fs.existsSync(this.sessionFile)) {
|
|
200
|
+
fs.unlinkSync(this.sessionFile);
|
|
201
|
+
console.error('🗑️ Session storage cleared');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error('❌ Failed to clear session storage:', error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Check if session exists and is valid
|
|
210
|
+
*/
|
|
211
|
+
hasValidSession() {
|
|
212
|
+
try {
|
|
213
|
+
if (!fs.existsSync(this.sessionFile)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const encryptedData = fs.readFileSync(this.sessionFile, 'utf8');
|
|
217
|
+
const jsonData = this.decrypt(encryptedData);
|
|
218
|
+
const sessionData = JSON.parse(jsonData);
|
|
219
|
+
if (!sessionData.session?.expiresAt) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
return Date.now() < sessionData.session.expiresAt;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get session storage location (for debugging)
|
|
230
|
+
*/
|
|
231
|
+
getStorageLocation() {
|
|
232
|
+
return this.storageDir;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ApiCatalog, ApiSelection } from '../types/api.js';
|
|
2
|
+
export interface FixedIssue {
|
|
3
|
+
issueId: string;
|
|
4
|
+
issue: any;
|
|
5
|
+
fixedAt: Date;
|
|
6
|
+
prompt: string;
|
|
7
|
+
status: "ai-fixed" | "pending-validation";
|
|
8
|
+
}
|
|
9
|
+
export interface UserSession {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
idToken: string;
|
|
12
|
+
tokenType: string;
|
|
13
|
+
userInfo: any;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
organizations?: any[];
|
|
16
|
+
selectedOrgId?: string;
|
|
17
|
+
selectedApiId?: string;
|
|
18
|
+
selectedApiData?: ApiCatalog;
|
|
19
|
+
selectedApiIdentifier?: ApiSelection;
|
|
20
|
+
selectedSecurityAppId?: string;
|
|
21
|
+
selectedDesignAppId?: string;
|
|
22
|
+
selectedQualityAppId?: string;
|
|
23
|
+
fixedIssues?: FixedIssue[];
|
|
24
|
+
fixedDesignIssues?: FixedIssue[];
|
|
25
|
+
fixedQualityIssues?: FixedIssue[];
|
|
26
|
+
appSequenceMap?: Map<number, string>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG3D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,oBAAoB,CAAC;CAC3C;AAGD,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,GAAG,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,UAAU,CAAC;IAC7B,qBAAqB,CAAC,EAAE,YAAY,CAAC;IACrC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,UAAU,EAAE,CAAC;IACjC,kBAAkB,CAAC,EAAE,UAAU,EAAE,CAAC;IAClC,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare const SERVER_INFO: {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const AUTH0_DOMAIN: string;
|
|
6
|
+
export declare const CLIENT_ID: string;
|
|
7
|
+
export declare const CLIENT_SECRET: string;
|
|
8
|
+
export declare const REDIRECT_URI: string;
|
|
9
|
+
export declare const API_ENDPOINTS: {
|
|
10
|
+
readonly TOKEN: `https://${string}/oauth/token`;
|
|
11
|
+
readonly USERINFO: `https://${string}/userinfo`;
|
|
12
|
+
readonly ORGANIZATIONS: "https://api.perfai.ai/api/v1/org-users/get_users_orgs";
|
|
13
|
+
readonly SECURITY_ISSUES: "https://api.perfai.ai/api/v1/sensitive-data-service/apps/issues/all-by-org";
|
|
14
|
+
readonly SECURITY_ISSUES_BY_APP: "https://api.perfai.ai/api/v1/sensitive-data-service/apps/issues/by-app";
|
|
15
|
+
readonly DESIGN_ISSUES_BY_APP: "https://api.perfai.ai/api/v1/design-analysis-service/apps/org/issues";
|
|
16
|
+
readonly APP_SUMMARY: "https://api.perfai.ai/api/v1/api-catalog/apps/summary";
|
|
17
|
+
readonly QUALITY_ISSUES_BY_APP: "https://api.perfai.ai/api/v1/spec-validation/tests";
|
|
18
|
+
readonly API_CATALOG: "https://api.perfai.ai/api/v1/api-catalog/apps/catalog_id_name_label";
|
|
19
|
+
readonly API_CATALOG_SERVICE_IDS: "https://api.perfai.ai/api/v1/api-catalog/apps/service-ids";
|
|
20
|
+
readonly WEBAPP_API: "https://api.perfai.ai/api/v1";
|
|
21
|
+
readonly DASHBOARD_API: "https://lm.perfai.ai/api/v1";
|
|
22
|
+
readonly DASHBOARD_API_AUTH: string;
|
|
23
|
+
readonly LLM_API: "https://llmservice.dev.perfai.ai/api/tokens";
|
|
24
|
+
readonly LLM_GENERATE_API: "http://localhost:4000/api/generate";
|
|
25
|
+
};
|
|
26
|
+
export declare const DOCKER_HUB: {
|
|
27
|
+
readonly USERNAME: string;
|
|
28
|
+
readonly PAT: string;
|
|
29
|
+
};
|
|
30
|
+
export declare const DOCKER_CONFIG: {
|
|
31
|
+
readonly IMAGE: "mohdrashid1/perfai-agent";
|
|
32
|
+
readonly TIMEOUT: number;
|
|
33
|
+
readonly ENV_VARS: {
|
|
34
|
+
readonly MODE: "LOCAL";
|
|
35
|
+
readonly TASK_TYPE: "PRIVACY";
|
|
36
|
+
readonly AGENT_ID: "MCP_LOCAL_DOCKER_PERFAI_AGENT";
|
|
37
|
+
readonly PERF_CATEGORY_FILE: "perf_categories.csv";
|
|
38
|
+
readonly TEST_CATEGORY_FILE: "test_categories.csv";
|
|
39
|
+
readonly PRIVACY_CATEGORY_FILE: "privacy_categories.csv";
|
|
40
|
+
readonly SPEC_VALIDATION_CATEGORY_FILE: "spec_validation_categories.csv";
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare const DOCKER_CONFIG_VISION_AI_APP_LEARNING: {
|
|
44
|
+
readonly IMAGE: "mohdrashid1/vision-ai-agent";
|
|
45
|
+
readonly TIMEOUT: number;
|
|
46
|
+
};
|
|
47
|
+
export declare const DESIGN_DOCKER_CONFIG: {
|
|
48
|
+
readonly IMAGE: "mohdrashid1/perfai-agent";
|
|
49
|
+
readonly TIMEOUT: number;
|
|
50
|
+
readonly ENV_VARS: {
|
|
51
|
+
readonly MODE: "LOCAL";
|
|
52
|
+
readonly TASK_TYPE: "GOVERNANCE";
|
|
53
|
+
readonly AGENT_ID: "MCP_LOCAL_DOCKER_PERFAI_AGENT_DESIGN";
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export declare const QUALITY_DOCKER_CONFIG: {
|
|
57
|
+
readonly IMAGE: "mohdrashid1/perfai-agent";
|
|
58
|
+
readonly TIMEOUT: number;
|
|
59
|
+
readonly ENV_VARS: {
|
|
60
|
+
readonly MODE: "LOCAL";
|
|
61
|
+
readonly TASK_TYPE: "SPEC_VALIDATION";
|
|
62
|
+
readonly AGENT_ID: "MCP_LOCAL_DOCKER_PERFAI_AGENT_QUALITY";
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,WAAW;;;CAGvB,CAAC;AAGF,eAAO,MAAM,YAAY,QAAsD,CAAC;AAChF,eAAO,MAAM,SAAS,QAA2E,CAAC;AAElG,eAAO,MAAM,aAAa,QAA+C,CAAC;AAE1E,eAAO,MAAM,YAAY,QAA6D,CAAC;AAGvF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;CAiBhB,CAAC;AAGX,eAAO,MAAM,UAAU;;;CAGb,CAAC;AAGX,eAAO,MAAM,aAAa;;;;;;;;;;;;CAYhB,CAAC;AAGX,eAAO,MAAM,oCAAoC;;;CAGvC,CAAC;AAGX,eAAO,MAAM,oBAAoB;;;;;;;;CAQvB,CAAC;AAGX,eAAO,MAAM,qBAAqB;;;;;;;;CAQxB,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Server configuration
|
|
2
|
+
export const SERVER_INFO = {
|
|
3
|
+
name: "mcp-typescript-server",
|
|
4
|
+
version: "1.0.0",
|
|
5
|
+
};
|
|
6
|
+
// Auth0 / PerfAI configuration (override via env / K8s Secret)
|
|
7
|
+
export const AUTH0_DOMAIN = process.env.PERFAI_AUTH0_DOMAIN || "auth.perfai.ai";
|
|
8
|
+
export const CLIENT_ID = process.env.PERFAI_AUTH0_CLIENT_ID || "OxipoJS8EIdpN3KzhVQ280zkKxwzGLfI";
|
|
9
|
+
// Leave empty by default to avoid bundling secrets in source.
|
|
10
|
+
export const CLIENT_SECRET = process.env.PERFAI_AUTH0_CLIENT_SECRET || "";
|
|
11
|
+
export const REDIRECT_URI = process.env.PERFAI_REDIRECT_URI || "http://localhost:5173";
|
|
12
|
+
// API endpoints
|
|
13
|
+
export const API_ENDPOINTS = {
|
|
14
|
+
TOKEN: `https://${AUTH0_DOMAIN}/oauth/token`,
|
|
15
|
+
USERINFO: `https://${AUTH0_DOMAIN}/userinfo`,
|
|
16
|
+
ORGANIZATIONS: 'https://api.perfai.ai/api/v1/org-users/get_users_orgs',
|
|
17
|
+
SECURITY_ISSUES: 'https://api.perfai.ai/api/v1/sensitive-data-service/apps/issues/all-by-org',
|
|
18
|
+
SECURITY_ISSUES_BY_APP: 'https://api.perfai.ai/api/v1/sensitive-data-service/apps/issues/by-app',
|
|
19
|
+
DESIGN_ISSUES_BY_APP: 'https://api.perfai.ai/api/v1/design-analysis-service/apps/org/issues',
|
|
20
|
+
APP_SUMMARY: 'https://api.perfai.ai/api/v1/api-catalog/apps/summary',
|
|
21
|
+
QUALITY_ISSUES_BY_APP: 'https://api.perfai.ai/api/v1/spec-validation/tests',
|
|
22
|
+
API_CATALOG: 'https://api.perfai.ai/api/v1/api-catalog/apps/catalog_id_name_label',
|
|
23
|
+
API_CATALOG_SERVICE_IDS: 'https://api.perfai.ai/api/v1/api-catalog/apps/service-ids',
|
|
24
|
+
WEBAPP_API: 'https://api.perfai.ai/api/v1',
|
|
25
|
+
DASHBOARD_API: 'https://lm.perfai.ai/api/v1',
|
|
26
|
+
// Provide full header value (e.g. "Authorization: Basic <base64>") via env / K8s Secret.
|
|
27
|
+
DASHBOARD_API_AUTH: process.env.PERFAI_DASHBOARD_API_AUTH || '',
|
|
28
|
+
LLM_API: 'https://llmservice.dev.perfai.ai/api/tokens',
|
|
29
|
+
LLM_GENERATE_API: 'http://localhost:4000/api/generate',
|
|
30
|
+
};
|
|
31
|
+
// Docker Hub credentials for pulling private images
|
|
32
|
+
export const DOCKER_HUB = {
|
|
33
|
+
USERNAME: process.env.DOCKER_HUB_USERNAME || 'mohdrashid1',
|
|
34
|
+
PAT: process.env.DOCKER_HUB_PAT || 'dckr_pat_g3sj9rLa0sYrlzT5BmgZS_24vl8',
|
|
35
|
+
};
|
|
36
|
+
// Docker configuration
|
|
37
|
+
export const DOCKER_CONFIG = {
|
|
38
|
+
IMAGE: 'mohdrashid1/perfai-agent',
|
|
39
|
+
TIMEOUT: 30 * 60 * 1000, // 30 minutes
|
|
40
|
+
ENV_VARS: {
|
|
41
|
+
MODE: 'LOCAL',
|
|
42
|
+
TASK_TYPE: 'PRIVACY',
|
|
43
|
+
AGENT_ID: 'MCP_LOCAL_DOCKER_PERFAI_AGENT',
|
|
44
|
+
PERF_CATEGORY_FILE: 'perf_categories.csv',
|
|
45
|
+
TEST_CATEGORY_FILE: 'test_categories.csv',
|
|
46
|
+
PRIVACY_CATEGORY_FILE: 'privacy_categories.csv',
|
|
47
|
+
SPEC_VALIDATION_CATEGORY_FILE: 'spec_validation_categories.csv',
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
// Docker configuration for Vision AI App Learning (GENERATE_SPEC / OAS generation)
|
|
51
|
+
export const DOCKER_CONFIG_VISION_AI_APP_LEARNING = {
|
|
52
|
+
IMAGE: 'mohdrashid1/vision-ai-agent',
|
|
53
|
+
TIMEOUT: 45 * 60 * 1000, // 45 minutes (OAS generation can be long)
|
|
54
|
+
};
|
|
55
|
+
// Design-specific Docker configuration (matches extension implementation)
|
|
56
|
+
export const DESIGN_DOCKER_CONFIG = {
|
|
57
|
+
IMAGE: 'mohdrashid1/perfai-agent',
|
|
58
|
+
TIMEOUT: 30 * 60 * 1000, // 30 minutes
|
|
59
|
+
ENV_VARS: {
|
|
60
|
+
MODE: 'LOCAL',
|
|
61
|
+
TASK_TYPE: 'GOVERNANCE', // Design testing uses GOVERNANCE task type
|
|
62
|
+
AGENT_ID: 'MCP_LOCAL_DOCKER_PERFAI_AGENT_DESIGN',
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
// Quality-specific Docker configuration (matches extension implementation)
|
|
66
|
+
export const QUALITY_DOCKER_CONFIG = {
|
|
67
|
+
IMAGE: 'mohdrashid1/perfai-agent',
|
|
68
|
+
TIMEOUT: 30 * 60 * 1000, // 30 minutes
|
|
69
|
+
ENV_VARS: {
|
|
70
|
+
MODE: 'LOCAL',
|
|
71
|
+
TASK_TYPE: 'SPEC_VALIDATION', // Quality testing uses SPEC_VALIDATION task type
|
|
72
|
+
AGENT_ID: 'MCP_LOCAL_DOCKER_PERFAI_AGENT_QUALITY',
|
|
73
|
+
}
|
|
74
|
+
};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":""}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
dotenv.config();
|
|
7
|
+
// Import configuration and types
|
|
8
|
+
import { SERVER_INFO } from './config.js';
|
|
9
|
+
import { AuthenticationManager } from './auth/authManager.js';
|
|
10
|
+
// Import tool definitions and handlers
|
|
11
|
+
import { PUBLIC_TOOLS, PROTECTED_TOOLS, handleLogin, handleAuthStatus, handleUserInfo, handleLogout, handleSetup, handleShowSecurityIssues, handleShowDesignIssues, handleShowQualityIssues, handleManageOrganizations, handleListApis, handleSelectApi, handleAiFixSecurityIssue, handleAiFixDesignIssue, handleAiFixQualityIssue, handleShowFixedIssues, handleCheckSecurityFixes, handleCheckDesignFixes, handleCheckQualityFixes, handleSummarizeIssues } from './tools/index.js';
|
|
12
|
+
const authManager = new AuthenticationManager();
|
|
13
|
+
// Create the server instance
|
|
14
|
+
const server = new Server(SERVER_INFO, {
|
|
15
|
+
capabilities: {
|
|
16
|
+
tools: { listChanged: true }, // 👈 Enable dynamic tool list updates
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
// List tools handler - returns different tools based on authentication status
|
|
20
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
21
|
+
const isAuthenticated = await authManager.isAuthenticated();
|
|
22
|
+
console.error(`🔍 ListTools called - Authenticated: ${isAuthenticated}`);
|
|
23
|
+
// ALWAYS show all tools - authentication is enforced in CallToolRequestSchema
|
|
24
|
+
console.error(`🛠️ Always returning all ${Object.keys(PUBLIC_TOOLS).length + Object.keys(PROTECTED_TOOLS).length} tools`);
|
|
25
|
+
return {
|
|
26
|
+
tools: [...Object.values(PUBLIC_TOOLS), ...Object.values(PROTECTED_TOOLS)],
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
// Call tool handler with authentication logic
|
|
30
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
31
|
+
const { name, arguments: args } = request.params;
|
|
32
|
+
try {
|
|
33
|
+
// Handle public tools (authentication tools)
|
|
34
|
+
switch (name) {
|
|
35
|
+
case "login": {
|
|
36
|
+
return await handleLogin(authManager);
|
|
37
|
+
}
|
|
38
|
+
case "auth_status": {
|
|
39
|
+
return await handleAuthStatus(authManager);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Check authentication for protected tools
|
|
43
|
+
if (!(await authManager.isAuthenticated())) {
|
|
44
|
+
throw new McpError(ErrorCode.InvalidRequest, `🔐 Authentication required. Please run the 'login' tool first to access '${name}'.`);
|
|
45
|
+
}
|
|
46
|
+
// Handle protected tools (require authentication)
|
|
47
|
+
switch (name) {
|
|
48
|
+
case "user_info": {
|
|
49
|
+
return await handleUserInfo(authManager);
|
|
50
|
+
}
|
|
51
|
+
case "logout": {
|
|
52
|
+
return await handleLogout(authManager);
|
|
53
|
+
}
|
|
54
|
+
case "setup": {
|
|
55
|
+
return await handleSetup(authManager, args);
|
|
56
|
+
}
|
|
57
|
+
case "show_security_issues": {
|
|
58
|
+
return await handleShowSecurityIssues(authManager, args);
|
|
59
|
+
}
|
|
60
|
+
case "show_design_issues": {
|
|
61
|
+
return await handleShowDesignIssues(authManager, args);
|
|
62
|
+
}
|
|
63
|
+
case "show_quality_issues": {
|
|
64
|
+
return await handleShowQualityIssues(authManager, args);
|
|
65
|
+
}
|
|
66
|
+
case "manage_organizations": {
|
|
67
|
+
return await handleManageOrganizations(authManager, args);
|
|
68
|
+
}
|
|
69
|
+
case "list_apps": {
|
|
70
|
+
return await handleListApis(authManager);
|
|
71
|
+
}
|
|
72
|
+
case "select_app": {
|
|
73
|
+
return await handleSelectApi(authManager, args);
|
|
74
|
+
}
|
|
75
|
+
case "ai_fix_security_issue": {
|
|
76
|
+
return await handleAiFixSecurityIssue(authManager, args);
|
|
77
|
+
}
|
|
78
|
+
case "ai_fix_design_issue": {
|
|
79
|
+
return await handleAiFixDesignIssue(authManager, args);
|
|
80
|
+
}
|
|
81
|
+
case "ai_fix_quality_issue": {
|
|
82
|
+
return await handleAiFixQualityIssue(authManager, args);
|
|
83
|
+
}
|
|
84
|
+
case "show_fixed_issues": {
|
|
85
|
+
return await handleShowFixedIssues(authManager);
|
|
86
|
+
}
|
|
87
|
+
case "check_security_fixes": {
|
|
88
|
+
return await handleCheckSecurityFixes(authManager, args);
|
|
89
|
+
}
|
|
90
|
+
case "check_design_fixes": {
|
|
91
|
+
return await handleCheckDesignFixes(authManager, args);
|
|
92
|
+
}
|
|
93
|
+
case "check_quality_fixes": {
|
|
94
|
+
return await handleCheckQualityFixes(authManager, args);
|
|
95
|
+
}
|
|
96
|
+
case "summarize_issues": {
|
|
97
|
+
return await handleSummarizeIssues(authManager);
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error instanceof McpError) {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error}`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// Error handling
|
|
111
|
+
server.onerror = (error) => {
|
|
112
|
+
// Log errors to stderr to avoid interfering with STDIO transport
|
|
113
|
+
console.error("[MCP Error]", error);
|
|
114
|
+
};
|
|
115
|
+
process.on("SIGINT", async () => {
|
|
116
|
+
await server.close();
|
|
117
|
+
process.exit(0);
|
|
118
|
+
});
|
|
119
|
+
// Start the server
|
|
120
|
+
async function main() {
|
|
121
|
+
// Only log to stderr for STDIO transport compatibility
|
|
122
|
+
console.error("🚀 Starting PerfAI MCP TypeScript Server with Authentication...");
|
|
123
|
+
console.error(`🔧 Server: ${SERVER_INFO.name} v${SERVER_INFO.version}`);
|
|
124
|
+
console.error("📋 Public tools:", Object.keys(PUBLIC_TOOLS).join(", "));
|
|
125
|
+
console.error("🔒 Protected tools:", Object.keys(PROTECTED_TOOLS).join(", "));
|
|
126
|
+
console.error("⚠️ Note: Protected tools require authentication via 'login' tool");
|
|
127
|
+
const transport = new StdioServerTransport();
|
|
128
|
+
await server.connect(transport);
|
|
129
|
+
console.error("✅ PerfAI MCP Server is running and ready to accept connections!");
|
|
130
|
+
console.error("🔑 Run 'login' tool to authenticate and access all features");
|
|
131
|
+
}
|
|
132
|
+
// Handle uncaught exceptions
|
|
133
|
+
process.on("uncaughtException", (error) => {
|
|
134
|
+
console.error("Uncaught Exception:", error);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
});
|
|
137
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
138
|
+
console.error("Unhandled Rejection at:", promise, "reason:", reason);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
|
141
|
+
main().catch((error) => {
|
|
142
|
+
console.error("Failed to start server:", error);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-config.d.ts","sourceRoot":"","sources":["../src/setup-config.ts"],"names":[],"mappings":";AAOA,iBAAS,mBAAmB,SA2D3B;AAUD,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
// Generate MCP configuration files
|
|
5
|
+
function generateConfigFiles() {
|
|
6
|
+
const configs = {
|
|
7
|
+
cursor: {
|
|
8
|
+
mcpServers: {
|
|
9
|
+
"perfai-mcp": {
|
|
10
|
+
type: "stdio",
|
|
11
|
+
command: "npx",
|
|
12
|
+
args: ["perfai-mcp-server"],
|
|
13
|
+
env: {
|
|
14
|
+
PERFAI_USERNAME: "your-username-here",
|
|
15
|
+
PERFAI_PASSWORD: "your-password-here"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
vscode: {
|
|
21
|
+
mcpServers: {
|
|
22
|
+
"perfai-mcp": {
|
|
23
|
+
type: "stdio",
|
|
24
|
+
command: "npx",
|
|
25
|
+
args: ["perfai-mcp-server"],
|
|
26
|
+
env: {
|
|
27
|
+
PERFAI_USERNAME: "your-username-here",
|
|
28
|
+
PERFAI_PASSWORD: "your-password-here"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// Get current working directory
|
|
35
|
+
const currentDir = process.cwd();
|
|
36
|
+
// Generate Cursor config
|
|
37
|
+
const cursorConfigPath = path.join(currentDir, 'cursor_mcp_config.json');
|
|
38
|
+
fs.writeFileSync(cursorConfigPath, JSON.stringify(configs.cursor, null, 2));
|
|
39
|
+
// Generate VS Code config
|
|
40
|
+
const vscodeConfigPath = path.join(currentDir, 'vscode_mcp_config.json');
|
|
41
|
+
fs.writeFileSync(vscodeConfigPath, JSON.stringify(configs.vscode, null, 2));
|
|
42
|
+
console.log('🎉 MCP Configuration files generated successfully!');
|
|
43
|
+
console.log('');
|
|
44
|
+
console.log('📁 Files created:');
|
|
45
|
+
console.log(` • ${cursorConfigPath}`);
|
|
46
|
+
console.log(` • ${vscodeConfigPath}`);
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log('📝 Next steps:');
|
|
49
|
+
console.log('1. Edit the config files and replace the placeholder credentials:');
|
|
50
|
+
console.log(' - Replace "your-username-here" with your PerfAI username');
|
|
51
|
+
console.log(' - Replace "your-password-here" with your PerfAI password');
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log('2. Copy the config to your MCP client:');
|
|
54
|
+
console.log(' • For Cursor: Copy cursor_mcp_config.json to your Cursor config directory');
|
|
55
|
+
console.log(' • For VS Code: Copy vscode_mcp_config.json to your VS Code config directory');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log('3. Restart your MCP client to load the new configuration');
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log('🔗 Package URL: https://www.npmjs.com/package/perfai-mcp-server');
|
|
60
|
+
}
|
|
61
|
+
// Main execution
|
|
62
|
+
try {
|
|
63
|
+
generateConfigFiles();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('❌ Error generating config files:', error);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
export { generateConfigFiles };
|