@ai-sdk/mcp 2.0.68 → 2.0.70
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/CHANGELOG.md +16 -0
- package/README.md +23 -0
- package/dist/index.d.ts +665 -348
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +368 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp-stdio/index.js +1 -0
- package/dist/mcp-stdio/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +24 -0
- package/src/tool/mcp-client.ts +62 -4
- package/src/tool/mcp-event-signature.ts +79 -0
- package/src/tool/mcp-event-types.ts +151 -0
- package/src/tool/mcp-event-webhook.ts +238 -0
- package/src/tool/mcp-events-adapter.ts +82 -0
- package/src/tool/mcp-events.ts +219 -0
- package/src/tool/oauth.ts +14 -1
- package/src/tool/types.ts +1 -0
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["symbol"],"sources":["../src/tool/json-rpc-message.ts","../src/tool/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["Request","symbol"],"sources":["../src/tool/json-rpc-message.ts","../src/tool/types.ts","../src/tool/mcp-event-types.ts","../src/tool/mcp-events-adapter.ts","../src/tool/oauth-types.ts","../src/error/oauth-error.ts","../src/tool/oauth.ts","../src/tool/mcp-transport.ts","../src/tool/mcp-client.ts","../src/tool/mcp-apps.ts","../src/tool/mcp-app-fingerprint.ts","../src/error/mcp-client-error.ts","../src/tool/mcp-event-webhook.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const RequestSchema = z.object({
|
|
|
29
29
|
/** @see https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation */
|
|
30
30
|
const ElicitationCapabilitySchema = z.object({ applyDefaults: z.optional(z.boolean()) }).loose();
|
|
31
31
|
const ServerCapabilitiesSchema = z.looseObject({
|
|
32
|
+
events: z.looseObject({ listChanged: z.boolean().nullish() }).nullish(),
|
|
32
33
|
experimental: z.optional(z.object({}).loose()),
|
|
33
34
|
logging: z.optional(z.object({}).loose()),
|
|
34
35
|
completions: z.optional(z.object({}).loose()),
|
|
@@ -296,8 +297,228 @@ var MCPClientError = class extends AISDKError {
|
|
|
296
297
|
}
|
|
297
298
|
};
|
|
298
299
|
//#endregion
|
|
300
|
+
//#region src/tool/mcp-event-signature.ts
|
|
301
|
+
function createMCPEventSecret() {
|
|
302
|
+
return `whsec_${convertUint8ArrayToBase64(crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32)))}`;
|
|
303
|
+
}
|
|
304
|
+
function decodeMCPEventSecret(secret) {
|
|
305
|
+
const encoded = secret.slice(6);
|
|
306
|
+
if (!secret.startsWith("whsec_") || !/^[A-Za-z0-9+/]+={0,2}$/.test(encoded)) throw new MCPClientError({ message: "Invalid MCP event signing secret" });
|
|
307
|
+
const bytes = convertBase64ToUint8Array(encoded);
|
|
308
|
+
if (bytes.length < 24 || bytes.length > 64) throw new MCPClientError({ message: "MCP event signing secrets must contain 24–64 random bytes" });
|
|
309
|
+
return bytes;
|
|
310
|
+
}
|
|
311
|
+
/** Standard Webhooks v1: authenticate the exact body bytes using Web Crypto. */
|
|
312
|
+
async function verifyMCPEventSignature({ headers, body, secret }) {
|
|
313
|
+
const id = headers.get("webhook-id");
|
|
314
|
+
const timestamp = headers.get("webhook-timestamp");
|
|
315
|
+
const signatures = headers.get("webhook-signature");
|
|
316
|
+
if (!id || !timestamp || !signatures || !/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1e3 - Number(timestamp)) > 300) return false;
|
|
317
|
+
const key = await crypto.subtle.importKey("raw", new Uint8Array(decodeMCPEventSecret(secret)), {
|
|
318
|
+
name: "HMAC",
|
|
319
|
+
hash: "SHA-256"
|
|
320
|
+
}, false, ["verify"]);
|
|
321
|
+
const prefix = new TextEncoder().encode(`${id}.${timestamp}.`);
|
|
322
|
+
const message = new Uint8Array(prefix.length + body.length);
|
|
323
|
+
message.set(prefix);
|
|
324
|
+
message.set(body, prefix.length);
|
|
325
|
+
for (const signature of signatures.split(" ")) {
|
|
326
|
+
const [version, value] = signature.split(",");
|
|
327
|
+
if (version !== "v1" || !value) continue;
|
|
328
|
+
try {
|
|
329
|
+
if (await crypto.subtle.verify("HMAC", key, new Uint8Array(convertBase64ToUint8Array(value)), message)) return true;
|
|
330
|
+
} catch {}
|
|
331
|
+
}
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
335
|
+
//#region src/tool/mcp-event-types.ts
|
|
336
|
+
const JsonSchemaSchema = z.union([z.boolean(), z.record(z.string(), z.unknown())]);
|
|
337
|
+
const MCPEventDefinitionSchema = z.looseObject({
|
|
338
|
+
name: z.string().min(1),
|
|
339
|
+
description: z.string().nullish(),
|
|
340
|
+
delivery: z.array(z.enum([
|
|
341
|
+
"webhook",
|
|
342
|
+
"poll",
|
|
343
|
+
"push"
|
|
344
|
+
])).min(1),
|
|
345
|
+
inputSchema: JsonSchemaSchema,
|
|
346
|
+
payloadSchema: JsonSchemaSchema
|
|
347
|
+
});
|
|
348
|
+
const ListEventsResultSchema = ResultSchema.extend({
|
|
349
|
+
events: z.array(MCPEventDefinitionSchema),
|
|
350
|
+
nextCursor: z.string().nullish()
|
|
351
|
+
});
|
|
352
|
+
const SubscribeEventResultSchema = ResultSchema.extend({
|
|
353
|
+
id: z.string().min(1),
|
|
354
|
+
refreshBefore: z.iso.datetime({ offset: true }).nullable(),
|
|
355
|
+
cursor: z.string().nullish().transform((cursor) => cursor ?? null),
|
|
356
|
+
truncated: z.boolean()
|
|
357
|
+
});
|
|
358
|
+
const MCPEventSchema = z.looseObject({
|
|
359
|
+
eventId: z.string().min(1),
|
|
360
|
+
name: z.string().min(1),
|
|
361
|
+
timestamp: z.iso.datetime({ offset: true }),
|
|
362
|
+
data: z.record(z.string(), z.unknown()),
|
|
363
|
+
cursor: z.string().nullish()
|
|
364
|
+
});
|
|
365
|
+
const MCPEventControlSchema = z.discriminatedUnion("type", [z.looseObject({
|
|
366
|
+
type: z.literal("gap"),
|
|
367
|
+
cursor: z.string()
|
|
368
|
+
}), z.looseObject({
|
|
369
|
+
type: z.literal("terminated"),
|
|
370
|
+
error: z.looseObject({
|
|
371
|
+
code: z.number().int(),
|
|
372
|
+
message: z.string(),
|
|
373
|
+
data: z.unknown().nullish()
|
|
374
|
+
})
|
|
375
|
+
})]).transform((control) => control.type === "gap" ? {
|
|
376
|
+
...control,
|
|
377
|
+
truncated: true
|
|
378
|
+
} : control);
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/tool/mcp-events.ts
|
|
381
|
+
const MCP_EVENT_CALLBACK_KEY = "mcp_event_subscription";
|
|
382
|
+
function createMCPEvents({ request, store, validateArguments }) {
|
|
383
|
+
function getStore() {
|
|
384
|
+
if (!store) throw new MCPClientError({ message: "Configure experimental_events.store to manage direct event subscriptions" });
|
|
385
|
+
return store;
|
|
386
|
+
}
|
|
387
|
+
async function getSubscription({ id, key }) {
|
|
388
|
+
const storage = getStore();
|
|
389
|
+
const subscription = id !== void 0 ? await storage.getById(id) : await storage.get(key);
|
|
390
|
+
if (!subscription) throw new MCPClientError({ message: `Unknown MCP event subscription: ${id ?? key}` });
|
|
391
|
+
return subscription;
|
|
392
|
+
}
|
|
393
|
+
async function subscribe(subscription, options) {
|
|
394
|
+
const result = await request({
|
|
395
|
+
request: {
|
|
396
|
+
method: "events/subscribe",
|
|
397
|
+
params: {
|
|
398
|
+
name: subscription.name,
|
|
399
|
+
arguments: subscription.arguments,
|
|
400
|
+
delivery: subscription.delivery,
|
|
401
|
+
cursor: subscription.cursor,
|
|
402
|
+
...subscription.ttlMs === void 0 ? {} : { ttlMs: subscription.ttlMs },
|
|
403
|
+
...subscription.maxAgeMs === void 0 ? {} : { maxAgeMs: subscription.maxAgeMs }
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
resultSchema: SubscribeEventResultSchema,
|
|
407
|
+
options
|
|
408
|
+
});
|
|
409
|
+
await getStore().update(subscription.key, {
|
|
410
|
+
id: result.id,
|
|
411
|
+
refreshBefore: result.refreshBefore,
|
|
412
|
+
cursor: result.cursor,
|
|
413
|
+
truncated: result.truncated,
|
|
414
|
+
status: "active"
|
|
415
|
+
});
|
|
416
|
+
return result;
|
|
417
|
+
}
|
|
418
|
+
const events = {
|
|
419
|
+
list({ params, options } = {}) {
|
|
420
|
+
return request({
|
|
421
|
+
request: {
|
|
422
|
+
method: "events/list",
|
|
423
|
+
params
|
|
424
|
+
},
|
|
425
|
+
resultSchema: ListEventsResultSchema,
|
|
426
|
+
options
|
|
427
|
+
});
|
|
428
|
+
},
|
|
429
|
+
async subscribe({ name, arguments: args = {}, delivery, cursor = null, ttlMs, maxAgeMs, options }) {
|
|
430
|
+
const storage = getStore();
|
|
431
|
+
const url = new URL(delivery.url);
|
|
432
|
+
const localHttp = delivery.allowInsecureLocalhost === true && url.protocol === "http:" && (url.hostname === "127.0.0.1" || url.hostname === "[::1]");
|
|
433
|
+
if (url.protocol !== "https:" && !localHttp || url.username || url.password || url.hash) throw new MCPClientError({ message: "MCP event callback URLs must use HTTPS without credentials or fragments" });
|
|
434
|
+
if (delivery.mode !== "webhook") throw new MCPClientError({ message: "Only webhook event delivery is supported" });
|
|
435
|
+
if (ttlMs != null && (!Number.isSafeInteger(ttlMs) || ttlMs <= 0) || maxAgeMs != null && (!Number.isSafeInteger(maxAgeMs) || maxAgeMs < 0)) throw new MCPClientError({ message: "Invalid MCP event subscription lifetime or replay age" });
|
|
436
|
+
let pageCursor;
|
|
437
|
+
const visited = /* @__PURE__ */ new Set();
|
|
438
|
+
let definition;
|
|
439
|
+
do {
|
|
440
|
+
const page = await events.list({
|
|
441
|
+
params: { cursor: pageCursor },
|
|
442
|
+
options
|
|
443
|
+
});
|
|
444
|
+
definition = page.events.find((event) => event.name === name);
|
|
445
|
+
if (definition) break;
|
|
446
|
+
pageCursor = page.nextCursor ?? void 0;
|
|
447
|
+
if (pageCursor != null) {
|
|
448
|
+
if (visited.has(pageCursor)) throw new MCPClientError({ message: "Repeated MCP events pagination cursor" });
|
|
449
|
+
visited.add(pageCursor);
|
|
450
|
+
}
|
|
451
|
+
} while (pageCursor != null);
|
|
452
|
+
if (!definition) throw new MCPClientError({ message: `Unknown MCP event: ${name}` });
|
|
453
|
+
if (!definition.delivery.includes("webhook")) throw new MCPClientError({ message: `MCP event ${name} does not support webhook delivery` });
|
|
454
|
+
await validateArguments?.({
|
|
455
|
+
definition,
|
|
456
|
+
arguments: args
|
|
457
|
+
});
|
|
458
|
+
const secret = delivery.secret ?? createMCPEventSecret();
|
|
459
|
+
decodeMCPEventSecret(secret);
|
|
460
|
+
const key = crypto.randomUUID();
|
|
461
|
+
url.searchParams.set(MCP_EVENT_CALLBACK_KEY, key);
|
|
462
|
+
const subscription = {
|
|
463
|
+
key,
|
|
464
|
+
name,
|
|
465
|
+
arguments: structuredClone(args),
|
|
466
|
+
definition,
|
|
467
|
+
delivery: {
|
|
468
|
+
mode: "webhook",
|
|
469
|
+
url: url.href,
|
|
470
|
+
secret
|
|
471
|
+
},
|
|
472
|
+
cursor,
|
|
473
|
+
truncated: false,
|
|
474
|
+
ttlMs,
|
|
475
|
+
maxAgeMs,
|
|
476
|
+
status: "pending"
|
|
477
|
+
};
|
|
478
|
+
await storage.set(subscription);
|
|
479
|
+
return subscribe(subscription, options);
|
|
480
|
+
},
|
|
481
|
+
async refresh(args) {
|
|
482
|
+
const subscription = await getSubscription(args);
|
|
483
|
+
await getStore().update(subscription.key, { status: "pending" });
|
|
484
|
+
return subscribe(subscription, args.options);
|
|
485
|
+
},
|
|
486
|
+
async unsubscribe(args) {
|
|
487
|
+
const subscription = await getSubscription(args);
|
|
488
|
+
await request({
|
|
489
|
+
request: {
|
|
490
|
+
method: "events/unsubscribe",
|
|
491
|
+
params: {
|
|
492
|
+
name: subscription.name,
|
|
493
|
+
arguments: subscription.arguments,
|
|
494
|
+
delivery: {
|
|
495
|
+
mode: "webhook",
|
|
496
|
+
url: subscription.delivery.url
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
resultSchema: ResultSchema,
|
|
501
|
+
options: args.options
|
|
502
|
+
});
|
|
503
|
+
await getStore().delete(subscription.key);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
return events;
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/tool/mcp-events-adapter.ts
|
|
510
|
+
function createManagedMCPEvents(adapter, list) {
|
|
511
|
+
return {
|
|
512
|
+
list,
|
|
513
|
+
subscribe: (input) => adapter.subscribe(input),
|
|
514
|
+
getSubscription: (input) => adapter.getSubscription(input),
|
|
515
|
+
listSubscriptions: (input) => adapter.listSubscriptions(input),
|
|
516
|
+
unsubscribe: (input) => adapter.unsubscribe(input)
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
//#endregion
|
|
299
520
|
//#region src/version.ts
|
|
300
|
-
const VERSION = "2.0.
|
|
521
|
+
const VERSION = "2.0.70";
|
|
301
522
|
//#endregion
|
|
302
523
|
//#region src/tool/oauth-types.ts
|
|
303
524
|
/**
|
|
@@ -764,8 +985,10 @@ function buildDiscoveryUrls(authorizationServerUrl) {
|
|
|
764
985
|
type: "oidc",
|
|
765
986
|
expectedIssuer: pathIssuer
|
|
766
987
|
});
|
|
988
|
+
const oidcDiscoveryUrl = new URL(url.origin);
|
|
989
|
+
oidcDiscoveryUrl.pathname = `${pathname}/.well-known/openid-configuration`;
|
|
767
990
|
urlsToTry.push({
|
|
768
|
-
url:
|
|
991
|
+
url: oidcDiscoveryUrl,
|
|
769
992
|
type: "oidc",
|
|
770
993
|
expectedIssuer: pathIssuer
|
|
771
994
|
});
|
|
@@ -776,7 +999,9 @@ function assertMetadataIssuerMatches(metadata, expectedIssuer) {
|
|
|
776
999
|
}
|
|
777
1000
|
async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = LATEST_PROTOCOL_VERSION, trustedOrigin } = {}) {
|
|
778
1001
|
const headers = { "MCP-Protocol-Version": protocolVersion };
|
|
1002
|
+
const authorizationServerOrigin = new URL(authorizationServerUrl).origin;
|
|
779
1003
|
const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
|
|
1004
|
+
if (urlsToTry.some(({ url }) => url.origin !== authorizationServerOrigin)) throw new MCPClientOAuthError({ message: "OAuth discovery URL changed authorization-server origin" });
|
|
780
1005
|
for (const { url: endpointUrl, type, expectedIssuer } of urlsToTry) {
|
|
781
1006
|
const response = await fetchWithCorsRetry(endpointUrl, headers, fetchFn, trustedOrigin);
|
|
782
1007
|
if (!response)
|
|
@@ -2093,12 +2318,12 @@ async function createMCPClient(config) {
|
|
|
2093
2318
|
* This client is meant to be used to communicate with a single server. To communicate and fetch tools across multiple servers, it's recommended to create a new client instance per server.
|
|
2094
2319
|
*
|
|
2095
2320
|
* Not supported:
|
|
2096
|
-
* - Accepting notifications
|
|
2321
|
+
* - Accepting in-band notifications (webhook events use a separate HTTP handler)
|
|
2097
2322
|
* - Automatic session persistence for Streamable HTTP transport
|
|
2098
2323
|
* - Resumable SSE streams
|
|
2099
2324
|
*/
|
|
2100
2325
|
var DefaultMCPClient = class {
|
|
2101
|
-
constructor({ transport: transportConfig, name, clientName = name ?? "ai-sdk-mcp-client", version = CLIENT_VERSION, onUncaughtError, maxRetries, capabilities, initialInitializeResult, initializationOptions, protocolVersionDiscovery = true }) {
|
|
2326
|
+
constructor({ experimental_events: events, transport: transportConfig, name, clientName = name ?? "ai-sdk-mcp-client", version = CLIENT_VERSION, onUncaughtError, maxRetries, capabilities, initialInitializeResult, initializationOptions, protocolVersionDiscovery = true }) {
|
|
2102
2327
|
this.requestMessageId = 0;
|
|
2103
2328
|
this.responseHandlers = /* @__PURE__ */ new Map();
|
|
2104
2329
|
this.serverCapabilities = {};
|
|
@@ -2115,6 +2340,7 @@ var DefaultMCPClient = class {
|
|
|
2115
2340
|
this.protocolVersion = LATEST_LEGACY_PROTOCOL_VERSION;
|
|
2116
2341
|
this.toolHeaderBindings = /* @__PURE__ */ new Map();
|
|
2117
2342
|
this.isClosed = true;
|
|
2343
|
+
if (events?.adapter !== void 0 && (events.store !== void 0 || events.validateArguments !== void 0)) throw new MCPClientError({ message: "Configure experimental_events with either an adapter or a store, not both. Managed adapters own argument validation." });
|
|
2118
2344
|
this.onUncaughtError = onUncaughtError;
|
|
2119
2345
|
this.maxRetries = prepareMaxRetries(maxRetries);
|
|
2120
2346
|
this.clientCapabilities = capabilities ?? {};
|
|
@@ -2137,6 +2363,12 @@ var DefaultMCPClient = class {
|
|
|
2137
2363
|
name: clientName,
|
|
2138
2364
|
version
|
|
2139
2365
|
};
|
|
2366
|
+
const directEvents = createMCPEvents({
|
|
2367
|
+
request: (args) => this.request(args),
|
|
2368
|
+
store: events?.store,
|
|
2369
|
+
validateArguments: events?.validateArguments
|
|
2370
|
+
});
|
|
2371
|
+
this.experimental_events = events?.adapter ? createManagedMCPEvents(events.adapter, directEvents.list) : directEvents;
|
|
2140
2372
|
}
|
|
2141
2373
|
get serverInfo() {
|
|
2142
2374
|
return this._serverInfo;
|
|
@@ -2266,6 +2498,11 @@ var DefaultMCPClient = class {
|
|
|
2266
2498
|
case "completion/complete":
|
|
2267
2499
|
if (!this.serverCapabilities.completions) throw new MCPClientError({ message: `Server does not support completions` });
|
|
2268
2500
|
break;
|
|
2501
|
+
case "events/list":
|
|
2502
|
+
case "events/subscribe":
|
|
2503
|
+
case "events/unsubscribe":
|
|
2504
|
+
if (!this.serverCapabilities.events) throw new MCPClientError({ message: "Server does not support events" });
|
|
2505
|
+
break;
|
|
2269
2506
|
case "tools/list":
|
|
2270
2507
|
case "tools/call":
|
|
2271
2508
|
if (!this.serverCapabilities.tools) throw new MCPClientError({ message: `Server does not support tools` });
|
|
@@ -2794,6 +3031,132 @@ function detectMCPAppResourceDrift(current, baseline) {
|
|
|
2794
3031
|
return current !== baseline;
|
|
2795
3032
|
}
|
|
2796
3033
|
//#endregion
|
|
2797
|
-
|
|
3034
|
+
//#region src/tool/mcp-event-webhook.ts
|
|
3035
|
+
const MAX_BODY_BYTES = 262144;
|
|
3036
|
+
const VerificationSchema = z.object({
|
|
3037
|
+
type: z.literal("verification"),
|
|
3038
|
+
challenge: z.string().min(1)
|
|
3039
|
+
});
|
|
3040
|
+
/**
|
|
3041
|
+
* Create a Web Request/Response handler for MCP webhook deliveries. Mount it
|
|
3042
|
+
* at the callback URL before subscribing. Verification is enabled automatically
|
|
3043
|
+
* using the pending delivery secret saved by the client's subscribe method.
|
|
3044
|
+
*/
|
|
3045
|
+
function createMCPEventWebhook({ store, validatePayload, onEvent, onGap, onTerminated, onError }) {
|
|
3046
|
+
return async (request) => {
|
|
3047
|
+
if (request.method !== "POST") return new Response(null, {
|
|
3048
|
+
status: 405,
|
|
3049
|
+
headers: { Allow: "POST" }
|
|
3050
|
+
});
|
|
3051
|
+
if (request.headers.get("content-type")?.split(";")[0].trim().toLowerCase() !== "application/json") return new Response(null, { status: 415 });
|
|
3052
|
+
if (Number(request.headers.get("content-length")) > MAX_BODY_BYTES) return new Response(null, { status: 413 });
|
|
3053
|
+
try {
|
|
3054
|
+
const key = new URL(request.url).searchParams.get(MCP_EVENT_CALLBACK_KEY);
|
|
3055
|
+
const id = request.headers.get("X-MCP-Subscription-Id");
|
|
3056
|
+
if (!key || !id) return new Response(null, { status: 401 });
|
|
3057
|
+
const subscription = await store.get(key);
|
|
3058
|
+
if (!subscription || subscription.id != null && subscription.id !== id) return new Response(null, { status: 401 });
|
|
3059
|
+
const reader = request.body?.getReader();
|
|
3060
|
+
if (!reader) return new Response(null, { status: 400 });
|
|
3061
|
+
const chunks = [];
|
|
3062
|
+
let length = 0;
|
|
3063
|
+
try {
|
|
3064
|
+
while (true) {
|
|
3065
|
+
const { done, value } = await reader.read();
|
|
3066
|
+
if (done) break;
|
|
3067
|
+
length += value.byteLength;
|
|
3068
|
+
if (length > MAX_BODY_BYTES) {
|
|
3069
|
+
await reader.cancel();
|
|
3070
|
+
return new Response(null, { status: 413 });
|
|
3071
|
+
}
|
|
3072
|
+
chunks.push(value);
|
|
3073
|
+
}
|
|
3074
|
+
} finally {
|
|
3075
|
+
reader.releaseLock();
|
|
3076
|
+
}
|
|
3077
|
+
const body = new Uint8Array(length);
|
|
3078
|
+
let offset = 0;
|
|
3079
|
+
for (const chunk of chunks) {
|
|
3080
|
+
body.set(chunk, offset);
|
|
3081
|
+
offset += chunk.length;
|
|
3082
|
+
}
|
|
3083
|
+
if (!await verifyMCPEventSignature({
|
|
3084
|
+
headers: request.headers,
|
|
3085
|
+
body,
|
|
3086
|
+
secret: subscription.delivery.secret
|
|
3087
|
+
})) return new Response(null, { status: 401 });
|
|
3088
|
+
const parsed = await safeParseJSON({ text: new TextDecoder().decode(body) });
|
|
3089
|
+
if (!parsed.success) return new Response(null, { status: 400 });
|
|
3090
|
+
const value = parsed.value;
|
|
3091
|
+
let control;
|
|
3092
|
+
if (value != null && typeof value === "object" && "type" in value) {
|
|
3093
|
+
if (value.type === "verification") {
|
|
3094
|
+
const verification = VerificationSchema.safeParse(value);
|
|
3095
|
+
if (!verification.success) return new Response(null, { status: 400 });
|
|
3096
|
+
return Response.json({ challenge: verification.data.challenge });
|
|
3097
|
+
}
|
|
3098
|
+
const parsedControl = MCPEventControlSchema.safeParse(value);
|
|
3099
|
+
if (!parsedControl.success) return new Response(null, { status: 400 });
|
|
3100
|
+
control = parsedControl.data;
|
|
3101
|
+
}
|
|
3102
|
+
if (subscription.id == null || subscription.status !== "active" && control?.type !== "terminated") return new Response(null, { status: 503 });
|
|
3103
|
+
const subscriptionInfo = {
|
|
3104
|
+
id: subscription.id,
|
|
3105
|
+
name: subscription.name,
|
|
3106
|
+
arguments: subscription.arguments,
|
|
3107
|
+
delivery: {
|
|
3108
|
+
mode: "webhook",
|
|
3109
|
+
url: subscription.delivery.url
|
|
3110
|
+
},
|
|
3111
|
+
refreshBefore: subscription.refreshBefore ?? null
|
|
3112
|
+
};
|
|
3113
|
+
if (control) {
|
|
3114
|
+
const messageId = request.headers.get("webhook-id");
|
|
3115
|
+
if (control.type === "gap") {
|
|
3116
|
+
await onGap?.({
|
|
3117
|
+
subscription: subscriptionInfo,
|
|
3118
|
+
messageId,
|
|
3119
|
+
gap: control
|
|
3120
|
+
});
|
|
3121
|
+
await store.update(subscription.key, {
|
|
3122
|
+
cursor: control.cursor,
|
|
3123
|
+
truncated: true
|
|
3124
|
+
});
|
|
3125
|
+
} else {
|
|
3126
|
+
await onTerminated?.({
|
|
3127
|
+
subscription: subscriptionInfo,
|
|
3128
|
+
messageId,
|
|
3129
|
+
termination: control
|
|
3130
|
+
});
|
|
3131
|
+
await store.delete(subscription.key);
|
|
3132
|
+
}
|
|
3133
|
+
return new Response(null, { status: 204 });
|
|
3134
|
+
}
|
|
3135
|
+
if (subscription.refreshBefore != null && Date.parse(subscription.refreshBefore) <= Date.now()) return new Response(null, { status: 410 });
|
|
3136
|
+
const event = MCPEventSchema.safeParse(value);
|
|
3137
|
+
if (!event.success || event.data.name !== subscription.name || event.data.eventId !== request.headers.get("webhook-id")) return new Response(null, { status: 400 });
|
|
3138
|
+
try {
|
|
3139
|
+
await validatePayload?.({
|
|
3140
|
+
definition: subscription.definition,
|
|
3141
|
+
data: event.data.data
|
|
3142
|
+
});
|
|
3143
|
+
} catch (error) {
|
|
3144
|
+
onError?.(error);
|
|
3145
|
+
return new Response(null, { status: 400 });
|
|
3146
|
+
}
|
|
3147
|
+
await onEvent({
|
|
3148
|
+
subscription: subscriptionInfo,
|
|
3149
|
+
event: event.data
|
|
3150
|
+
});
|
|
3151
|
+
if (event.data.cursor !== void 0) await store.update(subscription.key, { cursor: event.data.cursor });
|
|
3152
|
+
return new Response(null, { status: 204 });
|
|
3153
|
+
} catch (error) {
|
|
3154
|
+
onError?.(error);
|
|
3155
|
+
return new Response(null, { status: 503 });
|
|
3156
|
+
}
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
//#endregion
|
|
3160
|
+
export { AuthorizationServerMismatchError, ElicitResultSchema, ElicitationRequestSchema, MCPClientError, MCPClientOAuthError, MCP_APP_MIME_TYPE, UnauthorizedError, auth, createMCPClient, createMCPClient as experimental_createMCPClient, detectMCPAppResourceDrift, createMCPEventWebhook as experimental_createMCPEventWebhook, fingerprintMCPAppResource, mcpAppClientCapabilities, readMCPAppResource, splitMCPAppTools, validateJSONRPCMessage };
|
|
2798
3161
|
|
|
2799
3162
|
//# sourceMappingURL=index.js.map
|