@chrischall/myatriumhealth-mcp 1.3.3 → 1.3.5
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +4 -2
- package/dist/bundle.js +409 -76
- package/package.json +5 -5
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "MCP server for MyAtriumHealth (Atrium Health's Epic MyChart patient portal) — test results, medications, allergies, immunizations, health issues, visits and messages. Requests relay through the user's own signed-in browser tab via the ContextMint Bridge extension, reusing their existing session.",
|
|
10
|
-
"version": "1.3.
|
|
10
|
+
"version": "1.3.5"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "MyAtriumHealth",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Read MyAtriumHealth (Epic MyChart) records — test results, medications, allergies, immunizations, health issues, visits — by relaying requests through the user's signed-in browser tab via the ContextMint Bridge extension.",
|
|
18
|
-
"version": "1.3.
|
|
18
|
+
"version": "1.3.5",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myatriumhealth-mcp",
|
|
3
3
|
"displayName": "MyAtriumHealth",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.5",
|
|
5
5
|
"description": "MyAtriumHealth (Epic MyChart) access for Claude — test results, medications, allergies, immunizations, visits",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/README.md
CHANGED
|
@@ -125,7 +125,8 @@ first call prints a pair code — approve it once in the ContextMint Bridge popu
|
|
|
125
125
|
| `MAH_DEVICE_FILE` | `~/.myatriumhealth-mcp/device.json` | Session state (0600). Holds the **live cookie jar** as well as the device token — treat as a credential. |
|
|
126
126
|
| `MAH_WS_PORT` | `37149` | fetchproxy concentrator port (bridge mode only). The whole fleet shares this one port; override only when hosting. |
|
|
127
127
|
| `MAH_READ_ONLY` | `false` | `true` refuses `mah_reply_message` sends (previews still work). The tool stays listed either way. |
|
|
128
|
-
| `MCP_CONFIRM_MODE` | `ask-user` | What a reply does on a client that cannot show a confirmation prompt (claude.ai, Claude Desktop). `ask-user`: two steps — the first call sends nothing and returns a preview plus a token, and the model must get your approval in chat before calling again with it. `auto`: the same two steps, but the model may use the token after reviewing the preview itself. `refuse`: replies are refused on such clients. A client that can show prompts (Claude Code) always gets the real prompt
|
|
128
|
+
| `MCP_CONFIRM_MODE` | `ask-user` | What a reply does on a client that cannot show a confirmation prompt (claude.ai, Claude Desktop). `ask-user`: two steps — the first call sends nothing and returns a preview plus a token, and the model must get your approval in chat before calling again with it. `auto`: the same two steps, but the model may use the token after reviewing the preview itself. `refuse`: replies are refused on such clients. A client that can show prompts (Claude Code) always gets the real prompt, unless `MCP_CONFIRM_ELICITATION=off`. An unrecognised value is treated as `refuse`. |
|
|
129
|
+
| `MCP_CONFIRM_ELICITATION` | `on` | `off` never shows a confirmation prompt, so every client gets the `MCP_CONFIRM_MODE` path. Set it for a client that claims to support prompts but never shows one (the reply hangs — opencode 2.0.x). Any other value stays `on`, with a warning on stderr. |
|
|
129
130
|
| `MCP_CONFIRM_TTL_SECONDS` | `600` | How long a token stays valid. |
|
|
130
131
|
| `MCP_CONFIRM_SECRET` | random per process | Signing key; set it only if tokens must survive a server restart. |
|
|
131
132
|
|
|
@@ -213,7 +214,8 @@ local organization returns HTTP 500. The client assembles this from
|
|
|
213
214
|
`body`, and optionally `attachments`. It replies **as whichever patient is active**.
|
|
214
215
|
|
|
215
216
|
- **It asks before it sends.** A client that can show a confirmation prompt (Claude
|
|
216
|
-
Code) gets one with the thread, recipients, body and attachments
|
|
217
|
+
Code) gets one with the thread, recipients, body and attachments, unless
|
|
218
|
+
`MCP_CONFIRM_ELICITATION=off`. Elsewhere the first
|
|
217
219
|
call sends nothing and returns that preview plus a `confirmToken`; only a repeat call
|
|
218
220
|
with the same arguments and that token sends. `MCP_CONFIRM_MODE` (above) decides
|
|
219
221
|
whether the model must get your approval in chat before using it (`ask-user`, the
|
package/dist/bundle.js
CHANGED
|
@@ -20693,8 +20693,8 @@ var require_schemas2 = __commonJS({
|
|
|
20693
20693
|
return core._stringFormat(exports.ZodCustomStringFormat, "currency_code", regexes.currencyCode, _params);
|
|
20694
20694
|
}
|
|
20695
20695
|
function hash2(alg, params) {
|
|
20696
|
-
const
|
|
20697
|
-
const format = `${alg}_${
|
|
20696
|
+
const enc7 = params?.enc ?? "hex";
|
|
20697
|
+
const format = `${alg}_${enc7}`;
|
|
20698
20698
|
const regex = core.regexes[format];
|
|
20699
20699
|
if (!regex)
|
|
20700
20700
|
throw new Error(`Unrecognized hash format: ${format}`);
|
|
@@ -25993,11 +25993,23 @@ var require_websocket = __commonJS({
|
|
|
25993
25993
|
this._isServer = false;
|
|
25994
25994
|
this._redirects = 0;
|
|
25995
25995
|
if (protocols === void 0) {
|
|
25996
|
-
protocols
|
|
25996
|
+
if (!options || options.protocols === void 0) {
|
|
25997
|
+
protocols = [];
|
|
25998
|
+
} else if (Array.isArray(options.protocols)) {
|
|
25999
|
+
protocols = options.protocols;
|
|
26000
|
+
} else {
|
|
26001
|
+
protocols = [options.protocols];
|
|
26002
|
+
}
|
|
25997
26003
|
} else if (!Array.isArray(protocols)) {
|
|
25998
26004
|
if (typeof protocols === "object" && protocols !== null) {
|
|
25999
26005
|
options = protocols;
|
|
26000
|
-
protocols
|
|
26006
|
+
if (options.protocols === void 0) {
|
|
26007
|
+
protocols = [];
|
|
26008
|
+
} else if (Array.isArray(options.protocols)) {
|
|
26009
|
+
protocols = options.protocols;
|
|
26010
|
+
} else {
|
|
26011
|
+
protocols = [options.protocols];
|
|
26012
|
+
}
|
|
26001
26013
|
} else {
|
|
26002
26014
|
protocols = [protocols];
|
|
26003
26015
|
}
|
|
@@ -26194,7 +26206,6 @@ var require_websocket = __commonJS({
|
|
|
26194
26206
|
}
|
|
26195
26207
|
return;
|
|
26196
26208
|
}
|
|
26197
|
-
this._readyState = _WebSocket.CLOSING;
|
|
26198
26209
|
this._sender.close(code2, data, !this._isServer, (err) => {
|
|
26199
26210
|
if (err) return;
|
|
26200
26211
|
this._closeFrameSent = true;
|
|
@@ -26202,6 +26213,7 @@ var require_websocket = __commonJS({
|
|
|
26202
26213
|
this._socket.end();
|
|
26203
26214
|
}
|
|
26204
26215
|
});
|
|
26216
|
+
this._readyState = _WebSocket.CLOSING;
|
|
26205
26217
|
setCloseTimer(this);
|
|
26206
26218
|
}
|
|
26207
26219
|
/**
|
|
@@ -26426,6 +26438,7 @@ var require_websocket = __commonJS({
|
|
|
26426
26438
|
socketPath: void 0,
|
|
26427
26439
|
hostname: void 0,
|
|
26428
26440
|
protocol: void 0,
|
|
26441
|
+
protocols: void 0,
|
|
26429
26442
|
timeout: void 0,
|
|
26430
26443
|
method: "GET",
|
|
26431
26444
|
host: void 0,
|
|
@@ -28408,7 +28421,7 @@ var OAuthTokenRevocationRequestSchema = z.object({
|
|
|
28408
28421
|
token_type_hint: z.string().optional()
|
|
28409
28422
|
}).strip();
|
|
28410
28423
|
|
|
28411
|
-
// node_modules/@modelcontextprotocol/server/dist/src-
|
|
28424
|
+
// node_modules/@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs
|
|
28412
28425
|
var z2 = __toESM(require_v4(), 1);
|
|
28413
28426
|
var BRANDS = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
|
|
28414
28427
|
function stampErrorBrands(instance, ctor) {
|
|
@@ -30652,7 +30665,8 @@ function build() {
|
|
|
30652
30665
|
ListResourceTemplatesResultResponseSchema: wireResultResponse(ListResourceTemplatesResultSchema$1),
|
|
30653
30666
|
ReadResourceResultResponseSchema: wireResultResponse(z2.union([ReadResourceResultSchema$1, InputRequiredResultSchema])),
|
|
30654
30667
|
CompleteResultResponseSchema: wireResultResponse(CompleteResultSchema$1),
|
|
30655
|
-
DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1)
|
|
30668
|
+
DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1),
|
|
30669
|
+
SubscriptionsListenResultResponseSchema: wireResultResponse(SubscriptionsListenResultSchema$1)
|
|
30656
30670
|
};
|
|
30657
30671
|
}
|
|
30658
30672
|
var memo;
|
|
@@ -31160,6 +31174,9 @@ function isSpecRequestMethod(method) {
|
|
|
31160
31174
|
function isSpecNotificationMethod(method) {
|
|
31161
31175
|
return ALL_CODECS.some((codec2) => codec2.hasNotificationMethod(method));
|
|
31162
31176
|
}
|
|
31177
|
+
function isExtensionReusedRequestMethod(method) {
|
|
31178
|
+
return method === "tasks/get" || method === "tasks/cancel";
|
|
31179
|
+
}
|
|
31163
31180
|
var ALL_CODECS = [rev2025Codec, rev2026Codec];
|
|
31164
31181
|
function isPlainObject$3(value) {
|
|
31165
31182
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -32168,6 +32185,8 @@ var Protocol = class {
|
|
|
32168
32185
|
_transport;
|
|
32169
32186
|
_requestMessageId = 0;
|
|
32170
32187
|
_requestHandlers = /* @__PURE__ */ new Map();
|
|
32188
|
+
/** Methods registered with an explicit schema; the era gate in `_onrequest` reads it for the Tasks extension names. */
|
|
32189
|
+
_customSchemaRequestMethods = /* @__PURE__ */ new Set();
|
|
32171
32190
|
_requestHandlerAbortControllers = /* @__PURE__ */ new Map();
|
|
32172
32191
|
_notificationHandlers = /* @__PURE__ */ new Map();
|
|
32173
32192
|
_responseHandlers = /* @__PURE__ */ new Map();
|
|
@@ -32451,7 +32470,7 @@ var Protocol = class {
|
|
|
32451
32470
|
return;
|
|
32452
32471
|
}
|
|
32453
32472
|
}
|
|
32454
|
-
if (isSpecRequestMethod(request.method) && !codec2.hasRequestMethod(request.method)) {
|
|
32473
|
+
if (isSpecRequestMethod(request.method) && !codec2.hasRequestMethod(request.method) && !(isExtensionReusedRequestMethod(request.method) && this._customSchemaRequestMethods.has(request.method))) {
|
|
32455
32474
|
sendErrorResponse(ProtocolErrorCode.MethodNotFound, "Method not found");
|
|
32456
32475
|
return;
|
|
32457
32476
|
}
|
|
@@ -32584,8 +32603,11 @@ var Protocol = class {
|
|
|
32584
32603
|
}
|
|
32585
32604
|
request(request, schemaOrOptions, maybeOptions) {
|
|
32586
32605
|
const codec2 = this._resolveOutboundCodec(request.method);
|
|
32606
|
+
if (isStandardSchema(schemaOrOptions)) {
|
|
32607
|
+
if (!isExtensionReusedRequestMethod(request.method)) this._assertOutboundRequestInEra(codec2, request.method);
|
|
32608
|
+
return this._requestWithSchemaViaCodec(codec2, request, schemaOrOptions, maybeOptions);
|
|
32609
|
+
}
|
|
32587
32610
|
this._assertOutboundRequestInEra(codec2, request.method);
|
|
32588
|
-
if (isStandardSchema(schemaOrOptions)) return this._requestWithSchemaViaCodec(codec2, request, schemaOrOptions, maybeOptions);
|
|
32589
32611
|
const validate2 = codecResultValidator(codec2, request.method);
|
|
32590
32612
|
if (validate2 === void 0) throw new TypeError(`'${request.method}' is not a spec method; pass a result schema as the second argument to request().`);
|
|
32591
32613
|
return this._requestWithSchemaViaCodec(codec2, request, validate2, schemaOrOptions);
|
|
@@ -32829,6 +32851,8 @@ var Protocol = class {
|
|
|
32829
32851
|
return maybeHandler(parsed.data, ctx);
|
|
32830
32852
|
};
|
|
32831
32853
|
else throw new TypeError("setRequestHandler: handler is required");
|
|
32854
|
+
if (typeof schemasOrHandler === "function") this._customSchemaRequestMethods.delete(method);
|
|
32855
|
+
else this._customSchemaRequestMethods.add(method);
|
|
32832
32856
|
this._requestHandlers.set(method, this._wrapHandler(method, stored));
|
|
32833
32857
|
}
|
|
32834
32858
|
/**
|
|
@@ -32858,6 +32882,7 @@ var Protocol = class {
|
|
|
32858
32882
|
*/
|
|
32859
32883
|
removeRequestHandler(method) {
|
|
32860
32884
|
this._requestHandlers.delete(method);
|
|
32885
|
+
this._customSchemaRequestMethods.delete(method);
|
|
32861
32886
|
}
|
|
32862
32887
|
/**
|
|
32863
32888
|
* Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
|
|
@@ -40100,7 +40125,7 @@ var Ajv = import_ajv.Ajv;
|
|
|
40100
40125
|
// node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
|
|
40101
40126
|
import process2 from "node:process";
|
|
40102
40127
|
|
|
40103
|
-
// node_modules/@modelcontextprotocol/server/dist/mcp-
|
|
40128
|
+
// node_modules/@modelcontextprotocol/server/dist/mcp-DIH4cS6P.mjs
|
|
40104
40129
|
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
|
40105
40130
|
function isCompletable(schema) {
|
|
40106
40131
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
@@ -40165,7 +40190,7 @@ var StdioListenRouter = class {
|
|
|
40165
40190
|
_serverCapabilities;
|
|
40166
40191
|
/**
|
|
40167
40192
|
* The serving instance's identity, stamped onto the graceful-close
|
|
40168
|
-
* results' `_meta` (the spec's `
|
|
40193
|
+
* results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
|
|
40169
40194
|
* `ResultMetaObject`). Handed over together with the capabilities.
|
|
40170
40195
|
*/
|
|
40171
40196
|
_serverInfo;
|
|
@@ -40497,6 +40522,11 @@ var Server = class extends Protocol {
|
|
|
40497
40522
|
if (modernProtocolVersions(this._supportedProtocolVersions).length > 0) this.setRequestHandler("server/discover", () => this._ondiscover());
|
|
40498
40523
|
if (this._capabilities.logging) this._registerLoggingHandler();
|
|
40499
40524
|
}
|
|
40525
|
+
/** Attaches to the given transport; rejects while this instance is connected to another one. */
|
|
40526
|
+
async connect(transport2) {
|
|
40527
|
+
if (this.transport !== void 0) throw new SdkError(SdkErrorCode.AlreadyConnected, "Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.");
|
|
40528
|
+
await super.connect(transport2);
|
|
40529
|
+
}
|
|
40500
40530
|
/**
|
|
40501
40531
|
* Registers the built-in `logging/setLevel` request handler.
|
|
40502
40532
|
*
|
|
@@ -41050,21 +41080,41 @@ var Server = class extends Protocol {
|
|
|
41050
41080
|
function discoverAdvertisedCapabilities(capabilities) {
|
|
41051
41081
|
return { ...capabilities };
|
|
41052
41082
|
}
|
|
41083
|
+
function toolInputElementCount(value, max) {
|
|
41084
|
+
let count = 0;
|
|
41085
|
+
const stack = [value];
|
|
41086
|
+
while (stack.length > 0) {
|
|
41087
|
+
const node2 = stack.pop();
|
|
41088
|
+
if (node2 === null || typeof node2 !== "object") continue;
|
|
41089
|
+
if (Array.isArray(node2)) for (const child of node2) {
|
|
41090
|
+
if (++count > max) return count;
|
|
41091
|
+
if (child !== null && typeof child === "object") stack.push(child);
|
|
41092
|
+
}
|
|
41093
|
+
else for (const key in node2) {
|
|
41094
|
+
if (!Object.prototype.hasOwnProperty.call(node2, key)) continue;
|
|
41095
|
+
if (++count > max) return count;
|
|
41096
|
+
const child = node2[key];
|
|
41097
|
+
if (child !== null && typeof child === "object") stack.push(child);
|
|
41098
|
+
}
|
|
41099
|
+
}
|
|
41100
|
+
return count;
|
|
41101
|
+
}
|
|
41102
|
+
function resolveMaxToolInputElements(value) {
|
|
41103
|
+
if (value === void 0 || value === Infinity) return void 0;
|
|
41104
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 1) throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
|
|
41105
|
+
return value;
|
|
41106
|
+
}
|
|
41053
41107
|
var McpServer = class {
|
|
41054
41108
|
/**
|
|
41055
41109
|
* The underlying {@linkcode Server} instance, useful for advanced operations like sending notifications.
|
|
41056
41110
|
*/
|
|
41057
41111
|
server;
|
|
41112
|
+
_maxToolInputElements;
|
|
41058
41113
|
_registeredResources = {};
|
|
41059
41114
|
_registeredResourceTemplates = {};
|
|
41060
41115
|
_registeredTools = {};
|
|
41061
41116
|
_registeredPrompts = {};
|
|
41062
|
-
/**
|
|
41063
|
-
* Per-tool JSON-converted `inputSchema`, memoized so the SEP-2243
|
|
41064
|
-
* registration-time scan and the pre-dispatch validation step share one
|
|
41065
|
-
* conversion instead of paying it twice per request under the
|
|
41066
|
-
* per-request-factory `createMcpHandler` model.
|
|
41067
|
-
*/
|
|
41117
|
+
/** Per-tool JSON-converted `inputSchema`, filled on first use by `toolInputSchemaJson()`. */
|
|
41068
41118
|
_toolInputSchemaJson = {};
|
|
41069
41119
|
/**
|
|
41070
41120
|
* The JSON-serialized `inputSchema` of a registered tool, or `undefined`
|
|
@@ -41089,6 +41139,7 @@ var McpServer = class {
|
|
|
41089
41139
|
}
|
|
41090
41140
|
constructor(serverInfo, options) {
|
|
41091
41141
|
this.server = new Server(serverInfo, options);
|
|
41142
|
+
this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
|
|
41092
41143
|
if (options?.capabilities?.tools) this.setToolRequestHandlers();
|
|
41093
41144
|
if (options?.capabilities?.resources) this.setResourceRequestHandlers();
|
|
41094
41145
|
if (options?.capabilities?.prompts) this.setPromptRequestHandlers();
|
|
@@ -41161,7 +41212,7 @@ var McpServer = class {
|
|
|
41161
41212
|
name,
|
|
41162
41213
|
title: tool.title,
|
|
41163
41214
|
description: tool.description,
|
|
41164
|
-
inputSchema: tool.inputSchema ?
|
|
41215
|
+
inputSchema: tool.inputSchema ? convertListedInputSchema(name, tool.inputSchema) : EMPTY_OBJECT_JSON_SCHEMA,
|
|
41165
41216
|
annotations: tool.annotations,
|
|
41166
41217
|
icons: tool.icons,
|
|
41167
41218
|
execution: tool.execution,
|
|
@@ -41206,6 +41257,7 @@ var McpServer = class {
|
|
|
41206
41257
|
* Validates tool input arguments against the tool's input schema.
|
|
41207
41258
|
*/
|
|
41208
41259
|
async validateToolInput(tool, args, toolName) {
|
|
41260
|
+
if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
|
|
41209
41261
|
if (!tool.inputSchema) return;
|
|
41210
41262
|
const parseResult = await validateStandardSchema(tool.inputSchema, args ?? {});
|
|
41211
41263
|
if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${parseResult.error}`);
|
|
@@ -41480,20 +41532,19 @@ var McpServer = class {
|
|
|
41480
41532
|
}
|
|
41481
41533
|
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, scopeChallenge, _meta, handler) {
|
|
41482
41534
|
validateAndWarnToolName(name);
|
|
41483
|
-
if (inputSchema !== void 0) try {
|
|
41484
|
-
const json2 = standardSchemaToJsonSchema(inputSchema, "input");
|
|
41485
|
-
this._toolInputSchemaJson[name] = json2;
|
|
41486
|
-
const scan = scanXMcpHeaderDeclarations(json2);
|
|
41487
|
-
if (!scan.valid) console.warn(`[mcp-sdk] tool '${name}' carries an invalid x-mcp-header declaration and will be excluded by conforming Streamable HTTP clients: ${scan.reason}`);
|
|
41488
|
-
} catch {
|
|
41489
|
-
}
|
|
41490
41535
|
let currentHandler = handler;
|
|
41536
|
+
let outputSchemaJson;
|
|
41491
41537
|
const registeredTool = {
|
|
41492
41538
|
title,
|
|
41493
41539
|
description,
|
|
41494
41540
|
inputSchema,
|
|
41495
41541
|
outputSchema,
|
|
41496
|
-
outputSchemaJson
|
|
41542
|
+
get outputSchemaJson() {
|
|
41543
|
+
return outputSchemaJson ??= convertOutputSchemaJson(registeredTool.outputSchema);
|
|
41544
|
+
},
|
|
41545
|
+
set outputSchemaJson(value) {
|
|
41546
|
+
outputSchemaJson = value;
|
|
41547
|
+
},
|
|
41497
41548
|
annotations,
|
|
41498
41549
|
icons,
|
|
41499
41550
|
execution,
|
|
@@ -41620,6 +41671,12 @@ var EMPTY_OBJECT_JSON_SCHEMA = {
|
|
|
41620
41671
|
type: "object",
|
|
41621
41672
|
properties: {}
|
|
41622
41673
|
};
|
|
41674
|
+
function convertListedInputSchema(name, inputSchema) {
|
|
41675
|
+
const json2 = standardSchemaToJsonSchema(inputSchema, "input");
|
|
41676
|
+
const scan = scanXMcpHeaderDeclarations(json2);
|
|
41677
|
+
if (!scan.valid) console.warn(`[mcp-sdk] tool '${name}' carries an invalid x-mcp-header declaration and will be excluded by conforming Streamable HTTP clients: ${scan.reason}`);
|
|
41678
|
+
return json2;
|
|
41679
|
+
}
|
|
41623
41680
|
function convertOutputSchemaJson(outputSchema) {
|
|
41624
41681
|
if (outputSchema === void 0) return void 0;
|
|
41625
41682
|
try {
|
|
@@ -41632,7 +41689,7 @@ function createPromptHandler(name, argsSchema, callback) {
|
|
|
41632
41689
|
if (argsSchema) {
|
|
41633
41690
|
const typedCallback = callback;
|
|
41634
41691
|
return async (args, ctx) => {
|
|
41635
|
-
const parseResult = await validateStandardSchema(argsSchema, args);
|
|
41692
|
+
const parseResult = await validateStandardSchema(argsSchema, args ?? {});
|
|
41636
41693
|
if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${parseResult.error}`);
|
|
41637
41694
|
return typedCallback(parseResult.data, ctx);
|
|
41638
41695
|
};
|
|
@@ -59989,8 +60046,8 @@ function currencyCode2(_params) {
|
|
|
59989
60046
|
return _stringFormat(ZodCustomStringFormat, "currency_code", currencyCode, _params);
|
|
59990
60047
|
}
|
|
59991
60048
|
function hash(alg, params) {
|
|
59992
|
-
const
|
|
59993
|
-
const format = `${alg}_${
|
|
60049
|
+
const enc7 = params?.enc ?? "hex";
|
|
60050
|
+
const format = `${alg}_${enc7}`;
|
|
59994
60051
|
const regex = regexes_exports[format];
|
|
59995
60052
|
if (!regex)
|
|
59996
60053
|
throw new Error(`Unrecognized hash format: ${format}`);
|
|
@@ -62065,6 +62122,7 @@ function escapeKey(key) {
|
|
|
62065
62122
|
var DEFAULT_REQUEST_KEY = "confirmation";
|
|
62066
62123
|
var DEFAULT_CONFIRMATION_LABEL = "Confirm this action should proceed.";
|
|
62067
62124
|
var UNSUPPORTED_NOTE = "Nothing was done because this client cannot show a confirmation prompt (it declares no MCP elicitation capability), and this action is never taken without one";
|
|
62125
|
+
var DISABLED_NOTE = "Nothing was done because confirmation prompts are turned off for this server, and this action is never taken without one";
|
|
62068
62126
|
var STATE_PREFIX = "mcpu.confirm.v1.";
|
|
62069
62127
|
function bindingKey(binding) {
|
|
62070
62128
|
if (binding.ttlSeconds !== void 0 && !(Number.isFinite(binding.ttlSeconds) && binding.ttlSeconds > 0)) {
|
|
@@ -62137,6 +62195,15 @@ ${JSON.stringify(preview, null, 2)}`,
|
|
|
62137
62195
|
};
|
|
62138
62196
|
const response = inputResponse(ctx.mcpReq.inputResponses, requestKey);
|
|
62139
62197
|
const accepted = acceptedContent(ctx.mcpReq.inputResponses, requestKey, confirmationSchema);
|
|
62198
|
+
if (options.elicitation === false) {
|
|
62199
|
+
return textResult({
|
|
62200
|
+
confirmed: false,
|
|
62201
|
+
dispatched: false,
|
|
62202
|
+
action: options.action,
|
|
62203
|
+
reason: "confirmation-unsupported",
|
|
62204
|
+
note: options.unsupportedNote ? `${DISABLED_NOTE}. ${options.unsupportedNote}` : `${DISABLED_NOTE}.`
|
|
62205
|
+
});
|
|
62206
|
+
}
|
|
62140
62207
|
if (response.kind === "missing") {
|
|
62141
62208
|
if (callerAcceptsFormElicitation(ctx) === false) {
|
|
62142
62209
|
return textResult({
|
|
@@ -62331,7 +62398,7 @@ async function tokenConfirmation(action, fb) {
|
|
|
62331
62398
|
}
|
|
62332
62399
|
async function requireConfirmationWithFallback(ctx, options) {
|
|
62333
62400
|
const { tokenFallback, ...confirmation } = options;
|
|
62334
|
-
if (tokenFallback && callerAcceptsFormElicitation(ctx) === false) {
|
|
62401
|
+
if (tokenFallback && (options.elicitation === false || callerAcceptsFormElicitation(ctx) === false)) {
|
|
62335
62402
|
return tokenConfirmation(options.action, tokenFallback);
|
|
62336
62403
|
}
|
|
62337
62404
|
return requireConfirmation(ctx, confirmation);
|
|
@@ -62490,6 +62557,20 @@ function readConfirmMode(env = process.env) {
|
|
|
62490
62557
|
}
|
|
62491
62558
|
return "refuse";
|
|
62492
62559
|
}
|
|
62560
|
+
function readConfirmElicitation(env = process.env) {
|
|
62561
|
+
const raw = readEnvVar("MCP_CONFIRM_ELICITATION", { env })?.trim().toLowerCase();
|
|
62562
|
+
if (!raw || raw === "on")
|
|
62563
|
+
return "on";
|
|
62564
|
+
if (raw === "off")
|
|
62565
|
+
return "off";
|
|
62566
|
+
const key = `elicitation:${raw}`;
|
|
62567
|
+
if (!warned.has(key)) {
|
|
62568
|
+
warned.add(key);
|
|
62569
|
+
process.stderr.write(`MCP_CONFIRM_ELICITATION="${raw}" is not one of on, off; treating it as on.
|
|
62570
|
+
`);
|
|
62571
|
+
}
|
|
62572
|
+
return "on";
|
|
62573
|
+
}
|
|
62493
62574
|
function readConfirmTtl(env) {
|
|
62494
62575
|
const raw = readEnvVar("MCP_CONFIRM_TTL_SECONDS", { env })?.trim();
|
|
62495
62576
|
if (!raw)
|
|
@@ -62534,6 +62615,7 @@ function spentTokenStoreFromEnv(env = process.env) {
|
|
|
62534
62615
|
}
|
|
62535
62616
|
var CONFIRM_TOKEN_AUTO_INSTRUCTION = "Nothing has been done yet. Review this preview; if it is what was intended, call again with the same arguments plus confirmToken. (This server runs with MCP_CONFIRM_MODE=auto, so the user's approval in chat is not required.)";
|
|
62536
62617
|
var REFUSE_HINT = "Set MCP_CONFIRM_MODE=ask-user on the server to allow two-step confirmation instead.";
|
|
62618
|
+
var ELICITATION_OFF_HINT = "MCP_CONFIRM_ELICITATION=off on the server turns confirmation prompts off.";
|
|
62537
62619
|
var BAD_TTL_HINT = "MCP_CONFIRM_TTL_SECONDS on the server is not a positive whole number of seconds; fix it to allow two-step confirmation.";
|
|
62538
62620
|
function boundArgs(args) {
|
|
62539
62621
|
if (args === null || typeof args !== "object" || Array.isArray(args) || !("confirmToken" in args))
|
|
@@ -62547,10 +62629,13 @@ function confirmationFromEnv(options) {
|
|
|
62547
62629
|
const spent = callerSpent ?? spentTokenStoreFromEnv(env);
|
|
62548
62630
|
const mode = readConfirmMode(env);
|
|
62549
62631
|
const ttlSeconds = readConfirmTtl(env);
|
|
62632
|
+
const elicitationOff = readConfirmElicitation(env) === "off";
|
|
62550
62633
|
const bound = args === void 0 ? void 0 : boundArgs(args);
|
|
62551
|
-
const
|
|
62634
|
+
const bindingAdded = bound === void 0 || rest.binding ? rest : { ...rest, binding: { key: confirmKeyFromEnv(env), args: bound, ttlSeconds: ttlSeconds ?? DEFAULT_TTL_SECONDS2 } };
|
|
62635
|
+
const confirmation = elicitationOff ? { ...bindingAdded, elicitation: false } : bindingAdded;
|
|
62552
62636
|
if (mode === "refuse" || ttlSeconds === void 0) {
|
|
62553
|
-
const
|
|
62637
|
+
const reason = mode === "refuse" ? REFUSE_HINT : BAD_TTL_HINT;
|
|
62638
|
+
const hint = elicitationOff ? `${ELICITATION_OFF_HINT} ${reason}` : reason;
|
|
62554
62639
|
return {
|
|
62555
62640
|
...confirmation,
|
|
62556
62641
|
unsupportedNote: confirmation.unsupportedNote ? `${confirmation.unsupportedNote} ${hint}` : hint
|
|
@@ -62659,7 +62744,10 @@ function surfaceToolHints(server) {
|
|
|
62659
62744
|
));
|
|
62660
62745
|
}
|
|
62661
62746
|
async function createMcpServer(opts) {
|
|
62662
|
-
const server = new McpServer({ name: opts.name, version: opts.version }, {
|
|
62747
|
+
const server = new McpServer({ name: opts.name, version: opts.version }, {
|
|
62748
|
+
supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS],
|
|
62749
|
+
...opts.maxToolInputElements !== void 0 ? { maxToolInputElements: opts.maxToolInputElements } : {}
|
|
62750
|
+
});
|
|
62663
62751
|
if (opts.surfaceHints !== false)
|
|
62664
62752
|
surfaceToolHints(server);
|
|
62665
62753
|
if (opts.banner !== void 0) {
|
|
@@ -64482,6 +64570,60 @@ var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
64482
64570
|
"graphql",
|
|
64483
64571
|
"write_cookies"
|
|
64484
64572
|
]);
|
|
64573
|
+
var ACCOUNT_KEY_FRAME = "account-key";
|
|
64574
|
+
var ACCOUNT_ATTEST_FRAME = "account-attest";
|
|
64575
|
+
var BRIDGE_ROLE_FRAME = "bridge-role";
|
|
64576
|
+
var BRIDGE_SERVE_FRAME = "bridge-serve";
|
|
64577
|
+
var ROOM_PING_FRAME = "room-ping";
|
|
64578
|
+
var ROOM_PONG_FRAME = "room-pong";
|
|
64579
|
+
var BRIDGE_ROLE_LABEL_MAX = 64;
|
|
64580
|
+
var ROOM_FRAME_ACCEPTS = Object.freeze({
|
|
64581
|
+
[BRIDGE_ROLE_FRAME]: BRIDGE_ROLE_FRAME,
|
|
64582
|
+
[BRIDGE_SERVE_FRAME]: BRIDGE_SERVE_FRAME,
|
|
64583
|
+
[ROOM_PING_FRAME]: ROOM_PING_FRAME,
|
|
64584
|
+
[ROOM_PONG_FRAME]: ROOM_PING_FRAME
|
|
64585
|
+
});
|
|
64586
|
+
|
|
64587
|
+
// node_modules/@fetchproxy/protocol/dist/mcp-id.js
|
|
64588
|
+
var ID_RE = /^([^:]+):([^:]+):([0-9a-f]{16})$/;
|
|
64589
|
+
function generateMcpId(serverName, version2) {
|
|
64590
|
+
if (serverName.includes(":")) {
|
|
64591
|
+
throw new Error(`serverName cannot contain ':': ${serverName}`);
|
|
64592
|
+
}
|
|
64593
|
+
if (version2.includes(":")) {
|
|
64594
|
+
throw new Error(`version cannot contain ':': ${version2}`);
|
|
64595
|
+
}
|
|
64596
|
+
const rand = randomHex(8);
|
|
64597
|
+
return `${serverName}:${version2}:${rand}`;
|
|
64598
|
+
}
|
|
64599
|
+
function isValidMcpId(id) {
|
|
64600
|
+
return typeof id === "string" && ID_RE.test(id);
|
|
64601
|
+
}
|
|
64602
|
+
function randomHex(bytes) {
|
|
64603
|
+
const arr = new Uint8Array(bytes);
|
|
64604
|
+
globalThis.crypto.getRandomValues(arr);
|
|
64605
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
64606
|
+
}
|
|
64607
|
+
|
|
64608
|
+
// node_modules/@fetchproxy/protocol/dist/account-attest.js
|
|
64609
|
+
var ACCOUNT_ATTEST_CONSENTS = Object.freeze([
|
|
64610
|
+
"silent",
|
|
64611
|
+
"confirm",
|
|
64612
|
+
"confirm-each"
|
|
64613
|
+
]);
|
|
64614
|
+
var NO_SCOPE_DIGEST = "0".repeat(64);
|
|
64615
|
+
var ACCOUNT_ID_RE = /^[A-Za-z0-9_.\-]{1,128}$/;
|
|
64616
|
+
var HEX64_RE = /^[0-9a-f]{64}$/;
|
|
64617
|
+
var ACCOUNT_KID_RE = /^[0-9a-f]{16}$/;
|
|
64618
|
+
var enc2 = new TextEncoder();
|
|
64619
|
+
|
|
64620
|
+
// node_modules/@fetchproxy/protocol/dist/awaiting-approval.js
|
|
64621
|
+
var AWAITING_APPROVAL_REASON_PREFIX = "awaiting-approval:";
|
|
64622
|
+
function parseAwaitingApprovalReason(reason) {
|
|
64623
|
+
if (!reason.startsWith(AWAITING_APPROVAL_REASON_PREFIX))
|
|
64624
|
+
return null;
|
|
64625
|
+
return reason.slice(AWAITING_APPROVAL_REASON_PREFIX.length).trim();
|
|
64626
|
+
}
|
|
64485
64627
|
|
|
64486
64628
|
// node_modules/@fetchproxy/protocol/dist/capability-availability.js
|
|
64487
64629
|
var CAPABILITY_UNAVAILABLE_CODE = "capability_unavailable";
|
|
@@ -64507,27 +64649,6 @@ function parseUnsupportedCapabilityReason(reason) {
|
|
|
64507
64649
|
return rest.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
64508
64650
|
}
|
|
64509
64651
|
|
|
64510
|
-
// node_modules/@fetchproxy/protocol/dist/mcp-id.js
|
|
64511
|
-
var ID_RE = /^([^:]+):([^:]+):([0-9a-f]{16})$/;
|
|
64512
|
-
function generateMcpId(serverName, version2) {
|
|
64513
|
-
if (serverName.includes(":")) {
|
|
64514
|
-
throw new Error(`serverName cannot contain ':': ${serverName}`);
|
|
64515
|
-
}
|
|
64516
|
-
if (version2.includes(":")) {
|
|
64517
|
-
throw new Error(`version cannot contain ':': ${version2}`);
|
|
64518
|
-
}
|
|
64519
|
-
const rand = randomHex(8);
|
|
64520
|
-
return `${serverName}:${version2}:${rand}`;
|
|
64521
|
-
}
|
|
64522
|
-
function isValidMcpId(id) {
|
|
64523
|
-
return typeof id === "string" && ID_RE.test(id);
|
|
64524
|
-
}
|
|
64525
|
-
function randomHex(bytes) {
|
|
64526
|
-
const arr = new Uint8Array(bytes);
|
|
64527
|
-
globalThis.crypto.getRandomValues(arr);
|
|
64528
|
-
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
64529
|
-
}
|
|
64530
|
-
|
|
64531
64652
|
// node_modules/@fetchproxy/protocol/dist/json-pointer.js
|
|
64532
64653
|
function matchesDeclaredKey(requested, declared) {
|
|
64533
64654
|
for (const decl of declared) {
|
|
@@ -65123,6 +65244,16 @@ function validateFrame(raw) {
|
|
|
65123
65244
|
return { type: "extension-disconnected" };
|
|
65124
65245
|
if (t === "peer-gone")
|
|
65125
65246
|
return validatePeerGone(raw);
|
|
65247
|
+
if (t === ACCOUNT_KEY_FRAME)
|
|
65248
|
+
return validateAccountKey(raw);
|
|
65249
|
+
if (t === ACCOUNT_ATTEST_FRAME)
|
|
65250
|
+
return validateAccountAttest(raw);
|
|
65251
|
+
if (t === BRIDGE_ROLE_FRAME)
|
|
65252
|
+
return validateBridgeRole(raw);
|
|
65253
|
+
if (t === BRIDGE_SERVE_FRAME || t === ROOM_PING_FRAME || t === ROOM_PONG_FRAME) {
|
|
65254
|
+
assertExactFields(raw, TYPE_ONLY_FIELDS, t);
|
|
65255
|
+
return { type: t };
|
|
65256
|
+
}
|
|
65126
65257
|
throw new ProtocolError3(`unknown frame type: ${String(t)}`);
|
|
65127
65258
|
}
|
|
65128
65259
|
function validateHello(raw) {
|
|
@@ -65326,6 +65457,165 @@ function validateHelloRejected(raw) {
|
|
|
65326
65457
|
}
|
|
65327
65458
|
return { type: "hello-rejected", mcpId: raw.mcpId, reason: raw.reason };
|
|
65328
65459
|
}
|
|
65460
|
+
var DISPLAY_FORBIDDEN_RE = /[\u0000-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069]/;
|
|
65461
|
+
function assertExactFields(raw, allowed, label) {
|
|
65462
|
+
for (const k of Object.keys(raw)) {
|
|
65463
|
+
if (!allowed.includes(k)) {
|
|
65464
|
+
throw new ProtocolError3(`${label}: unexpected field ${JSON.stringify(k)}`);
|
|
65465
|
+
}
|
|
65466
|
+
}
|
|
65467
|
+
}
|
|
65468
|
+
function assertMatch(x, re, label, what) {
|
|
65469
|
+
assertString(x, label);
|
|
65470
|
+
if (!re.test(x))
|
|
65471
|
+
throw new ProtocolError3(`${label}: must be ${what}`);
|
|
65472
|
+
}
|
|
65473
|
+
function assertPositiveSafeInt(x, label) {
|
|
65474
|
+
if (typeof x !== "number" || !Number.isSafeInteger(x) || x <= 0) {
|
|
65475
|
+
throw new ProtocolError3(`${label}: expected positive safe integer`);
|
|
65476
|
+
}
|
|
65477
|
+
}
|
|
65478
|
+
function assertDisplayString(x, label, min, max) {
|
|
65479
|
+
assertString(x, label);
|
|
65480
|
+
if (x.length < min || x.length > max) {
|
|
65481
|
+
throw new ProtocolError3(`${label}: must be ${min}-${max} characters`);
|
|
65482
|
+
}
|
|
65483
|
+
if (DISPLAY_FORBIDDEN_RE.test(x)) {
|
|
65484
|
+
throw new ProtocolError3(`${label}: control or bidi-override character`);
|
|
65485
|
+
}
|
|
65486
|
+
}
|
|
65487
|
+
var ACCOUNT_KEY_FIELDS = [
|
|
65488
|
+
"type",
|
|
65489
|
+
"accountId",
|
|
65490
|
+
"slug",
|
|
65491
|
+
"displayName",
|
|
65492
|
+
"confirmedBy",
|
|
65493
|
+
"tokenId",
|
|
65494
|
+
"kid",
|
|
65495
|
+
"publicKey",
|
|
65496
|
+
"generation",
|
|
65497
|
+
"bridgedRegistrations"
|
|
65498
|
+
];
|
|
65499
|
+
function validateAccountKey(raw) {
|
|
65500
|
+
const L = "account-key";
|
|
65501
|
+
assertMatch(raw.accountId, ACCOUNT_ID_RE, `${L}.accountId`, "an account id");
|
|
65502
|
+
assertMatch(raw.slug, ACCOUNT_ID_RE, `${L}.slug`, "a slug");
|
|
65503
|
+
assertDisplayString(raw.displayName, `${L}.displayName`, 0, 128);
|
|
65504
|
+
assertDisplayString(raw.confirmedBy, `${L}.confirmedBy`, 1, 256);
|
|
65505
|
+
assertMatch(raw.tokenId, ACCOUNT_ID_RE, `${L}.tokenId`, "a token id");
|
|
65506
|
+
assertMatch(raw.kid, ACCOUNT_KID_RE, `${L}.kid`, "16 lowercase hex");
|
|
65507
|
+
assertBase64Bytes(raw.publicKey, `${L}.publicKey`, 32);
|
|
65508
|
+
assertPositiveSafeInt(raw.generation, `${L}.generation`);
|
|
65509
|
+
const n = raw.bridgedRegistrations;
|
|
65510
|
+
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 0) {
|
|
65511
|
+
throw new ProtocolError3(`${L}.bridgedRegistrations: expected non-negative safe integer`);
|
|
65512
|
+
}
|
|
65513
|
+
assertExactFields(raw, ACCOUNT_KEY_FIELDS, L);
|
|
65514
|
+
return {
|
|
65515
|
+
type: ACCOUNT_KEY_FRAME,
|
|
65516
|
+
accountId: raw.accountId,
|
|
65517
|
+
slug: raw.slug,
|
|
65518
|
+
displayName: raw.displayName,
|
|
65519
|
+
confirmedBy: raw.confirmedBy,
|
|
65520
|
+
tokenId: raw.tokenId,
|
|
65521
|
+
kid: raw.kid,
|
|
65522
|
+
publicKey: raw.publicKey,
|
|
65523
|
+
generation: raw.generation,
|
|
65524
|
+
bridgedRegistrations: n
|
|
65525
|
+
};
|
|
65526
|
+
}
|
|
65527
|
+
var ACCOUNT_ATTEST_FIELDS = [
|
|
65528
|
+
"type",
|
|
65529
|
+
"mcpId",
|
|
65530
|
+
"accountId",
|
|
65531
|
+
"generation",
|
|
65532
|
+
"tokenId",
|
|
65533
|
+
"kid",
|
|
65534
|
+
"registrationId",
|
|
65535
|
+
"slug",
|
|
65536
|
+
"identityHash",
|
|
65537
|
+
"identityEd25519Pub",
|
|
65538
|
+
"scopeDigest",
|
|
65539
|
+
"consent",
|
|
65540
|
+
"notAfter",
|
|
65541
|
+
"sig"
|
|
65542
|
+
];
|
|
65543
|
+
function validateAccountAttest(raw) {
|
|
65544
|
+
const L = "account-attest";
|
|
65545
|
+
if (!isValidMcpId(raw.mcpId))
|
|
65546
|
+
throw new ProtocolError3(`${L}.mcpId: invalid format`);
|
|
65547
|
+
assertMatch(raw.accountId, ACCOUNT_ID_RE, `${L}.accountId`, "an account id");
|
|
65548
|
+
assertPositiveSafeInt(raw.generation, `${L}.generation`);
|
|
65549
|
+
assertMatch(raw.tokenId, ACCOUNT_ID_RE, `${L}.tokenId`, "a token id");
|
|
65550
|
+
assertMatch(raw.kid, ACCOUNT_KID_RE, `${L}.kid`, "16 lowercase hex");
|
|
65551
|
+
assertMatch(raw.registrationId, ACCOUNT_ID_RE, `${L}.registrationId`, "a registration id");
|
|
65552
|
+
assertMatch(raw.slug, ACCOUNT_ID_RE, `${L}.slug`, "a slug");
|
|
65553
|
+
assertMatch(raw.identityHash, HEX64_RE, `${L}.identityHash`, "64 lowercase hex");
|
|
65554
|
+
assertBase64Bytes(raw.identityEd25519Pub, `${L}.identityEd25519Pub`, 32);
|
|
65555
|
+
assertMatch(raw.scopeDigest, HEX64_RE, `${L}.scopeDigest`, "64 lowercase hex");
|
|
65556
|
+
assertString(raw.consent, `${L}.consent`);
|
|
65557
|
+
const consent = raw.consent;
|
|
65558
|
+
const known = ACCOUNT_ATTEST_CONSENTS.find((c) => c === consent);
|
|
65559
|
+
if (known === void 0) {
|
|
65560
|
+
throw new ProtocolError3(`${L}.consent: must be one of ${ACCOUNT_ATTEST_CONSENTS.join(", ")}`);
|
|
65561
|
+
}
|
|
65562
|
+
assertPositiveSafeInt(raw.notAfter, `${L}.notAfter`);
|
|
65563
|
+
assertBase64Bytes(raw.sig, `${L}.sig`, 64);
|
|
65564
|
+
assertExactFields(raw, ACCOUNT_ATTEST_FIELDS, L);
|
|
65565
|
+
return {
|
|
65566
|
+
type: ACCOUNT_ATTEST_FRAME,
|
|
65567
|
+
mcpId: raw.mcpId,
|
|
65568
|
+
accountId: raw.accountId,
|
|
65569
|
+
generation: raw.generation,
|
|
65570
|
+
tokenId: raw.tokenId,
|
|
65571
|
+
kid: raw.kid,
|
|
65572
|
+
registrationId: raw.registrationId,
|
|
65573
|
+
slug: raw.slug,
|
|
65574
|
+
identityHash: raw.identityHash,
|
|
65575
|
+
identityEd25519Pub: raw.identityEd25519Pub,
|
|
65576
|
+
scopeDigest: raw.scopeDigest,
|
|
65577
|
+
consent: known,
|
|
65578
|
+
notAfter: raw.notAfter,
|
|
65579
|
+
sig: raw.sig
|
|
65580
|
+
};
|
|
65581
|
+
}
|
|
65582
|
+
var TYPE_ONLY_FIELDS = ["type"];
|
|
65583
|
+
var BRIDGE_ROLE_FIELDS = ["type", "role", "canServe", "serving"];
|
|
65584
|
+
var BRIDGE_ROLE_SERVING_FIELDS = ["label", "since"];
|
|
65585
|
+
function validateBridgeRole(raw) {
|
|
65586
|
+
const L = BRIDGE_ROLE_FRAME;
|
|
65587
|
+
const role = raw.role;
|
|
65588
|
+
if (role !== "serving" && role !== "standby") {
|
|
65589
|
+
throw new ProtocolError3(`${L}.role: must be one of serving, standby`);
|
|
65590
|
+
}
|
|
65591
|
+
const canServe = raw.canServe;
|
|
65592
|
+
assertBoolean(canServe, `${L}.canServe`);
|
|
65593
|
+
assertExactFields(raw, BRIDGE_ROLE_FIELDS, L);
|
|
65594
|
+
const serving = raw.serving;
|
|
65595
|
+
if (role === "serving") {
|
|
65596
|
+
if (serving !== void 0) {
|
|
65597
|
+
throw new ProtocolError3(`${L}.serving: must be absent when role is serving`);
|
|
65598
|
+
}
|
|
65599
|
+
return { type: BRIDGE_ROLE_FRAME, role, canServe };
|
|
65600
|
+
}
|
|
65601
|
+
if (serving === void 0) {
|
|
65602
|
+
throw new ProtocolError3(`${L}.serving: required when role is standby`);
|
|
65603
|
+
}
|
|
65604
|
+
assertObject(serving, `${L}.serving`);
|
|
65605
|
+
const label = serving.label;
|
|
65606
|
+
assertDisplayString(label, `${L}.serving.label`, 1, BRIDGE_ROLE_LABEL_MAX);
|
|
65607
|
+
const since = serving.since;
|
|
65608
|
+
if (typeof since !== "number" || !Number.isSafeInteger(since) || since < 0) {
|
|
65609
|
+
throw new ProtocolError3(`${L}.serving.since: expected non-negative safe integer (Unix ms)`);
|
|
65610
|
+
}
|
|
65611
|
+
assertExactFields(serving, BRIDGE_ROLE_SERVING_FIELDS, `${L}.serving`);
|
|
65612
|
+
return {
|
|
65613
|
+
type: BRIDGE_ROLE_FRAME,
|
|
65614
|
+
role,
|
|
65615
|
+
canServe,
|
|
65616
|
+
serving: { label, since }
|
|
65617
|
+
};
|
|
65618
|
+
}
|
|
65329
65619
|
function validateInnerFrame(raw) {
|
|
65330
65620
|
assertObject(raw, "inner");
|
|
65331
65621
|
const t = raw.type;
|
|
@@ -65793,10 +66083,10 @@ function validateInnerResponse(raw) {
|
|
|
65793
66083
|
}
|
|
65794
66084
|
|
|
65795
66085
|
// node_modules/@fetchproxy/protocol/dist/pair-code.js
|
|
65796
|
-
var
|
|
66086
|
+
var enc3 = new TextEncoder();
|
|
65797
66087
|
var PAIR_LABEL = "fetchproxy/4/pair";
|
|
65798
66088
|
async function pairTranscript(mcpIdentityPub, extIdentityPub, mcpNonce, extNonce, mcpSessionPub) {
|
|
65799
|
-
const label =
|
|
66089
|
+
const label = enc3.encode(PAIR_LABEL);
|
|
65800
66090
|
const parts = [mcpIdentityPub, extIdentityPub, mcpNonce, extNonce, mcpSessionPub];
|
|
65801
66091
|
const out = new Uint8Array(label.length + 1 + parts.reduce((n, p) => n + p.length, 0));
|
|
65802
66092
|
out.set(label, 0);
|
|
@@ -65817,7 +66107,7 @@ function digits(hash2) {
|
|
|
65817
66107
|
}
|
|
65818
66108
|
|
|
65819
66109
|
// node_modules/@fetchproxy/protocol/dist/seal.js
|
|
65820
|
-
var
|
|
66110
|
+
var enc4 = new TextEncoder();
|
|
65821
66111
|
var dec = new TextDecoder();
|
|
65822
66112
|
function randomIv() {
|
|
65823
66113
|
const iv = new Uint8Array(12);
|
|
@@ -65830,14 +66120,14 @@ function base64Length(byteLength) {
|
|
|
65830
66120
|
return Math.ceil(byteLength / 3) * 4;
|
|
65831
66121
|
}
|
|
65832
66122
|
function encodeInnerFrame(inner) {
|
|
65833
|
-
return
|
|
66123
|
+
return enc4.encode(JSON.stringify(inner));
|
|
65834
66124
|
}
|
|
65835
66125
|
function plaintextOf(inner) {
|
|
65836
66126
|
return inner instanceof Uint8Array ? inner : encodeInnerFrame(inner);
|
|
65837
66127
|
}
|
|
65838
66128
|
function sealedFrameWireBytes(mcpId, seq, inner) {
|
|
65839
66129
|
const plaintext = plaintextOf(inner).length;
|
|
65840
|
-
const envelope =
|
|
66130
|
+
const envelope = enc4.encode(JSON.stringify({ type: "frame", mcpId, seq, iv: "", ciphertext: "" })).length;
|
|
65841
66131
|
return envelope + IV_B64_BYTES + base64Length(plaintext + AES_GCM_TAG_BYTES);
|
|
65842
66132
|
}
|
|
65843
66133
|
var MAX_FRAME_BYTES = 42 * 1024 * 1024;
|
|
@@ -66177,14 +66467,23 @@ var FetchproxyHelloRejectedError = class extends Error {
|
|
|
66177
66467
|
platform;
|
|
66178
66468
|
/**
|
|
66179
66469
|
* #418: the remedy, when the reason is one this package knows how to
|
|
66180
|
-
* explain —
|
|
66181
|
-
*
|
|
66470
|
+
* explain — `unsupported-capability`, which blames the browser rather than
|
|
66471
|
+
* the MCP, and (D12) `awaiting-approval`, which says to approve in the
|
|
66472
|
+
* browser. `null` otherwise.
|
|
66182
66473
|
*/
|
|
66183
66474
|
hint;
|
|
66475
|
+
/**
|
|
66476
|
+
* D12: `true` only for an `awaiting-approval:` refusal — the extension has
|
|
66477
|
+
* queued an approval card and nobody is at the browser yet, so the same call
|
|
66478
|
+
* succeeds once the person approves. Every other refusal is the extension's
|
|
66479
|
+
* final answer, and retrying unchanged is refused the same way.
|
|
66480
|
+
*/
|
|
66481
|
+
retryable;
|
|
66184
66482
|
constructor(info) {
|
|
66185
66483
|
const unavailable = parseUnsupportedCapabilityReason(info.reason);
|
|
66484
|
+
const awaiting = parseAwaitingApprovalReason(info.reason);
|
|
66186
66485
|
const platform = info.platform ?? null;
|
|
66187
|
-
const hint = unavailable !== null ? `${platform ? `this browser (${platform})` : "this browser"} cannot serve any of the capabilities this MCP needs (${unavailable.join(", ")}) \u2014 ContextMint Bridge checked for the browser APIs and they are missing. Nothing is wrong with the MCP or your pairing, and updating will not change it: use this MCP from a browser that provides them (for example Chrome).` : null;
|
|
66486
|
+
const hint = awaiting !== null ? `The extension is waiting for you to approve this MCP` + (awaiting !== "" ? ` (${awaiting})` : "") + ` \u2014 open ContextMint Bridge in your browser, approve it there, then retry. Nothing is wrong with the MCP or your sign-in.` : unavailable !== null ? `${platform ? `this browser (${platform})` : "this browser"} cannot serve any of the capabilities this MCP needs (${unavailable.join(", ")}) \u2014 ContextMint Bridge checked for the browser APIs and they are missing. Nothing is wrong with the MCP or your pairing, and updating will not change it: use this MCP from a browser that provides them (for example Chrome).` : null;
|
|
66188
66487
|
super(`fetchproxy: the extension refused the connection for "${info.mcpId}": ${info.reason}. ` + (hint !== null ? hint : `This is the extension's own reason \u2014 it is not a timeout, and retrying unchanged will be refused the same way.`));
|
|
66189
66488
|
this.name = "FetchproxyHelloRejectedError";
|
|
66190
66489
|
this.mcpId = info.mcpId;
|
|
@@ -66192,6 +66491,7 @@ var FetchproxyHelloRejectedError = class extends Error {
|
|
|
66192
66491
|
this.unavailableCapabilities = unavailable ?? [];
|
|
66193
66492
|
this.platform = platform;
|
|
66194
66493
|
this.hint = hint;
|
|
66494
|
+
this.retryable = awaiting !== null;
|
|
66195
66495
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
66196
66496
|
}
|
|
66197
66497
|
};
|
|
@@ -66744,7 +67044,7 @@ function originVerdict(origin, allowLocal) {
|
|
|
66744
67044
|
}
|
|
66745
67045
|
var HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
66746
67046
|
var MAX_PAYLOAD_BYTES = MAX_FRAME_BYTES;
|
|
66747
|
-
var
|
|
67047
|
+
var enc5 = new TextEncoder();
|
|
66748
67048
|
var OWN_DECRYPT_FAILURES_BEFORE_REHANDSHAKE = 3;
|
|
66749
67049
|
async function startHost(opts) {
|
|
66750
67050
|
const allowLocalOrigins = envAllowsLocalOrigins();
|
|
@@ -66815,10 +67115,20 @@ async function startHost(opts) {
|
|
|
66815
67115
|
let resolveOwnSession;
|
|
66816
67116
|
let rejectOwnSession;
|
|
66817
67117
|
let ownSessionReady;
|
|
67118
|
+
let ownRetryableRefusal = false;
|
|
67119
|
+
let ownSessionSettled = false;
|
|
66818
67120
|
function resetSessionPromise() {
|
|
67121
|
+
ownRetryableRefusal = false;
|
|
67122
|
+
ownSessionSettled = false;
|
|
66819
67123
|
ownSessionReady = new Promise((resolve2, reject) => {
|
|
66820
|
-
resolveOwnSession =
|
|
66821
|
-
|
|
67124
|
+
resolveOwnSession = (st) => {
|
|
67125
|
+
ownSessionSettled = true;
|
|
67126
|
+
resolve2(st);
|
|
67127
|
+
};
|
|
67128
|
+
rejectOwnSession = (e) => {
|
|
67129
|
+
ownSessionSettled = true;
|
|
67130
|
+
reject(e);
|
|
67131
|
+
};
|
|
66822
67132
|
});
|
|
66823
67133
|
ownSessionReady.catch(() => {
|
|
66824
67134
|
});
|
|
@@ -67085,13 +67395,15 @@ async function startHost(opts) {
|
|
|
67085
67395
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
67086
67396
|
const shared = await ecdhX25519(held.priv, extPub);
|
|
67087
67397
|
const salt = await transcriptHash(held.nonce, extNonce, held.pub, extPub);
|
|
67088
|
-
const key = await hkdfSha256(shared, salt,
|
|
67398
|
+
const key = await hkdfSha256(shared, salt, enc5.encode(HKDF_SESSION_INFO), 32);
|
|
67089
67399
|
if (extensionWs !== ws)
|
|
67090
67400
|
return;
|
|
67091
67401
|
ownSession = new SessionState(key);
|
|
67092
67402
|
ownSessionInfo = sessionInfoFromHello(verifiedHello);
|
|
67093
67403
|
ownDecryptFailures = 0;
|
|
67094
67404
|
ownPendingPairCode = null;
|
|
67405
|
+
if (ownRetryableRefusal)
|
|
67406
|
+
resetSessionPromise();
|
|
67095
67407
|
resolveOwnSession(ownSession);
|
|
67096
67408
|
} else {
|
|
67097
67409
|
const slot = peers.get(frame.mcpId);
|
|
@@ -67160,11 +67472,17 @@ async function startHost(opts) {
|
|
|
67160
67472
|
}
|
|
67161
67473
|
if (frame.type === "hello-rejected" && identified === "extension") {
|
|
67162
67474
|
if (frame.mcpId === opts.ownMcpId) {
|
|
67163
|
-
|
|
67475
|
+
const refusal = new FetchproxyHelloRejectedError({
|
|
67164
67476
|
mcpId: frame.mcpId,
|
|
67165
67477
|
reason: frame.reason,
|
|
67166
67478
|
platform: extensionHello?.platform ?? null
|
|
67167
|
-
})
|
|
67479
|
+
});
|
|
67480
|
+
if (ownRetryableRefusal)
|
|
67481
|
+
resetSessionPromise();
|
|
67482
|
+
const settlesPending = !ownSessionSettled;
|
|
67483
|
+
rejectOwnSession(refusal);
|
|
67484
|
+
if (refusal.retryable && settlesPending)
|
|
67485
|
+
ownRetryableRefusal = true;
|
|
67168
67486
|
} else {
|
|
67169
67487
|
const slot = peers.get(frame.mcpId);
|
|
67170
67488
|
if (slot?.helloFrame.accepts?.includes("hello-rejected")) {
|
|
@@ -67280,7 +67598,7 @@ async function startHost(opts) {
|
|
|
67280
67598
|
|
|
67281
67599
|
// node_modules/@fetchproxy/server/dist/peer.js
|
|
67282
67600
|
var PEER_DIAL_TIMEOUT_MS = 5e3;
|
|
67283
|
-
var
|
|
67601
|
+
var enc6 = new TextEncoder();
|
|
67284
67602
|
function notSentError() {
|
|
67285
67603
|
return new Error("fetchproxy: request not sent \u2014 the connection to the bridge host closed before it went out, so nothing reached the browser. It is safe to retry.");
|
|
67286
67604
|
}
|
|
@@ -67380,7 +67698,9 @@ async function startPeer(opts) {
|
|
|
67380
67698
|
let rejectFirstReady;
|
|
67381
67699
|
let sessionPromise;
|
|
67382
67700
|
let sessionPromiseSettled = false;
|
|
67701
|
+
let retryableRefusal = false;
|
|
67383
67702
|
function resetSessionPromise() {
|
|
67703
|
+
retryableRefusal = false;
|
|
67384
67704
|
sessionPromiseSettled = false;
|
|
67385
67705
|
sessionPromise = new Promise((resolve2, reject) => {
|
|
67386
67706
|
resolveFirstReady = (st) => {
|
|
@@ -67477,6 +67797,8 @@ async function startPeer(opts) {
|
|
|
67477
67797
|
raw = JSON.parse(data.toString());
|
|
67478
67798
|
const frame = validateFrame(raw);
|
|
67479
67799
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
67800
|
+
if (retryableRefusal)
|
|
67801
|
+
resetSessionPromise();
|
|
67480
67802
|
extensionHello = frame;
|
|
67481
67803
|
const mintedKeypair = await generateSessionKeypair();
|
|
67482
67804
|
const mintedHello = await buildServerHello({
|
|
@@ -67506,6 +67828,8 @@ async function startPeer(opts) {
|
|
|
67506
67828
|
session = null;
|
|
67507
67829
|
if (sessionPromiseSettled)
|
|
67508
67830
|
resetSessionPromise();
|
|
67831
|
+
} else if (retryableRefusal) {
|
|
67832
|
+
resetSessionPromise();
|
|
67509
67833
|
}
|
|
67510
67834
|
extensionDisconnectListeners.forEach((cb) => cb());
|
|
67511
67835
|
return;
|
|
@@ -67527,7 +67851,7 @@ async function startPeer(opts) {
|
|
|
67527
67851
|
const extNonce = fromB64(authenticated.sessionNonce);
|
|
67528
67852
|
const shared = await ecdhX25519(held.priv, extPub);
|
|
67529
67853
|
const salt = await transcriptHash(held.nonce, extNonce, held.pub, extPub);
|
|
67530
|
-
const sessionKey = await hkdfSha256(shared, salt,
|
|
67854
|
+
const sessionKey = await hkdfSha256(shared, salt, enc6.encode(HKDF_SESSION_INFO), 32);
|
|
67531
67855
|
if (sessionEphemeral !== held) {
|
|
67532
67856
|
console.warn(`[fetchproxy] ${opts.serverName}: dropping a session derived against an ephemeral that was superseded while it was being derived`);
|
|
67533
67857
|
return;
|
|
@@ -67540,16 +67864,24 @@ async function startPeer(opts) {
|
|
|
67540
67864
|
if (isRenegotiation) {
|
|
67541
67865
|
renegotiateListeners.forEach((cb) => cb());
|
|
67542
67866
|
} else {
|
|
67867
|
+
if (retryableRefusal)
|
|
67868
|
+
resetSessionPromise();
|
|
67543
67869
|
resolveFirstReady(session);
|
|
67544
67870
|
}
|
|
67545
67871
|
return;
|
|
67546
67872
|
}
|
|
67547
67873
|
if (frame.type === "hello-rejected" && frame.mcpId === opts.mcpId) {
|
|
67548
|
-
|
|
67874
|
+
const refusal = new FetchproxyHelloRejectedError({
|
|
67549
67875
|
mcpId: frame.mcpId,
|
|
67550
67876
|
reason: frame.reason,
|
|
67551
67877
|
platform: extensionHello?.platform ?? null
|
|
67552
|
-
})
|
|
67878
|
+
});
|
|
67879
|
+
if (retryableRefusal)
|
|
67880
|
+
resetSessionPromise();
|
|
67881
|
+
const settlesPending = !sessionPromiseSettled;
|
|
67882
|
+
rejectFirstReady(refusal);
|
|
67883
|
+
if (refusal.retryable && settlesPending)
|
|
67884
|
+
retryableRefusal = true;
|
|
67553
67885
|
}
|
|
67554
67886
|
if (frame.type === "pair-pending" && frame.mcpId === opts.mcpId) {
|
|
67555
67887
|
const hello2 = extensionHello;
|
|
@@ -67713,8 +68045,9 @@ function classifyFetchError(error62, code2) {
|
|
|
67713
68045
|
|
|
67714
68046
|
// node_modules/@fetchproxy/server/dist/classify-bridge-error.js
|
|
67715
68047
|
function classifyBridgeError(err) {
|
|
67716
|
-
if (err instanceof FetchproxyHelloRejectedError)
|
|
67717
|
-
return "hello_rejected";
|
|
68048
|
+
if (err instanceof FetchproxyHelloRejectedError) {
|
|
68049
|
+
return err.retryable ? "awaiting_approval" : "hello_rejected";
|
|
68050
|
+
}
|
|
67718
68051
|
if (err instanceof FetchproxySessionNotReadyError)
|
|
67719
68052
|
return "session_not_ready";
|
|
67720
68053
|
if (err instanceof FetchproxyTimeoutError)
|
|
@@ -75921,7 +76254,7 @@ function registerVisitTools(server, client2, patients2) {
|
|
|
75921
76254
|
}
|
|
75922
76255
|
|
|
75923
76256
|
// src/version.ts
|
|
75924
|
-
var VERSION = "1.3.
|
|
76257
|
+
var VERSION = "1.3.5";
|
|
75925
76258
|
|
|
75926
76259
|
// src/index.ts
|
|
75927
76260
|
var dotenvCandidates = [
|
|
@@ -76006,7 +76339,7 @@ await runMcp({
|
|
|
76006
76339
|
});
|
|
76007
76340
|
/*! Bundled license information:
|
|
76008
76341
|
|
|
76009
|
-
@modelcontextprotocol/server/dist/src-
|
|
76342
|
+
@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs:
|
|
76010
76343
|
(*!
|
|
76011
76344
|
* content-type
|
|
76012
76345
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrischall/myatriumhealth-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "MCP server for MyAtriumHealth (Atrium Health's Epic MyChart patient portal) via the fetchproxy browser bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
"access": "public"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@chrischall/mcp-utils": "^2.
|
|
46
|
-
"@fetchproxy/server": "^3.
|
|
47
|
-
"@modelcontextprotocol/server": "^2.
|
|
45
|
+
"@chrischall/mcp-utils": "^2.15.0",
|
|
46
|
+
"@fetchproxy/server": "^3.6.0",
|
|
47
|
+
"@modelcontextprotocol/server": "^2.3.0",
|
|
48
48
|
"dotenv": "^18.0.1",
|
|
49
49
|
"node-html-parser": "^9.0.3",
|
|
50
50
|
"zod": "^4.6.5"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@modelcontextprotocol/client": "^2.
|
|
53
|
+
"@modelcontextprotocol/client": "^2.3.0",
|
|
54
54
|
"@types/node": "^26.0.0",
|
|
55
55
|
"@vitest/coverage-v8": "^5.0.0",
|
|
56
56
|
"esbuild": "^0.28.1",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/myatriumhealth-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.3.
|
|
9
|
+
"version": "1.3.5",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@chrischall/myatriumhealth-mcp",
|
|
14
|
-
"version": "1.3.
|
|
14
|
+
"version": "1.3.5",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|