mcp-prompt-optimizer 3.7.3 → 3.7.5

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/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.7.5] - 2026-07-18
9
+
10
+ ### Fixed
11
+ - **10 MCP tools were unconditionally broken**: `generate_agent_sop`, `transform_for_framework`, `generate_harness_bundle`, `explore_sop_approaches`, `get_prompt_by_slug`, `compile_prompt`, `list_template_versions`, `rollback_template`, `publish_template`, and `run_quick_evaluation` all called backend routes that only accepted a JWT — a credential this stdio client can never obtain (no browser, no login flow). Every call to these tools failed with an auth error, always. Backend now accepts API-key auth on these routes; verified the fix doesn't expand what a key is authorized to do (every route is scoped to the calling account's own data, same as routes that already worked).
12
+ - **Tier-upgrade messages never fired**: the 403-detection check looked for the literal string `'403'` inside the error body, which never appears there — only the HTTP status code carries it. Now checks the real status code, so upgrade prompts actually show up when a real tier gate is hit.
13
+ - **Fabricated fallback data**: `get_optimization_insights` and `get_real_time_status` silently returned hardcoded fake numbers (fake optimization counts, fake AG-UI metrics) on any backend error, indistinguishable from real account data. Both now say the data is unavailable instead of inventing it.
14
+ - **`formatRealTimeStatus` read the wrong fields**: it never matched the AG-UI status endpoint's actual response shape, even when the call succeeded.
15
+ - **`formatQuotaStatus` invented a fake `5000` quota limit** when the backend didn't report one.
16
+ - **Free-tier quota text said 7/month**; the real limit is 20.
17
+ - **Dead domain and wrong key-format references**: `promptoptimizer-blog.vercel.app` no longer resolves; some messages also claimed `sk-local-*` was a valid key format for this package (that's the sibling local package's prefix, not this one's).
18
+
19
+ ## [3.7.4] - 2026-07-16
20
+
21
+ ### Fixed
22
+ - **CLI commands were mostly non-functional**: the README documented 8 `mcp-prompt-optimizer <command>` subcommands, but the argv dispatcher only ever recognized `connect`. Running any of `check-status`, `validate-key`, `diagnose`, `clear-cache`, `help`, or `version` silently started the blocking stdio MCP server instead, hanging the terminal. All six are now wired to their existing `lib/*.js` implementations.
23
+ - **Unrecognized commands no longer fall through to the server.** Any argument that isn't a known command now prints an error and usage to stderr and exits 1, instead of silently starting the MCP server. This closes the bug class for future commands too, not just the six fixed here.
24
+ - **Dropped `test` from the documented CLI commands.** It mapped to `tests/test-runner.js`, a maintainer-only pre-publish validation script that isn't included in the published npm package (`tests/` is not in `files`), so it could never have worked for an end user.
25
+
8
26
  ## [3.7.3] - 2026-07-16
9
27
 
10
28
  ### Fixed
package/README.md CHANGED
@@ -368,7 +368,6 @@ All plans include AI context detection, template management, personal model conf
368
368
  npx mcp-prompt-optimizer connect # Interactive wizard: add API key to Claude Desktop config
369
369
  mcp-prompt-optimizer check-status # Check API key and quota status
370
370
  mcp-prompt-optimizer validate-key # Validate API key with backend
371
- mcp-prompt-optimizer test # Test backend integration
372
371
  mcp-prompt-optimizer diagnose # Run comprehensive diagnostic
373
372
  mcp-prompt-optimizer clear-cache # Clear validation cache
374
373
  mcp-prompt-optimizer help # Show help and setup instructions
package/index.js CHANGED
@@ -1056,31 +1056,7 @@ class MCPPromptOptimizer {
1056
1056
  return { content: [{ type: "text", text: this.formatOptimizationInsights(result) }] };
1057
1057
 
1058
1058
  } catch (error) {
1059
- // Fallback to mock insights
1060
- const mockInsights = {
1061
- bayesian_status: {
1062
- optimization_active: true,
1063
- total_optimizations: 47,
1064
- improvement_rate: '23.5%',
1065
- confidence_score: 0.89
1066
- },
1067
- parameter_insights: {
1068
- most_effective_goals: ['clarity', 'technical_accuracy', 'analytical_depth'],
1069
- context_performance: {
1070
- 'code_generation': 0.92,
1071
- 'llm_interaction': 0.87,
1072
- 'technical_automation': 0.84
1073
- },
1074
- optimization_trends: 'Steady improvement in technical contexts'
1075
- },
1076
- recommendations: args.include_recommendations !== false ? [
1077
- 'Focus on technical_accuracy for code generation prompts',
1078
- 'Combine clarity with analytical_depth for best results',
1079
- 'Consider using structured_output context for data tasks'
1080
- ] : []
1081
- };
1082
-
1083
- return { content: [{ type: "text", text: this.formatOptimizationInsights(mockInsights) }] };
1059
+ return { content: [{ type: "text", text: `🧠 Optimization insights are unavailable right now (${error.message}). This is not your data — no insights were generated.` }] };
1084
1060
  }
1085
1061
  }
