mcp-prompt-optimizer 3.7.5 → 3.8.2

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,75 @@ 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.2] - 2026-08-27
9
+
10
+ ### Added
11
+ - **Surface CE structural warnings in tool output.** The backend `/context-engineer/transform`
12
+ and `/context-engineer/generate-skill-package` responses now carry a non-fatal `warnings`
13
+ array (dropped SOP stages, duplicated tables, fabricated headings). `transform_for_framework`
14
+ and `generate_skill_package` previously discarded it, so a structurally degraded artifact
15
+ shipped silently. Both now render a `## ⚠️ Structural Warnings` section when the array is
16
+ non-empty. No change when it is empty (the normal case) — output is byte-identical.
17
+
18
+ ## [3.8.1] - 2026-07-29
19
+
20
+ ### Fixed
21
+ - **Security: the validation cache let any API key inherit a different key's cached identity.**
22
+ `~/.mcp-cloud-api-cache.json` is a single file per machine, not namespaced per key. Found live
23
+ while testing against a real registered key: validating key A succeeds and caches its result;
24
+ minutes later, validating a completely different, invalid key B gets a clean 401 from the
25
+ backend — but the client's fallback logic didn't check *which* key the cached entry belonged to,
26
+ and didn't check whether the error was a definitive rejection versus a network outage. It
27
+ silently returned key A's cached "valid" data and started the server authenticated as A's
28
+ account, having never actually validated B. Any string passed as `OPTIMIZER_API_KEY` within the
29
+ cache's 1-hour window (2 hours via the short-term fallback tier) would have worked, as long as
30
+ *some* real key had been validated on that machine recently.
31
+ Fixed two ways: (1) a cached entry is only used if its stored `apiKeyPrefix` matches the key
32
+ currently being validated, and (2) cache fallback is skipped entirely on a 4xx rejection —
33
+ it's reserved for genuine network/5xx outages of the *same* already-cached key, which was
34
+ always the intent (the surrounding code already distinguished 4xx from network errors for its
35
+ error-message hint text; the fallback branch above it just never used that distinction).
36
+ Added a network-free regression test (`tests/quick-test.js`) that seeds the cache with one
37
+ key's data, forces a 401 for a different key, and asserts the second key never inherits the
38
+ first's identity — and a second check confirming the legitimate same-key network-outage
39
+ fallback still works unchanged.
40
+
41
+ ## [3.8.0] - 2026-07-29
42
+
43
+ ### Added
44
+ - **`optimize_prompt` gains `reasoning_effort` and `execution_shape`**, the two Track C1 controls
45
+ that previously only reached `/api/v1/optimize` (the WebUI's endpoint) — `/api/v1/mcp/optimize`
46
+ (what this tool actually calls) never had them wired in at all, a pre-existing architectural
47
+ gap, not a regression. These matter *more* for a programmatic/agentic caller than a WebUI user:
48
+ there's no human watching each call to decide whether a prompt is worth paying for deeper
49
+ reasoning, or to notice a `multi_agent` request quietly running the LLM engine regardless of
50
+ the prompt's complexity. `multi_agent` is gated the same way on this endpoint as it is on
51
+ `/optimize` (`_gate_execution_shape`, downgraded to `direct` on tiers without repair access);
52
+ the response echoes back the *effective* shape that ran, and the formatter now surfaces a
53
+ downgrade when one happens. `stop_rule` (the third Track C1 control) is deliberately **not**
54
+ exposed here: this endpoint never runs the quick-eval-then-repair flow that gives `stop_rule`
55
+ any effect, so accepting it would advertise a guardrail that does nothing. Backend change
56
+ landed alongside this (`app/api/mcp_router.py`), verified with new endpoint tests covering the
57
+ free-tier downgrade, the pro-tier non-downgrade, and the team/enterprise-default non-downgrade
58
+ path (the last one guards against team keys getting silently capped if their tier lookup ever
59
+ comes back empty) — full backend suite (1740 passed) confirmed no regressions.
60
+ - **`generate_harness_bundle` gains `agent_read_only` and `agent_harness`**, mirroring the two
61
+ optional fields the backend's `HarnessBundleRequest` already accepts. `agent_harness` lets the
62
+ caller pick the generated agent.yaml's execution backend (`claude-sdk`/`codex`/`pi`) to match
63
+ whichever API key they actually have available wherever the bundle runs — without it, the
64
+ bundle silently defaults per-deploy-target (usually `claude-sdk`) and fails at runtime with a
65
+ missing-credential error if that's not the key the user holds. `agent_read_only` narrows the
66
+ generated subagent's tools to Read/Grep/Glob, for audit/review workflows that should never be
67
+ able to edit or execute anything. Both optional, both already validated server-side (a bad
68
+ `agent_harness` value gets a clear 422 listing valid choices).
69
+ - **`tests/e2e-stdio-smoke.js`**: black-box test that spawns the published binary as a real
70
+ subprocess and speaks JSON-RPC over stdio, exactly as an external MCP client would. Confirms
71
+ clean startup/shutdown behavior for missing/malformed/unregistered keys, including a live round
72
+ trip to the deployed backend. Full `tools/call` coverage requires `OPTIMIZER_API_KEY` set to a
73
+ real, backend-registered key — this package has no reachable mock/dev bypass (`developmentMode`
74
+ is hardcoded `false` in `index.js`; see the `dev`/`dev:mock` npm scripts, which are currently
75
+ dead for the same reason).
76
+
8
77
  ## [3.7.5] - 2026-07-18
9
78
 
10
79
  ### 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')) {
@@ -1254,7 +1289,7 @@ class MCPPromptOptimizer {
1254
1289
  sop_content: args.sop_content, goal: args.goal, framework: args.framework
1255
1290
  });
