ofw-mcp 2.20.1 → 2.20.2
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/dist/bundle.js +432 -139
- package/dist/client.js +86 -69
- package/dist/index.js +1 -1
- package/dist/tools/calendar.js +40 -7
- package/package.json +4 -4
- package/server.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -2335,11 +2335,23 @@ var require_websocket = __commonJS({
|
|
|
2335
2335
|
this._isServer = false;
|
|
2336
2336
|
this._redirects = 0;
|
|
2337
2337
|
if (protocols === void 0) {
|
|
2338
|
-
protocols
|
|
2338
|
+
if (!options || options.protocols === void 0) {
|
|
2339
|
+
protocols = [];
|
|
2340
|
+
} else if (Array.isArray(options.protocols)) {
|
|
2341
|
+
protocols = options.protocols;
|
|
2342
|
+
} else {
|
|
2343
|
+
protocols = [options.protocols];
|
|
2344
|
+
}
|
|
2339
2345
|
} else if (!Array.isArray(protocols)) {
|
|
2340
2346
|
if (typeof protocols === "object" && protocols !== null) {
|
|
2341
2347
|
options = protocols;
|
|
2342
|
-
protocols
|
|
2348
|
+
if (options.protocols === void 0) {
|
|
2349
|
+
protocols = [];
|
|
2350
|
+
} else if (Array.isArray(options.protocols)) {
|
|
2351
|
+
protocols = options.protocols;
|
|
2352
|
+
} else {
|
|
2353
|
+
protocols = [options.protocols];
|
|
2354
|
+
}
|
|
2343
2355
|
} else {
|
|
2344
2356
|
protocols = [protocols];
|
|
2345
2357
|
}
|
|
@@ -2536,7 +2548,6 @@ var require_websocket = __commonJS({
|
|
|
2536
2548
|
}
|
|
2537
2549
|
return;
|
|
2538
2550
|
}
|
|
2539
|
-
this._readyState = _WebSocket.CLOSING;
|
|
2540
2551
|
this._sender.close(code2, data, !this._isServer, (err) => {
|
|
2541
2552
|
if (err) return;
|
|
2542
2553
|
this._closeFrameSent = true;
|
|
@@ -2544,6 +2555,7 @@ var require_websocket = __commonJS({
|
|
|
2544
2555
|
this._socket.end();
|
|
2545
2556
|
}
|
|
2546
2557
|
});
|
|
2558
|
+
this._readyState = _WebSocket.CLOSING;
|
|
2547
2559
|
setCloseTimer(this);
|
|
2548
2560
|
}
|
|
2549
2561
|
/**
|
|
@@ -2768,6 +2780,7 @@ var require_websocket = __commonJS({
|
|
|
2768
2780
|
socketPath: void 0,
|
|
2769
2781
|
hostname: void 0,
|
|
2770
2782
|
protocol: void 0,
|
|
2783
|
+
protocols: void 0,
|
|
2771
2784
|
timeout: void 0,
|
|
2772
2785
|
method: "GET",
|
|
2773
2786
|
host: void 0,
|
|
@@ -21552,8 +21565,8 @@ function currencyCode2(_params) {
|
|
|
21552
21565
|
return _stringFormat(ZodCustomStringFormat, "currency_code", currencyCode, _params);
|
|
21553
21566
|
}
|
|
21554
21567
|
function hash(alg, params) {
|
|
21555
|
-
const
|
|
21556
|
-
const format = `${alg}_${
|
|
21568
|
+
const enc7 = params?.enc ?? "hex";
|
|
21569
|
+
const format = `${alg}_${enc7}`;
|
|
21557
21570
|
const regex = regexes_exports[format];
|
|
21558
21571
|
if (!regex)
|
|
21559
21572
|
throw new Error(`Unrecognized hash format: ${format}`);
|
|
@@ -24428,7 +24441,7 @@ var OAuthTokenRevocationRequestSchema = object({
|
|
|
24428
24441
|
token_type_hint: string2().optional()
|
|
24429
24442
|
}).strip();
|
|
24430
24443
|
|
|
24431
|
-
// node_modules/@modelcontextprotocol/server/dist/src-
|
|
24444
|
+
// node_modules/@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs
|
|
24432
24445
|
var BRANDS = /* @__PURE__ */ Symbol.for("mcp.sdk.errorBrands");
|
|
24433
24446
|
function stampErrorBrands(instance, ctor) {
|
|
24434
24447
|
const brands = /* @__PURE__ */ new Set();
|
|
@@ -26671,7 +26684,8 @@ function build() {
|
|
|
26671
26684
|
ListResourceTemplatesResultResponseSchema: wireResultResponse(ListResourceTemplatesResultSchema$1),
|
|
26672
26685
|
ReadResourceResultResponseSchema: wireResultResponse(union([ReadResourceResultSchema$1, InputRequiredResultSchema])),
|
|
26673
26686
|
CompleteResultResponseSchema: wireResultResponse(CompleteResultSchema$1),
|
|
26674
|
-
DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1)
|
|
26687
|
+
DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1),
|
|
26688
|
+
SubscriptionsListenResultResponseSchema: wireResultResponse(SubscriptionsListenResultSchema$1)
|
|
26675
26689
|
};
|
|
26676
26690
|
}
|
|
26677
26691
|
var memo2;
|
|
@@ -27179,6 +27193,9 @@ function isSpecRequestMethod(method) {
|
|
|
27179
27193
|
function isSpecNotificationMethod(method) {
|
|
27180
27194
|
return ALL_CODECS.some((codec2) => codec2.hasNotificationMethod(method));
|
|
27181
27195
|
}
|
|
27196
|
+
function isExtensionReusedRequestMethod(method) {
|
|
27197
|
+
return method === "tasks/get" || method === "tasks/cancel";
|
|
27198
|
+
}
|
|
27182
27199
|
var ALL_CODECS = [rev2025Codec, rev2026Codec];
|
|
27183
27200
|
function isPlainObject$3(value) {
|
|
27184
27201
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -28187,6 +28204,8 @@ var Protocol = class {
|
|
|
28187
28204
|
_transport;
|
|
28188
28205
|
_requestMessageId = 0;
|
|
28189
28206
|
_requestHandlers = /* @__PURE__ */ new Map();
|
|
28207
|
+
/** Methods registered with an explicit schema; the era gate in `_onrequest` reads it for the Tasks extension names. */
|
|
28208
|
+
_customSchemaRequestMethods = /* @__PURE__ */ new Set();
|
|
28190
28209
|
_requestHandlerAbortControllers = /* @__PURE__ */ new Map();
|
|
28191
28210
|
_notificationHandlers = /* @__PURE__ */ new Map();
|
|
28192
28211
|
_responseHandlers = /* @__PURE__ */ new Map();
|
|
@@ -28470,7 +28489,7 @@ var Protocol = class {
|
|
|
28470
28489
|
return;
|
|
28471
28490
|
}
|
|
28472
28491
|
}
|
|
28473
|
-
if (isSpecRequestMethod(request.method) && !codec2.hasRequestMethod(request.method)) {
|
|
28492
|
+
if (isSpecRequestMethod(request.method) && !codec2.hasRequestMethod(request.method) && !(isExtensionReusedRequestMethod(request.method) && this._customSchemaRequestMethods.has(request.method))) {
|
|
28474
28493
|
sendErrorResponse(ProtocolErrorCode.MethodNotFound, "Method not found");
|
|
28475
28494
|
return;
|
|
28476
28495
|
}
|
|
@@ -28603,8 +28622,11 @@ var Protocol = class {
|
|
|
28603
28622
|
}
|
|
28604
28623
|
request(request, schemaOrOptions, maybeOptions) {
|
|
28605
28624
|
const codec2 = this._resolveOutboundCodec(request.method);
|
|
28625
|
+
if (isStandardSchema(schemaOrOptions)) {
|
|
28626
|
+
if (!isExtensionReusedRequestMethod(request.method)) this._assertOutboundRequestInEra(codec2, request.method);
|
|
28627
|
+
return this._requestWithSchemaViaCodec(codec2, request, schemaOrOptions, maybeOptions);
|
|
28628
|
+
}
|
|
28606
28629
|
this._assertOutboundRequestInEra(codec2, request.method);
|
|
28607
|
-
if (isStandardSchema(schemaOrOptions)) return this._requestWithSchemaViaCodec(codec2, request, schemaOrOptions, maybeOptions);
|
|
28608
28630
|
const validate2 = codecResultValidator(codec2, request.method);
|
|
28609
28631
|
if (validate2 === void 0) throw new TypeError(`'${request.method}' is not a spec method; pass a result schema as the second argument to request().`);
|
|
28610
28632
|
return this._requestWithSchemaViaCodec(codec2, request, validate2, schemaOrOptions);
|
|
@@ -28848,6 +28870,8 @@ var Protocol = class {
|
|
|
28848
28870
|
return maybeHandler(parsed.data, ctx);
|
|
28849
28871
|
};
|
|
28850
28872
|
else throw new TypeError("setRequestHandler: handler is required");
|
|
28873
|
+
if (typeof schemasOrHandler === "function") this._customSchemaRequestMethods.delete(method);
|
|
28874
|
+
else this._customSchemaRequestMethods.add(method);
|
|
28851
28875
|
this._requestHandlers.set(method, this._wrapHandler(method, stored));
|
|
28852
28876
|
}
|
|
28853
28877
|
/**
|
|
@@ -28877,6 +28901,7 @@ var Protocol = class {
|
|
|
28877
28901
|
*/
|
|
28878
28902
|
removeRequestHandler(method) {
|
|
28879
28903
|
this._requestHandlers.delete(method);
|
|
28904
|
+
this._customSchemaRequestMethods.delete(method);
|
|
28880
28905
|
}
|
|
28881
28906
|
/**
|
|
28882
28907
|
* Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
|
|
@@ -36119,7 +36144,7 @@ var Ajv = import_ajv.Ajv;
|
|
|
36119
36144
|
// node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
|
|
36120
36145
|
import process2 from "node:process";
|
|
36121
36146
|
|
|
36122
|
-
// node_modules/@modelcontextprotocol/server/dist/mcp-
|
|
36147
|
+
// node_modules/@modelcontextprotocol/server/dist/mcp-DIH4cS6P.mjs
|
|
36123
36148
|
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
|
36124
36149
|
function isCompletable(schema) {
|
|
36125
36150
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
@@ -36184,7 +36209,7 @@ var StdioListenRouter = class {
|
|
|
36184
36209
|
_serverCapabilities;
|
|
36185
36210
|
/**
|
|
36186
36211
|
* The serving instance's identity, stamped onto the graceful-close
|
|
36187
|
-
* results' `_meta` (the spec's `
|
|
36212
|
+
* results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
|
|
36188
36213
|
* `ResultMetaObject`). Handed over together with the capabilities.
|
|
36189
36214
|
*/
|
|
36190
36215
|
_serverInfo;
|
|
@@ -36516,6 +36541,11 @@ var Server = class extends Protocol {
|
|
|
36516
36541
|
if (modernProtocolVersions(this._supportedProtocolVersions).length > 0) this.setRequestHandler("server/discover", () => this._ondiscover());
|
|
36517
36542
|
if (this._capabilities.logging) this._registerLoggingHandler();
|
|
36518
36543
|
}
|
|
36544
|
+
/** Attaches to the given transport; rejects while this instance is connected to another one. */
|
|
36545
|
+
async connect(transport) {
|
|
36546
|
+
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.");
|
|
36547
|
+
await super.connect(transport);
|
|
36548
|
+
}
|
|
36519
36549
|
/**
|
|
36520
36550
|
* Registers the built-in `logging/setLevel` request handler.
|
|
36521
36551
|
*
|
|
@@ -37069,21 +37099,41 @@ var Server = class extends Protocol {
|
|
|
37069
37099
|
function discoverAdvertisedCapabilities(capabilities) {
|
|
37070
37100
|
return { ...capabilities };
|
|
37071
37101
|
}
|
|
37102
|
+
function toolInputElementCount(value, max) {
|
|
37103
|
+
let count = 0;
|
|
37104
|
+
const stack = [value];
|
|
37105
|
+
while (stack.length > 0) {
|
|
37106
|
+
const node2 = stack.pop();
|
|
37107
|
+
if (node2 === null || typeof node2 !== "object") continue;
|
|
37108
|
+
if (Array.isArray(node2)) for (const child of node2) {
|
|
37109
|
+
if (++count > max) return count;
|
|
37110
|
+
if (child !== null && typeof child === "object") stack.push(child);
|
|
37111
|
+
}
|
|
37112
|
+
else for (const key in node2) {
|
|
37113
|
+
if (!Object.prototype.hasOwnProperty.call(node2, key)) continue;
|
|
37114
|
+
if (++count > max) return count;
|
|
37115
|
+
const child = node2[key];
|
|
37116
|
+
if (child !== null && typeof child === "object") stack.push(child);
|
|
37117
|
+
}
|
|
37118
|
+
}
|
|
37119
|
+
return count;
|
|
37120
|
+
}
|
|
37121
|
+
function resolveMaxToolInputElements(value) {
|
|
37122
|
+
if (value === void 0 || value === Infinity) return void 0;
|
|
37123
|
+
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)}`);
|
|
37124
|
+
return value;
|
|
37125
|
+
}
|
|
37072
37126
|
var McpServer = class {
|
|
37073
37127
|
/**
|
|
37074
37128
|
* The underlying {@linkcode Server} instance, useful for advanced operations like sending notifications.
|
|
37075
37129
|
*/
|
|
37076
37130
|
server;
|
|
37131
|
+
_maxToolInputElements;
|
|
37077
37132
|
_registeredResources = {};
|
|
37078
37133
|
_registeredResourceTemplates = {};
|
|
37079
37134
|
_registeredTools = {};
|
|
37080
37135
|
_registeredPrompts = {};
|
|
37081
|
-
/**
|
|
37082
|
-
* Per-tool JSON-converted `inputSchema`, memoized so the SEP-2243
|
|
37083
|
-
* registration-time scan and the pre-dispatch validation step share one
|
|
37084
|
-
* conversion instead of paying it twice per request under the
|
|
37085
|
-
* per-request-factory `createMcpHandler` model.
|
|
37086
|
-
*/
|
|
37136
|
+
/** Per-tool JSON-converted `inputSchema`, filled on first use by `toolInputSchemaJson()`. */
|
|
37087
37137
|
_toolInputSchemaJson = {};
|
|
37088
37138
|
/**
|
|
37089
37139
|
* The JSON-serialized `inputSchema` of a registered tool, or `undefined`
|
|
@@ -37108,6 +37158,7 @@ var McpServer = class {
|
|
|
37108
37158
|
}
|
|
37109
37159
|
constructor(serverInfo, options) {
|
|
37110
37160
|
this.server = new Server(serverInfo, options);
|
|
37161
|
+
this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
|
|
37111
37162
|
if (options?.capabilities?.tools) this.setToolRequestHandlers();
|
|
37112
37163
|
if (options?.capabilities?.resources) this.setResourceRequestHandlers();
|
|
37113
37164
|
if (options?.capabilities?.prompts) this.setPromptRequestHandlers();
|
|
@@ -37180,7 +37231,7 @@ var McpServer = class {
|
|
|
37180
37231
|
name,
|
|
37181
37232
|
title: tool.title,
|
|
37182
37233
|
description: tool.description,
|
|
37183
|
-
inputSchema: tool.inputSchema ?
|
|
37234
|
+
inputSchema: tool.inputSchema ? convertListedInputSchema(name, tool.inputSchema) : EMPTY_OBJECT_JSON_SCHEMA,
|
|
37184
37235
|
annotations: tool.annotations,
|
|
37185
37236
|
icons: tool.icons,
|
|
37186
37237
|
execution: tool.execution,
|
|
@@ -37225,6 +37276,7 @@ var McpServer = class {
|
|
|
37225
37276
|
* Validates tool input arguments against the tool's input schema.
|
|
37226
37277
|
*/
|
|
37227
37278
|
async validateToolInput(tool, args, toolName) {
|
|
37279
|
+
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`);
|
|
37228
37280
|
if (!tool.inputSchema) return;
|
|
37229
37281
|
const parseResult = await validateStandardSchema(tool.inputSchema, args ?? {});
|
|
37230
37282
|
if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${parseResult.error}`);
|
|
@@ -37499,20 +37551,19 @@ var McpServer = class {
|
|
|
37499
37551
|
}
|
|
37500
37552
|
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, scopeChallenge, _meta, handler) {
|
|
37501
37553
|
validateAndWarnToolName(name);
|
|
37502
|
-
if (inputSchema !== void 0) try {
|
|
37503
|
-
const json2 = standardSchemaToJsonSchema(inputSchema, "input");
|
|
37504
|
-
this._toolInputSchemaJson[name] = json2;
|
|
37505
|
-
const scan = scanXMcpHeaderDeclarations(json2);
|
|
37506
|
-
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}`);
|
|
37507
|
-
} catch {
|
|
37508
|
-
}
|
|
37509
37554
|
let currentHandler = handler;
|
|
37555
|
+
let outputSchemaJson;
|
|
37510
37556
|
const registeredTool = {
|
|
37511
37557
|
title,
|
|
37512
37558
|
description,
|
|
37513
37559
|
inputSchema,
|
|
37514
37560
|
outputSchema,
|
|
37515
|
-
outputSchemaJson
|
|
37561
|
+
get outputSchemaJson() {
|
|
37562
|
+
return outputSchemaJson ??= convertOutputSchemaJson(registeredTool.outputSchema);
|
|
37563
|
+
},
|
|
37564
|
+
set outputSchemaJson(value) {
|
|
37565
|
+
outputSchemaJson = value;
|
|
37566
|
+
},
|
|
37516
37567
|
annotations,
|
|
37517
37568
|
icons,
|
|
37518
37569
|
execution,
|
|
@@ -37639,6 +37690,12 @@ var EMPTY_OBJECT_JSON_SCHEMA = {
|
|
|
37639
37690
|
type: "object",
|
|
37640
37691
|
properties: {}
|
|
37641
37692
|
};
|
|
37693
|
+
function convertListedInputSchema(name, inputSchema) {
|
|
37694
|
+
const json2 = standardSchemaToJsonSchema(inputSchema, "input");
|
|
37695
|
+
const scan = scanXMcpHeaderDeclarations(json2);
|
|
37696
|
+
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}`);
|
|
37697
|
+
return json2;
|
|
37698
|
+
}
|
|
37642
37699
|
function convertOutputSchemaJson(outputSchema) {
|
|
37643
37700
|
if (outputSchema === void 0) return void 0;
|
|
37644
37701
|
try {
|
|
@@ -37651,7 +37708,7 @@ function createPromptHandler(name, argsSchema, callback) {
|
|
|
37651
37708
|
if (argsSchema) {
|
|
37652
37709
|
const typedCallback = callback;
|
|
37653
37710
|
return async (args, ctx) => {
|
|
37654
|
-
const parseResult = await validateStandardSchema(argsSchema, args);
|
|
37711
|
+
const parseResult = await validateStandardSchema(argsSchema, args ?? {});
|
|
37655
37712
|
if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${parseResult.error}`);
|
|
37656
37713
|
return typedCallback(parseResult.data, ctx);
|
|
37657
37714
|
};
|
|
@@ -38999,7 +39056,10 @@ function surfaceToolHints(server) {
|
|
|
38999
39056
|
));
|
|
39000
39057
|
}
|
|
39001
39058
|
async function createMcpServer(opts) {
|
|
39002
|
-
const server = new McpServer({ name: opts.name, version: opts.version }, {
|
|
39059
|
+
const server = new McpServer({ name: opts.name, version: opts.version }, {
|
|
39060
|
+
supportedProtocolVersions: [...SERVER_PROTOCOL_VERSIONS],
|
|
39061
|
+
...opts.maxToolInputElements !== void 0 ? { maxToolInputElements: opts.maxToolInputElements } : {}
|
|
39062
|
+
});
|
|
39003
39063
|
if (opts.surfaceHints !== false)
|
|
39004
39064
|
surfaceToolHints(server);
|
|
39005
39065
|
if (opts.banner !== void 0) {
|
|
@@ -40327,6 +40387,49 @@ var KNOWN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
40327
40387
|
"graphql",
|
|
40328
40388
|
"write_cookies"
|
|
40329
40389
|
]);
|
|
40390
|
+
var ACCOUNT_KEY_FRAME = "account-key";
|
|
40391
|
+
var ACCOUNT_ATTEST_FRAME = "account-attest";
|
|
40392
|
+
|
|
40393
|
+
// node_modules/@fetchproxy/protocol/dist/mcp-id.js
|
|
40394
|
+
var ID_RE = /^([^:]+):([^:]+):([0-9a-f]{16})$/;
|
|
40395
|
+
function generateMcpId(serverName, version2) {
|
|
40396
|
+
if (serverName.includes(":")) {
|
|
40397
|
+
throw new Error(`serverName cannot contain ':': ${serverName}`);
|
|
40398
|
+
}
|
|
40399
|
+
if (version2.includes(":")) {
|
|
40400
|
+
throw new Error(`version cannot contain ':': ${version2}`);
|
|
40401
|
+
}
|
|
40402
|
+
const rand = randomHex(8);
|
|
40403
|
+
return `${serverName}:${version2}:${rand}`;
|
|
40404
|
+
}
|
|
40405
|
+
function isValidMcpId(id) {
|
|
40406
|
+
return typeof id === "string" && ID_RE.test(id);
|
|
40407
|
+
}
|
|
40408
|
+
function randomHex(bytes) {
|
|
40409
|
+
const arr = new Uint8Array(bytes);
|
|
40410
|
+
globalThis.crypto.getRandomValues(arr);
|
|
40411
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
40412
|
+
}
|
|
40413
|
+
|
|
40414
|
+
// node_modules/@fetchproxy/protocol/dist/account-attest.js
|
|
40415
|
+
var ACCOUNT_ATTEST_CONSENTS = Object.freeze([
|
|
40416
|
+
"silent",
|
|
40417
|
+
"confirm",
|
|
40418
|
+
"confirm-each"
|
|
40419
|
+
]);
|
|
40420
|
+
var NO_SCOPE_DIGEST = "0".repeat(64);
|
|
40421
|
+
var ACCOUNT_ID_RE = /^[A-Za-z0-9_.\-]{1,128}$/;
|
|
40422
|
+
var HEX64_RE = /^[0-9a-f]{64}$/;
|
|
40423
|
+
var ACCOUNT_KID_RE = /^[0-9a-f]{16}$/;
|
|
40424
|
+
var enc2 = new TextEncoder();
|
|
40425
|
+
|
|
40426
|
+
// node_modules/@fetchproxy/protocol/dist/awaiting-approval.js
|
|
40427
|
+
var AWAITING_APPROVAL_REASON_PREFIX = "awaiting-approval:";
|
|
40428
|
+
function parseAwaitingApprovalReason(reason) {
|
|
40429
|
+
if (!reason.startsWith(AWAITING_APPROVAL_REASON_PREFIX))
|
|
40430
|
+
return null;
|
|
40431
|
+
return reason.slice(AWAITING_APPROVAL_REASON_PREFIX.length).trim();
|
|
40432
|
+
}
|
|
40330
40433
|
|
|
40331
40434
|
// node_modules/@fetchproxy/protocol/dist/capability-availability.js
|
|
40332
40435
|
var CAPABILITY_UNAVAILABLE_CODE = "capability_unavailable";
|
|
@@ -40352,27 +40455,6 @@ function parseUnsupportedCapabilityReason(reason) {
|
|
|
40352
40455
|
return rest.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
40353
40456
|
}
|
|
40354
40457
|
|
|
40355
|
-
// node_modules/@fetchproxy/protocol/dist/mcp-id.js
|
|
40356
|
-
var ID_RE = /^([^:]+):([^:]+):([0-9a-f]{16})$/;
|
|
40357
|
-
function generateMcpId(serverName, version2) {
|
|
40358
|
-
if (serverName.includes(":")) {
|
|
40359
|
-
throw new Error(`serverName cannot contain ':': ${serverName}`);
|
|
40360
|
-
}
|
|
40361
|
-
if (version2.includes(":")) {
|
|
40362
|
-
throw new Error(`version cannot contain ':': ${version2}`);
|
|
40363
|
-
}
|
|
40364
|
-
const rand = randomHex(8);
|
|
40365
|
-
return `${serverName}:${version2}:${rand}`;
|
|
40366
|
-
}
|
|
40367
|
-
function isValidMcpId(id) {
|
|
40368
|
-
return typeof id === "string" && ID_RE.test(id);
|
|
40369
|
-
}
|
|
40370
|
-
function randomHex(bytes) {
|
|
40371
|
-
const arr = new Uint8Array(bytes);
|
|
40372
|
-
globalThis.crypto.getRandomValues(arr);
|
|
40373
|
-
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
40374
|
-
}
|
|
40375
|
-
|
|
40376
40458
|
// node_modules/@fetchproxy/protocol/dist/json-pointer.js
|
|
40377
40459
|
function matchesDeclaredKey(requested, declared) {
|
|
40378
40460
|
for (const decl of declared) {
|
|
@@ -40968,6 +41050,10 @@ function validateFrame(raw) {
|
|
|
40968
41050
|
return { type: "extension-disconnected" };
|
|
40969
41051
|
if (t === "peer-gone")
|
|
40970
41052
|
return validatePeerGone(raw);
|
|
41053
|
+
if (t === ACCOUNT_KEY_FRAME)
|
|
41054
|
+
return validateAccountKey(raw);
|
|
41055
|
+
if (t === ACCOUNT_ATTEST_FRAME)
|
|
41056
|
+
return validateAccountAttest(raw);
|
|
40971
41057
|
throw new ProtocolError3(`unknown frame type: ${String(t)}`);
|
|
40972
41058
|
}
|
|
40973
41059
|
function validateHello(raw) {
|
|
@@ -41171,6 +41257,128 @@ function validateHelloRejected(raw) {
|
|
|
41171
41257
|
}
|
|
41172
41258
|
return { type: "hello-rejected", mcpId: raw.mcpId, reason: raw.reason };
|
|
41173
41259
|
}
|
|
41260
|
+
var DISPLAY_FORBIDDEN_RE = /[\u0000-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069]/;
|
|
41261
|
+
function assertExactFields(raw, allowed, label) {
|
|
41262
|
+
for (const k of Object.keys(raw)) {
|
|
41263
|
+
if (!allowed.includes(k)) {
|
|
41264
|
+
throw new ProtocolError3(`${label}: unexpected field ${JSON.stringify(k)}`);
|
|
41265
|
+
}
|
|
41266
|
+
}
|
|
41267
|
+
}
|
|
41268
|
+
function assertMatch(x, re, label, what) {
|
|
41269
|
+
assertString(x, label);
|
|
41270
|
+
if (!re.test(x))
|
|
41271
|
+
throw new ProtocolError3(`${label}: must be ${what}`);
|
|
41272
|
+
}
|
|
41273
|
+
function assertPositiveSafeInt(x, label) {
|
|
41274
|
+
if (typeof x !== "number" || !Number.isSafeInteger(x) || x <= 0) {
|
|
41275
|
+
throw new ProtocolError3(`${label}: expected positive safe integer`);
|
|
41276
|
+
}
|
|
41277
|
+
}
|
|
41278
|
+
function assertDisplayString(x, label, min, max) {
|
|
41279
|
+
assertString(x, label);
|
|
41280
|
+
if (x.length < min || x.length > max) {
|
|
41281
|
+
throw new ProtocolError3(`${label}: must be ${min}-${max} characters`);
|
|
41282
|
+
}
|
|
41283
|
+
if (DISPLAY_FORBIDDEN_RE.test(x)) {
|
|
41284
|
+
throw new ProtocolError3(`${label}: control or bidi-override character`);
|
|
41285
|
+
}
|
|
41286
|
+
}
|
|
41287
|
+
var ACCOUNT_KEY_FIELDS = [
|
|
41288
|
+
"type",
|
|
41289
|
+
"accountId",
|
|
41290
|
+
"slug",
|
|
41291
|
+
"displayName",
|
|
41292
|
+
"confirmedBy",
|
|
41293
|
+
"tokenId",
|
|
41294
|
+
"kid",
|
|
41295
|
+
"publicKey",
|
|
41296
|
+
"generation",
|
|
41297
|
+
"bridgedRegistrations"
|
|
41298
|
+
];
|
|
41299
|
+
function validateAccountKey(raw) {
|
|
41300
|
+
const L = "account-key";
|
|
41301
|
+
assertMatch(raw.accountId, ACCOUNT_ID_RE, `${L}.accountId`, "an account id");
|
|
41302
|
+
assertMatch(raw.slug, ACCOUNT_ID_RE, `${L}.slug`, "a slug");
|
|
41303
|
+
assertDisplayString(raw.displayName, `${L}.displayName`, 0, 128);
|
|
41304
|
+
assertDisplayString(raw.confirmedBy, `${L}.confirmedBy`, 1, 256);
|
|
41305
|
+
assertMatch(raw.tokenId, ACCOUNT_ID_RE, `${L}.tokenId`, "a token id");
|
|
41306
|
+
assertMatch(raw.kid, ACCOUNT_KID_RE, `${L}.kid`, "16 lowercase hex");
|
|
41307
|
+
assertBase64Bytes(raw.publicKey, `${L}.publicKey`, 32);
|
|
41308
|
+
assertPositiveSafeInt(raw.generation, `${L}.generation`);
|
|
41309
|
+
const n = raw.bridgedRegistrations;
|
|
41310
|
+
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 0) {
|
|
41311
|
+
throw new ProtocolError3(`${L}.bridgedRegistrations: expected non-negative safe integer`);
|
|
41312
|
+
}
|
|
41313
|
+
assertExactFields(raw, ACCOUNT_KEY_FIELDS, L);
|
|
41314
|
+
return {
|
|
41315
|
+
type: ACCOUNT_KEY_FRAME,
|
|
41316
|
+
accountId: raw.accountId,
|
|
41317
|
+
slug: raw.slug,
|
|
41318
|
+
displayName: raw.displayName,
|
|
41319
|
+
confirmedBy: raw.confirmedBy,
|
|
41320
|
+
tokenId: raw.tokenId,
|
|
41321
|
+
kid: raw.kid,
|
|
41322
|
+
publicKey: raw.publicKey,
|
|
41323
|
+
generation: raw.generation,
|
|
41324
|
+
bridgedRegistrations: n
|
|
41325
|
+
};
|
|
41326
|
+
}
|
|
41327
|
+
var ACCOUNT_ATTEST_FIELDS = [
|
|
41328
|
+
"type",
|
|
41329
|
+
"mcpId",
|
|
41330
|
+
"accountId",
|
|
41331
|
+
"generation",
|
|
41332
|
+
"tokenId",
|
|
41333
|
+
"kid",
|
|
41334
|
+
"registrationId",
|
|
41335
|
+
"slug",
|
|
41336
|
+
"identityHash",
|
|
41337
|
+
"identityEd25519Pub",
|
|
41338
|
+
"scopeDigest",
|
|
41339
|
+
"consent",
|
|
41340
|
+
"notAfter",
|
|
41341
|
+
"sig"
|
|
41342
|
+
];
|
|
41343
|
+
function validateAccountAttest(raw) {
|
|
41344
|
+
const L = "account-attest";
|
|
41345
|
+
if (!isValidMcpId(raw.mcpId))
|
|
41346
|
+
throw new ProtocolError3(`${L}.mcpId: invalid format`);
|
|
41347
|
+
assertMatch(raw.accountId, ACCOUNT_ID_RE, `${L}.accountId`, "an account id");
|
|
41348
|
+
assertPositiveSafeInt(raw.generation, `${L}.generation`);
|
|
41349
|
+
assertMatch(raw.tokenId, ACCOUNT_ID_RE, `${L}.tokenId`, "a token id");
|
|
41350
|
+
assertMatch(raw.kid, ACCOUNT_KID_RE, `${L}.kid`, "16 lowercase hex");
|
|
41351
|
+
assertMatch(raw.registrationId, ACCOUNT_ID_RE, `${L}.registrationId`, "a registration id");
|
|
41352
|
+
assertMatch(raw.slug, ACCOUNT_ID_RE, `${L}.slug`, "a slug");
|
|
41353
|
+
assertMatch(raw.identityHash, HEX64_RE, `${L}.identityHash`, "64 lowercase hex");
|
|
41354
|
+
assertBase64Bytes(raw.identityEd25519Pub, `${L}.identityEd25519Pub`, 32);
|
|
41355
|
+
assertMatch(raw.scopeDigest, HEX64_RE, `${L}.scopeDigest`, "64 lowercase hex");
|
|
41356
|
+
assertString(raw.consent, `${L}.consent`);
|
|
41357
|
+
const consent = raw.consent;
|
|
41358
|
+
const known = ACCOUNT_ATTEST_CONSENTS.find((c) => c === consent);
|
|
41359
|
+
if (known === void 0) {
|
|
41360
|
+
throw new ProtocolError3(`${L}.consent: must be one of ${ACCOUNT_ATTEST_CONSENTS.join(", ")}`);
|
|
41361
|
+
}
|
|
41362
|
+
assertPositiveSafeInt(raw.notAfter, `${L}.notAfter`);
|
|
41363
|
+
assertBase64Bytes(raw.sig, `${L}.sig`, 64);
|
|
41364
|
+
assertExactFields(raw, ACCOUNT_ATTEST_FIELDS, L);
|
|
41365
|
+
return {
|
|
41366
|
+
type: ACCOUNT_ATTEST_FRAME,
|
|
41367
|
+
mcpId: raw.mcpId,
|
|
41368
|
+
accountId: raw.accountId,
|
|
41369
|
+
generation: raw.generation,
|
|
41370
|
+
tokenId: raw.tokenId,
|
|
41371
|
+
kid: raw.kid,
|
|
41372
|
+
registrationId: raw.registrationId,
|
|
41373
|
+
slug: raw.slug,
|
|
41374
|
+
identityHash: raw.identityHash,
|
|
41375
|
+
identityEd25519Pub: raw.identityEd25519Pub,
|
|
41376
|
+
scopeDigest: raw.scopeDigest,
|
|
41377
|
+
consent: known,
|
|
41378
|
+
notAfter: raw.notAfter,
|
|
41379
|
+
sig: raw.sig
|
|
41380
|
+
};
|
|
41381
|
+
}
|
|
41174
41382
|
function validateInnerFrame(raw) {
|
|
41175
41383
|
assertObject(raw, "inner");
|
|
41176
41384
|
const t = raw.type;
|
|
@@ -41638,10 +41846,10 @@ function validateInnerResponse(raw) {
|
|
|
41638
41846
|
}
|
|
41639
41847
|
|
|
41640
41848
|
// node_modules/@fetchproxy/protocol/dist/pair-code.js
|
|
41641
|
-
var
|
|
41849
|
+
var enc3 = new TextEncoder();
|
|
41642
41850
|
var PAIR_LABEL = "fetchproxy/4/pair";
|
|
41643
41851
|
async function pairTranscript(mcpIdentityPub, extIdentityPub, mcpNonce, extNonce, mcpSessionPub) {
|
|
41644
|
-
const label =
|
|
41852
|
+
const label = enc3.encode(PAIR_LABEL);
|
|
41645
41853
|
const parts = [mcpIdentityPub, extIdentityPub, mcpNonce, extNonce, mcpSessionPub];
|
|
41646
41854
|
const out = new Uint8Array(label.length + 1 + parts.reduce((n, p) => n + p.length, 0));
|
|
41647
41855
|
out.set(label, 0);
|
|
@@ -41662,7 +41870,7 @@ function digits(hash2) {
|
|
|
41662
41870
|
}
|
|
41663
41871
|
|
|
41664
41872
|
// node_modules/@fetchproxy/protocol/dist/seal.js
|
|
41665
|
-
var
|
|
41873
|
+
var enc4 = new TextEncoder();
|
|
41666
41874
|
var dec = new TextDecoder();
|
|
41667
41875
|
function randomIv() {
|
|
41668
41876
|
const iv = new Uint8Array(12);
|
|
@@ -41675,14 +41883,14 @@ function base64Length(byteLength) {
|
|
|
41675
41883
|
return Math.ceil(byteLength / 3) * 4;
|
|
41676
41884
|
}
|
|
41677
41885
|
function encodeInnerFrame(inner) {
|
|
41678
|
-
return
|
|
41886
|
+
return enc4.encode(JSON.stringify(inner));
|
|
41679
41887
|
}
|
|
41680
41888
|
function plaintextOf(inner) {
|
|
41681
41889
|
return inner instanceof Uint8Array ? inner : encodeInnerFrame(inner);
|
|
41682
41890
|
}
|
|
41683
41891
|
function sealedFrameWireBytes(mcpId, seq, inner) {
|
|
41684
41892
|
const plaintext = plaintextOf(inner).length;
|
|
41685
|
-
const envelope =
|
|
41893
|
+
const envelope = enc4.encode(JSON.stringify({ type: "frame", mcpId, seq, iv: "", ciphertext: "" })).length;
|
|
41686
41894
|
return envelope + IV_B64_BYTES + base64Length(plaintext + AES_GCM_TAG_BYTES);
|
|
41687
41895
|
}
|
|
41688
41896
|
var MAX_FRAME_BYTES = 42 * 1024 * 1024;
|
|
@@ -42022,14 +42230,23 @@ var FetchproxyHelloRejectedError = class extends Error {
|
|
|
42022
42230
|
platform;
|
|
42023
42231
|
/**
|
|
42024
42232
|
* #418: the remedy, when the reason is one this package knows how to
|
|
42025
|
-
* explain —
|
|
42026
|
-
*
|
|
42233
|
+
* explain — `unsupported-capability`, which blames the browser rather than
|
|
42234
|
+
* the MCP, and (D12) `awaiting-approval`, which says to approve in the
|
|
42235
|
+
* browser. `null` otherwise.
|
|
42027
42236
|
*/
|
|
42028
42237
|
hint;
|
|
42238
|
+
/**
|
|
42239
|
+
* D12: `true` only for an `awaiting-approval:` refusal — the extension has
|
|
42240
|
+
* queued an approval card and nobody is at the browser yet, so the same call
|
|
42241
|
+
* succeeds once the person approves. Every other refusal is the extension's
|
|
42242
|
+
* final answer, and retrying unchanged is refused the same way.
|
|
42243
|
+
*/
|
|
42244
|
+
retryable;
|
|
42029
42245
|
constructor(info) {
|
|
42030
42246
|
const unavailable = parseUnsupportedCapabilityReason(info.reason);
|
|
42247
|
+
const awaiting = parseAwaitingApprovalReason(info.reason);
|
|
42031
42248
|
const platform = info.platform ?? null;
|
|
42032
|
-
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;
|
|
42249
|
+
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;
|
|
42033
42250
|
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.`));
|
|
42034
42251
|
this.name = "FetchproxyHelloRejectedError";
|
|
42035
42252
|
this.mcpId = info.mcpId;
|
|
@@ -42037,6 +42254,7 @@ var FetchproxyHelloRejectedError = class extends Error {
|
|
|
42037
42254
|
this.unavailableCapabilities = unavailable ?? [];
|
|
42038
42255
|
this.platform = platform;
|
|
42039
42256
|
this.hint = hint;
|
|
42257
|
+
this.retryable = awaiting !== null;
|
|
42040
42258
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
42041
42259
|
}
|
|
42042
42260
|
};
|
|
@@ -42589,7 +42807,7 @@ function originVerdict(origin, allowLocal) {
|
|
|
42589
42807
|
}
|
|
42590
42808
|
var HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
42591
42809
|
var MAX_PAYLOAD_BYTES = MAX_FRAME_BYTES;
|
|
42592
|
-
var
|
|
42810
|
+
var enc5 = new TextEncoder();
|
|
42593
42811
|
var OWN_DECRYPT_FAILURES_BEFORE_REHANDSHAKE = 3;
|
|
42594
42812
|
async function startHost(opts) {
|
|
42595
42813
|
const allowLocalOrigins = envAllowsLocalOrigins();
|
|
@@ -42660,10 +42878,20 @@ async function startHost(opts) {
|
|
|
42660
42878
|
let resolveOwnSession;
|
|
42661
42879
|
let rejectOwnSession;
|
|
42662
42880
|
let ownSessionReady;
|
|
42881
|
+
let ownRetryableRefusal = false;
|
|
42882
|
+
let ownSessionSettled = false;
|
|
42663
42883
|
function resetSessionPromise() {
|
|
42884
|
+
ownRetryableRefusal = false;
|
|
42885
|
+
ownSessionSettled = false;
|
|
42664
42886
|
ownSessionReady = new Promise((resolve7, reject) => {
|
|
42665
|
-
resolveOwnSession =
|
|
42666
|
-
|
|
42887
|
+
resolveOwnSession = (st) => {
|
|
42888
|
+
ownSessionSettled = true;
|
|
42889
|
+
resolve7(st);
|
|
42890
|
+
};
|
|
42891
|
+
rejectOwnSession = (e) => {
|
|
42892
|
+
ownSessionSettled = true;
|
|
42893
|
+
reject(e);
|
|
42894
|
+
};
|
|
42667
42895
|
});
|
|
42668
42896
|
ownSessionReady.catch(() => {
|
|
42669
42897
|
});
|
|
@@ -42930,13 +43158,15 @@ async function startHost(opts) {
|
|
|
42930
43158
|
const extPub = fromB64(frame.extensionSessionPub);
|
|
42931
43159
|
const shared = await ecdhX25519(held.priv, extPub);
|
|
42932
43160
|
const salt = await transcriptHash(held.nonce, extNonce, held.pub, extPub);
|
|
42933
|
-
const key = await hkdfSha256(shared, salt,
|
|
43161
|
+
const key = await hkdfSha256(shared, salt, enc5.encode(HKDF_SESSION_INFO), 32);
|
|
42934
43162
|
if (extensionWs !== ws)
|
|
42935
43163
|
return;
|
|
42936
43164
|
ownSession = new SessionState(key);
|
|
42937
43165
|
ownSessionInfo = sessionInfoFromHello(verifiedHello);
|
|
42938
43166
|
ownDecryptFailures = 0;
|
|
42939
43167
|
ownPendingPairCode = null;
|
|
43168
|
+
if (ownRetryableRefusal)
|
|
43169
|
+
resetSessionPromise();
|
|
42940
43170
|
resolveOwnSession(ownSession);
|
|
42941
43171
|
} else {
|
|
42942
43172
|
const slot = peers.get(frame.mcpId);
|
|
@@ -43005,11 +43235,17 @@ async function startHost(opts) {
|
|
|
43005
43235
|
}
|
|
43006
43236
|
if (frame.type === "hello-rejected" && identified === "extension") {
|
|
43007
43237
|
if (frame.mcpId === opts.ownMcpId) {
|
|
43008
|
-
|
|
43238
|
+
const refusal = new FetchproxyHelloRejectedError({
|
|
43009
43239
|
mcpId: frame.mcpId,
|
|
43010
43240
|
reason: frame.reason,
|
|
43011
43241
|
platform: extensionHello?.platform ?? null
|
|
43012
|
-
})
|
|
43242
|
+
});
|
|
43243
|
+
if (ownRetryableRefusal)
|
|
43244
|
+
resetSessionPromise();
|
|
43245
|
+
const settlesPending = !ownSessionSettled;
|
|
43246
|
+
rejectOwnSession(refusal);
|
|
43247
|
+
if (refusal.retryable && settlesPending)
|
|
43248
|
+
ownRetryableRefusal = true;
|
|
43013
43249
|
} else {
|
|
43014
43250
|
const slot = peers.get(frame.mcpId);
|
|
43015
43251
|
if (slot?.helloFrame.accepts?.includes("hello-rejected")) {
|
|
@@ -43125,7 +43361,7 @@ async function startHost(opts) {
|
|
|
43125
43361
|
|
|
43126
43362
|
// node_modules/@fetchproxy/server/dist/peer.js
|
|
43127
43363
|
var PEER_DIAL_TIMEOUT_MS = 5e3;
|
|
43128
|
-
var
|
|
43364
|
+
var enc6 = new TextEncoder();
|
|
43129
43365
|
function notSentError() {
|
|
43130
43366
|
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.");
|
|
43131
43367
|
}
|
|
@@ -43225,7 +43461,9 @@ async function startPeer(opts) {
|
|
|
43225
43461
|
let rejectFirstReady;
|
|
43226
43462
|
let sessionPromise;
|
|
43227
43463
|
let sessionPromiseSettled = false;
|
|
43464
|
+
let retryableRefusal = false;
|
|
43228
43465
|
function resetSessionPromise() {
|
|
43466
|
+
retryableRefusal = false;
|
|
43229
43467
|
sessionPromiseSettled = false;
|
|
43230
43468
|
sessionPromise = new Promise((resolve7, reject) => {
|
|
43231
43469
|
resolveFirstReady = (st) => {
|
|
@@ -43322,6 +43560,8 @@ async function startPeer(opts) {
|
|
|
43322
43560
|
raw = JSON.parse(data.toString());
|
|
43323
43561
|
const frame = validateFrame(raw);
|
|
43324
43562
|
if (frame.type === "hello" && frame.role === "extension") {
|
|
43563
|
+
if (retryableRefusal)
|
|
43564
|
+
resetSessionPromise();
|
|
43325
43565
|
extensionHello = frame;
|
|
43326
43566
|
const mintedKeypair = await generateSessionKeypair();
|
|
43327
43567
|
const mintedHello = await buildServerHello({
|
|
@@ -43351,6 +43591,8 @@ async function startPeer(opts) {
|
|
|
43351
43591
|
session = null;
|
|
43352
43592
|
if (sessionPromiseSettled)
|
|
43353
43593
|
resetSessionPromise();
|
|
43594
|
+
} else if (retryableRefusal) {
|
|
43595
|
+
resetSessionPromise();
|
|
43354
43596
|
}
|
|
43355
43597
|
extensionDisconnectListeners.forEach((cb) => cb());
|
|
43356
43598
|
return;
|
|
@@ -43372,7 +43614,7 @@ async function startPeer(opts) {
|
|
|
43372
43614
|
const extNonce = fromB64(authenticated.sessionNonce);
|
|
43373
43615
|
const shared = await ecdhX25519(held.priv, extPub);
|
|
43374
43616
|
const salt = await transcriptHash(held.nonce, extNonce, held.pub, extPub);
|
|
43375
|
-
const sessionKey = await hkdfSha256(shared, salt,
|
|
43617
|
+
const sessionKey = await hkdfSha256(shared, salt, enc6.encode(HKDF_SESSION_INFO), 32);
|
|
43376
43618
|
if (sessionEphemeral !== held) {
|
|
43377
43619
|
console.warn(`[fetchproxy] ${opts.serverName}: dropping a session derived against an ephemeral that was superseded while it was being derived`);
|
|
43378
43620
|
return;
|
|
@@ -43385,16 +43627,24 @@ async function startPeer(opts) {
|
|
|
43385
43627
|
if (isRenegotiation) {
|
|
43386
43628
|
renegotiateListeners.forEach((cb) => cb());
|
|
43387
43629
|
} else {
|
|
43630
|
+
if (retryableRefusal)
|
|
43631
|
+
resetSessionPromise();
|
|
43388
43632
|
resolveFirstReady(session);
|
|
43389
43633
|
}
|
|
43390
43634
|
return;
|
|
43391
43635
|
}
|
|
43392
43636
|
if (frame.type === "hello-rejected" && frame.mcpId === opts.mcpId) {
|
|
43393
|
-
|
|
43637
|
+
const refusal = new FetchproxyHelloRejectedError({
|
|
43394
43638
|
mcpId: frame.mcpId,
|
|
43395
43639
|
reason: frame.reason,
|
|
43396
43640
|
platform: extensionHello?.platform ?? null
|
|
43397
|
-
})
|
|
43641
|
+
});
|
|
43642
|
+
if (retryableRefusal)
|
|
43643
|
+
resetSessionPromise();
|
|
43644
|
+
const settlesPending = !sessionPromiseSettled;
|
|
43645
|
+
rejectFirstReady(refusal);
|
|
43646
|
+
if (refusal.retryable && settlesPending)
|
|
43647
|
+
retryableRefusal = true;
|
|
43398
43648
|
}
|
|
43399
43649
|
if (frame.type === "pair-pending" && frame.mcpId === opts.mcpId) {
|
|
43400
43650
|
const hello2 = extensionHello;
|
|
@@ -43558,8 +43808,9 @@ function classifyFetchError(error62, code2) {
|
|
|
43558
43808
|
|
|
43559
43809
|
// node_modules/@fetchproxy/server/dist/classify-bridge-error.js
|
|
43560
43810
|
function classifyBridgeError(err) {
|
|
43561
|
-
if (err instanceof FetchproxyHelloRejectedError)
|
|
43562
|
-
return "hello_rejected";
|
|
43811
|
+
if (err instanceof FetchproxyHelloRejectedError) {
|
|
43812
|
+
return err.retryable ? "awaiting_approval" : "hello_rejected";
|
|
43813
|
+
}
|
|
43563
43814
|
if (err instanceof FetchproxySessionNotReadyError)
|
|
43564
43815
|
return "session_not_ready";
|
|
43565
43816
|
if (err instanceof FetchproxyTimeoutError)
|
|
@@ -46330,7 +46581,7 @@ async function loginWithPassword(username, password) {
|
|
|
46330
46581
|
// package.json
|
|
46331
46582
|
var package_default = {
|
|
46332
46583
|
name: "ofw-mcp",
|
|
46333
|
-
version: "2.20.
|
|
46584
|
+
version: "2.20.2",
|
|
46334
46585
|
license: "MIT",
|
|
46335
46586
|
mcpName: "io.github.chrischall/ofw-mcp",
|
|
46336
46587
|
description: "OurFamilyWizard MCP server for Claude \u2014 developed and maintained by AI (Claude Code)",
|
|
@@ -46364,14 +46615,14 @@ var package_default = {
|
|
|
46364
46615
|
typecheck: "tsc -p tsconfig.json --noEmit"
|
|
46365
46616
|
},
|
|
46366
46617
|
dependencies: {
|
|
46367
|
-
"@chrischall/mcp-utils": "^2.
|
|
46618
|
+
"@chrischall/mcp-utils": "^2.14.0",
|
|
46368
46619
|
"@fetchproxy/bootstrap": "^3.4.1",
|
|
46369
|
-
"@modelcontextprotocol/server": "^2.
|
|
46620
|
+
"@modelcontextprotocol/server": "^2.3.0",
|
|
46370
46621
|
dotenv: "^18.0.0",
|
|
46371
46622
|
zod: "^4.6.5"
|
|
46372
46623
|
},
|
|
46373
46624
|
devDependencies: {
|
|
46374
|
-
"@modelcontextprotocol/client": "^2.
|
|
46625
|
+
"@modelcontextprotocol/client": "^2.3.0",
|
|
46375
46626
|
"@types/node": "^26.0.0",
|
|
46376
46627
|
"@vitest/coverage-v8": "^5.0.0",
|
|
46377
46628
|
esbuild: "^0.28.0",
|
|
@@ -46519,10 +46770,10 @@ var OFW_REFRESH_SENTINEL = "ofw";
|
|
|
46519
46770
|
async function throwIfEdgeBlocked(response, method, path) {
|
|
46520
46771
|
if (response.ok) return;
|
|
46521
46772
|
let edge = detectEdgeBlock({ headers: response.headers, status: response.status });
|
|
46522
|
-
if (edge === null && response.status === 429 && !/json/i.test(response.headers.get("content-type") ?? "")) {
|
|
46773
|
+
if (edge === null && (response.status === 401 || response.status === 429) && !/json/i.test(response.headers.get("content-type") ?? "")) {
|
|
46523
46774
|
let body = "";
|
|
46524
46775
|
try {
|
|
46525
|
-
body = await response.clone().text();
|
|
46776
|
+
body = await (response.status === 401 ? response.clone() : response).text();
|
|
46526
46777
|
} catch {
|
|
46527
46778
|
}
|
|
46528
46779
|
edge = detectEdgeBlock({ body, headers: response.headers, status: response.status });
|
|
@@ -46585,63 +46836,73 @@ var OFWClient = class {
|
|
|
46585
46836
|
return this.tokenManager;
|
|
46586
46837
|
}
|
|
46587
46838
|
async request(method, path, body) {
|
|
46588
|
-
|
|
46589
|
-
|
|
46590
|
-
|
|
46591
|
-
|
|
46592
|
-
|
|
46593
|
-
|
|
46839
|
+
return this.fetchAuthed(method, path, body, "application/json", async (response) => {
|
|
46840
|
+
const text = await response.text();
|
|
46841
|
+
if (debugLogEnabled()) {
|
|
46842
|
+
console.error(`[ofw-debug] response body: ${text || "<empty>"}`);
|
|
46843
|
+
}
|
|
46844
|
+
return text ? JSON.parse(text) : null;
|
|
46845
|
+
});
|
|
46594
46846
|
}
|
|
46595
46847
|
/** Like `request`, but returns the raw bytes plus Content-Type/-Disposition metadata. */
|
|
46596
46848
|
async requestBinary(method, path) {
|
|
46597
|
-
|
|
46598
|
-
return {
|
|
46849
|
+
return this.fetchAuthed(method, path, void 0, "application/octet-stream", async (response) => ({
|
|
46599
46850
|
body: Buffer.from(await response.arrayBuffer()),
|
|
46600
46851
|
contentType: response.headers.get("content-type"),
|
|
46601
46852
|
suggestedFileName: parseContentDispositionFilename(response.headers.get("content-disposition") ?? "") ?? null
|
|
46602
|
-
};
|
|
46853
|
+
}));
|
|
46603
46854
|
}
|
|
46604
46855
|
// Authenticated fetch for both JSON and binary callers. Auth (proactive
|
|
46605
46856
|
// refresh inside the skew window + one 401-replay, guarded against a
|
|
46606
46857
|
// double-refresh under concurrency) is delegated to the shared TokenManager's
|
|
46607
46858
|
// `withAuth`. The 429 wait-and-replay and the non-2xx → throw remain here.
|
|
46608
|
-
async fetchAuthed(method, path, body, accept) {
|
|
46859
|
+
async fetchAuthed(method, path, body, accept, consume) {
|
|
46609
46860
|
let attempt = 0;
|
|
46610
|
-
let
|
|
46611
|
-
|
|
46612
|
-
|
|
46613
|
-
|
|
46861
|
+
let result;
|
|
46862
|
+
const call = async (retry) => {
|
|
46863
|
+
let authAttempt = 0;
|
|
46864
|
+
return this.getTokenManager().withAuth((token) => {
|
|
46865
|
+
const firstAuthAttempt = authAttempt++ === 0;
|
|
46866
|
+
return this.fetchOnce(method, path, body, accept, token, async (response2) => {
|
|
46867
|
+
await throwIfEdgeBlocked(response2, method, path);
|
|
46868
|
+
if (response2.status === 401 && firstAuthAttempt) {
|
|
46869
|
+
await releaseBody(response2);
|
|
46870
|
+
return;
|
|
46871
|
+
}
|
|
46872
|
+
if (response2.status === 429) {
|
|
46873
|
+
await releaseBody(response2);
|
|
46874
|
+
return;
|
|
46875
|
+
}
|
|
46876
|
+
if (!response2.ok) {
|
|
46877
|
+
const errorBody = await response2.text();
|
|
46878
|
+
const edge = detectEdgeBlock({ body: errorBody, headers: response2.headers, status: response2.status });
|
|
46879
|
+
if (edge !== null) {
|
|
46880
|
+
throw new EdgeBlockedError(response2.status, edge.vendor, { service: "OurFamilyWizard", method, path });
|
|
46881
|
+
}
|
|
46882
|
+
const safeBody = redactSecrets(errorBody).replace(/\s+/g, " ").trim().slice(0, 4e3);
|
|
46883
|
+
throw new Error(
|
|
46884
|
+
`OFW API error: ${response2.status} ${response2.statusText} for ${method} ${path}` + (safeBody ? ` \u2014 ${safeBody}` : "")
|
|
46885
|
+
);
|
|
46886
|
+
}
|
|
46887
|
+
result = await consume(response2);
|
|
46888
|
+
}, retry || attempt++ > 0);
|
|
46889
|
+
});
|
|
46890
|
+
};
|
|
46891
|
+
const response = await call(false);
|
|
46614
46892
|
if (response.status === 429) {
|
|
46615
|
-
await releaseBody(response);
|
|
46616
46893
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
46617
|
-
|
|
46618
|
-
(token) => this.fetchOnce(method, path, body, accept, token, true)
|
|
46619
|
-
);
|
|
46620
|
-
await throwIfEdgeBlocked(response, method, path);
|
|
46621
|
-
if (response.status === 429) {
|
|
46622
|
-
await releaseBody(response);
|
|
46894
|
+
if ((await call(true)).status === 429) {
|
|
46623
46895
|
throw new Error("Rate limited by OFW API");
|
|
46624
46896
|
}
|
|
46625
46897
|
}
|
|
46626
|
-
|
|
46627
|
-
const errorBody = await response.text();
|
|
46628
|
-
const edge = detectEdgeBlock({ body: errorBody, headers: response.headers, status: response.status });
|
|
46629
|
-
if (edge !== null) {
|
|
46630
|
-
throw new EdgeBlockedError(response.status, edge.vendor, { service: "OurFamilyWizard", method, path });
|
|
46631
|
-
}
|
|
46632
|
-
const safeBody = redactSecrets(errorBody).replace(/\s+/g, " ").trim().slice(0, 4e3);
|
|
46633
|
-
throw new Error(
|
|
46634
|
-
`OFW API error: ${response.status} ${response.statusText} for ${method} ${path}` + (safeBody ? ` \u2014 ${safeBody}` : "")
|
|
46635
|
-
);
|
|
46636
|
-
}
|
|
46637
|
-
return response;
|
|
46898
|
+
return result;
|
|
46638
46899
|
}
|
|
46639
46900
|
// A single OFW API fetch with the bearer token supplied by `withAuth`.
|
|
46640
46901
|
// Carries the per-request timeout (AbortController + setTimeout so vitest
|
|
46641
46902
|
// fake timers can drive it and we attach a clear error message) and the
|
|
46642
|
-
// OFW_DEBUG_LOG instrumentation.
|
|
46643
|
-
//
|
|
46644
|
-
async fetchOnce(method, path, body, accept, token, isRetry = false) {
|
|
46903
|
+
// OFW_DEBUG_LOG instrumentation. The handler completes body consumption or
|
|
46904
|
+
// release before this attempt returns its Response to `withAuth`.
|
|
46905
|
+
async fetchOnce(method, path, body, accept, token, handleResponse, isRetry = false) {
|
|
46645
46906
|
const isFormData = body instanceof FormData;
|
|
46646
46907
|
const headers = {
|
|
46647
46908
|
...OFW_PROTOCOL_HEADERS,
|
|
@@ -46661,22 +46922,31 @@ var OFWClient = class {
|
|
|
46661
46922
|
const ac = new AbortController();
|
|
46662
46923
|
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
46663
46924
|
const startedAt = Date.now();
|
|
46664
|
-
|
|
46925
|
+
const signal = withAmbientCancellation(ac.signal);
|
|
46926
|
+
let onAbort;
|
|
46927
|
+
const aborted2 = new Promise((_resolve, reject) => {
|
|
46928
|
+
onAbort = () => reject(signal.reason);
|
|
46929
|
+
if (signal.aborted) onAbort();
|
|
46930
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
46931
|
+
});
|
|
46665
46932
|
try {
|
|
46666
|
-
|
|
46667
|
-
|
|
46668
|
-
|
|
46669
|
-
|
|
46670
|
-
|
|
46671
|
-
|
|
46672
|
-
|
|
46673
|
-
|
|
46674
|
-
|
|
46675
|
-
|
|
46676
|
-
|
|
46677
|
-
|
|
46678
|
-
|
|
46679
|
-
|
|
46933
|
+
return await Promise.race([aborted2, (async () => {
|
|
46934
|
+
signal.throwIfAborted();
|
|
46935
|
+
const response = await fetch(url2, {
|
|
46936
|
+
method,
|
|
46937
|
+
headers,
|
|
46938
|
+
// The caller's cancellation and our timeout both cover headers,
|
|
46939
|
+
// body reads and releases; only our controller diagnoses slowness.
|
|
46940
|
+
signal,
|
|
46941
|
+
...body !== void 0 ? { body: isFormData ? body : JSON.stringify(body) } : {}
|
|
46942
|
+
});
|
|
46943
|
+
if (debugLogEnabled()) {
|
|
46944
|
+
console.error(`[ofw-debug] \u2190 ${response.status} ${response.statusText} (${Date.now() - startedAt}ms)`);
|
|
46945
|
+
}
|
|
46946
|
+
await handleResponse(response);
|
|
46947
|
+
signal.throwIfAborted();
|
|
46948
|
+
return response;
|
|
46949
|
+
})()]);
|
|
46680
46950
|
} catch (err) {
|
|
46681
46951
|
const elapsed = Date.now() - startedAt;
|
|
46682
46952
|
if (ac.signal.aborted) {
|
|
@@ -46693,11 +46963,8 @@ var OFWClient = class {
|
|
|
46693
46963
|
throw err;
|
|
46694
46964
|
} finally {
|
|
46695
46965
|
clearTimeout(timer);
|
|
46966
|
+
signal.removeEventListener("abort", onAbort);
|
|
46696
46967
|
}
|
|
46697
|
-
if (debugLogEnabled()) {
|
|
46698
|
-
console.error(`[ofw-debug] \u2190 ${response.status} ${response.statusText} (${Date.now() - startedAt}ms)`);
|
|
46699
|
-
}
|
|
46700
|
-
return response;
|
|
46701
46968
|
}
|
|
46702
46969
|
};
|
|
46703
46970
|
var client = new OFWClient();
|
|
@@ -50942,7 +51209,7 @@ function registerCalendarTools(server, client2) {
|
|
|
50942
51209
|
});
|
|
50943
51210
|
});
|
|
50944
51211
|
if (allowWrites) server.registerTool("ofw_update_event", {
|
|
50945
|
-
description: "Update an existing OurFamilyWizard calendar event. Fetches the event, applies the given changes, and writes the merged result back (OFW has no partial update). A change to an event the co-parent can see (shared before or after the change) is confirmed first; the confirmation is bound to the event exactly as read, so if it changes on OFW in between (say the co-parent edited it) the update is refused instead of overwriting their edit. " + CONFIRM_NOTE,
|
|
51212
|
+
description: "Update an existing OurFamilyWizard calendar event. Fetches the event, applies the given changes, and writes the merged result back (OFW has no partial update). A change to an event the co-parent can see (shared before or after the change) is confirmed first; the confirmation is bound to the event exactly as read, so if it changes on OFW in between (say the co-parent edited it) the update is refused instead of overwriting their edit. If the write fails without a definitive answer, or lands but cannot be re-read to confirm it, the result is EVENT_UNCONFIRMED: the change may already be on OFW, so do NOT retry until ofw_list_events shows whether it landed. " + CONFIRM_NOTE,
|
|
50946
51213
|
annotations: { destructiveHint: true },
|
|
50947
51214
|
inputSchema: external_exports.object({
|
|
50948
51215
|
eventId: external_exports.string().describe("Event id \u2014 the `id` from ofw_list_events / eventRecurrenceId from ofw_create_event"),
|
|
@@ -50991,13 +51258,30 @@ function registerCalendarTools(server, client2) {
|
|
|
50991
51258
|
});
|
|
50992
51259
|
if (gate) return gate;
|
|
50993
51260
|
}
|
|
50994
|
-
|
|
50995
|
-
|
|
50996
|
-
|
|
50997
|
-
|
|
51261
|
+
try {
|
|
51262
|
+
await requestWrite(client2, "PUT", `/pub/v3/events/${id}`, payload);
|
|
51263
|
+
} catch (e) {
|
|
51264
|
+
if (!(e instanceof UnconfirmedWriteError)) throw e;
|
|
51265
|
+
return unconfirmedWriteResponse(e, {
|
|
51266
|
+
result: "EVENT_UNCONFIRMED",
|
|
51267
|
+
what: `update event ${eventId}`,
|
|
51268
|
+
checkWith: "ofw_list_events for this event and date range"
|
|
51269
|
+
});
|
|
51270
|
+
}
|
|
51271
|
+
try {
|
|
51272
|
+
const rawAfter = await client2.request("GET", `/pub/v3/events/${id}`);
|
|
51273
|
+
const event = parseLenient(eventDetailSchema, rawAfter, { label: "ofw-mcp", context: `GET /pub/v3/events/${eventId} (post-update)`, mode: "strict" });
|
|
51274
|
+
return jsonResponse({ note: "Event updated; returning re-fetched event state.", event });
|
|
51275
|
+
} catch (e) {
|
|
51276
|
+
return unconfirmedWriteResponse(new UnconfirmedWriteError(null, e), {
|
|
51277
|
+
result: "EVENT_UNCONFIRMED",
|
|
51278
|
+
what: `verify the update to event ${eventId}`,
|
|
51279
|
+
checkWith: "ofw_list_events for this event and date range"
|
|
51280
|
+
});
|
|
51281
|
+
}
|
|
50998
51282
|
});
|
|
50999
51283
|
if (allowWrites) server.registerTool("ofw_delete_event", {
|
|
51000
|
-
description: "Delete an OurFamilyWizard calendar event. Reads the event first; deleting one the co-parent can see is confirmed first, with a preview of exactly which event (title, date, time) is removed, and is refused if the event changed on OFW after that preview. " + CONFIRM_NOTE,
|
|
51284
|
+
description: "Delete an OurFamilyWizard calendar event. Reads the event first; deleting one the co-parent can see is confirmed first, with a preview of exactly which event (title, date, time) is removed, and is refused if the event changed on OFW after that preview. If the request fails without a definitive answer the result is EVENT_UNCONFIRMED: the event may already be gone, so do NOT retry until ofw_list_events shows whether it is still there. " + CONFIRM_NOTE,
|
|
51001
51285
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
51002
51286
|
inputSchema: external_exports.object({
|
|
51003
51287
|
eventId: external_exports.string().describe("Event id \u2014 the `id` from ofw_list_events / eventRecurrenceId from ofw_create_event"),
|
|
@@ -51030,7 +51314,16 @@ function registerCalendarTools(server, client2) {
|
|
|
51030
51314
|
});
|
|
51031
51315
|
if (gate) return gate;
|
|
51032
51316
|
}
|
|
51033
|
-
|
|
51317
|
+
try {
|
|
51318
|
+
await requestWrite(client2, "DELETE", `/pub/v3/events/${id}?includeFuture=${includeFuture}`);
|
|
51319
|
+
} catch (e) {
|
|
51320
|
+
if (!(e instanceof UnconfirmedWriteError)) throw e;
|
|
51321
|
+
return unconfirmedWriteResponse(e, {
|
|
51322
|
+
result: "EVENT_UNCONFIRMED",
|
|
51323
|
+
what: `delete event ${args.eventId}${includeFuture ? " and its future occurrences" : ""}`,
|
|
51324
|
+
checkWith: "ofw_list_events for this event and date range (including future occurrences if requested)"
|
|
51325
|
+
});
|
|
51326
|
+
}
|
|
51034
51327
|
return textResponse(`Event ${args.eventId} ("${current.title}") deleted`);
|
|
51035
51328
|
});
|
|
51036
51329
|
}
|
|
@@ -52206,7 +52499,7 @@ var tools = selectToolRegistrars({
|
|
|
52206
52499
|
});
|
|
52207
52500
|
await runMcp({
|
|
52208
52501
|
name: "ofw",
|
|
52209
|
-
version: "2.20.
|
|
52502
|
+
version: "2.20.2",
|
|
52210
52503
|
// x-release-please-version
|
|
52211
52504
|
deps: client,
|
|
52212
52505
|
tools,
|
|
@@ -52214,7 +52507,7 @@ await runMcp({
|
|
|
52214
52507
|
});
|
|
52215
52508
|
/*! Bundled license information:
|
|
52216
52509
|
|
|
52217
|
-
@modelcontextprotocol/server/dist/src-
|
|
52510
|
+
@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs:
|
|
52218
52511
|
(*!
|
|
52219
52512
|
* content-type
|
|
52220
52513
|
* Copyright(c) 2015 Douglas Christopher Wilson
|