1086
1062
 
@@ -1095,22 +1071,7 @@ class MCPPromptOptimizer {
1095
1071
  return { content: [{ type: "text", text: this.formatRealTimeStatus(result) }] };
1096
1072
 
1097
1073
  } catch (error) {
1098
- const mockStatus = {
1099
- agui_status: 'available',
1100
- streaming_optimization: true,
1101
- websocket_support: true,
1102
- real_time_analytics: true,
1103
- active_optimizations: 3,
1104
- average_response_time: '1.2s',
1105
- features: {
1106
- live_optimization: true,
1107
- collaborative_editing: true,
1108
- instant_feedback: true,
1109
- performance_monitoring: true
1110
- }
1111
- };
1112
-
1113
- return { content: [{ type: "text", text: this.formatRealTimeStatus(mockStatus) }] };
1074
+ return { content: [{ type: "text", text: `⚡ AG-UI real-time status is unavailable right now (${error.message}).` }] };
1114
1075
  }
1115
1076
  }
1116
1077
 
@@ -1434,8 +1395,8 @@ class MCPPromptOptimizer {
1434
1395
  }]
1435
1396
  };
1436
1397
  } catch (error) {
1437
- if (error.message && error.message.includes('403')) {
1438
- return { content: [{ type: "text", text: "Error: SOP exploration requires Innovator tier. Upgrade at /pricing." }] };
1398
+ if (error.statusCode === 403) {
1399
+ return { content: [{ type: "text", text: `Error: SOP exploration requires Innovator tier. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1439
1400
  }
1440
1401
  throw new Error(`Failed to explore SOP approaches: ${error.message}`);
1441
1402
  }
@@ -1450,9 +1411,8 @@ class MCPPromptOptimizer {
1450
1411
  output += `**Body:**\n\`\`\`\n${result.optimized_prompt || result.body || ''}\n\`\`\`\n`;
1451
1412
  return { content: [{ type: "text", text: output }] };
1452
1413
  } catch (error) {
1453
- const msg = error.message || '';
1454
- if (msg.includes('403') || msg.includes('TIER')) {
1455
- return { content: [{ type: "text", text: `Upgrade required: runtime prompt delivery requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
1414
+ if (error.statusCode === 403) {
1415
+ return { content: [{ type: "text", text: `Upgrade required: runtime prompt delivery requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1456
1416
  }
1457
1417
  throw new Error(`Failed to fetch prompt by slug: ${error.message}`);
1458
1418
  }
@@ -1465,9 +1425,8 @@ class MCPPromptOptimizer {
1465
1425
  const compiled = result.compiled_prompt || result.body || JSON.stringify(result, null, 2);
1466
1426
  return { content: [{ type: "text", text: `# Compiled Prompt\n\n\`\`\`\n${compiled}\n\`\`\`\n` }] };
1467
1427
  } catch (error) {
1468
- const msg = error.message || '';
1469
- if (msg.includes('403') || msg.includes('TIER')) {
1470
- return { content: [{ type: "text", text: `Upgrade required: prompt compilation requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
1428
+ if (error.statusCode === 403) {
1429
+ return { content: [{ type: "text", text: `Upgrade required: prompt compilation requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1471
1430
  }
1472
1431
  throw new Error(`Failed to compile prompt: ${error.message}`);
1473
1432
  }
@@ -1493,9 +1452,8 @@ class MCPPromptOptimizer {
1493
1452
  }
1494
1453
  return { content: [{ type: "text", text: output }] };
1495
1454
  } catch (error) {
1496
- const msg = error.message || '';
1497
- if (msg.includes('403') || msg.includes('TIER')) {
1498
- return { content: [{ type: "text", text: `Upgrade required: template versioning requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
1455
+ if (error.statusCode === 403) {
1456
+ return { content: [{ type: "text", text: `Upgrade required: template versioning requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1499
1457
  }
1500
1458
  throw new Error(`Failed to list template versions: ${error.message}`);
1501
1459
  }
@@ -1509,9 +1467,8 @@ class MCPPromptOptimizer {
1509
1467
  const msg = result.message || `Template rolled back to version ${args.version_number}`;
1510
1468
  return { content: [{ type: "text", text: `# ✅ Rollback Complete\n\n${msg}\n\n**Template ID:** \`${args.template_id}\`\n**Version:** ${args.version_number}` }] };
1511
1469
  } catch (error) {
1512
- const msg = error.message || '';
1513
- if (msg.includes('403') || msg.includes('TIER')) {
1514
- return { content: [{ type: "text", text: `Upgrade required: template rollback requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
1470
+ if (error.statusCode === 403) {
1471
+ return { content: [{ type: "text", text: `Upgrade required: template rollback requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1515
1472
  }
1516
1473
  throw new Error(`Failed to rollback template: ${error.message}`);
1517
1474
  }
@@ -1524,9 +1481,8 @@ class MCPPromptOptimizer {
1524
1481
  const msg = result.message || `Template ${args.template_id} published successfully`;
1525
1482
  return { content: [{ type: "text", text: `# ✅ Template Published\n\n${msg}\n\nThe template is now available for runtime delivery via \`get_prompt_by_slug\`.` }] };
1526
1483
  } catch (error) {
1527
- const msg = error.message || '';
1528
- if (msg.includes('403') || msg.includes('TIER')) {
1529
- return { content: [{ type: "text", text: `Upgrade required: template publishing requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
1484
+ if (error.statusCode === 403) {
1485
+ return { content: [{ type: "text", text: `Upgrade required: template publishing requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1530
1486
  }
1531
1487
  throw new Error(`Failed to publish template: ${error.message}`);
1532
1488
  }
@@ -1556,9 +1512,8 @@ class MCPPromptOptimizer {
1556
1512
  }
1557
1513
  return { content: [{ type: "text", text: output }] };
1558
1514
  } catch (error) {
1559
- const msg = error.message || '';
1560
- if (msg.includes('403') || msg.includes('TIER')) {
1561
- return { content: [{ type: "text", text: `Upgrade required: evaluations require Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
1515
+ if (error.statusCode === 403) {
1516
+ return { content: [{ type: "text", text: `Upgrade required: evaluations require Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
1562
1517
  }
1563
1518
  throw new Error(`Failed to run quick evaluation: ${error.message}`);
1564
1519
  }
@@ -1631,7 +1586,9 @@ class MCPPromptOptimizer {
1631
1586
  } catch {
1632
1587
  errorMessage = `HTTP ${res.statusCode}: ${responseData}`;
1633
1588
  }
1634
- reject(new Error(errorMessage));
1589
+ const httpError = new Error(errorMessage);
1590
+ httpError.statusCode = res.statusCode;
1591
+ reject(httpError);
1635
1592
  }
1636
1593
  } catch (parseError) {
1637
1594
  reject(new Error(`Invalid response format: ${parseError.message}`));
@@ -1787,7 +1744,7 @@ class MCPPromptOptimizer {
1787
1744
  output += `2. Generate your API key at https://promptoptimizer.xyz/dashboard\n`;
1788
1745
  output += `3. Run in your terminal:\n\n`;
1789
1746
  output += `\`\`\`\nnpx mcp-prompt-optimizer connect\n\`\`\`\n\n`;
1790
- output += `You get **7 LLM optimizations/month free**. Upgrade anytime for more.\n`;
1747
+ output += `You get **20 LLM optimizations/month free**. Upgrade anytime for more.\n`;
1791
1748
  }
1792
1749
 
1793
1750
  return output;
@@ -1801,25 +1758,30 @@ class MCPPromptOptimizer {
1801
1758
  output += `**Usage:** 🟢 Unlimited\n`;
1802
1759
  } else {
1803
1760
  const used = quota.used || 0;
1804
- const limit = quota.limit || 5000;
1761
+ const limit = quota.limit;
1762
+
1763
+ if (limit === undefined || limit === null) {
1764
+ output += `**Usage:** ${used} used (limit unavailable — backend did not report a quota limit)\n`;
1765
+ } else {
1805
1766
  const percentage = limit > 0 ? ((used / limit) * 100).toFixed(1) : 0;
1806
-
1767
+
1807
1768
  let statusIcon = '🟢';
1808
1769
  if (percentage >= 90) statusIcon = '🔴';
1809
1770
  else if (percentage >= 75) statusIcon = '🟡';
1810
-
1771
+
1811
1772
  output += `**Usage:** ${statusIcon} ${used}/${limit} (${percentage}%)\n`;
1812
1773
 
1813
1774
  const remaining = limit - used;
1814
1775
  if (remaining <= 0) {
1815
1776
  output += `\n❌ **Quota Exhausted** — You have no optimizations remaining this month.\n`;
1816
- output += `Upgrade at https://promptoptimizer.xyz/local-license\n`;
1777
+ output += `Upgrade at https://promptoptimizer.xyz/pricing\n`;
1817
1778
  output += `*(Quota resets at the start of your next billing cycle)*\n`;
1818
1779
  } else if (percentage >= 90) {
1819
- output += `\n⚠️ **Critical** — ${remaining} optimization${remaining === 1 ? '' : 's'} remaining. Upgrade at https://promptoptimizer.xyz/local-license\n`;
1780
+ output += `\n⚠️ **Critical** — ${remaining} optimization${remaining === 1 ? '' : 's'} remaining. Upgrade at https://promptoptimizer.xyz/pricing\n`;
1820
1781
  } else if (percentage >= 75) {
1821
1782
  output += `\n⚠️ **Warning** — Approaching your monthly limit.\n`;
1822
1783
  }
1784
+ }
1823
1785
  }
1824
1786
 
1825
1787
  output += `\n## ✨ **Available Features**\n`;
@@ -1841,9 +1803,9 @@ class MCPPromptOptimizer {
1841
1803
  }
1842
1804
 
1843
1805
  output += `\n## 🔗 **Account Management**\n`;
1844
- output += `- Dashboard: https://promptoptimizer-blog.vercel.app/dashboard\n`;
1845
- output += `- Analytics: https://promptoptimizer-blog.vercel.app/analytics\n`;
1846
- output += `- Upgrade: https://promptoptimizer.xyz/local-license\n`;
1806
+ output += `- Dashboard: https://promptoptimizer.xyz/dashboard\n`;
1807
+ output += `- Analytics: https://promptoptimizer.xyz/analytics\n`;
1808
+ output += `- Upgrade: https://promptoptimizer.xyz/pricing\n`;
1847
1809
 
1848
1810
  return output;
1849
1811
  }
@@ -1949,41 +1911,40 @@ class MCPPromptOptimizer {
1949
1911
  }
1950
1912
 
1951
1913
  output += `## 🔗 **Advanced Analytics**\n`;
1952
- output += `- Full Analytics: https://promptoptimizer-blog.vercel.app/analytics\n`;
1953
- output += `- Performance Dashboard: https://promptoptimizer-blog.vercel.app/dashboard\n`;
1914
+ output += `- Full Analytics: https://promptoptimizer.xyz/analytics\n`;
1915
+ output += `- Performance Dashboard: https://promptoptimizer.xyz/dashboard\n`;
1954
1916
 
1955
1917
  return output;
1956
1918
  }
1957
1919
 
1958
1920
  formatRealTimeStatus(status) {
1959
1921
  let output = `# ⚡ AG-UI Real-Time Status\n\n`;
1960
-
1922
+ const metrics = status.metrics || {};
1923
+
1961
1924
  output += `## 🚀 **Service Status**\n`;
1962
- output += `- **AG-UI Status:** ${status.agui_status === 'available' ? '🟢 Available' : '🔴 Unavailable'}\n`;
1963
- output += `- **Streaming Optimization:** ${status.streaming_optimization ? '✅ Enabled' : '❌ Disabled'}\n`;
1964
- output += `- **WebSocket Support:** ${status.websocket_support ? '✅ Enabled' : '❌ Disabled'}\n`;
1965
- output += `- **Real-time Analytics:** ${status.real_time_analytics ? '✅ Enabled' : '❌ Disabled'}\n\n`;
1966
-
1967
- if (status.active_optimizations !== undefined) {
1968
- output += `## 📈 **Current Activity**\n`;
1969
- output += `- **Active Optimizations:** ${status.active_optimizations}\n`;
1970
- output += `- **Average Response Time:** ${status.average_response_time}\n\n`;
1925
+ output += `- **AG-UI Status:** ${status.status === 'healthy' ? '🟢 Healthy' : '🔴 Degraded'}\n\n`;
1926
+
1927
+ output += `## 📈 **Current Activity**\n`;
1928
+ output += `- **Active Sessions:** ${metrics.active_sessions ?? 'unknown'}\n`;
1929
+ output += `- **Total Connections:** ${metrics.total_connections ?? 'unknown'}\n`;
1930
+ output += `- **Total Optimizations:** ${metrics.total_optimizations ?? 'unknown'}\n`;
1931
+ if (metrics.uptime_seconds !== undefined) {
1932
+ output += `- **Uptime:** ${Math.round(metrics.uptime_seconds)}s\n`;
1971
1933
  }
1972
-
1973
- if (status.features) {
1974
- const features = status.features;
1934
+ output += `\n`;
1935
+
1936
+ if (metrics.features_enabled) {
1975
1937
  output += `## ⚡ **Available Features**\n`;
1976
- if (features.live_optimization) output += `✅ Live Optimization\n`;
1977
- if (features.collaborative_editing) output += `✅ Collaborative Editing\n`;
1978
- if (features.instant_feedback) output += `✅ Instant Feedback\n`;
1979
- if (features.performance_monitoring) output += `✅ Performance Monitoring\n`;
1938
+ for (const [feature, enabled] of Object.entries(metrics.features_enabled)) {
1939
+ if (enabled) output += `✅ ${feature}\n`;
1940
+ }
1980
1941
  output += `\n`;
1981
1942
  }
1982
-
1943
+
1983
1944
  output += `## 🔗 **Real-Time Access**\n`;
1984
- output += `- Live Dashboard: https://promptoptimizer-blog.vercel.app/live\n`;
1945
+ output += `- Live Dashboard: https://promptoptimizer.xyz/live\n`;
1985
1946
  output += `- WebSocket Endpoint: Available via API\n`;
1986
-
1947
+
1987
1948
  return output;
1988
1949
  }
1989
1950
 
@@ -2001,7 +1962,7 @@ async function startValidatedMCPServer() {
2001
1962
  try {
2002
1963
  const apiKey = process.env.OPTIMIZER_API_KEY;
2003
1964
  if (!apiKey) {
2004
- console.error('❌ API key required. Get one at https://promptoptimizer.xyz/local-license');
1965
+ console.error('❌ API key required. Get one free at https://promptoptimizer.xyz/dashboard');
2005
1966
  process.exit(1);
2006
1967
  }
2007
1968
 
@@ -2179,7 +2140,7 @@ async function runConnectWizard() {
2179
2140
  }
2180
2141
  if (ok > 0) {
2181
2142
  console.log('\n👉 Restart your MCP client(s) to activate LLM optimization.');
2182
- console.log(' Free plan: 7 LLM optimizations/month.');
2143
+ console.log(' Free plan: 20 LLM optimizations/month.');
2183
2144
  console.log(' Upgrade at https://promptoptimizer.xyz/pricing\n');
2184
2145
  }
2185
2146
  }
@@ -2198,12 +2159,50 @@ async function runConnectWizard() {
2198
2159
  });
2199
2160
  }
2200
2161
 
2162
+ function printCliHelp() {
2163
+ console.log(`MCP Prompt Optimizer v${packageJson.version}
2164
+
2165
+ Usage:
2166
+ mcp-prompt-optimizer Start the MCP server (used by MCP clients)
2167
+ mcp-prompt-optimizer connect Interactive wizard: add API key to Claude Desktop config
2168
+ mcp-prompt-optimizer check-status Check API key and quota status
2169
+ mcp-prompt-optimizer validate-key Validate API key with backend
2170
+ mcp-prompt-optimizer diagnose Run comprehensive diagnostic
2171
+ mcp-prompt-optimizer clear-cache Clear validation cache
2172
+ mcp-prompt-optimizer help Show this help
2173
+ mcp-prompt-optimizer version Show version information`);
2174
+ }
2175
+
2176
+ function printCliVersion() {
2177
+ console.log(packageJson.version);
2178
+ }
2179
+
2180
+ const CLI_COMMANDS = {
2181
+ connect: runConnectWizard,
2182
+ 'check-status': () => require('./lib/check-status')().then(() => process.exit(0)),
2183
+ 'validate-key': () => require('./lib/validate-key')().then(() => process.exit(0)),
2184
+ 'clear-cache': () => require('./lib/clear-cache')().then(() => process.exit(0)),
2185
+ diagnose: () => require('./lib/diagnose')().then(() => process.exit(0)),
2186
+ help: printCliHelp,
2187
+ '--help': printCliHelp,
2188
+ '-h': printCliHelp,
2189
+ version: printCliVersion,
2190
+ '--version': printCliVersion,
2191
+ '-v': printCliVersion,
2192
+ };
2193
+
2201
2194
  if (require.main === module) {
2202
2195
  const args = process.argv.slice(2);
2203
- if (args[0] === 'connect') {
2204
- runConnectWizard();
2205
- } else {
2196
+ const cmd = args[0];
2197
+
2198
+ if (!cmd) {
2206
2199
  startValidatedMCPServer();
2200
+ } else if (CLI_COMMANDS[cmd]) {
2201
+ CLI_COMMANDS[cmd]();
2202
+ } else {
2203
+ console.error(`❌ Unknown command: ${cmd}\n`);
2204
+ printCliHelp();
2205
+ process.exit(1);
2207
2206
  }
2208
2207
  }
2209
2208
 
@@ -241,7 +241,11 @@ class CloudApiKeyManager {
241
241
  // SECURITY: Offline mode removed - backend validation required
242
242
  // No cache fallback beyond 2 hours
243
243
 
244
- throw new Error(`API key validation failed: ${error.message}. Please check your internet connection.`);
244
+ // A 4xx means the backend rejected the key itself, not a connectivity
245
+ // problem — telling the user to check their internet is wrong there.
246
+ const isClientError = error.statusCode >= 400 && error.statusCode < 500;
247
+ const hint = isClientError ? '' : ' Please check your internet connection.';
248
+ throw new Error(`API key validation failed: ${error.message}.${hint}`);
245
249
  }
246
250
  }
247
251
 
@@ -268,7 +272,17 @@ class CloudApiKeyManager {
268
272
  } catch (error) {
269
273
  lastError = error;
270
274
  this.log(`Attempt ${attempt} failed: ${error.message}`, 'warn');
271
-
275
+
276
+ // A 4xx means the backend definitively rejected the request (bad/expired
277
+ // key, malformed request) — retrying with the same key can't change that.
278
+ // Only network errors and 5xx (transient/server-side) are worth retrying.
279
+ // 429 is the exception: rate limits are exactly what backoff is for.
280
+ const isClientError = error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429;
281
+ if (isClientError) {
282
+ this.log('Non-retryable client error — not retrying', 'error');
283
+ break;
284
+ }
285
+
272
286
  if (attempt < this.maxRetries) {
273
287
  const delay = this.calculateRetryDelay(attempt);
274
288
  this.log(`Retrying in ${delay}ms...`);
@@ -579,7 +593,7 @@ class CloudApiKeyManager {
579
593
 
580
594
  throw new Error(
581
595
  'API key required. Set the OPTIMIZER_API_KEY environment variable.\n' +
582
- 'Get your API key at: https://promptoptimizer.xyz/local-license'
596
+ 'Get your API key at: https://promptoptimizer.xyz/dashboard'
583
597
  );
584
598
  }
585
599
 
@@ -690,7 +704,9 @@ class CloudApiKeyManager {
690
704
  } catch {
691
705
  errorMessage = `HTTP ${res.statusCode}: ${responseData}`;
692
706
  }
693
- reject(new Error(errorMessage));
707
+ const httpError = new Error(errorMessage);
708
+ httpError.statusCode = res.statusCode;
709
+ reject(httpError);
694
710
  }
695
711
  } catch (parseError) {
696
712
  reject(new Error(`Invalid response format: ${parseError.message}`));
@@ -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-local-your-key-here'); // Aligned with free tier/development
13
+ console.log(' export OPTIMIZER_API_KEY=sk-opt-your-key-here');
14
14
  if (developmentMode) {
15
15
  console.log('\n🧪 Development Mode Options:');
16
16
  console.log(' export OPTIMIZER_API_KEY=sk-dev-test-key');
@@ -25,7 +25,13 @@ async function checkStatus() {
25
25
  console.log('='.repeat(50));
26
26
  console.log(`🎯 Subscription Tier: ${apiKeyInfo.tier}`);
27
27
  console.log(`🔑 API Key Type: ${apiKeyInfo.keyType}`);
28
- console.log(`✅ Key Status: ${apiKeyInfo.isValid ? 'Valid' : 'Invalid'}`);
28
+ console.log(`${apiKeyInfo.isValid ? '✅' : '❌'} Key Status: ${apiKeyInfo.isValid ? 'Valid' : 'Invalid'}`);
29
+ if (!apiKeyInfo.isValid) {
30
+ // getApiKeyInfo() reports failure via isValid:false rather than throwing,
31
+ // so this catch block never sees it — exit(1) has to happen here instead.
32
+ if (apiKeyInfo.error) console.log(` ${apiKeyInfo.error}`);
33
+ process.exit(1);
34
+ }
29
35
  } catch (error) {
30
36
  console.error(`❌ Status check failed: ${error.message}\n`);
31
37
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-prompt-optimizer",
3
- "version": "3.7.3",
3
+ "version": "3.7.5",
4
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": {
@@ -124,8 +124,8 @@
124
124
  "required_keys": [
125
125
  {
126
126
  "name": "OPTIMIZER_API_KEY",
127
- "format": "sk-opt-*, sk-team-*, sk-dev-*, or sk-local-*",
128
- "description": "Cloud API key from promptoptimizer.xyz/local-license or development key for testing",
127
+ "format": "sk-opt-*, sk-team-*, or sk-dev-*",
128
+ "description": "Cloud API key from promptoptimizer.xyz/dashboard or development key for testing",
129
129
  "required": true
130
130
  }
131
131
  ],
@@ -195,6 +195,28 @@
195
195
  "last_sync": "2026-06-01T00:00:00Z"
196
196
  },
197
197
  "release_notes": {
198
+ "v3.7.5": {
199
+ "major_features": [
200
+ "Fixed: 10 MCP tools (generate_agent_sop, transform_for_framework, generate_harness_bundle, explore_sop_approaches, get_prompt_by_slug, compile_prompt, list_template_versions, rollback_template, publish_template, run_quick_evaluation) called backend routes that only accepted a JWT, which this stdio client can never obtain — every call failed unconditionally. Backend now accepts API-key auth on these routes too.",
201
+ "Fixed: the tier-upgrade message for these tools never fired because the code checked for the literal string '403' inside the error body, which never appears there (that's the HTTP status code, not body text). Now checks the real status code.",
202
+ "Fixed: get_optimization_insights and get_real_time_status fell back to hardcoded fake data (fabricated optimization counts, fake AG-UI metrics) on any backend error, presented as real account data. Both now report unavailability honestly instead.",
203
+ "Fixed: formatRealTimeStatus was never reading the AG-UI status endpoint's actual response shape, even on success.",
204
+ "Fixed: formatQuotaStatus invented a fake 5000 quota limit when the backend didn't report one.",
205
+ "Fixed: free-tier quota text said 7/month; actual limit is 20.",
206
+ "Fixed: dead promptoptimizer-blog.vercel.app domain and wrong sk-local-* key-format claims (that's the sibling local package's prefix, not this one's) across index.js, check-status.js, and package.json."
207
+ ],
208
+ "breaking_changes": [],
209
+ "migration_guide": "No migration required. Users who called any of the 10 previously-broken MCP tools will now get real results instead of an auth error."
210
+ },
211
+ "v3.7.4": {
212
+ "major_features": [
213
+ "Fixed: 6 of 8 documented CLI commands (check-status, validate-key, diagnose, clear-cache, help, version) were not implemented in the argv dispatcher and silently started the blocking MCP server instead, hanging the terminal. Now wired to their lib/*.js implementations.",
214
+ "Fixed: unrecognized CLI arguments now print an error + usage to stderr and exit(1), instead of silently falling through to server startup. Removes the entire hung-terminal bug class, not just the known commands.",
215
+ "Removed 'test' from documented CLI commands: it mapped to a maintainer-only pre-publish script (tests/test-runner.js) that isn't included in the published package."
216
+ ],
217
+ "breaking_changes": [],
218
+ "migration_guide": "No migration required. Only affects users who ran documented CLI subcommands other than 'connect', which previously hung."
219
+ },
198
220
  "v3.7.3": {
199
221
  "major_features": [
200
222
  "Docs only: corrected stale free-tier quota in README (7 optimizations/month -> 20), matching the backend enforcement fix"