1256
1291
  const code = result.code || result.content || result.result || JSON.stringify(result, null, 2);
1257
- return { content: [{ type: "text", text: `# ${args.framework} Implementation\n\n\`\`\`python\n${code}\n\`\`\`\n\n---\n*Transformed by MCP Prompt Optimizer CE*` }] };
1292
+ return { content: [{ type: "text", text: `# ${args.framework} Implementation\n\n\`\`\`python\n${code}\n\`\`\`${this._formatWarnings(result)}\n\n---\n*Transformed by MCP Prompt Optimizer CE*` }] };
1258
1293
  } catch (error) {
1259
1294
  throw new Error(`Failed to transform: ${error.message}`);
1260
1295
  }
@@ -1307,6 +1342,8 @@ class MCPPromptOptimizer {
1307
1342
  user_goal: args.goal,
1308
1343
  sop_content: args.sop_content || "",
1309
1344
  };
1345
+ if (args.agent_read_only) payload.agent_read_only = true;
1346
+ if (args.agent_harness) payload.agent_harness = args.agent_harness;
1310
1347
 
1311
1348
  // If session_id provided, first fetch session artifacts for sop_content
1312
1349
  if (args.session_id) {
@@ -1529,10 +1566,23 @@ class MCPPromptOptimizer {
1529
1566
  } else {
1530
1567
  sections.push('\n```json\n' + JSON.stringify(result, null, 2) + '\n```');
1531
1568
  }
1569
+ const warn = this._formatWarnings(result);
1570
+ if (warn) sections.push(warn);
1532
1571
  sections.push('\n---\n*Generated by MCP Prompt Optimizer CE*');
1533
1572
  return sections.join('\n');
1534
1573
  }
1535
1574
 
1575
+ // Backend CE responses carry a non-fatal `warnings` array (dropped SOP
1576
+ // stages, duplicated tables, fabricated headings). Surface it so a degraded
1577
+ // artifact travels with the reason instead of shipping silently. Returns ''
1578
+ // when there is nothing to report.
1579
+ _formatWarnings(result) {
1580
+ const w = result && (result.warnings || result.validation_warnings);
1581
+ if (!Array.isArray(w) || w.length === 0) return '';
1582
+ return `\n\n## ⚠️ Structural Warnings\n\n${w.map(x => `- ${x}`).join('\n')}\n\n` +
1583
+ `_The artifact is delivered as-is; review these before use._`;
1584
+ }
1585
+
1536
1586
  _buildUrl(path) {
1537
1587
  return `${this.backendUrl}${path}`;
1538
1588
  }
@@ -1655,6 +1705,11 @@ class MCPPromptOptimizer {
1655
1705
  if (result.metadata?.routing_score != null) {
1656
1706
  output += `**Routing Score:** ${result.metadata.routing_score.toFixed(3)} (${result.metadata?.routing_tier || 'unknown'})\n`;
1657
1707
  }
1708
+ if (result.execution_shape && context.requestedExecutionShape && result.execution_shape !== context.requestedExecutionShape) {
1709
+ output += `**Execution Shape:** \`${result.execution_shape}\` *(requested \`${context.requestedExecutionShape}\`, downgraded — your tier doesn't have repair access)*\n`;
1710
+ } else if (result.reasoning_effort && result.reasoning_effort !== 'standard') {
1711
+ output += `**Reasoning Effort:** \`${result.reasoning_effort}\`\n`;
1712
+ }
1658
1713
  if (!result.rules_based && !result.fallback_mode && result.metadata?.model_used) {
1659
1714
  output += `**Model:** ${result.metadata.model_used}\n`;
1660
1715
  }
@@ -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
- // Enhanced fallback strategy
224
- const cachedValidation = await this.getCachedValidation();
225
-
226
- if (cachedValidation && !this.isCacheExpired(cachedValidation)) {
227
- this.log('Using cached API key validation', 'warn');
228
- return cachedValidation.data;
229
- }
230
-
231
- // SECURITY: Limited fallback for brief network issues only (2 hours max)
232
- if (cachedValidation && !this.isFallbackCacheExpired(cachedValidation)) {
233
- this.log('Using short-term fallback cache due to network issues', 'warn');
234
- const fallbackData = cachedValidation.data;
235
- fallbackData.fallback_mode = true;
236
- fallbackData.network_issue = error.message;
237
- fallbackData.expires_soon = true;
238
- return fallbackData;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-prompt-optimizer",
3
- "version": "3.7.5",
3
+ "version": "3.8.2",
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,8 @@
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",
29
+ "test:prod-canary": "node tests/prod-canary.js",
28
30
  "pretest": "npm run health-check",
29
31
  "prepublishOnly": "npm run test:quick",
30
32
  "version": "echo 'Updating version...' && npm run test:quick"