mcp-prompt-optimizer 3.7.2 → 3.7.4

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,18 @@ 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.4] - 2026-07-16
9
+
10
+ ### Fixed
11
+ - **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.
12
+ - **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.
13
+ - **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.
14
+
15
+ ## [3.7.3] - 2026-07-16
16
+
17
+ ### Fixed
18
+ - **Stale free-tier quota in README**: docs said 7 optimizations/month, live enforcement is 20. Docs-only fix, no code change.
19
+
8
20
  ## [3.7.2] - 2026-07-05
9
21
 
10
22
  ### Fixed
package/README.md CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  **1. Get your API key:**
28
28
 
29
- - **🆓 Free Tier** (`sk-opt-*`): 7 LLM optimizations/month, 1 API key — no credit card required
29
+ - **🆓 Free Tier** (`sk-opt-*`): 20 LLM optimizations/month, 1 API key — no credit card required
30
30
  - **⭐ Pro** (`sk-opt-*`): 500 optimizations/month, full model config, Context Engineer
31
31
  - **🏢 Enterprise** (`sk-team-*`): Unlimited optimizations, team keys, shared quotas
32
32
 
@@ -352,7 +352,7 @@ Configure custom models in the WebUI and the MCP server uses them automatically.
352
352
 
353
353
  | Plan | Price | Optimizations/month | CE Credits | API Keys |
354
354
  |---|---|---|---|---|
355
- | 🆓 Free | $0 | 7 LLM | — | 1 |
355
+ | 🆓 Free | $0 | 20 LLM | — | 1 |
356
356
  | ⭐ Pro | $19/mo | 500 | 5 | 1 |
357
357
  | 🏢 Enterprise | Custom | Unlimited | 50 | 10 (shared) |
358
358
 
@@ -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
@@ -501,4 +500,4 @@ Windsurf, Cline, VS Code, Zed, Replit, JetBrains IDEs, and Neovim are all suppor
501
500
 
502
501
  ---
503
502
 
