@uphealth/mcp-server 1.0.0 → 1.1.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.
Files changed (40) hide show
  1. package/DISTRIBUTION.md +11 -10
  2. package/README.md +3 -3
  3. package/dist/auth.js +36 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/config.js +1 -1
  6. package/dist/generated/signal-catalog.js +243 -0
  7. package/dist/generated/signal-catalog.js.map +1 -0
  8. package/dist/index.js +15 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/output-schemas.js +55 -0
  11. package/dist/output-schemas.js.map +1 -1
  12. package/dist/prompts.js +90 -0
  13. package/dist/prompts.js.map +1 -0
  14. package/dist/rails-client.js +30 -0
  15. package/dist/rails-client.js.map +1 -1
  16. package/dist/resources.js +127 -0
  17. package/dist/resources.js.map +1 -0
  18. package/dist/server.js +16 -2
  19. package/dist/server.js.map +1 -1
  20. package/dist/stdio-context.js +19 -0
  21. package/dist/stdio-context.js.map +1 -0
  22. package/dist/stdio.js +23 -14
  23. package/dist/stdio.js.map +1 -1
  24. package/dist/tool-auth-policy.js +52 -0
  25. package/dist/tool-auth-policy.js.map +1 -0
  26. package/dist/tool-result.js +34 -3
  27. package/dist/tool-result.js.map +1 -1
  28. package/dist/tools/create-patient-stream.js +36 -8
  29. package/dist/tools/create-patient-stream.js.map +1 -1
  30. package/dist/tools/get-next-cued-message.js +7 -2
  31. package/dist/tools/get-next-cued-message.js.map +1 -1
  32. package/dist/tools/index.js +5 -1
  33. package/dist/tools/index.js.map +1 -1
  34. package/dist/tools/list-sandbox-topics.js +4 -1
  35. package/dist/tools/list-sandbox-topics.js.map +1 -1
  36. package/dist/tools/lookup-health-fact.js +66 -0
  37. package/dist/tools/lookup-health-fact.js.map +1 -0
  38. package/dist/tools/read-stream-state.js +4 -2
  39. package/dist/tools/read-stream-state.js.map +1 -1
  40. package/package.json +1 -1
package/DISTRIBUTION.md CHANGED
@@ -1,19 +1,20 @@
1
1
  # Use Uphealth Signal from an AI agent (MCP)
2
2
 
