mcp-prompt-optimizer 1.4.2 → 2.2.3

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.
@@ -15,7 +15,7 @@ const packageJson = require('../package.json');
15
15
  class CloudApiKeyManager {
16
16
  constructor(apiKey, options = {}) {
17
17
  this.apiKey = apiKey;
18
- this.backendUrl = options.backendUrl || process.env.OPTIMIZER_BACKEND_URL || 'https://p01--project-optimizer--fvrdk8m9k9j.code.run';
18
+ this.backendUrl = options.backendUrl || process.env.OPTIMIZER_BACKEND_URL || 'https://p01--project-optimizer--fvmrdk8m9k9j.code.run';
19
19
  this.cacheFile = path.join(os.homedir(), '.mcp-cloud-api-cache.json');
20
20
  this.healthFile = path.join(os.homedir(), '.mcp-cloud-health.json');
21
21
  this.cacheExpiry = options.cacheExpiry || 24 * 60 * 60 * 1000; // 24 hours
@@ -196,8 +196,7 @@ class CloudApiKeyManager {
196
196
 
197
197
  this.log(`API key format valid: ${formatCheck.keyType}`);
198
198
 
199
- // Step 2: Development mode handling
200
- if (this.developmentMode || formatCheck.keyType === 'development' || formatCheck.keyType === 'testing') {
199
+ if (this.developmentMode || formatCheck.keyType === 'testing') {
201
200
  this.log('Development/testing mode detected, using mock validation', 'warn');
202
201
  const mockValidation = this.generateMockValidation(formatCheck.keyType);
203
202
  await this.cacheValidation(mockValidation);
@@ -290,99 +289,23 @@ class CloudApiKeyManager {
290
289
 
291
290
  // ✅ FIXED: Correct API endpoint URL
292
291
  async validateWithBackend() {
293
- return new Promise((resolve, reject) => {
294
- const url = `${this.backendUrl}/api/v1/mcp/validate-key`;
295
-
296
- const options = {
297
- method: 'GET',
298
- headers: {
299
- 'x-api-key': this.apiKey,
300
- 'Content-Type': 'application/json',
301
- 'User-Agent': `mcp-prompt-optimizer/${packageJson.version}`,
302
- 'Accept': 'application/json',
303
- 'Connection': 'close' // Ensure connection cleanup
304
- },
305
- timeout: this.requestTimeout
306
- };
307
-
308
- this.log(`Making request to: ${url}`);
309
- this.log(`Using API key: ${this.apiKey.substring(0, 16)}...`);
310
-
311
- const client = this.backendUrl.startsWith('https://') ? https : http;
312
- const req = client.request(url, options, (res) => {
313
- let data = '';
314
-
315
- res.on('data', (chunk) => {
316
- data += chunk;
317
- });
318
-
319
- res.on('end', () => {
320
- this.log(`Response status: ${res.statusCode}`);
321
-
322
- try {
323
- if (res.statusCode === 200) {
324
- const validation = JSON.parse(data);
325
- this.log(`Validation successful: ${JSON.stringify(validation, null, 2)}`);
326
- resolve(validation);
327
- } else if (res.statusCode === 401) {
328
- reject(new Error('Invalid API key or unauthorized access'));
329
- } else if (res.statusCode === 403) {
330
- reject(new Error('API key expired or quota exceeded'));
331
- } else if (res.statusCode === 429) {
332
- reject(new Error('Rate limit exceeded. Please try again later.'));
333
- } else if (res.statusCode === 500) {
334
- reject(new Error('Backend server error. Please try again later.'));
335
- } else if (res.statusCode === 503) {
336
- reject(new Error('Backend service temporarily unavailable. Please try again later.'));
337
- } else {
338
- let errorMessage;
339
- try {
340
- const error = JSON.parse(data);
341
- errorMessage = error.detail || error.message || `HTTP ${res.statusCode}`;
342
- } catch {
343
- errorMessage = `HTTP ${res.statusCode}: ${data}`;
344
- }
345
- reject(new Error(errorMessage));
346
- }
347
- } catch (parseError) {
348
- this.log(`Parse error: ${parseError.message}`, 'error');
349
- this.log(`Raw response: ${data}`, 'error');
350
- reject(new Error(`Invalid response format: ${parseError.message}`));
351
- }
352
- });
353
- });
354
-
355
- req.on('error', (error) => {
356
- this.log(`Network error: ${error.message}`, 'error');
357
-
358
- // Enhanced error classification
359
- if (error.code === 'ENOTFOUND') {
360
- reject(new Error(`DNS resolution failed: Cannot resolve ${this.backendUrl.replace(/^https?:\/\//, '')}`));
361
- } else if (error.code === 'ECONNREFUSED') {
362
- reject(new Error(`Connection refused: Backend server may be down`));
363
- } else if (error.code === 'ETIMEDOUT') {
364
- reject(new Error(`Connection timeout: Backend server is not responding`));
365
- } else if (error.code === 'ECONNRESET') {
366
- reject(new Error(`Connection reset: Network instability detected`));
367
- } else {
368
- reject(new Error(`Network error: ${error.message}`));
369
- }
370
- });
371
-
372
- req.on('timeout', () => {
373
- req.destroy();
374
- reject(new Error('Request timeout - backend may be unavailable'));
375
- });
376
-
377
- req.setTimeout(this.requestTimeout);
378
- req.end();
379
- });
292
+ const endpoint = '/api/v1/api-keys/validate';
293
+ const method = 'POST';
294
+
295
+ try {
296
+ const validation = await this._makeBackendRequest(endpoint, null, method);
297
+ this.log(`Validation successful: ${JSON.stringify(validation, null, 2)}`);
298
+ return validation;
299
+ } catch (error) {
300
+ this.log(`Backend validation request failed: ${error.message}`, 'error');
301
+ throw error;
302
+ }
380
303
  }
381
304
 
382
305
  // ✅ FIXED: Correct API endpoint URL
383
306
  async getQuotaStatus() {
384
307
  try {
385
- const url = `${this.backendUrl}/api/v1/mcp/quota-status`;
308
+ const url = `${this.backendUrl}/api/v1/api-keys/quota-status`;
386
309
 
387
310
  const options = {
388
311
  method: 'GET',
@@ -462,38 +385,7 @@ class CloudApiKeyManager {
462
385
  }
463
386
  }
464
387
 
465
- // Enhanced quota status checking
466
- async checkQuotaStatus(validation) {
467
- const quota = validation.quota || {};
468
-
469
- if (quota.unlimited) {
470
- return { allowed: true, unlimited: true };
471
- }
472
388
 
473
- const quotaUsed = quota.used || 0;
474
- const quotaLimit = quota.limit || 5000;
475
- const quotaRemaining = quota.remaining || (quotaLimit - quotaUsed);
476
-
477
- if (quotaUsed >= quotaLimit) {
478
- const tier = validation.tier || 'explorer';
479
- const upgradeMessage = tier === 'explorer'
480
- ? 'Upgrade to Creator ($25.99/mo) for 18,000 optimizations: https://promptoptimizer-blog.vercel.app/pricing'
481
- : 'Quota will reset on your next billing cycle.';
482
-
483
- throw new Error(
484
- `Monthly quota exceeded (${quotaUsed}/${quotaLimit}). ${upgradeMessage}`
485
- );
486
- }
487
-
488
- return {
489
- allowed: true,
490
- unlimited: false,
491
- used: quotaUsed,
492
- limit: quotaLimit,
493
- remaining: quotaRemaining,
494
- usage_percentage: (quotaUsed / quotaLimit) * 100
495
- };
496
- }
497
389
 
498
390
  // Enhanced caching with metadata
499
391
  async cacheValidation(validation) {
@@ -593,13 +485,10 @@ class CloudApiKeyManager {
593
485
  // Step 1: Validate API key
594
486
  const validation = await this.validateApiKey();
595
487
 
596
- // Step 2: Check quota (skip for development/mock modes)
597
- let quotaStatus;
598
- if (validation.mock_mode || validation.fallback_mode || validation.offline_mode) {
599
- quotaStatus = validation.quota || { allowed: true, unlimited: true };
600
- } else {
601
- quotaStatus = await this.checkQuotaStatus(validation);
602
- }
488
+ // Step 2: Get comprehensive quota status
489
+ const info = await this.getApiKeyInfo();
490
+ let quotaStatus = info.quota;
491
+ validation = info; // Use info as the main validation object for consistency
603
492
 
604
493
  // Step 3: Log success
605
494
  const mode = validation.mock_mode ? '(mock)' :
@@ -633,24 +522,57 @@ class CloudApiKeyManager {
633
522
 
634
523
  // Enhanced API key info with mode detection
635
524
  async getApiKeyInfo() {
525
+ const formatCheck = this.validateApiKeyFormat(this.apiKey);
526
+ if (this.developmentMode || formatCheck.keyType === 'development' || formatCheck.keyType === 'testing') {
527
+ this.log('Development/testing mode detected for getApiKeyInfo, returning mock data.', 'warn');
528
+ return {
529
+ tier: 'testing',
530
+ features: {
531
+ optimization: true,
532
+ template_search: true,
533
+ template_auto_save: true,
534
+ optimization_insights: true,
535
+ bayesian_optimization: true,
536
+ agui_features: true
537
+ },
538
+ quota: {
539
+ unlimited: false,
540
+ used: Math.floor(Math.random() * 100),
541
+ limit: 1000,
542
+ remaining: 1000 - Math.floor(Math.random() * 100)
543
+ },
544
+ isValid: true,
545
+ keyType: formatCheck.keyType,
546
+ mode: {
547
+ mock: true,
548
+ fallback: false,
549
+ offline: false,
550
+ development: this.developmentMode
551
+ }
552
+ };
553
+ }
554
+
636
555
  try {
637
- const validation = await this.validateApiKey();
638
- const quotaStatus = await this.checkQuotaStatus(validation);
556
+ // Directly call the new quota-status endpoint
557
+ const quotaStatusResponse = await this._makeBackendRequest('/api/v1/api-keys/quota-status', null, 'GET');
639
558
 
559
+ // The backend /mcp/quota-status endpoint returns a comprehensive object
560
+ // that includes tier, quota details, and features.
640
561
  return {
641
- tier: validation.tier,
642
- features: validation.features || {},
643
- quota: quotaStatus,
562
+ tier: quotaStatusResponse.tier,
563
+ features: quotaStatusResponse.features_available || {},
564
+ quota: quotaStatusResponse.quota,
644
565
  isValid: true,
645
- keyType: validation.api_key_type || this.validateApiKeyFormat(this.apiKey).keyType,
566
+ keyType: quotaStatusResponse.account_type || this.validateApiKeyFormat(this.apiKey).keyType,
646
567
  mode: {
647
- mock: validation.mock_mode || false,
648
- fallback: validation.fallback_mode || false,
649
- offline: validation.offline_mode || false,
568
+ mock: false, // This endpoint doesn't return mock status
569
+ fallback: false,
570
+ offline: false,
650
571
  development: this.developmentMode
651
572
  }
652
573
  };
653
574
  } catch (error) {
575
+ this.log(`Error getting API key info: ${error.message}`, 'error');
654
576
  return {
655
577
  tier: null,
656
578
  features: {},
@@ -692,6 +614,137 @@ class CloudApiKeyManager {
692
614
  if (!this.apiKey) return 'No key';
693
615
  return `${this.apiKey.substring(0, 8)}...${this.apiKey.slice(-4)}`;
694
616
  }
617
+
618
+ async getDiagnosticInfo() {
619
+ const diagnosticInfo = {
620
+ apiKey: this.apiKey ? this.formatKeyForDisplay() : 'Not set',
621
+ backendUrl: this.backendUrl,
622
+ cacheFile: this.cacheFile,
623
+ healthFile: this.healthFile,
624
+ cacheExpiry: this.cacheExpiry,
625
+ fallbackCacheExpiry: this.fallbackCacheExpiry,
626
+ offlineMode: this.offlineMode,
627
+ developmentMode: this.developmentMode,
628
+ maxRetries: this.maxRetries,
629
+ requestTimeout: this.requestTimeout,
630
+ nodeEnv: process.env.NODE_ENV || 'not set',
631
+ packageVersion: packageJson.version,
632
+ networkHealth: { ...this.networkHealth },
633
+ cache: {},
634
+ keyFormat: this.validateApiKeyFormat(this.apiKey),
635
+ backendConnectivity: { status: 'unknown', error: null, responseTime: null }
636
+ };
637
+
638
+ // Get cache status
639
+ try {
640
+ const cached = await this.getCachedValidation();
641
+ if (cached) {
642
+ diagnosticInfo.cache.exists = true;
643
+ diagnosticInfo.cache.expired = this.isCacheExpired(cached);
644
+ diagnosticInfo.cache.fallbackExpired = this.isFallbackCacheExpired(cached);
645
+ diagnosticInfo.cache.age = Math.round((Date.now() - cached.timestamp) / 1000 / 60);
646
+ diagnosticInfo.cache.backendUrl = cached.backendUrl;
647
+ diagnosticInfo.cache.packageVersion = cached.packageVersion;
648
+ } else {
649
+ diagnosticInfo.cache.exists = false;
650
+ }
651
+ } catch (error) {
652
+ diagnosticInfo.cache.error = error.message;
653
+ diagnosticInfo.cache.exists = false;
654
+ }
655
+
656
+ // Check backend connectivity
657
+ if (this.apiKey) { // Only check if API key is present
658
+ try {
659
+ const startTime = Date.now();
660
+ await this.validateWithBackend(); // This will attempt to connect to the backend
661
+ const responseTime = Date.now() - startTime;
662
+ diagnosticInfo.backendConnectivity.status = 'success';
663
+ diagnosticInfo.backendConnectivity.responseTime = responseTime;
664
+ } catch (error) {
665
+ diagnosticInfo.backendConnectivity.status = 'failed';
666
+ diagnosticInfo.backendConnectivity.error = error.message;
667
+ }
668
+ } else {
669
+ diagnosticInfo.backendConnectivity.status = 'skipped';
670
+ diagnosticInfo.backendConnectivity.error = 'No API key provided for connectivity check.';
671
+ }
672
+
673
+ return diagnosticInfo;
674
+ }
675
+
676
+ async _makeBackendRequest(endpoint, data, method = 'POST') {
677
+ return new Promise((resolve, reject) => {
678
+ const url = `${this.backendUrl}${endpoint}`;
679
+
680
+ const options = {
681
+ method: method,
682
+ headers: {
683
+ 'x-api-key': this.apiKey,
684
+ 'Content-Type': 'application/json',
685
+ 'User-Agent': `mcp-prompt-optimizer/${packageJson.version}`,
686
+ 'Accept': 'application/json',
687
+ 'Connection': 'close'
688
+ },
689
+ timeout: this.requestTimeout
690
+ };
691
+
692
+ const client = this.backendUrl.startsWith('https://') ? https : http;
693
+ const req = client.request(url, options, (res) => {
694
+ let responseData = '';
695
+
696
+ res.on('data', (chunk) => {
697
+ responseData += chunk;
698
+ });
699
+
700
+ res.on('end', () => {
701
+ try {
702
+ if (res.statusCode >= 200 && res.statusCode < 300) {
703
+ const parsed = JSON.parse(responseData);
704
+ resolve(parsed);
705
+ } else {
706
+ let errorMessage;
707
+ try {
708
+ const error = JSON.parse(responseData);
709
+ errorMessage = error.detail || error.message || `HTTP ${res.statusCode}`;
710
+ } catch {
711
+ errorMessage = `HTTP ${res.statusCode}: ${responseData}`;
712
+ }
713
+ reject(new Error(errorMessage));
714
+ }
715
+ } catch (parseError) {
716
+ reject(new Error(`Invalid response format: ${parseError.message}`));
717
+ }
718
+ });
719
+ });
720
+
721
+ req.on('error', (error) => {
722
+ if (error.code === 'ENOTFOUND') {
723
+ reject(new Error(`DNS resolution failed: Cannot resolve ${this.backendUrl.replace(/^https?:\/\//, '')}`));
724
+ } else if (error.code === 'ECONNREFUSED') {
725
+ reject(new Error(`Connection refused: Backend server may be down`));
726
+ } else if (error.code === 'ETIMEDOUT') {
727
+ reject(new Error(`Connection timeout: Backend server is not responding`));
728
+ } else if (error.code === 'ECONNRESET') {
729
+ reject(new Error(`Connection reset: Network instability detected`));
730
+ } else {
731
+ reject(new Error(`Network error: ${error.message}`));
732
+ }
733
+ });
734
+
735
+ req.on('timeout', () => {
736
+ req.destroy();
737
+ reject(new Error('Request timeout - backend may be unavailable'));
738
+ });
739
+
740
+ req.setTimeout(this.requestTimeout);
741
+
742
+ if (method !== 'GET' && data) {
743
+ req.write(JSON.stringify(data));
744
+ }
745
+ req.end();
746
+ });
747
+ }
695
748
  }
696
749
 
697
750
  module.exports = CloudApiKeyManager;
@@ -10,7 +10,7 @@ async function checkStatus() {
10
10
  if (!apiKey) {
11
11
  console.error('❌ No API key found');
12
12
  console.log('\n📝 Set your API key to check status:');
13
- console.log(' export OPTIMIZER_API_KEY=sk-opt-your-key-here');
13
+ console.log(' export OPTIMIZER_API_KEY=sk-local-your-key-here'); // Aligned with free tier/development
14
14
  if (developmentMode) {
15
15
  console.log('\n🧪 Development Mode Options:');
16
16
  console.log(' export OPTIMIZER_API_KEY=sk-dev-test-key');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-prompt-optimizer",
3
- "version": "1.4.2",
4
- "description": "Professional cloud-based MCP server for AI-powered prompt optimization with intelligent context detection, template auto-save, optimization insights, personal model configuration via WebUI, team collaboration, enterprise-grade features, production resilience, and startup validation. Universal compatibility with Claude Desktop, Cursor, Windsurf, and 17+ MCP clients.",
3
+ "version": "2.2.3",
4
+ "description": "Professional cloud-based MCP server for AI-powered prompt optimization with intelligent context detection, Bayesian optimization, AG-UI real-time optimization, template auto-save, optimization insights, personal model configuration via WebUI, team collaboration, enterprise-grade features, production resilience, and startup validation. Universal compatibility with Claude Desktop, Cursor, Windsurf, and 17+ MCP clients.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "mcp-prompt-optimizer": "index.js"
@@ -23,8 +23,10 @@
23
23
  "test:integration": "node tests/integration-test.js",
24
24
  "test:comprehensive": "node tests/comprehensive-test.js",
25
25
  "test:runner": "node tests/test-runner.js",
26
+ "test:simple": "node tests/simple-test.js",
26
27
  "pretest": "npm run health-check",
27
- "prepublishOnly": "npm run test:quick"
28
+ "prepublishOnly": "npm run test:quick",
29
+ "version": "echo 'Updating version...' && npm run test:quick"
28
30
  },
29
31
  "dependencies": {
30
32
  "@modelcontextprotocol/sdk": "^1.15.1",
@@ -85,7 +87,17 @@
85
87
  "windows",
86
88
  "macos",
87
89
  "linux",
88
- "arm64"
90
+ "arm64",
91
+ "bayesian-optimization",
92
+ "ag-ui-real-time",
93
+ "streaming-optimization",
94
+ "websocket-support",
95
+ "performance-optimization",
96
+ "advanced-analytics",
97
+ "intelligent-context",
98
+ "ai-aware-rules",
99
+ "parameter-tuning",
100
+ "optimization-strategies"
89
101
  ],
90
102
  "author": "Prompt Optimizer Team <support@promptoptimizer.help>",
91
103
  "license": "SEE LICENSE IN LICENSE",
@@ -100,7 +112,6 @@
100
112
  "files": [
101
113
  "index.js",
102
114
  "lib/",
103
- "tests/",
104
115
  "README.md",
105
116
  "CHANGELOG.md",
106
117
  "CROSS-PLATFORM.md",
@@ -161,9 +172,73 @@
161
172
  "description": "Enable development mode (true/false)",
162
173
  "required": false,
163
174
  "default": "false"
175
+ },
176
+ {
177
+ "name": "ENABLE_BAYESIAN_OPTIMIZATION",
178
+ "description": "Enable Bayesian optimization features (true/false)",
179
+ "required": false,
180
+ "default": "true"
181
+ },
182
+ {
183
+ "name": "ENABLE_AGUI_FEATURES",
184
+ "description": "Enable AG-UI real-time optimization features (true/false)",
185
+ "required": false,
186
+ "default": "true"
164
187
  }
165
188
  ]
166
189
  },
190
+ "features": {
191
+ "core": {
192
+ "ai_context_detection": true,
193
+ "template_management": true,
194
+ "optimization_insights": true,
195
+ "quota_management": true,
196
+ "multi_tier_support": true,
197
+ "error_handling": true,
198
+ "caching": true,
199
+ "fallback_modes": true
200
+ },
201
+ "advanced": {
202
+ "bayesian_optimization": true,
203
+ "ag_ui_real_time": true,
204
+ "streaming_optimization": true,
205
+ "intelligent_routing": true,
206
+ "performance_analytics": true,
207
+ "ai_aware_rules": true,
208
+ "context_aware_templates": true,
209
+ "parameter_tuning": true
210
+ },
211
+ "enterprise": {
212
+ "team_collaboration": true,
213
+ "advanced_analytics": true,
214
+ "custom_models": true,
215
+ "priority_support": true,
216
+ "sla_guarantees": true,
217
+ "dedicated_resources": true
218
+ }
219
+ },
220
+ "backend_alignment": {
221
+ "version": "production-v2.2.0-stable",
222
+ "api_version": "v1",
223
+ "endpoints_aligned": true,
224
+ "feature_parity": true,
225
+ "bayesian_support": true,
226
+ "agui_support": true,
227
+ "last_sync": "2025-09-25T00:00:00Z"
228
+ },
229
+ "release_notes": {
230
+ "v1.5.0": {
231
+ "major_features": [
232
+ "Bayesian optimization with parameter tuning",
233
+ "AG-UI real-time optimization capabilities",
234
+ "Enhanced AI context detection with weighted scoring",
235
+ "Advanced template search with AI-aware filtering"
236
+ ],
237
+ "breaking_changes": [],
238
+ "migration_guide": "All existing functionality remains compatible. New features are opt-in via environment variables.",
239
+ "backend_compatibility": "Fully aligned with FastAPI Backend v2.1.0-bayesian"
240
+ }
241
+ },
167
242
  "support": {
168
243
  "email": "support@promptoptimizer.help",
169
244
  "documentation": "https://promptoptimizer-blog.vercel.app/docs",