504
- *Start free at [promptoptimizer.xyz](https://promptoptimizer.xyz) — 7 LLM optimizations/month, no credit card required.*
503
+ *Start free at [promptoptimizer.xyz](https://promptoptimizer.xyz) — 20 LLM optimizations/month, no credit card required.*
package/index.js CHANGED
@@ -57,6 +57,18 @@ const ENDPOINTS = {
57
57
  /** AG‑UI status (GET) */
58
58
  AGUI_STATUS: '/api/status',
59
59
 
60
+ /** Prompt delivery by slug */
61
+ GET_PROMPT_BY_SLUG: (slug) => `/api/v1/prompts/${slug}`,
62
+ COMPILE_PROMPT: (slug) => `/api/v1/prompts/${slug}/compiled`,
63
+
64
+ /** Template governance (versioning, publish) */
65
+ TEMPLATE_VERSIONS: (id) => `/api/v1/templates/${id}/versions`,
66
+ ROLLBACK_TEMPLATE: (id, n) => `/api/v1/templates/${id}/rollback/${n}`,
67
+ PUBLISH_TEMPLATE: (id) => `/api/v1/templates/${id}/publish`,
68
+
69
+ /** Quick evaluation (stateless) */
70
+ QUICK_EVALUATE: '/api/v1/evaluations/quick-evaluate',
71
+
60
72
  /** Context Engineer (CE) endpoints */
61
73
  CE: {
62
74
  SOP: '/api/v1/context-engineer/sop',
@@ -73,7 +85,8 @@ const ENDPOINTS = {
73
85
  const DEPLOY_TARGET_ENUM = [
74
86
  "claude_code", "claude_desktop", "cursor", "copilot",
75
87
  "windsurf", "cline", "zed", "replit", "openai_agents", "ollama",
76
- "amazon_q", "aider", "continue_dev", "crewai"
88
+ "amazon_q", "aider", "continue_dev", "crewai",
89
+ "codex_cli",
77
90
  ];
78
91
 
79
92
  class MCPPromptOptimizer {
@@ -465,6 +478,79 @@ class MCPPromptOptimizer {
465
478
  additionalProperties: false
466
479
  }
467
480
  },
481
+ {
482
+ name: "get_prompt_by_slug",
483
+ description: "Fetch your latest published prompt template by slug for runtime use — decouple prompts from deploys.",
484
+ inputSchema: {
485
+ type: "object",
486
+ properties: {
487
+ slug: { type: "string", description: "The URL-safe slug of the prompt template (e.g., product-writer-a3f9c21b)" }
488
+ },
489
+ required: ["slug"]
490
+ }
491
+ },
492
+ {
493
+ name: "compile_prompt",
494
+ description: "Compile a prompt template with variable interpolation for runtime delivery — returns the fully interpolated prompt string ready for use.",
495
+ inputSchema: {
496
+ type: "object",
497
+ properties: {
498
+ slug: { type: "string", description: "The URL-safe slug of the prompt template" },
499
+ variables: {
500
+ type: "object",
501
+ description: "Variable values to interpolate (e.g. {\"user_name\": \"Alex\", \"plan\": \"Pro\"})",
502
+ additionalProperties: { type: "string" }
503
+ }
504
+ },
505
+ required: ["slug"]
506
+ }
507
+ },
508
+ {
509
+ name: "list_template_versions",
510
+ description: "List all version snapshots of a saved template — every update creates a snapshot you can inspect or restore.",
511
+ inputSchema: {
512
+ type: "object",
513
+ properties: {
514
+ template_id: { type: "string", description: "The ID of the template" }
515
+ },
516
+ required: ["template_id"]
517
+ }
518
+ },
519
+ {
520
+ name: "rollback_template",
521
+ description: "Restore a template to a previous version snapshot — undo unwanted changes instantly.",
522
+ inputSchema: {
523
+ type: "object",
524
+ properties: {
525
+ template_id: { type: "string", description: "The ID of the template" },
526
+ version_number: { type: "number", description: "The version number to roll back to (use list_template_versions to find available versions)" }
527
+ },
528
+ required: ["template_id", "version_number"]
529
+ }
530
+ },
531
+ {
532
+ name: "publish_template",
533
+ description: "Publish a template — makes it available for runtime delivery via get_prompt_by_slug.",
534
+ inputSchema: {
535
+ type: "object",
536
+ properties: {
537
+ template_id: { type: "string", description: "The ID of the template to publish" }
538
+ },
539
+ required: ["template_id"]
540
+ }
541
+ },
542
+ {
543
+ name: "run_quick_evaluation",
544
+ description: "Run a stateless one-shot evaluation of an optimized prompt using LLM judges — get actionable quality scoring without creating a dataset.",
545
+ inputSchema: {
546
+ type: "object",
547
+ properties: {
548
+ prompt: { type: "string", description: "The optimized prompt to evaluate" },
549
+ original_prompt: { type: "string", description: "The original prompt for comparison scoring" }
550
+ },
551
+ required: ["prompt", "original_prompt"]
552
+ }
553
+ },
468
554
  ];
469
555
 
470
556
  // Add advanced tools if Bayesian optimization is enabled
@@ -524,6 +610,12 @@ class MCPPromptOptimizer {
524
610
  case "get_ce_quota_status": return await this.handleGetCEQuotaStatus();
525
611
  case "generate_harness_bundle": return await this.handleGenerateHarnessBundle(args);
526
612
  case "explore_sop_approaches": return await this.handleExploreSopApproaches(args);
613
+ case "get_prompt_by_slug": return await this.handleGetPromptBySlug(args);
614
+ case "compile_prompt": return await this.handleCompilePrompt(args);
615
+ case "list_template_versions": return await this.handleListTemplateVersions(args);
616
+ case "rollback_template": return await this.handleRollbackTemplate(args);
617
+ case "publish_template": return await this.handlePublishTemplate(args);
618
+ case "run_quick_evaluation": return await this.handleRunQuickEvaluation(args);
527
619
  default: throw new Error(`Unknown tool: ${name}`);
528
620
  }
529
621
  } catch (error) {
@@ -1349,6 +1441,129 @@ class MCPPromptOptimizer {
1349
1441
  }
1350
1442
  }
1351
1443
 
1444
+ async handleGetPromptBySlug(args) {
1445
+ if (!args.slug) throw new Error('Slug is required');
1446
+ try {
1447
+ const result = await this.callBackendAPI(ENDPOINTS.GET_PROMPT_BY_SLUG(args.slug), null, 'GET');
1448
+ let output = `# 📦 Prompt by Slug: \`${args.slug}\`\n\n`;
1449
+ output += `**Title:** ${result.title || 'N/A'}\n`;
1450
+ output += `**Body:**\n\`\`\`\n${result.optimized_prompt || result.body || ''}\n\`\`\`\n`;
1451
+ return { content: [{ type: "text", text: output }] };
1452
+ } 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}` }] };
1456
+ }
1457
+ throw new Error(`Failed to fetch prompt by slug: ${error.message}`);
1458
+ }
1459
+ }
1460
+
1461
+ async handleCompilePrompt(args) {
1462
+ if (!args.slug) throw new Error('Slug is required');
1463
+ try {
1464
+ const result = await this.callBackendAPI(ENDPOINTS.COMPILE_PROMPT(args.slug), { variables: args.variables || {} });
1465
+ const compiled = result.compiled_prompt || result.body || JSON.stringify(result, null, 2);
1466
+ return { content: [{ type: "text", text: `# Compiled Prompt\n\n\`\`\`\n${compiled}\n\`\`\`\n` }] };
1467
+ } 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}` }] };
1471
+ }
1472
+ throw new Error(`Failed to compile prompt: ${error.message}`);
1473
+ }
1474
+ }
1475
+
1476
+ async handleListTemplateVersions(args) {
1477
+ if (!args.template_id) throw new Error('Template ID is required');
1478
+ try {
1479
+ const result = await this.callBackendAPI(ENDPOINTS.TEMPLATE_VERSIONS(args.template_id), null, 'GET');
1480
+ const versions = result.versions || result.data || [];
1481
+ let output = `# 📋 Template Versions\n\n`;
1482
+ output += `**Template ID:** \`${args.template_id}\`\n\n`;
1483
+ if (versions.length === 0) {
1484
+ output += 'No version history found for this template.';
1485
+ } else {
1486
+ versions.forEach((v, i) => {
1487
+ output += `**${i + 1}.** Version ${v.version_number || 'N/A'}`;
1488
+ if (v.created_at) output += ` — ${v.created_at}`;
1489
+ if (v.change_summary) output += `\n _${v.change_summary}_`;
1490
+ output += `\n`;
1491
+ });
1492
+ output += `\nUse \`rollback_template\` with a version number to restore.`;
1493
+ }
1494
+ return { content: [{ type: "text", text: output }] };
1495
+ } 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}` }] };
1499
+ }
1500
+ throw new Error(`Failed to list template versions: ${error.message}`);
1501
+ }
1502
+ }
1503
+
1504
+ async handleRollbackTemplate(args) {
1505
+ if (!args.template_id) throw new Error('Template ID is required');
1506
+ if (args.version_number === undefined || args.version_number === null) throw new Error('version_number is required');
1507
+ try {
1508
+ const result = await this.callBackendAPI(ENDPOINTS.ROLLBACK_TEMPLATE(args.template_id, args.version_number), {});
1509
+ const msg = result.message || `Template rolled back to version ${args.version_number}`;
1510
+ return { content: [{ type: "text", text: `# ✅ Rollback Complete\n\n${msg}\n\n**Template ID:** \`${args.template_id}\`\n**Version:** ${args.version_number}` }] };
1511
+ } 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}` }] };
1515
+ }
1516
+ throw new Error(`Failed to rollback template: ${error.message}`);
1517
+ }
1518
+ }
1519
+
1520
+ async handlePublishTemplate(args) {
1521
+ if (!args.template_id) throw new Error('Template ID is required');
1522
+ try {
1523
+ const result = await this.callBackendAPI(ENDPOINTS.PUBLISH_TEMPLATE(args.template_id), {});
1524
+ const msg = result.message || `Template ${args.template_id} published successfully`;
1525
+ 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
+ } 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}` }] };
1530
+ }
1531
+ throw new Error(`Failed to publish template: ${error.message}`);
1532
+ }
1533
+ }
1534
+
1535
+ async handleRunQuickEvaluation(args) {
1536
+ if (!args.prompt) throw new Error('prompt is required');
1537
+ if (!args.original_prompt) throw new Error('original_prompt is required');
1538
+ try {
1539
+ const result = await this.callBackendAPI(ENDPOINTS.QUICK_EVALUATE, {
1540
+ prompt: args.prompt,
1541
+ original_prompt: args.original_prompt,
1542
+ assertions: [
1543
+ {
1544
+ type: "llm-rubric",
1545
+ value: "The optimized prompt is clearer, more specific, and better achieves the original intent than the input prompt.",
1546
+ weight: 1.0
1547
+ }
1548
+ ]
1549
+ });
1550
+ let output = `# 📊 Quick Evaluation Result\n\n`;
1551
+ output += `**Score:** ${result.overall_score != null ? (result.overall_score * 100).toFixed(1) + '%' : 'N/A'}\n`;
1552
+ output += `**Passed:** ${result.passed ? '✅ Yes' : '❌ No'}\n`;
1553
+ if (result.context_detected) output += `**Context:** ${result.context_detected}\n`;
1554
+ if (result.actionable_feedback && result.actionable_feedback.length) {
1555
+ output += `\n**Feedback:**\n${result.actionable_feedback.map(f => `- ${f}`).join('\n')}\n`;
1556
+ }
1557
+ return { content: [{ type: "text", text: output }] };
1558
+ } 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}` }] };
1562
+ }
1563
+ throw new Error(`Failed to run quick evaluation: ${error.message}`);
1564
+ }
1565
+ }
1566
+
1352
1567
  _formatSkillPackage(result) {
1353
1568
  const sections = ['# Skill Package Generated'];
1354
1569
  const artifacts = result.artifacts || result.steps || {};
@@ -1983,12 +2198,50 @@ async function runConnectWizard() {
1983
2198
  });
1984
2199
  }
1985
2200
 
2201
+ function printCliHelp() {
2202
+ console.log(`MCP Prompt Optimizer v${packageJson.version}
2203
+
2204
+ Usage:
2205
+ mcp-prompt-optimizer Start the MCP server (used by MCP clients)
2206
+ mcp-prompt-optimizer connect Interactive wizard: add API key to Claude Desktop config
2207
+ mcp-prompt-optimizer check-status Check API key and quota status
2208
+ mcp-prompt-optimizer validate-key Validate API key with backend
2209
+ mcp-prompt-optimizer diagnose Run comprehensive diagnostic
2210
+ mcp-prompt-optimizer clear-cache Clear validation cache
2211
+ mcp-prompt-optimizer help Show this help
2212
+ mcp-prompt-optimizer version Show version information`);
2213
+ }
2214
+
2215
+ function printCliVersion() {
2216
+ console.log(packageJson.version);
2217
+ }
2218
+
2219
+ const CLI_COMMANDS = {
2220
+ connect: runConnectWizard,
2221
+ 'check-status': () => require('./lib/check-status')().then(() => process.exit(0)),
2222
+ 'validate-key': () => require('./lib/validate-key')().then(() => process.exit(0)),
2223
+ 'clear-cache': () => require('./lib/clear-cache')().then(() => process.exit(0)),
2224
+ diagnose: () => require('./lib/diagnose')().then(() => process.exit(0)),
2225
+ help: printCliHelp,
2226
+ '--help': printCliHelp,
2227
+ '-h': printCliHelp,
2228
+ version: printCliVersion,
2229
+ '--version': printCliVersion,
2230
+ '-v': printCliVersion,
2231
+ };
2232
+
1986
2233
  if (require.main === module) {
1987
2234
  const args = process.argv.slice(2);
1988
- if (args[0] === 'connect') {
1989
- runConnectWizard();
1990
- } else {
2235
+ const cmd = args[0];
2236
+
2237
+ if (!cmd) {
1991
2238
  startValidatedMCPServer();
2239
+ } else if (CLI_COMMANDS[cmd]) {
2240
+ CLI_COMMANDS[cmd]();
2241
+ } else {
2242
+ console.error(`❌ Unknown command: ${cmd}\n`);
2243
+ printCliHelp();
2244
+ process.exit(1);
1992
2245
  }
1993
2246
  }
1994
2247
 
@@ -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...`);
@@ -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}`));
@@ -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.2",
3
+ "version": "3.7.4",
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": {
@@ -195,6 +195,22 @@
195
195
  "last_sync": "2026-06-01T00:00:00Z"
196
196
  },
197
197
  "release_notes": {
198
+ "v3.7.4": {
199
+ "major_features": [
200
+ "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.",
201
+ "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.",
202
+ "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."
203
+ ],
204
+ "breaking_changes": [],
205
+ "migration_guide": "No migration required. Only affects users who ran documented CLI subcommands other than 'connect', which previously hung."
206
+ },
207
+ "v3.7.3": {
208
+ "major_features": [
209
+ "Docs only: corrected stale free-tier quota in README (7 optimizations/month -> 20), matching the backend enforcement fix"
210
+ ],
211
+ "breaking_changes": [],
212
+ "migration_guide": "No migration required."
213
+ },
198
214
  "v3.7.2": {
199
215
  "major_features": [
200
216
  "MCP server startup logs (banner, status lines, mode indicators, key validation messages) now write to stderr so stdout stays a clean JSON-RPC channel for stdio MCP clients"