@phi-longevity/mcp-server 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +45 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -52,6 +52,34 @@ function telemetry(ev) {
52
52
  function textResult(text) {
53
53
  return { content: [{ type: "text", text }] };
54
54
  }
55
+ function errorResult(text) {
56
+ return { content: [{ type: "text", text }], isError: true };
57
+ }
58
+ /**
59
+ * Extract a human-actionable detail string from a non-OK engine/gateway response body.
60
+ * The synthetic-only engine returns JSON like {"error": "..."} (and sometimes "detail").
61
+ * Truncated defensively; never contains biomarker values (errors are validation/auth-shaped).
62
+ */
63
+ async function readErrorDetail(res) {
64
+ try {
65
+ const raw = (await res.text()).slice(0, 500);
66
+ try {
67
+ const j = JSON.parse(raw);
68
+ const msg = [j.error, j.detail].filter((x) => typeof x === "string" && x).join(" — ");
69
+ if (msg)
70
+ return msg.slice(0, 300);
71
+ }
72
+ catch { /* not JSON — fall through */ }
73
+ return raw.slice(0, 300);
74
+ }
75
+ catch {
76
+ return "";
77
+ }
78
+ }
79
+ const EMPTY_PANEL_HELP = "No biomarkers were provided — the `biomarkers` map was empty. Provide at least one biomarker as " +
80
+ 'name -> numeric value, e.g. { "Hemoglobin A1c": 5.4, "LDL Cholesterol": 130 }. ' +
81
+ "Call the list_supported_biomarkers tool for the 51 supported names, units, and reference ranges. " +
82
+ SYNTHETIC_WARNING;
55
83
  // ── Owner-conversion attribution (Layer 3.6, Steve directive 2026-07-17) ──────────────────────
56
84
  // Appended to analysis results so the agent can route its OWNER to a full PRISM report.
57
85
  // UTM params make the agent channel truthfully measurable in GA4 (D2 discipline).
@@ -73,11 +101,15 @@ async function callEngine(body) {
73
101
  "x-phi-mcp-key": MCP_KEY,
74
102
  };
75
103
  const res = await fetch(GATEWAY_URL, { method: "POST", headers, body: JSON.stringify(body) });
76
- if (!res.ok)
77
- throw new Error(`Gateway returned ${res.status}.`);
104
+ if (!res.ok) {
105
+ // Surface the engine/gateway error detail so calling agents can self-correct
106
+ // (2026-07-19 lesson: an opaque "Engine returned 400." cost our first external session).
107
+ const detail = await readErrorDetail(res);
108
+ throw new Error(`Gateway returned ${res.status}${detail ? `: ${detail}` : ""}.`);
109
+ }
78
110
  return res.json();
79
111
  }
80
- const server = new McpServer({ name: "phi-longevity", version: "0.6.2" }, {
112
+ const server = new McpServer({ name: "phi-longevity", version: "0.6.3" }, {
81
113
  instructions: "Phi Longevity PRISM — longevity-optimized analysis of biomarker panels. " + SYNTHETIC_WARNING,
82
114
  });
83
115
  // ── Tool 1: analyze_biomarkers (the core) ─────────────────────────────────────────────────────
@@ -104,6 +136,13 @@ server.tool("analyze_biomarkers", "Analyze a SYNTHETIC biomarker panel with Phi
104
136
  // The engine expects biomarkers as an array of {name, value}; the tool takes an
105
137
  // agent-friendly {name: value} map → transform here.
106
138
  const biomarkerArray = Object.entries(args.biomarkers || {}).map(([name, value]) => ({ name, value }));
139
+ // Empty-panel guard: an empty map passes schema validation but the engine will
140
+ // (correctly) reject it. Answer locally with actionable guidance instead of
141
+ // spending an engine round-trip on a guaranteed 400.
142
+ if (biomarkerArray.length === 0) {
143
+ telemetry({ tool: "analyze_biomarkers", error: "empty_panel_guard", elapsed_ms: Date.now() - t0 });
144
+ return errorResult(EMPTY_PANEL_HELP);
145
+ }
107
146
  const out = await callEngine({
108
147
  biomarkers: biomarkerArray,
109
148
  conditionFocus: args.conditionFocus ?? "general_wellness",
@@ -124,7 +163,9 @@ server.tool("analyze_biomarkers", "Analyze a SYNTHETIC biomarker panel with Phi
124
163
  }
125
164
  catch (e) {
126
165
  telemetry({ tool: "analyze_biomarkers", error: e.message, elapsed_ms: Date.now() - t0 });
127
- return textResult(`Could not analyze: ${e.message}`);
166
+ return errorResult(`Could not analyze: ${e.message} ` +
167
+ "If the input was rejected, check biomarker names and numeric values via the " +
168
+ "list_supported_biomarkers tool and retry.");
128
169
  }
129
170
  });
130
171
  // ── Tool 2: list_supported_biomarkers ─────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-longevity/mcp-server",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Model Context Protocol server for Phi Longevity \u2014 agents can call the PRISM longevity recommendation engine on SYNTHETIC biomarker panels. Synthetic/de-identified data only; no PHI; stateless.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",