3
- The Uphealth MCP server lets any [Model Context Protocol](https://modelcontextprotocol.io/) client — Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, or your own agent — drive a cued health-message stream as four tools. It wraps the [Signal API](https://uphealth.us/docs); the model never sees your raw HTTP layer.
3
+ The Uphealth MCP server lets any [Model Context Protocol](https://modelcontextprotocol.io/) client — Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, or your own agent — look up federal health facts and drive a cued health-message stream as five tools (one needs no key). It wraps the [Signal API](https://uphealth.us/docs); the model never sees your raw HTTP layer.
4
4
 
5
- **Get a free sandbox key** (no credit card, no PHI, no BAA) at **https://uphealth.us/signup**, then pick an install method below.
5
+ **Try it with zero setup:** `lookup_health_fact` needs **no key** — one already-public federal fact per question. For the adaptive stream, **get a free sandbox key** (no credit card, no PHI, no BAA) at **https://uphealth.us/signup**, then pick an install method below.
6
6
 
7
- ## The four tools
7
+ ## The five tools
8
8
 
9
- | Tool | Wraps | Does |
10
- |---|---|---|
11
- | `create_patient_stream` | `POST /v1/signal/streams` | Start a stream from a template; returns the opening cue |
12
- | `get_next_cued_message` | `POST /v1/signal/streams/:id/cue` | Submit the patient's response, get the next message |
13
- | `read_stream_state` | `GET /v1/signal/streams/:id` | Read state + current cue without advancing |
14
- | `list_sandbox_topics` | `GET /v1/signal/sandbox-topics` | List the five sandbox topics |
9
+ | Tool | Wraps | Key? | Does |
10
+ |---|---|---|---|
11
+ | `lookup_health_fact` | `GET /v1/public/health-fact` | **none** | One already-public federal fact for a question/topic + its source + an uphealth.me link |
12
+ | `create_patient_stream` | `POST /v1/signal/streams` | `up_…` | Start a stream from a template; returns the opening cue |
13
+ | `get_next_cued_message` | `POST /v1/signal/streams/:id/cue` | `up_…` | Submit the patient's response, get the next message |
14
+ | `read_stream_state` | `GET /v1/signal/streams/:id` | `up_…` | Read state + current cue without advancing |
15
+ | `list_sandbox_topics` | `GET /v1/signal/sandbox-topics` | `up_…` | List the five sandbox topics |
15
16
 
16
- **The one rule:** every cue after the first requires feedback on the prior one (the model passes the patient's `response_action`). The tool descriptions teach this, so a capable agent drives the loop correctly.
17
+ **The one rule** (keyed tools): every cue after the first requires feedback on the prior one (the model passes the patient's `response_action`). The tool descriptions teach this, so a capable agent drives the loop correctly.
17
18
 
18
19
  ---
19
20
 
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Uphealth MCP Server (Track A A4)
2
2
 
3
- Wraps the [`api.uphealth.us` cued-stream API](../docs/spec_track_a_a1_cued_stream_api.md) as agent-callable [Model Context Protocol](https://modelcontextprotocol.io/) tools. Buyers configure their Claude Desktop / Cursor / agentic platform with the MCP server URL + a `up_sandbox_…` API token; the agent then calls 4 MCP tools to drive a cued patient stream.
3
+ Wraps the [`api.uphealth.us` cued-stream API](../docs/spec_track_a_a1_cued_stream_api.md) as agent-callable [Model Context Protocol](https://modelcontextprotocol.io/) tools. The agent can call `lookup_health_fact` with **zero setup no key** to get one already-public federal health fact, then configure a free `up_sandbox_…` key to drive an adaptive cued patient stream with the 4 keyed tools.
4
4
 
5
5
  Locks captured in [`docs/spec_track_a_a4_mcp_server.md`](../docs/spec_track_a_a4_mcp_server.md):
6
6
 
7
7
  - **SDK** — TypeScript via `@modelcontextprotocol/sdk` (flipped from Python at 2026-05-22 build-kickoff revalidation)
8
- - **Tool surface** — 4 tools: `create_patient_stream` / `get_next_cued_message` / `read_stream_state` / `list_sandbox_topics`
9
- - **Auth** — Bearer-token passthrough (MCP server is stateless thin proxy; Rails validates)
8
+ - **Tool surface** — 5 tools: a **no-key** `lookup_health_fact` (one already-public federal fact per question, zero setup) + 4 keyed tools (`create_patient_stream` / `get_next_cued_message` / `read_stream_state` / `list_sandbox_topics`)
9
+ - **Auth** — Bearer-token passthrough (MCP server is stateless thin proxy; Rails validates). `lookup_health_fact` is the sole exception — it needs no key; the per-tool policy still 401s every keyed tool without one.
10
10
  - **Corpus** — sandbox only at v1
11
11
  - **Hostname** — `mcp.uphealth.us` subdomain (Option A locked)
12
12
  - **Transport** — Streamable HTTP
package/dist/auth.js ADDED
@@ -0,0 +1,36 @@
1
+ // Authorization header parsing for the Streamable HTTP transport.
2
+ //
3
+ // Extracted from index.ts as a pure, side-effect-free module so it can be
4
+ // unit-tested without booting the Express listener (index.ts calls
5
+ // startHttpServer() at import time).
6
+ //
7
+ // Accepts BOTH forms of the Authorization header value:
8
+ // - "Bearer up_sandbox_…" — the standard form sent by /docs, the SDKs, and
9
+ // any client that follows the quickstart.
10
+ // - "up_sandbox_…" — a BARE key with no "Bearer " prefix. The Smithery
11
+ // gateway forwards a config field to an upstream header via the config-schema
12
+ // `x-to: { header: "Authorization" }` mapping, but it forwards only the RAW
13
+ // value (no "Bearer " prefix / no templating — per Smithery's session-config
14
+ // docs). Accepting the bare key lets the Smithery `uphealth/signal` listing be
15
+ // connectable with a clean "paste your key" UX instead of forcing the user to
16
+ // type "Bearer " into the API-key field.
17
+ //
18
+ // Rails remains the auth authority — this only extracts a *candidate* token to
19
+ // forward. A header that is neither "Bearer …" nor a bare up_… key reads as "no
20
+ // key" so the caller can return the 401 onboarding hint rather than forwarding
21
+ // garbage (e.g. "Basic …") to Rails.
22
+ const BARE_KEY = /^up_[A-Za-z0-9_-]+$/;
23
+ export function parseAuthorizationHeader(headerValue) {
24
+ if (!headerValue)
25
+ return null;
26
+ const trimmed = headerValue.trim();
27
+ const bearer = /^Bearer\s+(.+)$/i.exec(trimmed);
28
+ if (bearer)
29
+ return bearer[1].trim();
30
+ // Bare key (Smithery raw-forwarded). Restricted to our key shape so a
31
+ // malformed/other-scheme header still yields the 401 onboarding hint.
32
+ if (BARE_KEY.test(trimmed))
33
+ return trimmed;
34
+ return null;
35
+ }
36
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,0EAA0E;AAC1E,mEAAmE;AACnE,qCAAqC;AACrC,EAAE;AACF,wDAAwD;AACxD,8EAA8E;AAC9E,8CAA8C;AAC9C,iFAAiF;AACjF,kFAAkF;AAClF,gFAAgF;AAChF,iFAAiF;AACjF,mFAAmF;AACnF,kFAAkF;AAClF,6CAA6C;AAC7C,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,qCAAqC;AAErC,MAAM,QAAQ,GAAG,qBAAqB,CAAC;AAEvC,MAAM,UAAU,wBAAwB,CACtC,WAAsC;IAEtC,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IAEnC,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpC,sEAAsE;IACtE,sEAAsE;IACtE,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAE3C,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/config.js CHANGED
@@ -18,6 +18,6 @@ export const config = {
18
18
  nodeEnv: optionalEnv("NODE_ENV", "development"),
19
19
  logLevel: optionalEnv("LOG_LEVEL", "info"),
20
20
  serverName: "uphealth-mcp",
21
- serverVersion: "1.0.0",
21
+ serverVersion: "1.1.1",
22
22
  };
23
23
  //# sourceMappingURL=config.js.map
@@ -0,0 +1,243 @@
1
+ // GENERATED FILE — do not edit by hand.
2
+ // Source: app/services/cued_streams/mcp_catalog.rb (CuedStreams::McpCatalog),
3
+ // built from CuedStreams::TemplateRegistry + config/stream_templates/*.yml.
4
+ // Regenerate on .me (needs Rails + the template YAMLs):
5
+ // bundle exec rake signal:mcp_catalog
6
+ // Byte-exact drift-guarded by spec/lib/cued_streams/mcp_catalog_drift_spec.rb
7
+ // (Rails CI; runs even on TS-only PRs since mcp-server/** is not paths-ignored).
8
+ //
9
+ // The metadata-only, non-PHI Signal template catalog. Served by the MCP
10
+ // `signal://catalog` resource; the create_patient_stream tool derives its
11
+ // template_id enum from SIGNAL_TEMPLATE_IDS. Carries NO corpus bodies and NO
12
+ // patient data — only the gating contract (audience tags + de-identified
13
+ // patient_context keys) each live template publishes.
14
+ export const SIGNAL_CATALOG = {
15
+ "schema_version": 1,
16
+ "templates": [
17
+ {
18
+ "template_id": "diabetes_pharma_initiation_30day",
19
+ "display_name": "Diabetes Pharma Initiation (30-day)",
20
+ "status": "live",
21
+ "marketing_slug": "diabetes-pharma-init",
22
+ "expected_duration_days": 30,
23
+ "required_audience_tags": [
24
+ "diabetes_medication_optin"
25
+ ],
26
+ "optional_audience_tags": [
27
+ "t2d_optin"
28
+ ],
29
+ "excluded_audience_tags": [
30
+ "t1d_optin"
31
+ ],
32
+ "b_plus_gated_tag": "diabetes_medication_optin",
33
+ "required_patient_context": [
34
+ {
35
+ "key": "medication_class",
36
+ "type": "enum",
37
+ "enum": [
38
+ "metformin",
39
+ "sglt2",
40
+ "glp1",
41
+ "dpp4",
42
+ "sulfonylurea",
43
+ "insulin",
44
+ "other"
45
+ ]
46
+ }
47
+ ],
48
+ "delivery_modes_supported": [
49
+ "display",
50
+ "deliver"
51
+ ],
52
+ "crisis_footer_rendered_on": "never",
53
+ "default_cadence": "daily"
54
+ },
55
+ {
56
+ "template_id": "general_wellness_daily",
57
+ "display_name": "General Wellness",
58
+ "status": "live",
59
+ "marketing_slug": "general-wellness",
60
+ "expected_duration_days": "indefinite",
61
+ "required_audience_tags": [],
62
+ "optional_audience_tags": [
63
+ "mental_health_optin",
64
+ "substance_use_optin",
65
+ "sex_repro_optin",
66
+ "child_health_optin",
67
+ "lgbtq_optin",
68
+ "cancer_diagnosis_optin",
69
+ "cancer_survivor_optin",
70
+ "cancer_caregiver_optin",
71
+ "parent_pediatric_cancer_optin",
72
+ "diabetes_medication_optin",
73
+ "veteran_optin",
74
+ "postpartum_optin"
75
+ ],
76
+ "excluded_audience_tags": [
77
+ "t1d_optin",
78
+ "t2d_optin"
79
+ ],
80
+ "b_plus_gated_tag": null,
81
+ "required_patient_context": [],
82
+ "delivery_modes_supported": [
83
+ "display",
84
+ "deliver"
85
+ ],
86
+ "crisis_footer_rendered_on": "never",
87
+ "default_cadence": "sometimes"
88
+ },
89
+ {
90
+ "template_id": "heart_pharma_initiation_30day",
91
+ "display_name": "Heart Pharma Initiation (30-day)",
92
+ "status": "live",
93
+ "marketing_slug": "heart-pharma-init",
94
+ "expected_duration_days": 30,
95
+ "required_audience_tags": [
96
+ "heart_medication_optin"
97
+ ],
98
+ "optional_audience_tags": [
99
+ "t2d_optin"
100
+ ],
101
+ "excluded_audience_tags": [],
102
+ "b_plus_gated_tag": "heart_medication_optin",
103
+ "required_patient_context": [
104
+ {
105
+ "key": "medication_class",
106
+ "type": "enum",
107
+ "enum": [
108
+ "statin",
109
+ "antihypertensive",
110
+ "anticoagulant",
111
+ "other"
112
+ ]
113
+ }
114
+ ],
115
+ "delivery_modes_supported": [
116
+ "display",
117
+ "deliver"
118
+ ],
119
+ "crisis_footer_rendered_on": "never",
120
+ "default_cadence": "daily"
121
+ },
122
+ {
123
+ "template_id": "hypertension_lifestyle_90day",
124
+ "display_name": "Hypertension Lifestyle (90-day)",
125
+ "status": "live",
126
+ "marketing_slug": "hypertension-lifestyle",
127
+ "expected_duration_days": 90,
128
+ "required_audience_tags": [],
129
+ "optional_audience_tags": [],
130
+ "excluded_audience_tags": [],
131
+ "b_plus_gated_tag": null,
132
+ "required_patient_context": [],
133
+ "delivery_modes_supported": [
134
+ "display",
135
+ "deliver"
136
+ ],
137
+ "crisis_footer_rendered_on": "never",
138
+ "default_cadence": "often"
139
+ },
140
+ {
141
+ "template_id": "quit_smoking_90day",
142
+ "display_name": "Quit Smoking (90-day)",
143
+ "status": "live",
144
+ "marketing_slug": "quit-smoking",
145
+ "expected_duration_days": 90,
146
+ "required_audience_tags": [],
147
+ "optional_audience_tags": [
148
+ "veteran_optin"
149
+ ],
150
+ "excluded_audience_tags": [],
151
+ "b_plus_gated_tag": null,
152
+ "required_patient_context": [
153
+ {
154
+ "key": "quit_date",
155
+ "type": "date",
156
+ "validation": "ISO 8601 date"
157
+ }
158
+ ],
159
+ "delivery_modes_supported": [
160
+ "display",
161
+ "deliver"
162
+ ],
163
+ "crisis_footer_rendered_on": "never",
164
+ "default_cadence": "daily"
165
+ },
166
+ {
167
+ "template_id": "relapse_prevention_90day",
168
+ "display_name": "Relapse Prevention (90-day)",
169
+ "status": "live",
170
+ "marketing_slug": "relapse-prevention",
171
+ "expected_duration_days": 90,
172
+ "required_audience_tags": [
173
+ "substance_use_optin"
174
+ ],
175
+ "optional_audience_tags": [
176
+ "mental_health_optin",
177
+ "lgbtq_optin",
178
+ "veteran_optin",
179
+ "mat_optin"
180
+ ],
181
+ "excluded_audience_tags": [
182
+ "t1d_optin",
183
+ "t2d_optin"
184
+ ],
185
+ "b_plus_gated_tag": null,
186
+ "required_patient_context": [
187
+ {
188
+ "key": "weeks_since_onset",
189
+ "type": "integer",
190
+ "validation": "0..520"
191
+ }
192
+ ],
193
+ "delivery_modes_supported": [
194
+ "display",
195
+ "deliver"
196
+ ],
197
+ "crisis_footer_rendered_on": "every_cue",
198
+ "default_cadence": "daily"
199
+ },
200
+ {
201
+ "template_id": "weight_glp1_initiation_30day",
202
+ "display_name": "Weight Management GLP-1 Initiation (30-day)",
203
+ "status": "live",
204
+ "marketing_slug": "weight-glp1-init",
205
+ "expected_duration_days": 30,
206
+ "required_audience_tags": [
207
+ "weight_loss_medication_optin"
208
+ ],
209
+ "optional_audience_tags": [
210
+ "t2d_optin"
211
+ ],
212
+ "excluded_audience_tags": [],
213
+ "b_plus_gated_tag": "weight_loss_medication_optin",
214
+ "required_patient_context": [
215
+ {
216
+ "key": "medication_class",
217
+ "type": "enum",
218
+ "enum": [
219
+ "glp1",
220
+ "dual_gip_glp1",
221
+ "other"
222
+ ]
223
+ }
224
+ ],
225
+ "delivery_modes_supported": [
226
+ "display",
227
+ "deliver"
228
+ ],
229
+ "crisis_footer_rendered_on": "never",
230
+ "default_cadence": "daily"
231
+ }
232
+ ]
233
+ };
234
+ export const SIGNAL_TEMPLATE_IDS = [
235
+ "diabetes_pharma_initiation_30day",
236
+ "general_wellness_daily",
237
+ "heart_pharma_initiation_30day",
238
+ "hypertension_lifestyle_90day",
239
+ "quit_smoking_90day",
240
+ "relapse_prevention_90day",
241
+ "weight_glp1_initiation_30day"
242
+ ];
243
+ //# sourceMappingURL=signal-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-catalog.js","sourceRoot":"","sources":["../../src/generated/signal-catalog.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -27,16 +27,18 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
27
27
  import { config } from "./config.js";
28
28
  import { withRequestContext } from "./context.js";
29
29
  import { buildServer } from "./server.js";
30
+ import { parseAuthorizationHeader } from "./auth.js";
31
+ import { requestAllowedWithoutBearer } from "./tool-auth-policy.js";
30
32
  // The MCP Server factory lives in server.ts and is shared with the stdio
31
33
  // entrypoint (stdio.ts), so tools + annotations are registered in exactly
32
34
  // one place. This module is the Streamable HTTP transport only.
33
35
  // ---------- Express wrapper ----------
34
36
  function extractBearer(req) {
35
37
  const header = req.header("authorization") ?? req.header("Authorization");
36
- if (!header)
37
- return null;
38
- const match = /^Bearer\s+(.+)$/i.exec(header.trim());
39
- return match ? match[1].trim() : null;
38
+ // parseAuthorizationHeader accepts "Bearer up_…" AND a bare "up_…" key
39
+ // (the latter is how the Smithery gateway forwards the raw config value —
40
+ // see auth.ts). Rails stays the auth authority.
41
+ return parseAuthorizationHeader(header);
40
42
  }
41
43
  function startHttpServer() {
42
44
  const app = express();
@@ -49,15 +51,21 @@ function startHttpServer() {
49
51
  // server-initiated streams (Streamable HTTP transport spec).
50
52
  const mcpHandler = async (req, res, _next) => {
51
53
  const bearer = extractBearer(req);
52
- if (!bearer) {
54
+ // Per-tool policy: a request with no Bearer is allowed ONLY if every message
55
+ // is keyless-eligible (the handshake, discovery, or a lookup_health_fact
56
+ // call). Anything else — notably a keyed tools/call — still gets the 401
57
+ // onboarding hint. With a Bearer present the flow is unchanged.
58
+ if (!bearer && !requestAllowedWithoutBearer(req.body)) {
53
59
  res.status(401).json({
54
60
  error: "unauthorized",
55
- message: "Missing or malformed Authorization: Bearer header. Get a free sandbox key at https://uphealth.us/signup, then send it as 'Authorization: Bearer up_sandbox_…'.",
61
+ message: "Missing or malformed Authorization: Bearer header. Get a free sandbox key at https://uphealth.us/signup, then send it as 'Authorization: Bearer up_sandbox_…'. (No key needed for the lookup_health_fact tool.)",
56
62
  });
57
63
  return;
58
64
  }
59
65
  try {
60
- await withRequestContext({ bearer }, async () => {
66
+ // bearer may be null on the keyless path; the lookup tool calls Rails with
67
+ // no Authorization, and keyed tools can't reach here without one.
68
+ await withRequestContext({ bearer: bearer ?? "" }, async () => {
61
69
  const server = buildServer();
62
70
  const transport = new StreamableHTTPServerTransport({
63
71
  sessionIdGenerator: undefined, // stateless mode per A4 spec §4
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,27 @@ const CUE_SCHEMA = {
33
33
  },
34
34
  additionalProperties: true,
35
35
  };
36
+ // A5 parity (2026-06-14) — Discovery cue-cap headroom, surfaced from the Rails
37
+ // RateLimit-* response headers (streams_controller#set_rate_limit_headers).
38
+ // PRESENT ONLY on Discovery-tier responses — Rails omits the headers on other
39
+ // tiers (no cue cap), so this block is absent (never required, never zero-filled)
40
+ // there. Lets an agent self-throttle before the monthly cue cap's 429.
41
+ const RATE_LIMIT_SCHEMA = {
42
+ type: "object",
43
+ description: "Discovery monthly cue-cap headroom. Absent on non-Discovery tiers (no cap).",
44
+ properties: {
45
+ limit: { type: "integer", description: "Discovery monthly cue cap." },
46
+ remaining: {
47
+ type: "integer",
48
+ description: "Cues remaining in the current calendar month (UTC).",
49
+ },
50
+ reset: {
51
+ type: "integer",
52
+ description: "Seconds until the cap window resets (start of next calendar month, UTC; same basis as the 429 Retry-After).",
53
+ },
54
+ },
55
+ additionalProperties: true,
56
+ };
36
57
  // Legal/disclosure block returned on every success (A1 §6.7). Do not strip in your UI.
37
58
  const META_SCHEMA = {
38
59
  type: "object",
@@ -54,6 +75,7 @@ export const createPatientStreamOutputSchema = {
54
75
  state: { type: "string" },
55
76
  first_cue: CUE_SCHEMA,
56
77
  next_cue_eligible_at: { type: ["string", "null"] },
78
+ rate_limit: RATE_LIMIT_SCHEMA,
57
79
  _meta: META_SCHEMA,
58
80
  },
59
81
  required: ["stream_id", "state", "first_cue", "_meta"],
@@ -69,6 +91,7 @@ export const getNextCuedMessageOutputSchema = {
69
91
  // StreamExhausted fields (end-of-stream marker):
70
92
  sandbox_exhausted: { type: "boolean", description: "Present + true when the sandbox subset is exhausted; no next_cue then." },
71
93
  message: { type: "string" },
94
+ rate_limit: RATE_LIMIT_SCHEMA,
72
95
  _meta: META_SCHEMA,
73
96
  },
74
97
  // next_cue is omitted from `required` because the exhausted-stream result has
@@ -84,6 +107,7 @@ export const readStreamStateOutputSchema = {
84
107
  current_cue: { type: ["object", "null"], additionalProperties: true },
85
108
  next_cue_eligible: { type: "boolean" },
86
109
  events_count: { type: "integer" },
110
+ rate_limit: RATE_LIMIT_SCHEMA,
87
111
  _meta: META_SCHEMA,
88
112
  },
89
113
  required: ["stream_id", "state", "_meta"],
@@ -97,4 +121,35 @@ export const listSandboxTopicsOutputSchema = {
97
121
  },
98
122
  required: ["topic_labels", "_meta"],
99
123
  };
124
+ // Rung-0 (2026-06-15) — the no-auth lookup_health_fact result. ONE already-public
125
+ // federal fact + federal source + an uphealth.me link + the upgrade CTA. NO
126
+ // numeric scores / model outputs ever (intelligence-framing lock) — those stay
127
+ // behind auth + metering. This schema describes the SUCCESS shape; a no-match /
128
+ // rate-limited / corpus-down lookup returns isError text (no structuredContent).
129
+ export const lookupHealthFactOutputSchema = {
130
+ type: "object",
131
+ properties: {
132
+ fact: {
133
+ type: "object",
134
+ description: "The single already-public federal fact.",
135
+ properties: {
136
+ kind: { type: "string", description: "fact | list | tip" },
137
+ body: { type: "string" },
138
+ agency: { type: "string", description: "Federal source agency label, e.g. CDC / MedlinePlus / NIDDK." },
139
+ source_url: { type: "string", description: "The federal public-domain source." },
140
+ uphealth_url: { type: "string", description: "The public uphealth.me topic page." },
141
+ },
142
+ additionalProperties: true,
143
+ },
144
+ topic: { type: "string", description: "The matched topic's title." },
145
+ disclaimer: { type: "string" },
146
+ attribution: { type: "string", description: "\"Source: <agency> via Uphealth\"." },
147
+ upgrade: {
148
+ type: "object",
149
+ description: "The free-key upsell — adaptive sequencing lives behind a Discovery key.",
150
+ additionalProperties: true,
151
+ },
152
+ },
153
+ required: ["fact", "disclaimer", "attribution", "upgrade"],
154
+ };
100
155
  //# sourceMappingURL=output-schemas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"output-schemas.js","sourceRoot":"","sources":["../src/output-schemas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output-schemas.js","sourceRoot":"","sources":["../src/output-schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ // A5 dev-surface (2026-06-15) — MCP prompts (user-controlled reusable workflows).
2
+ //
3
+ // Two prompts encode the canonical "pick a template, then create a stream and run
4
+ // an episode" path so a host raises tool-selection accuracy instead of guessing:
5
+ // - pick_template : choose the right Signal template for a clinical context
6
+ // - evaluate_signal : the full create + cue-loop episode, end to end
7
+ //
8
+ // BOUNDARY (locked): Display-mode, non-PHI, read-only guidance. The prompts steer
9
+ // the agent to read the signal://catalog resource and pre-satisfy gating; they
10
+ // never instruct it to send PHI or fabricate health content. Not medical advice.
11
+ export const prompts = [
12
+ {
13
+ name: "pick_template",
14
+ description: "Choose the single best Signal stream template for a patient/clinical context, " +
15
+ "and list the audience_tags + de-identified patient_context that create_patient_stream " +
16
+ "will require. Reads the signal://catalog resource so it never invents a template_id.",
17
+ arguments: [
18
+ {
19
+ name: "clinical_context",
20
+ description: "What the messaging journey is about — e.g. 'adult starting a GLP-1 for weight " +
21
+ "management', 'opioid-use-disorder relapse prevention, 6 weeks since onset', " +
22
+ "'general wellness onboarding'.",
23
+ required: true,
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ name: "evaluate_signal",
29
+ description: "Run a complete Signal episode end to end: read the catalog, pick a template, create a " +
30
+ "Display-mode stream with no PHI, then loop cues — submitting the prior response each " +
31
+ "time — and branch on the receptivity score + audience-safety verdict. The canonical " +
32
+ "way to evaluate whether Signal fits before integrating.",
33
+ arguments: [
34
+ {
35
+ name: "goal",
36
+ description: "The outcome you are evaluating Signal for — e.g. 'a 30-day medication-initiation " +
37
+ "adherence journey for our diabetes cohort'.",
38
+ required: true,
39
+ },
40
+ {
41
+ name: "patient_summary",
42
+ description: "Optional de-identified description of the test patient (NO PHI) — e.g. 'adult, " +
43
+ "starting metformin, week 0'. Used to fill audience_tags + patient_context.",
44
+ required: false,
45
+ },
46
+ ],
47
+ },
48
+ ];
49
+ function userMessage(text) {
50
+ return [{ role: "user", content: { type: "text", text } }];
51
+ }
52
+ function str(value, fallback) {
53
+ return typeof value === "string" && value.trim().length > 0 ? value : fallback;
54
+ }
55
+ // GetPrompt handler. Throws on an unknown name (the SDK maps the throw to a
56
+ // JSON-RPC error).
57
+ export function getPrompt(name, args) {
58
+ switch (name) {
59
+ case "pick_template": {
60
+ const context = str(args.clinical_context, "(no clinical context given)");
61
+ return {
62
+ description: "Pick the best Signal template for a clinical context.",
63
+ messages: userMessage(`I'm building a Signal messaging journey for: ${context}\n\n` +
64
+ `1. Read the \`signal://catalog\` MCP resource (the live template list + each template's gating).\n` +
65
+ `2. Choose the SINGLE best \`template_id\` — use only ids that appear in the catalog; never invent one.\n` +
66
+ `3. List, for that template: its required_audience_tags, any optional tags worth adding, its excluded_audience_tags (never include these), and its required_patient_context fields (key + type + allowed values).\n` +
67
+ `4. Explain in one or two sentences why it fits, and call out the crisis-footer / b_plus gating if the catalog flags any.\n\n` +
68
+ `Do not create the stream yet — just recommend the template and the exact arguments create_patient_stream will need. This is wellness content, not medical advice.`),
69
+ };
70
+ }
71
+ case "evaluate_signal": {
72
+ const goal = str(args.goal, "(no goal given)");
73
+ const patient = str(args.patient_summary, "a de-identified test patient (no PHI)");
74
+ return {
75
+ description: "Run a full Signal create + cue episode to evaluate fit.",
76
+ messages: userMessage(`Evaluate Signal for this goal: ${goal}\n` +
77
+ `Test patient: ${patient}\n\n` +
78
+ `Run the full episode in Display mode (no PHI, no BAA):\n` +
79
+ `1. Read the \`signal://catalog\` resource; pick the best template_id and compute audience_tags (include every required tag, no excluded tag) + de-identified patient_context.\n` +
80
+ `2. Call create_patient_stream with that template_id, audience_tags, attributes, and an idempotency_key (UUID) so a retry is safe. Keep the returned stream_id and the first cue.\n` +
81
+ `3. Loop: read the cue's body + its receptivity score (0–1, advisory) + audience-safety verdict, decide the patient's response_action (from the catalog's cue vocabulary), then call get_next_cued_message with that feedback. Every cue after the first REQUIRES the prior response — the engine returns 409 feedback_required if you skip it (the sequencing moat).\n` +
82
+ `4. After a few turns, summarize: did the sequence stay on-topic and federally-sourced, did receptivity track the feedback, and did any cue get safety-filtered?\n\n` +
83
+ `Surface the federal source on each cue. This is wellness content, not medical advice; do not send PHI.`),
84
+ };
85
+ }
86
+ default:
87
+ throw new Error(`Unknown prompt: ${name}. Available prompts: pick_template, evaluate_signal.`);
88
+ }
89
+ }
90
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,EAAE;AACF,kFAAkF;AAClF,iFAAiF;AACjF,gFAAgF;AAChF,uEAAuE;AACvE,EAAE;AACF,kFAAkF;AAClF,+EAA+E;AAC/E,iFAAiF;AAcjF,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC;QACE,IAAI,EAAE,eAAe;QACrB,WAAW,EACT,gFAAgF;YAChF,wFAAwF;YACxF,sFAAsF;QACxF,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,kBAAkB;gBACxB,WAAW,EACT,gFAAgF;oBAChF,8EAA8E;oBAC9E,gCAAgC;gBAClC,QAAQ,EAAE,IAAI;aACf;SACF;KACF;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,WAAW,EACT,wFAAwF;YACxF,uFAAuF;YACvF,sFAAsF;YACtF,yDAAyD;QAC3D,SAAS,EAAE;YACT;gBACE,IAAI,EAAE,MAAM;gBACZ,WAAW,EACT,mFAAmF;oBACnF,6CAA6C;gBAC/C,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EACT,iFAAiF;oBACjF,4EAA4E;gBAC9E,QAAQ,EAAE,KAAK;aAChB;SACF;KACF;CACF,CAAC;AASF,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,GAAG,CAAC,KAAc,EAAE,QAAgB;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AACjF,CAAC;AAED,4EAA4E;AAC5E,mBAAmB;AACnB,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,IAA6B;IAE7B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,6BAA6B,CAAC,CAAC;YAC1E,OAAO;gBACL,WAAW,EAAE,uDAAuD;gBACpE,QAAQ,EAAE,WAAW,CACnB,gDAAgD,OAAO,MAAM;oBAC3D,oGAAoG;oBACpG,0GAA0G;oBAC1G,oNAAoN;oBACpN,8HAA8H;oBAC9H,mKAAmK,CACtK;aACF,CAAC;QACJ,CAAC;QACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;YACvB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;YAC/C,MAAM,OAAO,GAAG,GAAG,CACjB,IAAI,CAAC,eAAe,EACpB,uCAAuC,CACxC,CAAC;YACF,OAAO;gBACL,WAAW,EAAE,yDAAyD;gBACtE,QAAQ,EAAE,WAAW,CACnB,kCAAkC,IAAI,IAAI;oBACxC,iBAAiB,OAAO,MAAM;oBAC9B,0DAA0D;oBAC1D,iLAAiL;oBACjL,oLAAoL;oBACpL,wWAAwW;oBACxW,qKAAqK;oBACrK,wGAAwG,CAC3G;aACF,CAAC;QACJ,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,sDAAsD,CAC9E,CAAC;IACN,CAAC;AACH,CAAC"}