@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/dist/rails-client.js
CHANGED
|
@@ -18,8 +18,15 @@ import { config } from "./config.js";
|
|
|
18
18
|
export async function callRails(opts) {
|
|
19
19
|
const url = new URL(opts.path, config.railsApiBaseUrl).toString();
|
|
20
20
|
const headers = {
|
|
21
|
+
...(opts.headers ?? {}),
|
|
21
22
|
Authorization: `Bearer ${opts.bearer}`,
|
|
22
23
|
Accept: "application/json",
|
|
24
|
+
// NOW #5 (2026-06-14) — identify the MCP server as the integration surface so
|
|
25
|
+
// the activation report can split cues by raw HTTP vs SDK vs MCP. Rails reads
|
|
26
|
+
// X-Uphealth-Client (falling back to User-Agent) onto the cued event payload.
|
|
27
|
+
// See CuedStreams::IntegrationSurface. The SDKs send the analogous token.
|
|
28
|
+
"User-Agent": `uphealth-mcp-server/${config.serverVersion}`,
|
|
29
|
+
"X-Uphealth-Client": `uphealth-mcp-server/${config.serverVersion}`,
|
|
23
30
|
};
|
|
24
31
|
const fetchInit = {
|
|
25
32
|
method: opts.method,
|
|
@@ -29,6 +36,29 @@ export async function callRails(opts) {
|
|
|
29
36
|
headers["Content-Type"] = "application/json";
|
|
30
37
|
fetchInit.body = JSON.stringify(opts.body);
|
|
31
38
|
}
|
|
39
|
+
return dispatch(url, fetchInit);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Perform an UNAUTHENTICATED GET to the Rails public API surface — Rung-0, the
|
|
43
|
+
* no-auth /v1/public/* lookup (2026-06-15). Sends NO Authorization header: the
|
|
44
|
+
* endpoint bypasses RequireTenant entirely, so there is no token to forward.
|
|
45
|
+
* Still carries the client-attribution tokens so server-side [public-lookup]
|
|
46
|
+
* traffic stays recognizable.
|
|
47
|
+
*/
|
|
48
|
+
export async function callRailsPublic(opts) {
|
|
49
|
+
const url = new URL(opts.path, config.railsApiBaseUrl).toString();
|
|
50
|
+
const headers = {
|
|
51
|
+
...(opts.headers ?? {}),
|
|
52
|
+
Accept: "application/json",
|
|
53
|
+
"User-Agent": `uphealth-mcp-server/${config.serverVersion}`,
|
|
54
|
+
"X-Uphealth-Client": `uphealth-mcp-server/${config.serverVersion}`,
|
|
55
|
+
};
|
|
56
|
+
return dispatch(url, { method: opts.method, headers });
|
|
57
|
+
}
|
|
58
|
+
// Shared fetch + JSON-parse for both the authenticated (callRails) and the
|
|
59
|
+
// unauthenticated (callRailsPublic) paths. Returns the full Rails response
|
|
60
|
+
// (status + parsed body + lowercased headers) so callers can branch on codes.
|
|
61
|
+
async function dispatch(url, fetchInit) {
|
|
32
62
|
const response = await fetch(url, fetchInit);
|
|
33
63
|
const responseHeaders = {};
|
|
34
64
|
response.headers.forEach((value, key) => {
|
package/dist/rails-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rails-client.js","sourceRoot":"","sources":["../src/rails-client.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,oEAAoE;AACpE,mEAAmE;AACnE,+DAA+D;AAC/D,iEAAiE;AACjE,sEAAsE;AACtE,EAAE;AACF,qEAAqE;AAErE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAgBrC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAc,
|
|
1
|
+
{"version":3,"file":"rails-client.js","sourceRoot":"","sources":["../src/rails-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// A5 dev-surface (2026-06-15) — MCP resources.
|
|
2
|
+
//
|
|
3
|
+
// Two read-only resources:
|
|
4
|
+
// - `signal://catalog` — the live Signal template catalog (from the GENERATED,
|
|
5
|
+
// drift-guarded src/generated/signal-catalog.ts) PLUS the accepted cue
|
|
6
|
+
// `response_action` vocabulary (single-sourced from cue-actions.ts).
|
|
7
|
+
// - `signal://openapi` — the machine-readable OpenAPI 3.1 contract URLs +
|
|
8
|
+
// the receptivity-score semantics (strategy §4 #5 — "lets the server
|
|
9
|
+
// self-describe; cheap, read-only"). Points an agent at the spec to generate
|
|
10
|
+
// a client; carries no spec body (no runtime fetch, no drift).
|
|
11
|
+
//
|
|
12
|
+
// WHY: an agent reads `signal://catalog` BEFORE create_patient_stream so it can
|
|
13
|
+
// pick a template and pre-satisfy that template's audience-tag + patient-context
|
|
14
|
+
// gating — turning a clinical-template 422 into a 201 + first cue.
|
|
15
|
+
//
|
|
16
|
+
// BOUNDARY (locked, A1 §6.6 + dev-PMF strategy §4): read-only, metadata-only,
|
|
17
|
+
// non-PHI, Display-mode. The catalog carries the GATING CONTRACT only — never
|
|
18
|
+
// corpus message bodies (those are returned per-cue at runtime, after a stream
|
|
19
|
+
// exists) and never patient data. The resource handlers make NO Rails call.
|
|
20
|
+
import { SIGNAL_CATALOG } from "./generated/signal-catalog.js";
|
|
21
|
+
import { CUE_RESPONSE_ACTIONS } from "./cue-actions.js";
|
|
22
|
+
export const SIGNAL_CATALOG_URI = "signal://catalog";
|
|
23
|
+
export const SIGNAL_OPENAPI_URI = "signal://openapi";
|
|
24
|
+
// Stable public URLs for the machine-readable contract (same as the docs
|
|
25
|
+
// surfaces single-source in UphealthUs::DocsMarkdown). The spec is a static file
|
|
26
|
+
// served on uphealth.us; these are not the API base URL.
|
|
27
|
+
const OPENAPI_YAML_URL = "https://uphealth.us/openapi/signal-v1.yaml";
|
|
28
|
+
const OPENAPI_JSON_URL = "https://uphealth.us/openapi/signal-v1.json";
|
|
29
|
+
const POSTMAN_URL = "https://uphealth.us/openapi/uphealth-signal.postman_collection.json";
|
|
30
|
+
const DOCS_URL = "https://uphealth.us/docs";
|
|
31
|
+
// = AdaptiveResponse::Archetypes::CUE_FALLBACK_ACTION (Rails folds an
|
|
32
|
+
// unrecognized-but-well-formed response_action to this acknowledge-class value).
|
|
33
|
+
const CUE_FALLBACK_ACTION = "acknowledge";
|
|
34
|
+
// The resource body, assembled once at module load (static — no I/O).
|
|
35
|
+
const catalogBody = {
|
|
36
|
+
...SIGNAL_CATALOG,
|
|
37
|
+
cue_actions: {
|
|
38
|
+
accepted: [...CUE_RESPONSE_ACTIONS],
|
|
39
|
+
fallback_action: CUE_FALLBACK_ACTION,
|
|
40
|
+
forward_compat: "The cue vocabulary grows over time. Submit an accepted value, or any " +
|
|
41
|
+
"well-formed token matching /^[a-z][a-z0-9_]{0,63}$/ — unknown well-formed " +
|
|
42
|
+
"tokens are accepted (200) and echoed back as normalized_action='acknowledge'; " +
|
|
43
|
+
"a malformed token returns 422 (invalid_feedback). Treat unknown response_action " +
|
|
44
|
+
"values in responses as acknowledge-class.",
|
|
45
|
+
},
|
|
46
|
+
gating: {
|
|
47
|
+
to_create: "audience_tags MUST include every required_audience_tags and MUST NOT include " +
|
|
48
|
+
"any excluded_audience_tags, or create returns 422 (missing_required_audience_tags " +
|
|
49
|
+
"/ incompatible_audience_tag) on a production-tier key. A free sandbox (Discovery) " +
|
|
50
|
+
"key skips the gate and always cues the curated sandbox corpus — see list_sandbox_topics.",
|
|
51
|
+
patient_context: "Populate required_patient_context with de-identified values before create; a " +
|
|
52
|
+
"production create missing a required field is rejected. Never send PHI — Display " +
|
|
53
|
+
"mode renders in your own app, so no PHI and no BAA are involved.",
|
|
54
|
+
b_plus: "Where b_plus_gated_tag is set, Lists and Tips outside the template's primary " +
|
|
55
|
+
"category additionally require that tag on the patient; Facts are universal.",
|
|
56
|
+
},
|
|
57
|
+
usage: "Pick a template by clinical context, compute audience_tags from required/optional " +
|
|
58
|
+
"(never an excluded tag), fill required_patient_context with de-identified values, " +
|
|
59
|
+
"then call create_patient_stream — gating is pre-satisfied, so you get a 201 + first " +
|
|
60
|
+
"cue instead of a 422. Every cue is federally-sourced and audience-safety-checked. " +
|
|
61
|
+
"Not medical advice.",
|
|
62
|
+
};
|
|
63
|
+
// The OpenAPI / receptivity reference body (static — no I/O, no spec body, so it
|
|
64
|
+
// never drifts from or hammers the served spec). Points the agent at the
|
|
65
|
+
// machine-readable contract and explains the advisory receptivity score.
|
|
66
|
+
const openapiBody = {
|
|
67
|
+
description: "The machine-readable OpenAPI 3.1 contract for the Signal cued-stream API. " +
|
|
68
|
+
"Fetch one of the spec URLs below to self-describe the API or generate a client.",
|
|
69
|
+
openapi: {
|
|
70
|
+
yaml_url: OPENAPI_YAML_URL,
|
|
71
|
+
json_url: OPENAPI_JSON_URL,
|
|
72
|
+
postman_url: POSTMAN_URL,
|
|
73
|
+
docs_url: DOCS_URL,
|
|
74
|
+
},
|
|
75
|
+
receptivity: {
|
|
76
|
+
range: "0.0–1.0",
|
|
77
|
+
neutral: 0.5,
|
|
78
|
+
present_on: "every cue object (first_cue / next_cue / current_cue)",
|
|
79
|
+
meaning: "An advisory estimate of how receptive the patient is right now, built from " +
|
|
80
|
+
"their own feedback on prior cues; 0.50 is the neutral state of a brand-new " +
|
|
81
|
+
"patient. It is yours to read or ignore — the engine already applies it to " +
|
|
82
|
+
"choose the message.",
|
|
83
|
+
},
|
|
84
|
+
note: "Read-only reference. Not medical advice.",
|
|
85
|
+
};
|
|
86
|
+
// ListResources payload.
|
|
87
|
+
export const resources = [
|
|
88
|
+
{
|
|
89
|
+
uri: SIGNAL_CATALOG_URI,
|
|
90
|
+
name: "Signal template catalog & cue vocabulary",
|
|
91
|
+
description: "The live Signal stream templates (id, audience-tag + de-identified patient-context " +
|
|
92
|
+
"gating, duration, delivery modes, crisis-footer policy) plus the accepted cue " +
|
|
93
|
+
"response_action vocabulary. Read this BEFORE create_patient_stream to pick a template " +
|
|
94
|
+
"and pre-satisfy its gating. Metadata only — no patient data, no message bodies.",
|
|
95
|
+
mimeType: "application/json",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
uri: SIGNAL_OPENAPI_URI,
|
|
99
|
+
name: "OpenAPI specification & receptivity reference",
|
|
100
|
+
description: "The machine-readable OpenAPI 3.1 contract URLs (YAML / JSON / Postman) + the docs " +
|
|
101
|
+
"home, plus the advisory receptivity-score semantics every cue carries. Read this to " +
|
|
102
|
+
"self-describe the API or generate a client.",
|
|
103
|
+
mimeType: "application/json",
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
// ReadResource handler. Throws on an unknown uri (the SDK maps the throw to a
|
|
107
|
+
// JSON-RPC error). Both resources are static JSON, assembled at module load.
|
|
108
|
+
const RESOURCE_BODIES = {
|
|
109
|
+
[SIGNAL_CATALOG_URI]: catalogBody,
|
|
110
|
+
[SIGNAL_OPENAPI_URI]: openapiBody,
|
|
111
|
+
};
|
|
112
|
+
export function readResource(uri) {
|
|
113
|
+
const body = RESOURCE_BODIES[uri];
|
|
114
|
+
if (body === undefined) {
|
|
115
|
+
throw new Error(`Unknown resource: ${uri}. Available: ${SIGNAL_CATALOG_URI}, ${SIGNAL_OPENAPI_URI}.`);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
contents: [
|
|
119
|
+
{
|
|
120
|
+
uri,
|
|
121
|
+
mimeType: "application/json",
|
|
122
|
+
text: JSON.stringify(body, null, 2),
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -8,12 +8,18 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Spec: docs/spec_track_a_a4_mcp_server.md §3 + §4 + §6.
|
|
10
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
12
12
|
import { config } from "./config.js";
|
|
13
13
|
import { tools, findToolByName } from "./tools/index.js";
|
|
14
14
|
import { errorResult } from "./tool-result.js";
|
|
15
|
+
import { resources, readResource } from "./resources.js";
|
|
16
|
+
import { prompts, getPrompt } from "./prompts.js";
|
|
15
17
|
export function buildServer() {
|
|
16
|
-
const server = new Server({ name: config.serverName, version: config.serverVersion },
|
|
18
|
+
const server = new Server({ name: config.serverName, version: config.serverVersion },
|
|
19
|
+
// A5 dev-surface (2026-06-15) — resources (the signal://catalog template +
|
|
20
|
+
// cue-vocabulary metadata) and prompts (pick_template / evaluate_signal) join
|
|
21
|
+
// tools so an agent can pick a template + pre-satisfy gating before creating.
|
|
22
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
17
23
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
18
24
|
tools: tools.map((t) => ({
|
|
19
25
|
name: t.name,
|
|
@@ -24,6 +30,14 @@ export function buildServer() {
|
|
|
24
30
|
outputSchema: t.outputSchema,
|
|
25
31
|
})),
|
|
26
32
|
}));
|
|
33
|
+
// A5 dev-surface — resources: read-only, metadata-only signal://catalog.
|
|
34
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
35
|
+
resources: resources.map((r) => ({ ...r })),
|
|
36
|
+
}));
|
|
37
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (req) => readResource(req.params.uri));
|
|
38
|
+
// A5 dev-surface — prompts: the canonical pick-template / run-an-episode flows.
|
|
39
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts }));
|
|
40
|
+
server.setRequestHandler(GetPromptRequestSchema, async (req) => getPrompt(req.params.name, req.params.arguments ?? {}));
|
|
27
41
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
28
42
|
const toolName = req.params.name;
|
|
29
43
|
const args = (req.params.arguments ?? {});
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,kEAAkE;AAClE,4EAA4E;AAC5E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAChF,EAAE;AACF,yDAAyD;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GAEvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Pure resolver for the stdio entrypoint's process-level fallback context.
|
|
2
|
+
//
|
|
3
|
+
// Split into its own side-effect-free module (only a type-only import) so it can
|
|
4
|
+
// be unit-tested WITHOUT importing stdio.ts — whose bottom-of-file main() boots a
|
|
5
|
+
// transport on import. (An `import.meta.url === argv[1]` entrypoint guard was
|
|
6
|
+
// considered and rejected: npm runs the published bin via a `.bin/…` symlink, so
|
|
7
|
+
// argv[1] is the symlink path while import.meta.url is the real dist/stdio.js —
|
|
8
|
+
// they don't match, and main() would never run. Keeping the helper separate lets
|
|
9
|
+
// stdio.ts call main() unconditionally, as the entrypoint must.)
|
|
10
|
+
//
|
|
11
|
+
// Contract: a missing / whitespace-only key yields an EMPTY bearer (keyless
|
|
12
|
+
// mode) — NOT a process exit. With an empty bearer, lookup_health_fact still
|
|
13
|
+
// works (it calls the public endpoint with no bearer) and every keyed tool
|
|
14
|
+
// forwards the empty bearer so Rails 401s it at call-time. Rails is the auth
|
|
15
|
+
// authority. A present key is returned trimmed.
|
|
16
|
+
export function stdioFallbackContext(rawKey) {
|
|
17
|
+
return { bearer: (rawKey ?? "").trim() };
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=stdio-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio-context.js","sourceRoot":"","sources":["../src/stdio-context.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,8EAA8E;AAC9E,iFAAiF;AACjF,gFAAgF;AAChF,iFAAiF;AACjF,iEAAiE;AACjE,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,gDAAgD;AAIhD,MAAM,UAAU,oBAAoB,CAAC,MAA0B;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AAC3C,CAAC"}
|
package/dist/stdio.js
CHANGED
|
@@ -4,30 +4,39 @@
|
|
|
4
4
|
//
|
|
5
5
|
// npx -y @uphealth/mcp-server
|
|
6
6
|
//
|
|
7
|
-
//
|
|
7
|
+
// KEYLESS by default. The one keyless tool (lookup_health_fact) works with NO
|
|
8
|
+
// key, matching the hosted Streamable HTTP deployment at mcp.uphealth.us
|
|
9
|
+
// (src/index.ts) — so "zero setup" is true on BOTH the npx and the remote path,
|
|
10
|
+
// not just the remote. Supply a free sandbox key in the environment to unlock the
|
|
11
|
+
// keyed tools (create_patient_stream, get_next_cued_message, …):
|
|
8
12
|
//
|
|
9
13
|
// UPHEALTH_API_KEY=up_sandbox_…
|
|
10
14
|
//
|
|
11
|
-
//
|
|
12
|
-
// hosted Streamable HTTP deployment at mcp.uphealth.us (src/index.ts). The two
|
|
13
|
-
// share the same tools and the same Server factory (server.ts).
|
|
15
|
+
// The two transports share the same tools and the same Server factory (server.ts).
|
|
14
16
|
//
|
|
15
|
-
// A stdio process is single-tenant: one user, one
|
|
16
|
-
// that
|
|
17
|
-
// that read the per-request bearer over HTTP work unchanged here.
|
|
17
|
+
// A stdio process is single-tenant: one user, one bearer for its lifetime. We set
|
|
18
|
+
// that bearer as the process-level fallback context, so the same tool handlers
|
|
19
|
+
// that read the per-request bearer over HTTP work unchanged here. With NO key we
|
|
20
|
+
// set an EMPTY bearer: lookup_health_fact hits the public endpoint (no bearer
|
|
21
|
+
// needed) and works; every keyed tool forwards the empty bearer and Rails 401s it
|
|
22
|
+
// at call-time. Rails stays the single auth authority (defense in depth) — we do
|
|
23
|
+
// not pre-reject keyless here; we let the keyless tool through and let Rails gate
|
|
24
|
+
// the keyed ones, exactly as the HTTP per-tool policy does (tool-auth-policy.ts).
|
|
18
25
|
import "./bootstrap-env.js"; // must run before anything imports config.ts
|
|
19
26
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
27
|
import { setFallbackContext } from "./context.js";
|
|
28
|
+
import { stdioFallbackContext } from "./stdio-context.js";
|
|
21
29
|
import { buildServer } from "./server.js";
|
|
22
30
|
async function main() {
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
const ctx = stdioFallbackContext(process.env.UPHEALTH_API_KEY);
|
|
32
|
+
if (!ctx.bearer) {
|
|
33
|
+
// Informational, NOT fatal. stdout is the MCP JSON-RPC channel, so guidance
|
|
34
|
+
// goes to stderr. Keyless mode is fully functional for lookup_health_fact.
|
|
35
|
+
console.error("Uphealth MCP: running keyless — lookup_health_fact works with no key. " +
|
|
36
|
+
"For the keyed tools (patient streams + cues), get a free sandbox key at " +
|
|
37
|
+
"https://uphealth.us/signup and set UPHEALTH_API_KEY in your MCP client config.");
|
|
29
38
|
}
|
|
30
|
-
setFallbackContext(
|
|
39
|
+
setFallbackContext(ctx);
|
|
31
40
|
const server = buildServer();
|
|
32
41
|
const transport = new StdioServerTransport();
|
|
33
42
|
await server.connect(transport);
|
package/dist/stdio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA,uEAAuE;AACvE,4EAA4E;AAC5E,EAAE;AACF,gCAAgC;AAChC,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA,uEAAuE;AACvE,4EAA4E;AAC5E,EAAE;AACF,gCAAgC;AAChC,EAAE;AACF,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,kFAAkF;AAClF,iEAAiE;AACjE,EAAE;AACF,kCAAkC;AAClC,EAAE;AACF,mFAAmF;AACnF,EAAE;AACF,kFAAkF;AAClF,+EAA+E;AAC/E,iFAAiF;AACjF,8EAA8E;AAC9E,kFAAkF;AAClF,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAElF,OAAO,oBAAoB,CAAC,CAAC,6CAA6C;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/D,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QAChB,4EAA4E;QAC5E,2EAA2E;QAC3E,OAAO,CAAC,KAAK,CACX,wEAAwE;YACtE,0EAA0E;YAC1E,gFAAgF,CACnF,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAExB,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,2EAA2E;IAC3E,kEAAkE;AACpE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Per-tool auth policy for the Streamable HTTP transport (Rung-0, 2026-06-15).
|
|
2
|
+
//
|
|
3
|
+
// Extracted from index.ts as a pure, side-effect-free module — like auth.ts — so
|
|
4
|
+
// it can be unit-tested without booting the Express listener (index.ts calls
|
|
5
|
+
// startHttpServer() at import time).
|
|
6
|
+
//
|
|
7
|
+
// The guard used to be all-or-nothing: NO Bearer → 401 for EVERY /mcp request,
|
|
8
|
+
// including the handshake. Rung-0 relaxes it to a per-tool policy so the one
|
|
9
|
+
// keyless tool (lookup_health_fact) — and ONLY the JSON-RPC methods needed to
|
|
10
|
+
// reach it — work without a key, while every keyed tool still 401s. Rails stays
|
|
11
|
+
// the auth authority (defense in depth): even if this gate were bypassed, a keyed
|
|
12
|
+
// tool forwards an empty Bearer and Rails 401s it.
|
|
13
|
+
// The ONLY tool callable without a Bearer. Add a tool here = make it keyless.
|
|
14
|
+
export const NO_AUTH_TOOLS = new Set(["lookup_health_fact"]);
|
|
15
|
+
// MCP handshake / discovery methods that must work keyless so a client can
|
|
16
|
+
// initialize a session and discover lookup_health_fact at all. These expose only
|
|
17
|
+
// public catalog metadata (tool/resource/prompt names + schemas) — no secrets,
|
|
18
|
+
// no tenant data. Notably ABSENT: resources/read + prompts/get + every keyed
|
|
19
|
+
// tools/call, which still require a Bearer.
|
|
20
|
+
export const NO_AUTH_METHODS = new Set([
|
|
21
|
+
"initialize",
|
|
22
|
+
"notifications/initialized",
|
|
23
|
+
"ping",
|
|
24
|
+
"tools/list",
|
|
25
|
+
"resources/list",
|
|
26
|
+
"prompts/list",
|
|
27
|
+
]);
|
|
28
|
+
export function messageAllowedWithoutBearer(msg) {
|
|
29
|
+
if (!msg || typeof msg !== "object")
|
|
30
|
+
return false;
|
|
31
|
+
const method = msg.method;
|
|
32
|
+
if (typeof method !== "string")
|
|
33
|
+
return false;
|
|
34
|
+
if (NO_AUTH_METHODS.has(method))
|
|
35
|
+
return true;
|
|
36
|
+
if (method === "tools/call") {
|
|
37
|
+
const name = msg.params?.name;
|
|
38
|
+
return typeof name === "string" && NO_AUTH_TOOLS.has(name);
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
// A request (single message OR a JSON-RPC batch) may proceed without a Bearer
|
|
43
|
+
// ONLY if EVERY message in it is individually allowed — a batch that mixes a
|
|
44
|
+
// keyless method with a keyed tools/call is rejected wholesale. An empty/garbage
|
|
45
|
+
// body is not allowed (falls through to the 401 onboarding hint).
|
|
46
|
+
export function requestAllowedWithoutBearer(body) {
|
|
47
|
+
const messages = Array.isArray(body) ? body : [body];
|
|
48
|
+
if (messages.length === 0)
|
|
49
|
+
return false;
|
|
50
|
+
return messages.every(messageAllowedWithoutBearer);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=tool-auth-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-auth-policy.js","sourceRoot":"","sources":["../src/tool-auth-policy.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,iFAAiF;AACjF,6EAA6E;AAC7E,qCAAqC;AACrC,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,8EAA8E;AAC9E,gFAAgF;AAChF,kFAAkF;AAClF,mDAAmD;AAEnD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAS,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAErE,2EAA2E;AAC3E,iFAAiF;AACjF,+EAA+E;AAC/E,6EAA6E;AAC7E,4CAA4C;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS;IAC7C,YAAY;IACZ,2BAA2B;IAC3B,MAAM;IACN,YAAY;IACZ,gBAAgB;IAChB,cAAc;CACf,CAAC,CAAC;AAEH,MAAM,UAAU,2BAA2B,CAAC,GAAY;IACtD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,MAAM,GAAI,GAA4B,CAAC,MAAM,CAAC;IACpD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAI,GAAuC,CAAC,MAAM,EAAE,IAAI,CAAC;QACnE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,iFAAiF;AACjF,kEAAkE;AAClE,MAAM,UAAU,2BAA2B,CAAC,IAAa;IACvD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,QAAQ,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;AACrD,CAAC"}
|
package/dist/tool-result.js
CHANGED
|
@@ -4,13 +4,44 @@
|
|
|
4
4
|
// responses propagate as isError with a structured message. Successful
|
|
5
5
|
// responses include the Rails body verbatim (preserving the _meta
|
|
6
6
|
// block per A1 §6.7).
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Pull the RateLimit-Limit/Remaining/Reset triple out of a Rails response's
|
|
9
|
+
* headers. `fetch` lowercases header names, so we read the lowercase keys.
|
|
10
|
+
* Returns undefined when none are present — Rails omits them on non-Discovery
|
|
11
|
+
* tiers (no cue cap), so the field is absent rather than zero-filled there.
|
|
12
|
+
*/
|
|
13
|
+
export function extractRateLimit(headers) {
|
|
14
|
+
if (!headers)
|
|
15
|
+
return undefined;
|
|
16
|
+
const limit = headers["ratelimit-limit"];
|
|
17
|
+
const remaining = headers["ratelimit-remaining"];
|
|
18
|
+
const reset = headers["ratelimit-reset"];
|
|
19
|
+
if (limit == null && remaining == null && reset == null)
|
|
20
|
+
return undefined;
|
|
21
|
+
const rl = {};
|
|
22
|
+
if (limit != null)
|
|
23
|
+
rl.limit = Number(limit);
|
|
24
|
+
if (remaining != null)
|
|
25
|
+
rl.remaining = Number(remaining);
|
|
26
|
+
if (reset != null)
|
|
27
|
+
rl.reset = Number(reset);
|
|
28
|
+
return rl;
|
|
29
|
+
}
|
|
30
|
+
export function successResult(body, headers) {
|
|
31
|
+
// Merge an advisory `rate_limit` block into the payload when Rails sent the
|
|
32
|
+
// headers. Only when body is a plain object (every Signal 2xx envelope is) —
|
|
33
|
+
// otherwise pass it through untouched. When no headers are present the body is
|
|
34
|
+
// returned VERBATIM (identity preserved), matching the pre-A5-parity contract.
|
|
35
|
+
const rateLimit = extractRateLimit(headers);
|
|
36
|
+
const structured = rateLimit && body !== null && typeof body === "object" && !Array.isArray(body)
|
|
37
|
+
? { ...body, rate_limit: rateLimit }
|
|
38
|
+
: body;
|
|
8
39
|
return {
|
|
9
40
|
// The text block stays as the JSON serialization of structuredContent so
|
|
10
41
|
// hosts that don't support structuredContent still get the full payload
|
|
11
42
|
// (MCP backward-compat guidance).
|
|
12
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
13
|
-
structuredContent:
|
|
43
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
44
|
+
structuredContent: structured,
|
|
14
45
|
};
|
|
15
46
|
}
|
|
16
47
|
export function errorResult(message) {
|
package/dist/tool-result.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-result.js","sourceRoot":"","sources":["../src/tool-result.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,gEAAgE;AAChE,uEAAuE;AACvE,kEAAkE;AAClE,sBAAsB;
|
|
1
|
+
{"version":3,"file":"tool-result.js","sourceRoot":"","sources":["../src/tool-result.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,gEAAgE;AAChE,uEAAuE;AACvE,kEAAkE;AAClE,sBAAsB;AA6BtB;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAgC;IAEhC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzC,IAAI,KAAK,IAAI,IAAI,IAAI,SAAS,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAC1E,MAAM,EAAE,GAAc,EAAE,CAAC;IACzB,IAAI,KAAK,IAAI,IAAI;QAAE,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,SAAS,IAAI,IAAI;QAAE,EAAE,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,KAAK,IAAI,IAAI;QAAE,EAAE,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5C,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,IAAa,EACb,OAAgC;IAEhC,4EAA4E;IAC5E,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,UAAU,GACd,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAC5E,CAAC,CAAC,EAAE,GAAI,IAAgC,EAAE,UAAU,EAAE,SAAS,EAAE;QACjE,CAAC,CAAC,IAAI,CAAC;IACX,OAAO;QACL,yEAAyE;QACzE,wEAAwE;QACxE,kCAAkC;QAClC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACtE,iBAAiB,EAAE,UAAU;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC1C,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,IAA2B,EAC3B,OAAgB,EAChB,OAAgC;IAEhC,MAAM,OAAO,GAAG,IAAI,EAAE,KAAK,IAAI,eAAe,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,GAAG,SAAS,+JAA+J,CAC5K,CAAC;QACJ,KAAK,GAAG;YACN,IAAI,OAAO,KAAK,oBAAoB,EAAE,CAAC;gBACrC,OAAO,WAAW,CAChB,GAAG,SAAS,6CAA6C,IAAI,EAAE,QAAQ,IAAI,UAAU,iDAAiD,CACvI,CAAC;YACJ,CAAC;YACD,OAAO,WAAW,CAAC,GAAG,SAAS,cAAc,OAAO,EAAE,CAAC,CAAC;QAC1D,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,GAAG,SAAS,wDAAwD,CACrE,CAAC;QACJ,KAAK,GAAG;YACN,IAAI,OAAO,KAAK,mBAAmB,EAAE,CAAC;gBACpC,OAAO,WAAW,CAChB,GAAG,SAAS,8GAA8G,IAAI,EAAE,iBAAiB,4EAA4E,CAC9N,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,KAAK,0BAA0B,EAAE,CAAC;gBAC3C,OAAO,WAAW,CAChB,GAAG,SAAS,oFAAoF,IAAI,EAAE,iBAAiB,6DAA6D,CACrL,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;gBAC/B,OAAO,WAAW,CAAC,GAAG,SAAS,0CAA0C,CAAC,CAAC;YAC7E,CAAC;YACD,OAAO,WAAW,CAAC,GAAG,SAAS,aAAa,OAAO,EAAE,CAAC,CAAC;QACzD,KAAK,GAAG;YACN,IAAI,OAAO,KAAK,gCAAgC,EAAE,CAAC;gBACjD,OAAO,WAAW,CAChB,GAAG,SAAS,0CAA0C,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,yEAAyE,CACpK,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,KAAK,2BAA2B,EAAE,CAAC;gBAC5C,OAAO,WAAW,CAChB,GAAG,SAAS,gCAAgC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,YAAY,IAAI,EAAE,CAAC,yEAAyE,CAC9J,CAAC;YACJ,CAAC;YACD,OAAO,WAAW,CAChB,GAAG,SAAS,uBAAuB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,EAAE,CAC5F,CAAC;QACJ,KAAK,GAAG;YACN,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;gBAC7B,sEAAsE;gBACtE,uEAAuE;gBACvE,wEAAwE;gBACxE,0EAA0E;gBAC1E,MAAM,UAAU,GAAG,OAAO,EAAE,CAAC,aAAa,CAAC,CAAC;gBAC5C,MAAM,KAAK,GACT,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,aAAa,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/F,MAAM,OAAO,GAAG,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,yBAAyB,IAAI,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvF,MAAM,KAAK,GAAG,UAAU;oBACtB,CAAC,CAAC,iFAAiF,UAAU,IAAI;oBACjG,CAAC,CAAC,EAAE,CAAC;gBACP,OAAO,WAAW,CAChB,GAAG,SAAS,oCAAoC,KAAK,uGAAuG,OAAO,GAAG,KAAK,EAAE,CAC9K,CAAC;YACJ,CAAC;YACD,OAAO,WAAW,CAAC,GAAG,SAAS,sBAAsB,OAAO,EAAE,CAAC,CAAC;QAClE,KAAK,GAAG;YACN,IAAI,OAAO,KAAK,sBAAsB,EAAE,CAAC;gBACvC,OAAO,WAAW,CAChB,GAAG,SAAS,oDAAoD,CACjE,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,KAAK,qBAAqB,EAAE,CAAC;gBACtC,OAAO,WAAW,CAChB,GAAG,SAAS,gGAAgG,CAC7G,CAAC;YACJ,CAAC;YACD,OAAO,WAAW,CAAC,GAAG,SAAS,wBAAwB,OAAO,EAAE,CAAC,CAAC;QACpE;YACE,OAAO,WAAW,CAChB,GAAG,SAAS,qCAAqC,MAAM,MAAM,OAAO,EAAE,CACvE,CAAC;IACN,CAAC;AACH,CAAC"}
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
// cued message. Each subsequent cue requires submitting the prior
|
|
7
7
|
// message's feedback (the moat per A1 §3).
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// template_id accepts any LIVE template (SIGNAL_TEMPLATE_IDS, derived from the
|
|
10
|
+
// drift-guarded generated catalog — the single source shared with the
|
|
11
|
+
// signal://catalog resource). Clinical templates require audience_tags +
|
|
12
|
+
// patient_context the agent should pre-satisfy from the catalog resource (else a
|
|
13
|
+
// production create 422s). delivery_mode is `display` in v1; Deliver unlocks at
|
|
14
|
+
// Per-Episode tier after BAA execution.
|
|
12
15
|
//
|
|
13
16
|
// Scope required: stream:create.
|
|
14
17
|
// Spec: docs/spec_track_a_a4_mcp_server.md §6.3.
|
|
@@ -16,9 +19,16 @@ import { callRails, isErrorStatus } from "../rails-client.js";
|
|
|
16
19
|
import { currentContext } from "../context.js";
|
|
17
20
|
import { successResult, railsErrorToToolResult, } from "../tool-result.js";
|
|
18
21
|
import { createPatientStreamOutputSchema } from "../output-schemas.js";
|
|
22
|
+
import { SIGNAL_TEMPLATE_IDS } from "../generated/signal-catalog.js";
|
|
19
23
|
export const createPatientStreamTool = {
|
|
20
24
|
name: "create_patient_stream",
|
|
21
|
-
description: "Create a cued stream for
|
|
25
|
+
description: "Create a Signal cued stream for one patient and get the first cue back synchronously. " +
|
|
26
|
+
"Every cue is federally-sourced, audience-safety-checked, and sequenced on the patient's " +
|
|
27
|
+
"prior response — so your agent never fabricates health content. Choose a template_id from " +
|
|
28
|
+
"the signal://catalog resource and pre-satisfy its required audience_tags + patient_context " +
|
|
29
|
+
"(clinical templates 422 without them). Display mode renders in your app: no PHI, no BAA. " +
|
|
30
|
+
"Each later cue MUST submit the prior message's feedback — the engine refuses to advance " +
|
|
31
|
+
"without it (the sequencing moat).",
|
|
22
32
|
annotations: {
|
|
23
33
|
title: "Create patient stream",
|
|
24
34
|
readOnlyHint: false,
|
|
@@ -30,8 +40,13 @@ export const createPatientStreamTool = {
|
|
|
30
40
|
properties: {
|
|
31
41
|
template_id: {
|
|
32
42
|
type: "string",
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
// Single-sourced from the drift-guarded generated catalog (same source as
|
|
44
|
+
// the signal://catalog resource) — never hand-list a second copy here.
|
|
45
|
+
enum: [...SIGNAL_TEMPLATE_IDS],
|
|
46
|
+
description: "One of the live Signal templates. Read the signal://catalog resource for each " +
|
|
47
|
+
"template's required audience_tags + patient_context. On a production key a clinical " +
|
|
48
|
+
"template 422s if its required tags/context are missing — pre-satisfy them from the " +
|
|
49
|
+
"catalog. A free sandbox (Discovery) key always cues the curated sandbox corpus.",
|
|
35
50
|
},
|
|
36
51
|
attributes: {
|
|
37
52
|
type: "object",
|
|
@@ -52,6 +67,11 @@ export const createPatientStreamTool = {
|
|
|
52
67
|
type: "integer",
|
|
53
68
|
default: 14,
|
|
54
69
|
},
|
|
70
|
+
idempotency_key: {
|
|
71
|
+
type: "string",
|
|
72
|
+
format: "uuid",
|
|
73
|
+
description: "Optional. Stripe-style create idempotency: a retried create with the same key returns the ORIGINAL stream instead of minting (and billing) a second one. Forwarded as the Idempotency-Key request header. UUID v4 recommended.",
|
|
74
|
+
},
|
|
55
75
|
},
|
|
56
76
|
required: ["template_id"],
|
|
57
77
|
additionalProperties: true,
|
|
@@ -59,16 +79,24 @@ export const createPatientStreamTool = {
|
|
|
59
79
|
outputSchema: createPatientStreamOutputSchema,
|
|
60
80
|
async handler(args) {
|
|
61
81
|
const { bearer } = currentContext();
|
|
82
|
+
// idempotency_key is a REQUEST HEADER on the Rails side (read at
|
|
83
|
+
// streams_controller#create), not a body param — strip it from the body and
|
|
84
|
+
// forward it as Idempotency-Key. Everything else passes through verbatim.
|
|
85
|
+
const { idempotency_key, ...streamArgs } = args;
|
|
86
|
+
const headers = typeof idempotency_key === "string" && idempotency_key.length > 0
|
|
87
|
+
? { "Idempotency-Key": idempotency_key }
|
|
88
|
+
: undefined;
|
|
62
89
|
const response = await callRails({
|
|
63
90
|
method: "POST",
|
|
64
91
|
path: "/v1/signal/streams",
|
|
65
92
|
bearer,
|
|
66
|
-
body:
|
|
93
|
+
body: streamArgs,
|
|
94
|
+
headers,
|
|
67
95
|
});
|
|
68
96
|
if (isErrorStatus(response.status)) {
|
|
69
97
|
return railsErrorToToolResult(response.status, response.body, "create_patient_stream", response.headers);
|
|
70
98
|
}
|
|
71
|
-
return successResult(response.body);
|
|
99
|
+
return successResult(response.body, response.headers);
|
|
72
100
|
},
|
|
73
101
|
};
|
|
74
102
|
//# sourceMappingURL=create-patient-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-patient-stream.js","sourceRoot":"","sources":["../../src/tools/create-patient-stream.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,eAAe;AACf,4BAA4B;AAC5B,iEAAiE;AACjE,kEAAkE;AAClE,2CAA2C;AAC3C,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"create-patient-stream.js","sourceRoot":"","sources":["../../src/tools/create-patient-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,7 +16,12 @@ import { CUE_RESPONSE_ACTIONS } from "../cue-actions.js";
|
|
|
16
16
|
import { getNextCuedMessageOutputSchema } from "../output-schemas.js";
|
|
17
17
|
export const getNextCuedMessageTool = {
|
|
18
18
|
name: "get_next_cued_message",
|
|
19
|
-
description: "Submit the
|
|
19
|
+
description: "Submit the patient's response to the prior cue and get the next one — federally-sourced, " +
|
|
20
|
+
"audience-safety-checked, and re-sequenced on that feedback. REFUSES WITHOUT PRIOR-MESSAGE " +
|
|
21
|
+
"FEEDBACK (the moat): the engine will not advance a stream blind, returning 409 if you skip " +
|
|
22
|
+
"it. response_action is one of the accepted cue vocabulary (see the signal://catalog " +
|
|
23
|
+
"resource). Same idempotency_key + same feedback returns the cached cue; conflicting " +
|
|
24
|
+
"feedback under the same key returns 409.",
|
|
20
25
|
annotations: {
|
|
21
26
|
title: "Submit feedback, get next cued message",
|
|
22
27
|
readOnlyHint: false,
|
|
@@ -72,7 +77,7 @@ export const getNextCuedMessageTool = {
|
|
|
72
77
|
if (isErrorStatus(response.status)) {
|
|
73
78
|
return railsErrorToToolResult(response.status, response.body, "get_next_cued_message", response.headers);
|
|
74
79
|
}
|
|
75
|
-
return successResult(response.body);
|
|
80
|
+
return successResult(response.body, response.headers);
|
|
76
81
|
},
|
|
77
82
|
};
|
|
78
83
|
//# sourceMappingURL=get-next-cued-message.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-next-cued-message.js","sourceRoot":"","sources":["../../src/tools/get-next-cued-message.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,eAAe;AACf,oCAAoC;AACpC,kEAAkE;AAClE,uEAAuE;AACvE,qEAAqE;AACrE,kEAAkE;AAClE,EAAE;AACF,8BAA8B;AAC9B,qEAAqE;AAErE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAuB,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,WAAW,GAEZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AAEtE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,
|
|
1
|
+
{"version":3,"file":"get-next-cued-message.js","sourceRoot":"","sources":["../../src/tools/get-next-cued-message.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// Track A A4 PR 2 — MCP tool registry.
|
|
2
|
-
//
|
|
2
|
+
// The 4 keyed v1 tools per docs/spec_track_a_a4_mcp_server.md §6.3, plus the
|
|
3
|
+
// Rung-0 no-auth lookup_health_fact (2026-06-15) — the one keyless tool (the
|
|
4
|
+
// per-tool guard relax lives in index.ts; the others still 401 without a key).
|
|
3
5
|
import { createPatientStreamTool } from "./create-patient-stream.js";
|
|
4
6
|
import { getNextCuedMessageTool } from "./get-next-cued-message.js";
|
|
5
7
|
import { readStreamStateTool } from "./read-stream-state.js";
|
|
6
8
|
import { listSandboxTopicsTool } from "./list-sandbox-topics.js";
|
|
9
|
+
import { lookupHealthFactTool } from "./lookup-health-fact.js";
|
|
7
10
|
export const tools = [
|
|
11
|
+
lookupHealthFactTool,
|
|
8
12
|
createPatientStreamTool,
|
|
9
13
|
getNextCuedMessageTool,
|
|
10
14
|
readStreamStateTool,
|