mcp-prompt-optimizer 3.7.4 → 3.8.1
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 +70 -0
- package/index.js +100 -97
- package/lib/api-key-manager.js +30 -22
- package/lib/check-status.js +1 -1
- package/package.json +17 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,76 @@ 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.8.1] - 2026-07-29
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Security: the validation cache let any API key inherit a different key's cached identity.**
|
|
12
|
+
`~/.mcp-cloud-api-cache.json` is a single file per machine, not namespaced per key. Found live
|
|
13
|
+
while testing against a real registered key: validating key A succeeds and caches its result;
|
|
14
|
+
minutes later, validating a completely different, invalid key B gets a clean 401 from the
|
|
15
|
+
backend — but the client's fallback logic didn't check *which* key the cached entry belonged to,
|
|
16
|
+
and didn't check whether the error was a definitive rejection versus a network outage. It
|
|
17
|
+
silently returned key A's cached "valid" data and started the server authenticated as A's
|
|
18
|
+
account, having never actually validated B. Any string passed as `OPTIMIZER_API_KEY` within the
|
|
19
|
+
cache's 1-hour window (2 hours via the short-term fallback tier) would have worked, as long as
|
|
20
|
+
*some* real key had been validated on that machine recently.
|
|
21
|
+
Fixed two ways: (1) a cached entry is only used if its stored `apiKeyPrefix` matches the key
|
|
22
|
+
currently being validated, and (2) cache fallback is skipped entirely on a 4xx rejection —
|
|
23
|
+
it's reserved for genuine network/5xx outages of the *same* already-cached key, which was
|
|
24
|
+
always the intent (the surrounding code already distinguished 4xx from network errors for its
|
|
25
|
+
error-message hint text; the fallback branch above it just never used that distinction).
|
|
26
|
+
Added a network-free regression test (`tests/quick-test.js`) that seeds the cache with one
|
|
27
|
+
key's data, forces a 401 for a different key, and asserts the second key never inherits the
|
|
28
|
+
first's identity — and a second check confirming the legitimate same-key network-outage
|
|
29
|
+
fallback still works unchanged.
|
|
30
|
+
|
|
31
|
+
## [3.8.0] - 2026-07-29
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **`optimize_prompt` gains `reasoning_effort` and `execution_shape`**, the two Track C1 controls
|
|
35
|
+
that previously only reached `/api/v1/optimize` (the WebUI's endpoint) — `/api/v1/mcp/optimize`
|
|
36
|
+
(what this tool actually calls) never had them wired in at all, a pre-existing architectural
|
|
37
|
+
gap, not a regression. These matter *more* for a programmatic/agentic caller than a WebUI user:
|
|
38
|
+
there's no human watching each call to decide whether a prompt is worth paying for deeper
|
|
39
|
+
reasoning, or to notice a `multi_agent` request quietly running the LLM engine regardless of
|
|
40
|
+
the prompt's complexity. `multi_agent` is gated the same way on this endpoint as it is on
|
|
41
|
+
`/optimize` (`_gate_execution_shape`, downgraded to `direct` on tiers without repair access);
|
|
42
|
+
the response echoes back the *effective* shape that ran, and the formatter now surfaces a
|
|
43
|
+
downgrade when one happens. `stop_rule` (the third Track C1 control) is deliberately **not**
|
|
44
|
+
exposed here: this endpoint never runs the quick-eval-then-repair flow that gives `stop_rule`
|
|
45
|
+
any effect, so accepting it would advertise a guardrail that does nothing. Backend change
|
|
46
|
+
landed alongside this (`app/api/mcp_router.py`), verified with new endpoint tests covering the
|
|
47
|
+
free-tier downgrade, the pro-tier non-downgrade, and the team/enterprise-default non-downgrade
|
|
48
|
+
path (the last one guards against team keys getting silently capped if their tier lookup ever
|
|
49
|
+
comes back empty) — full backend suite (1740 passed) confirmed no regressions.
|
|
50
|
+
- **`generate_harness_bundle` gains `agent_read_only` and `agent_harness`**, mirroring the two
|
|
51
|
+
optional fields the backend's `HarnessBundleRequest` already accepts. `agent_harness` lets the
|
|
52
|
+
caller pick the generated agent.yaml's execution backend (`claude-sdk`/`codex`/`pi`) to match
|
|
53
|
+
whichever API key they actually have available wherever the bundle runs — without it, the
|
|
54
|
+
bundle silently defaults per-deploy-target (usually `claude-sdk`) and fails at runtime with a
|
|
55
|
+
missing-credential error if that's not the key the user holds. `agent_read_only` narrows the
|
|
56
|
+
generated subagent's tools to Read/Grep/Glob, for audit/review workflows that should never be
|
|
57
|
+
able to edit or execute anything. Both optional, both already validated server-side (a bad
|
|
58
|
+
`agent_harness` value gets a clear 422 listing valid choices).
|
|
59
|
+
- **`tests/e2e-stdio-smoke.js`**: black-box test that spawns the published binary as a real
|
|
60
|
+
subprocess and speaks JSON-RPC over stdio, exactly as an external MCP client would. Confirms
|
|
61
|
+
clean startup/shutdown behavior for missing/malformed/unregistered keys, including a live round
|
|
62
|
+
trip to the deployed backend. Full `tools/call` coverage requires `OPTIMIZER_API_KEY` set to a
|
|
63
|
+
real, backend-registered key — this package has no reachable mock/dev bypass (`developmentMode`
|
|
64
|
+
is hardcoded `false` in `index.js`; see the `dev`/`dev:mock` npm scripts, which are currently
|
|
65
|
+
dead for the same reason).
|
|
66
|
+
|
|
67
|
+
## [3.7.5] - 2026-07-18
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
- **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).
|
|
71
|
+
- **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.
|
|
72
|
+
- **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.
|
|
73
|
+
- **`formatRealTimeStatus` read the wrong fields**: it never matched the AG-UI status endpoint's actual response shape, even when the call succeeded.
|
|
74
|
+
- **`formatQuotaStatus` invented a fake `5000` quota limit** when the backend didn't report one.
|
|
75
|
+
- **Free-tier quota text said 7/month**; the real limit is 20.
|
|
76
|
+
- **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).
|
|
77
|
+
|
|
8
78
|
## [3.7.4] - 2026-07-16
|
|
9
79
|
|
|
10
80
|
### Fixed
|
package/index.js
CHANGED
|
@@ -187,6 +187,16 @@ class MCPPromptOptimizer {
|
|
|
187
187
|
description: "Narrative description of what a successful optimized output achieves (e.g. 'reader understands why churn drives flat revenue even with user growth')."
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
+
},
|
|
191
|
+
reasoning_effort: {
|
|
192
|
+
type: "string",
|
|
193
|
+
enum: ["minimal", "standard", "deep"],
|
|
194
|
+
description: "How much reasoning to apply: 'minimal' biases toward faster/cheaper routing, 'standard' is the default, 'deep' biases toward the LLM tier for maximum analysis. Most useful when calling this tool programmatically in a loop or pipeline, where there's no human watching each call to decide whether it's worth paying for more depth."
|
|
195
|
+
},
|
|
196
|
+
execution_shape: {
|
|
197
|
+
type: "string",
|
|
198
|
+
enum: ["direct", "hybrid", "multi_agent"],
|
|
199
|
+
description: "Execution style, independent of the tier the prompt would normally route to: 'direct' is single-pass, 'hybrid' adds rules+LLM verification, 'multi_agent' forces plan-and-execute with sub-agents regardless of the prompt's complexity. 'multi_agent' is downgraded to 'direct' on tiers without repair access — the response echoes back whichever one actually ran, so you can detect a downgrade."
|
|
190
200
|
}
|
|
191
201
|
},
|
|
192
202
|
required: ["prompt"]
|
|
@@ -442,6 +452,24 @@ class MCPPromptOptimizer {
|
|
|
442
452
|
sop_content: {
|
|
443
453
|
type: "string",
|
|
444
454
|
description: "The SOP content to base the harness on (required if no session_id)."
|
|
455
|
+
},
|
|
456
|
+
agent_read_only: {
|
|
457
|
+
type: "boolean",
|
|
458
|
+
description: (
|
|
459
|
+
"Narrow the generated Claude Code subagent's tools to Read/Grep/Glob only. "
|
|
460
|
+
+ "Set this for audit/review workflows that should never edit or execute anything. "
|
|
461
|
+
+ "Default: false (full capability)."
|
|
462
|
+
)
|
|
463
|
+
},
|
|
464
|
+
agent_harness: {
|
|
465
|
+
type: "string",
|
|
466
|
+
enum: ["claude-sdk", "codex", "pi"],
|
|
467
|
+
description: (
|
|
468
|
+
"Execution backend for the generated agent.yaml: 'claude-sdk' (needs ANTHROPIC_API_KEY), "
|
|
469
|
+
+ "'codex' (needs OPENROUTER_API_KEY or OPENAI_API_KEY), or 'pi' (needs PI_API_KEY). "
|
|
470
|
+
+ "Set this to match the API key available wherever the bundle will actually run — "
|
|
471
|
+
+ "the default is per-deploy-target (usually claude-sdk) and won't know which key you have."
|
|
472
|
+
)
|
|
445
473
|
}
|
|
446
474
|
},
|
|
447
475
|
required: ["goal"]
|
|
@@ -931,10 +959,17 @@ class MCPPromptOptimizer {
|
|
|
931
959
|
}
|
|
932
960
|
}
|
|
933
961
|
|
|
962
|
+
if (args.reasoning_effort) {
|
|
963
|
+
optimizationPayload.reasoning_effort = args.reasoning_effort;
|
|
964
|
+
}
|
|
965
|
+
if (args.execution_shape) {
|
|
966
|
+
optimizationPayload.execution_shape = args.execution_shape;
|
|
967
|
+
}
|
|
968
|
+
|
|
934
969
|
const result = await this.callBackendAPI(ENDPOINTS.OPTIMIZE, optimizationPayload);
|
|
935
|
-
|
|
970
|
+
|
|
936
971
|
const enableBayesian = args.enable_bayesian !== false && this.bayesianOptimizationEnabled;
|
|
937
|
-
return { content: [{ type: "text", text: this.formatOptimizationResult(result, { detectedContext, enableBayesian }) }] };
|
|
972
|
+
return { content: [{ type: "text", text: this.formatOptimizationResult(result, { detectedContext, enableBayesian, requestedExecutionShape: args.execution_shape }) }] };
|
|
938
973
|
|
|
939
974
|
} catch (error) {
|
|
940
975
|
if (error.message.includes('Network') || error.message.includes('DNS') || error.message.includes('timeout') || error.message.includes('Connection')) {
|
|
@@ -1056,31 +1091,7 @@ class MCPPromptOptimizer {
|
|
|
1056
1091
|
return { content: [{ type: "text", text: this.formatOptimizationInsights(result) }] };
|
|
1057
1092
|
|
|
1058
1093
|
} catch (error) {
|
|
1059
|
-
|
|
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) }] };
|
|
1094
|
+
return { content: [{ type: "text", text: `🧠 Optimization insights are unavailable right now (${error.message}). This is not your data — no insights were generated.` }] };
|
|
1084
1095
|
}
|
|
1085
1096
|
}
|
|
1086
1097
|
|
|
@@ -1095,22 +1106,7 @@ class MCPPromptOptimizer {
|
|
|
1095
1106
|
return { content: [{ type: "text", text: this.formatRealTimeStatus(result) }] };
|
|
1096
1107
|
|
|
1097
1108
|
} catch (error) {
|
|
1098
|
-
|
|
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) }] };
|
|
1109
|
+
return { content: [{ type: "text", text: `⚡ AG-UI real-time status is unavailable right now (${error.message}).` }] };
|
|
1114
1110
|
}
|
|
1115
1111
|
}
|
|
1116
1112
|
|
|
@@ -1346,6 +1342,8 @@ class MCPPromptOptimizer {
|
|
|
1346
1342
|
user_goal: args.goal,
|
|
1347
1343
|
sop_content: args.sop_content || "",
|
|
1348
1344
|
};
|
|
1345
|
+
if (args.agent_read_only) payload.agent_read_only = true;
|
|
1346
|
+
if (args.agent_harness) payload.agent_harness = args.agent_harness;
|
|
1349
1347
|
|
|
1350
1348
|
// If session_id provided, first fetch session artifacts for sop_content
|
|
1351
1349
|
if (args.session_id) {
|
|
@@ -1434,8 +1432,8 @@ class MCPPromptOptimizer {
|
|
|
1434
1432
|
}]
|
|
1435
1433
|
};
|
|
1436
1434
|
} catch (error) {
|
|
1437
|
-
if (error.
|
|
1438
|
-
return { content: [{ type: "text", text:
|
|
1435
|
+
if (error.statusCode === 403) {
|
|
1436
|
+
return { content: [{ type: "text", text: `Error: SOP exploration requires Innovator tier. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1439
1437
|
}
|
|
1440
1438
|
throw new Error(`Failed to explore SOP approaches: ${error.message}`);
|
|
1441
1439
|
}
|
|
@@ -1450,9 +1448,8 @@ class MCPPromptOptimizer {
|
|
|
1450
1448
|
output += `**Body:**\n\`\`\`\n${result.optimized_prompt || result.body || ''}\n\`\`\`\n`;
|
|
1451
1449
|
return { content: [{ type: "text", text: output }] };
|
|
1452
1450
|
} catch (error) {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
return { content: [{ type: "text", text: `Upgrade required: runtime prompt delivery requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
|
|
1451
|
+
if (error.statusCode === 403) {
|
|
1452
|
+
return { content: [{ type: "text", text: `Upgrade required: runtime prompt delivery requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1456
1453
|
}
|
|
1457
1454
|
throw new Error(`Failed to fetch prompt by slug: ${error.message}`);
|
|
1458
1455
|
}
|
|
@@ -1465,9 +1462,8 @@ class MCPPromptOptimizer {
|
|
|
1465
1462
|
const compiled = result.compiled_prompt || result.body || JSON.stringify(result, null, 2);
|
|
1466
1463
|
return { content: [{ type: "text", text: `# Compiled Prompt\n\n\`\`\`\n${compiled}\n\`\`\`\n` }] };
|
|
1467
1464
|
} catch (error) {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
return { content: [{ type: "text", text: `Upgrade required: prompt compilation requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
|
|
1465
|
+
if (error.statusCode === 403) {
|
|
1466
|
+
return { content: [{ type: "text", text: `Upgrade required: prompt compilation requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1471
1467
|
}
|
|
1472
1468
|
throw new Error(`Failed to compile prompt: ${error.message}`);
|
|
1473
1469
|
}
|
|
@@ -1493,9 +1489,8 @@ class MCPPromptOptimizer {
|
|
|
1493
1489
|
}
|
|
1494
1490
|
return { content: [{ type: "text", text: output }] };
|
|
1495
1491
|
} catch (error) {
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
return { content: [{ type: "text", text: `Upgrade required: template versioning requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
|
|
1492
|
+
if (error.statusCode === 403) {
|
|
1493
|
+
return { content: [{ type: "text", text: `Upgrade required: template versioning requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1499
1494
|
}
|
|
1500
1495
|
throw new Error(`Failed to list template versions: ${error.message}`);
|
|
1501
1496
|
}
|
|
@@ -1509,9 +1504,8 @@ class MCPPromptOptimizer {
|
|
|
1509
1504
|
const msg = result.message || `Template rolled back to version ${args.version_number}`;
|
|
1510
1505
|
return { content: [{ type: "text", text: `# ✅ Rollback Complete\n\n${msg}\n\n**Template ID:** \`${args.template_id}\`\n**Version:** ${args.version_number}` }] };
|
|
1511
1506
|
} catch (error) {
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
return { content: [{ type: "text", text: `Upgrade required: template rollback requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
|
|
1507
|
+
if (error.statusCode === 403) {
|
|
1508
|
+
return { content: [{ type: "text", text: `Upgrade required: template rollback requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1515
1509
|
}
|
|
1516
1510
|
throw new Error(`Failed to rollback template: ${error.message}`);
|
|
1517
1511
|
}
|
|
@@ -1524,9 +1518,8 @@ class MCPPromptOptimizer {
|
|
|
1524
1518
|
const msg = result.message || `Template ${args.template_id} published successfully`;
|
|
1525
1519
|
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
1520
|
} catch (error) {
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
return { content: [{ type: "text", text: `Upgrade required: template publishing requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
|
|
1521
|
+
if (error.statusCode === 403) {
|
|
1522
|
+
return { content: [{ type: "text", text: `Upgrade required: template publishing requires Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1530
1523
|
}
|
|
1531
1524
|
throw new Error(`Failed to publish template: ${error.message}`);
|
|
1532
1525
|
}
|
|
@@ -1556,9 +1549,8 @@ class MCPPromptOptimizer {
|
|
|
1556
1549
|
}
|
|
1557
1550
|
return { content: [{ type: "text", text: output }] };
|
|
1558
1551
|
} catch (error) {
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
return { content: [{ type: "text", text: `Upgrade required: evaluations require Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${msg}` }] };
|
|
1552
|
+
if (error.statusCode === 403) {
|
|
1553
|
+
return { content: [{ type: "text", text: `Upgrade required: evaluations require Pro tier or higher. Upgrade at /pricing.\n\nBackend error: ${error.message}` }] };
|
|
1562
1554
|
}
|
|
1563
1555
|
throw new Error(`Failed to run quick evaluation: ${error.message}`);
|
|
1564
1556
|
}
|
|
@@ -1631,7 +1623,9 @@ class MCPPromptOptimizer {
|
|
|
1631
1623
|
} catch {
|
|
1632
1624
|
errorMessage = `HTTP ${res.statusCode}: ${responseData}`;
|
|
1633
1625
|
}
|
|
1634
|
-
|
|
1626
|
+
const httpError = new Error(errorMessage);
|
|
1627
|
+
httpError.statusCode = res.statusCode;
|
|
1628
|
+
reject(httpError);
|
|
1635
1629
|
}
|
|
1636
1630
|
} catch (parseError) {
|
|
1637
1631
|
reject(new Error(`Invalid response format: ${parseError.message}`));
|
|
@@ -1698,6 +1692,11 @@ class MCPPromptOptimizer {
|
|
|
1698
1692
|
if (result.metadata?.routing_score != null) {
|
|
1699
1693
|
output += `**Routing Score:** ${result.metadata.routing_score.toFixed(3)} (${result.metadata?.routing_tier || 'unknown'})\n`;
|
|
1700
1694
|
}
|
|
1695
|
+
if (result.execution_shape && context.requestedExecutionShape && result.execution_shape !== context.requestedExecutionShape) {
|
|
1696
|
+
output += `**Execution Shape:** \`${result.execution_shape}\` *(requested \`${context.requestedExecutionShape}\`, downgraded — your tier doesn't have repair access)*\n`;
|
|
1697
|
+
} else if (result.reasoning_effort && result.reasoning_effort !== 'standard') {
|
|
1698
|
+
output += `**Reasoning Effort:** \`${result.reasoning_effort}\`\n`;
|
|
1699
|
+
}
|
|
1701
1700
|
if (!result.rules_based && !result.fallback_mode && result.metadata?.model_used) {
|
|
1702
1701
|
output += `**Model:** ${result.metadata.model_used}\n`;
|
|
1703
1702
|
}
|
|
@@ -1787,7 +1786,7 @@ class MCPPromptOptimizer {
|
|
|
1787
1786
|
output += `2. Generate your API key at https://promptoptimizer.xyz/dashboard\n`;
|
|
1788
1787
|
output += `3. Run in your terminal:\n\n`;
|
|
1789
1788
|
output += `\`\`\`\nnpx mcp-prompt-optimizer connect\n\`\`\`\n\n`;
|
|
1790
|
-
output += `You get **
|
|
1789
|
+
output += `You get **20 LLM optimizations/month free**. Upgrade anytime for more.\n`;
|
|
1791
1790
|
}
|
|
1792
1791
|
|
|
1793
1792
|
return output;
|
|
@@ -1801,25 +1800,30 @@ class MCPPromptOptimizer {
|
|
|
1801
1800
|
output += `**Usage:** 🟢 Unlimited\n`;
|
|
1802
1801
|
} else {
|
|
1803
1802
|
const used = quota.used || 0;
|
|
1804
|
-
const limit = quota.limit
|
|
1803
|
+
const limit = quota.limit;
|
|
1804
|
+
|
|
1805
|
+
if (limit === undefined || limit === null) {
|
|
1806
|
+
output += `**Usage:** ${used} used (limit unavailable — backend did not report a quota limit)\n`;
|
|
1807
|
+
} else {
|
|
1805
1808
|
const percentage = limit > 0 ? ((used / limit) * 100).toFixed(1) : 0;
|
|
1806
|
-
|
|
1809
|
+
|
|
1807
1810
|
let statusIcon = '🟢';
|
|
1808
1811
|
if (percentage >= 90) statusIcon = '🔴';
|
|
1809
1812
|
else if (percentage >= 75) statusIcon = '🟡';
|
|
1810
|
-
|
|
1813
|
+
|
|
1811
1814
|
output += `**Usage:** ${statusIcon} ${used}/${limit} (${percentage}%)\n`;
|
|
1812
1815
|
|
|
1813
1816
|
const remaining = limit - used;
|
|
1814
1817
|
if (remaining <= 0) {
|
|
1815
1818
|
output += `\n❌ **Quota Exhausted** — You have no optimizations remaining this month.\n`;
|
|
1816
|
-
output += `Upgrade at https://promptoptimizer.xyz/
|
|
1819
|
+
output += `Upgrade at https://promptoptimizer.xyz/pricing\n`;
|
|
1817
1820
|
output += `*(Quota resets at the start of your next billing cycle)*\n`;
|
|
1818
1821
|
} else if (percentage >= 90) {
|
|
1819
|
-
output += `\n⚠️ **Critical** — ${remaining} optimization${remaining === 1 ? '' : 's'} remaining. Upgrade at https://promptoptimizer.xyz/
|
|
1822
|
+
output += `\n⚠️ **Critical** — ${remaining} optimization${remaining === 1 ? '' : 's'} remaining. Upgrade at https://promptoptimizer.xyz/pricing\n`;
|
|
1820
1823
|
} else if (percentage >= 75) {
|
|
1821
1824
|
output += `\n⚠️ **Warning** — Approaching your monthly limit.\n`;
|
|
1822
1825
|
}
|
|
1826
|
+
}
|
|
1823
1827
|
}
|
|
1824
1828
|
|
|
1825
1829
|
output += `\n## ✨ **Available Features**\n`;
|
|
@@ -1841,9 +1845,9 @@ class MCPPromptOptimizer {
|
|
|
1841
1845
|
}
|
|
1842
1846
|
|
|
1843
1847
|
output += `\n## 🔗 **Account Management**\n`;
|
|
1844
|
-
output += `- Dashboard: https://promptoptimizer
|
|
1845
|
-
output += `- Analytics: https://promptoptimizer
|
|
1846
|
-
output += `- Upgrade: https://promptoptimizer.xyz/
|
|
1848
|
+
output += `- Dashboard: https://promptoptimizer.xyz/dashboard\n`;
|
|
1849
|
+
output += `- Analytics: https://promptoptimizer.xyz/analytics\n`;
|
|
1850
|
+
output += `- Upgrade: https://promptoptimizer.xyz/pricing\n`;
|
|
1847
1851
|
|
|
1848
1852
|
return output;
|
|
1849
1853
|
}
|
|
@@ -1949,41 +1953,40 @@ class MCPPromptOptimizer {
|
|
|
1949
1953
|
}
|
|
1950
1954
|
|
|
1951
1955
|
output += `## 🔗 **Advanced Analytics**\n`;
|
|
1952
|
-
output += `- Full Analytics: https://promptoptimizer
|
|
1953
|
-
output += `- Performance Dashboard: https://promptoptimizer
|
|
1956
|
+
output += `- Full Analytics: https://promptoptimizer.xyz/analytics\n`;
|
|
1957
|
+
output += `- Performance Dashboard: https://promptoptimizer.xyz/dashboard\n`;
|
|
1954
1958
|
|
|
1955
1959
|
return output;
|
|
1956
1960
|
}
|
|
1957
1961
|
|
|
1958
1962
|
formatRealTimeStatus(status) {
|
|
1959
1963
|
let output = `# ⚡ AG-UI Real-Time Status\n\n`;
|
|
1960
|
-
|
|
1964
|
+
const metrics = status.metrics || {};
|
|
1965
|
+
|
|
1961
1966
|
output += `## 🚀 **Service Status**\n`;
|
|
1962
|
-
output += `- **AG-UI Status:** ${status.
|
|
1963
|
-
|
|
1964
|
-
output +=
|
|
1965
|
-
output += `- **
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
output += `- **
|
|
1970
|
-
output += `- **Average Response Time:** ${status.average_response_time}\n\n`;
|
|
1967
|
+
output += `- **AG-UI Status:** ${status.status === 'healthy' ? '🟢 Healthy' : '🔴 Degraded'}\n\n`;
|
|
1968
|
+
|
|
1969
|
+
output += `## 📈 **Current Activity**\n`;
|
|
1970
|
+
output += `- **Active Sessions:** ${metrics.active_sessions ?? 'unknown'}\n`;
|
|
1971
|
+
output += `- **Total Connections:** ${metrics.total_connections ?? 'unknown'}\n`;
|
|
1972
|
+
output += `- **Total Optimizations:** ${metrics.total_optimizations ?? 'unknown'}\n`;
|
|
1973
|
+
if (metrics.uptime_seconds !== undefined) {
|
|
1974
|
+
output += `- **Uptime:** ${Math.round(metrics.uptime_seconds)}s\n`;
|
|
1971
1975
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1976
|
+
output += `\n`;
|
|
1977
|
+
|
|
1978
|
+
if (metrics.features_enabled) {
|
|
1975
1979
|
output += `## ⚡ **Available Features**\n`;
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
if (features.performance_monitoring) output += `✅ Performance Monitoring\n`;
|
|
1980
|
+
for (const [feature, enabled] of Object.entries(metrics.features_enabled)) {
|
|
1981
|
+
if (enabled) output += `✅ ${feature}\n`;
|
|
1982
|
+
}
|
|
1980
1983
|
output += `\n`;
|
|
1981
1984
|
}
|
|
1982
|
-
|
|
1985
|
+
|
|
1983
1986
|
output += `## 🔗 **Real-Time Access**\n`;
|
|
1984
|
-
output += `- Live Dashboard: https://promptoptimizer
|
|
1987
|
+
output += `- Live Dashboard: https://promptoptimizer.xyz/live\n`;
|
|
1985
1988
|
output += `- WebSocket Endpoint: Available via API\n`;
|
|
1986
|
-
|
|
1989
|
+
|
|
1987
1990
|
return output;
|
|
1988
1991
|
}
|
|
1989
1992
|
|
|
@@ -2001,7 +2004,7 @@ async function startValidatedMCPServer() {
|
|
|
2001
2004
|
try {
|
|
2002
2005
|
const apiKey = process.env.OPTIMIZER_API_KEY;
|
|
2003
2006
|
if (!apiKey) {
|
|
2004
|
-
console.error('❌ API key required. Get one at https://promptoptimizer.xyz/
|
|
2007
|
+
console.error('❌ API key required. Get one free at https://promptoptimizer.xyz/dashboard');
|
|
2005
2008
|
process.exit(1);
|
|
2006
2009
|
}
|
|
2007
2010
|
|
|
@@ -2179,7 +2182,7 @@ async function runConnectWizard() {
|
|
|
2179
2182
|
}
|
|
2180
2183
|
if (ok > 0) {
|
|
2181
2184
|
console.log('\n👉 Restart your MCP client(s) to activate LLM optimization.');
|
|
2182
|
-
console.log(' Free plan:
|
|
2185
|
+
console.log(' Free plan: 20 LLM optimizations/month.');
|
|
2183
2186
|
console.log(' Upgrade at https://promptoptimizer.xyz/pricing\n');
|
|
2184
2187
|
}
|
|
2185
2188
|
}
|
package/lib/api-key-manager.js
CHANGED
|
@@ -220,30 +220,38 @@ class CloudApiKeyManager {
|
|
|
220
220
|
this.log(`Backend validation failed: ${error.message}`, 'warn');
|
|
221
221
|
await this.updateNetworkHealth(false, error.message);
|
|
222
222
|
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
223
|
+
// A 4xx means the backend rejected THIS key itself — not a connectivity
|
|
224
|
+
// problem. Falling back to a cache entry here would mean any key,
|
|
225
|
+
// valid or not, silently inherits whatever key was last cached on this
|
|
226
|
+
// machine (cacheFile is a single global file, not namespaced per key).
|
|
227
|
+
// Cache fallback exists only for genuine network/5xx outages of the
|
|
228
|
+
// SAME key that was already validated and cached — never for a
|
|
229
|
+
// definitive rejection, and never for a cache entry from a different key.
|
|
230
|
+
const isClientError = error.statusCode >= 400 && error.statusCode < 500;
|
|
231
|
+
|
|
232
|
+
if (!isClientError) {
|
|
233
|
+
const cachedValidation = await this.getCachedValidation();
|
|
234
|
+
const cacheMatchesThisKey = cachedValidation
|
|
235
|
+
&& cachedValidation.apiKeyPrefix === this.apiKey.substring(0, 20) + '...';
|
|
236
|
+
|
|
237
|
+
if (cacheMatchesThisKey && !this.isCacheExpired(cachedValidation)) {
|
|
238
|
+
this.log('Using cached API key validation', 'warn');
|
|
239
|
+
return cachedValidation.data;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// SECURITY: Limited fallback for brief network issues only (2 hours max)
|
|
243
|
+
if (cacheMatchesThisKey && !this.isFallbackCacheExpired(cachedValidation)) {
|
|
244
|
+
this.log('Using short-term fallback cache due to network issues', 'warn');
|
|
245
|
+
const fallbackData = cachedValidation.data;
|
|
246
|
+
fallbackData.fallback_mode = true;
|
|
247
|
+
fallbackData.network_issue = error.message;
|
|
248
|
+
fallbackData.expires_soon = true;
|
|
249
|
+
return fallbackData;
|
|
250
|
+
}
|
|
239
251
|
}
|
|
240
252
|
|
|
241
253
|
// SECURITY: Offline mode removed - backend validation required
|
|
242
|
-
// No cache fallback beyond 2 hours
|
|
243
|
-
|
|
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;
|
|
254
|
+
// No cache fallback beyond 2 hours, and none at all for a 4xx rejection.
|
|
247
255
|
const hint = isClientError ? '' : ' Please check your internet connection.';
|
|
248
256
|
throw new Error(`API key validation failed: ${error.message}.${hint}`);
|
|
249
257
|
}
|
|
@@ -593,7 +601,7 @@ class CloudApiKeyManager {
|
|
|
593
601
|
|
|
594
602
|
throw new Error(
|
|
595
603
|
'API key required. Set the OPTIMIZER_API_KEY environment variable.\n' +
|
|
596
|
-
'Get your API key at: https://promptoptimizer.xyz/
|
|
604
|
+
'Get your API key at: https://promptoptimizer.xyz/dashboard'
|
|
597
605
|
);
|
|
598
606
|
}
|
|
599
607
|
|
package/lib/check-status.js
CHANGED
|
@@ -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-
|
|
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');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-prompt-optimizer",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
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": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"test:runner": "node tests/test-runner.js",
|
|
26
26
|
"test:simple": "node tests/simple-test.js",
|
|
27
27
|
"test:contract": "node tests/contract-check.js",
|
|
28
|
+
"test:e2e": "node tests/e2e-stdio-smoke.js",
|
|
28
29
|
"pretest": "npm run health-check",
|
|
29
30
|
"prepublishOnly": "npm run test:quick",
|
|
30
31
|
"version": "echo 'Updating version...' && npm run test:quick"
|
|
@@ -124,8 +125,8 @@
|
|
|
124
125
|
"required_keys": [
|
|
125
126
|
{
|
|
126
127
|
"name": "OPTIMIZER_API_KEY",
|
|
127
|
-
"format": "sk-opt-*, sk-team-*,
|
|
128
|
-
"description": "Cloud API key from promptoptimizer.xyz/
|
|
128
|
+
"format": "sk-opt-*, sk-team-*, or sk-dev-*",
|
|
129
|
+
"description": "Cloud API key from promptoptimizer.xyz/dashboard or development key for testing",
|
|
129
130
|
"required": true
|
|
130
131
|
}
|
|
131
132
|
],
|
|
@@ -195,6 +196,19 @@
|
|
|
195
196
|
"last_sync": "2026-06-01T00:00:00Z"
|
|
196
197
|
},
|
|
197
198
|
"release_notes": {
|
|
199
|
+
"v3.7.5": {
|
|
200
|
+
"major_features": [
|
|
201
|
+
"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.",
|
|
202
|
+
"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.",
|
|
203
|
+
"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.",
|
|
204
|
+
"Fixed: formatRealTimeStatus was never reading the AG-UI status endpoint's actual response shape, even on success.",
|
|
205
|
+
"Fixed: formatQuotaStatus invented a fake 5000 quota limit when the backend didn't report one.",
|
|
206
|
+
"Fixed: free-tier quota text said 7/month; actual limit is 20.",
|
|
207
|
+
"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."
|
|
208
|
+
],
|
|
209
|
+
"breaking_changes": [],
|
|
210
|
+
"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."
|
|
211
|
+
},
|
|
198
212
|
"v3.7.4": {
|
|
199
213
|
"major_features": [
|
|
200
214
|
"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.",
|