@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.
- package/DISTRIBUTION.md +11 -10
- package/README.md +3 -3
- package/dist/auth.js +36 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/generated/signal-catalog.js +243 -0
- package/dist/generated/signal-catalog.js.map +1 -0
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/output-schemas.js +55 -0
- package/dist/output-schemas.js.map +1 -1
- package/dist/prompts.js +90 -0
- package/dist/prompts.js.map +1 -0
- package/dist/rails-client.js +30 -0
- package/dist/rails-client.js.map +1 -1
- package/dist/resources.js +127 -0
- package/dist/resources.js.map +1 -0
- package/dist/server.js +16 -2
- package/dist/server.js.map +1 -1
- package/dist/stdio-context.js +19 -0
- package/dist/stdio-context.js.map +1 -0
- package/dist/stdio.js +23 -14
- package/dist/stdio.js.map +1 -1
- package/dist/tool-auth-policy.js +52 -0
- package/dist/tool-auth-policy.js.map +1 -0
- package/dist/tool-result.js +34 -3
- package/dist/tool-result.js.map +1 -1
- package/dist/tools/create-patient-stream.js +36 -8
- package/dist/tools/create-patient-stream.js.map +1 -1
- package/dist/tools/get-next-cued-message.js +7 -2
- package/dist/tools/get-next-cued-message.js.map +1 -1
- package/dist/tools/index.js +5 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-sandbox-topics.js +4 -1
- package/dist/tools/list-sandbox-topics.js.map +1 -1
- package/dist/tools/lookup-health-fact.js +66 -0
- package/dist/tools/lookup-health-fact.js.map +1 -0
- package/dist/tools/read-stream-state.js +4 -2
- package/dist/tools/read-stream-state.js.map +1 -1
- 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
|
|
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
|
-
**
|
|
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
|
|
7
|
+
## The five tools
|
|
8
8
|
|
|
9
|
-
| Tool | Wraps | Does |
|
|
10
|
-
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
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
|
|
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.
|
|
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** —
|
|
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
|
package/dist/auth.js.map
ADDED
|
@@ -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
|
@@ -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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
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
|
|
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
|
-
|
|
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":"AAAA,4CAA4C;AAC5C,EAAE;AACF,SAAS;AACT,4EAA4E;AAC5E,EAAE;AACF,oBAAoB;AACpB,iFAAiF;AACjF,qEAAqE;AACrE,iDAAiD;AACjD,sEAAsE;AACtE,wEAAwE;AACxE,kEAAkE;AAClE,uEAAuE;AACvE,oDAAoD;AACpD,uEAAuE;AACvE,+CAA+C;AAC/C,sEAAsE;AACtE,2DAA2D;AAC3D,EAAE;AACF,oEAAoE;AACpE,sEAAsE;AACtE,+BAA+B;AAC/B,EAAE;AACF,yDAAyD;AAEzD,OAAO,OAA2D,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,SAAS;AACT,4EAA4E;AAC5E,EAAE;AACF,oBAAoB;AACpB,iFAAiF;AACjF,qEAAqE;AACrE,iDAAiD;AACjD,sEAAsE;AACtE,wEAAwE;AACxE,kEAAkE;AAClE,uEAAuE;AACvE,oDAAoD;AACpD,uEAAuE;AACvE,+CAA+C;AAC/C,sEAAsE;AACtE,2DAA2D;AAC3D,EAAE;AACF,oEAAoE;AACpE,sEAAsE;AACtE,+BAA+B;AAC/B,EAAE;AACF,yDAAyD;AAEzD,OAAO,OAA2D,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEpE,yEAAyE;AACzE,0EAA0E;AAC1E,gEAAgE;AAEhE,wCAAwC;AAExC,SAAS,aAAa,CAAC,GAAY;IACjC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC1E,uEAAuE;IACvE,0EAA0E;IAC1E,gDAAgD;IAChD,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAExC,+EAA+E;IAC/E,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IACnG,CAAC,CAAC,CAAC;IAEH,kEAAkE;IAClE,6DAA6D;IAC7D,MAAM,UAAU,GAAG,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,KAAmB,EAAE,EAAE;QAC5E,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,6EAA6E;QAC7E,yEAAyE;QACzE,yEAAyE;QACzE,gEAAgE;QAChE,IAAI,CAAC,MAAM,IAAI,CAAC,2BAA2B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACtD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,cAAc;gBACrB,OAAO,EACL,iNAAiN;aACpN,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,2EAA2E;YAC3E,kEAAkE;YAClE,MAAM,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE;gBAC5D,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;gBAC7B,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;oBAClD,kBAAkB,EAAE,SAAS,EAAE,gCAAgC;iBAChE,CAAC,CAAC;gBACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,2DAA2D;YAC3D,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;YAClD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC7B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE5B,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC3B,OAAO,CAAC,GAAG,CACT,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,aAAa,mBAAmB,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,eAAe,EAAE,CAC1G,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,QAAQ;AACR,eAAe,EAAE,CAAC"}
|
package/dist/output-schemas.js
CHANGED
|
@@ -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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"}
|
package/dist/prompts.js
ADDED
|
@@ -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"}
|