@lovable.dev/mcp-js 0.21.0-rc.3 → 0.21.0

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.
Files changed (39) hide show
  1. package/README.md +2 -1
  2. package/dist/{base-DTtaX5Rf.d.ts → base-CFy5vZ5Z.d.ts} +7 -2
  3. package/dist/chunk-6QFZYYUV.js +11 -0
  4. package/dist/{chunk-5W6JK6OS.js → chunk-CI7JYLV3.js} +6 -2
  5. package/dist/{chunk-SIDWSSWV.js → chunk-E3Q3FRKQ.js} +1 -1
  6. package/dist/{chunk-NENAFXZJ.js → chunk-ENJDNFEB.js} +1 -1
  7. package/dist/{chunk-TVU5FL2T.js → chunk-LFXK3FNF.js} +1 -1
  8. package/dist/{chunk-JIOOUIDX.js → chunk-QPKPYDPB.js} +6 -2
  9. package/dist/{chunk-CIAVINZ5.js → chunk-YLU2JTHL.js} +45 -80
  10. package/dist/cli/extract-manifest.cjs +3 -1
  11. package/dist/cli/extract-manifest.js +5 -3
  12. package/dist/index.cjs +12 -0
  13. package/dist/index.d.cts +3 -2
  14. package/dist/index.d.ts +3 -2
  15. package/dist/index.js +11 -1
  16. package/dist/protocols/mcp/index.cjs +52 -79
  17. package/dist/protocols/mcp/index.d.cts +2 -2
  18. package/dist/protocols/mcp/index.d.ts +2 -2
  19. package/dist/protocols/mcp/index.js +4 -3
  20. package/dist/protocols/oauth-metadata.cjs +40 -60
  21. package/dist/protocols/oauth-metadata.d.cts +1 -1
  22. package/dist/protocols/oauth-metadata.d.ts +1 -1
  23. package/dist/protocols/oauth-metadata.js +3 -3
  24. package/dist/protocols/rest/index.cjs +52 -79
  25. package/dist/protocols/rest/index.d.cts +2 -2
  26. package/dist/protocols/rest/index.d.ts +2 -2
  27. package/dist/protocols/rest/index.js +5 -4
  28. package/dist/stacks/supabase/index.cjs +57 -82
  29. package/dist/stacks/supabase/index.d.cts +1 -1
  30. package/dist/stacks/supabase/index.d.ts +1 -1
  31. package/dist/stacks/supabase/index.js +7 -6
  32. package/dist/stacks/supabase/vite.cjs +1 -1
  33. package/dist/stacks/supabase/vite.js +1 -1
  34. package/dist/stacks/tanstack/index.cjs +57 -82
  35. package/dist/stacks/tanstack/index.d.cts +1 -1
  36. package/dist/stacks/tanstack/index.d.ts +1 -1
  37. package/dist/stacks/tanstack/index.js +7 -6
  38. package/dist/{types-CPkhCRxc.d.ts → types-qqrmJ736.d.ts} +10 -0
  39. package/package.json +1 -1
@@ -148,23 +148,9 @@ function parseSafeUrl(subject, raw, ErrorClass = Error) {
148
148
 
149
149
  // src/auth/discovery.ts
150
150
  var OAuthConfigurationError = class extends Error {
151
- constructor(message, stage) {
152
- super(message);
153
- this.stage = stage;
154
- this.name = "OAuthConfigurationError";
155
- }
156
- };
157
- var OAuthUpstreamUnreachableError = class extends Error {
158
- constructor(message, stage) {
159
- super(message);
160
- this.stage = stage;
161
- this.name = "OAuthUpstreamUnreachableError";
162
- }
163
- };
164
- var MetadataUnreachableError = class extends Error {
165
151
  constructor(message) {
166
152
  super(message);
167
- this.name = "MetadataUnreachableError";
153
+ this.name = "OAuthConfigurationError";
168
154
  }
169
155
  };
170
156
  var METADATA_FETCH_TIMEOUT_MS = 5e3;
@@ -196,28 +182,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
196
182
  try {
197
183
  return await fetchOAuthServerMetadata(url, expectedIssuer);
198
184
  } catch (err) {
199
- const error = err instanceof Error ? err : new Error(String(err));
200
- log.debug("oauth.discovery.attempt_failed", { url, ...describeError(error) });
201
- errors.push(error);
185
+ log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
186
+ errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
202
187
  }
203
188
  }
204
- const unreachable = errors.some((e) => e instanceof MetadataUnreachableError);
205
- const detail = errors.map((e) => e.message).join("; ");
206
- log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
207
- const message = `failed to discover OAuth server metadata (${detail})`;
208
- throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
189
+ log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
190
+ throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
209
191
  }
210
192
  async function fetchOAuthServerMetadata(url, expectedIssuer) {
211
193
  log.debug("oauth.discovery.fetch", { url });
212
- let response;
213
- try {
214
- response = await fetch(url, {
215
- signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS),
216
- redirect: "manual"
217
- });
218
- } catch (err) {
219
- throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
220
- }
194
+ const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
221
195
  if (!response.ok) {
222
196
  throw new Error(String(response.status));
223
197
  }
@@ -240,21 +214,14 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
240
214
  try {
241
215
  return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
242
216
  } catch (err) {
243
- const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
244
- if (err instanceof MetadataUnreachableError) {
245
- log.error("oauth.discovery.unreachable", {
246
- issuer,
247
- ...describeError(err),
248
- outcome: "503 authorization server unreachable"
249
- });
250
- throw new OAuthUpstreamUnreachableError(message, "discovery");
251
- }
252
217
  log.error("oauth.discovery.config_error", {
253
218
  issuer,
254
219
  ...describeError(err),
255
220
  outcome: "500 oauth configuration error"
256
221
  });
257
- throw new OAuthConfigurationError(message, "discovery");
222
+ throw new OAuthConfigurationError(
223
+ `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
224
+ );
258
225
  }
259
226
  }
260
227
  function createOAuthDiscoveryResolver(auth) {
@@ -314,13 +281,18 @@ function readString(value) {
314
281
  function splitScopes(value) {
315
282
  if (typeof value === "string")
316
283
  return value.split(/\s+/).filter(Boolean);
317
- if (Array.isArray(value))
318
- return value.filter((entry) => typeof entry === "string" && entry.length > 0);
319
- return [];
284
+ return stringClaimList(value);
320
285
  }
321
286
  function stringClaim(claims, name) {
322
287
  return readString(claims[name]);
323
288
  }
289
+ function stringClaimList(value) {
290
+ if (typeof value === "string")
291
+ return value === "" ? [] : [value];
292
+ if (Array.isArray(value))
293
+ return value.filter((entry) => typeof entry === "string" && entry !== "");
294
+ return [];
295
+ }
324
296
 
325
297
  // src/auth/verifier.ts
326
298
  var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
@@ -347,24 +319,15 @@ function tokenHeaderFields(token) {
347
319
  }
348
320
  }
349
321
  async function fetchVerificationKeySet(jwksUri) {
350
- let response;
351
- try {
352
- response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
353
- } catch (err) {
354
- log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
355
- throw new OAuthUpstreamUnreachableError(
356
- `JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`,
357
- "jwks"
358
- );
359
- }
360
322
  try {
323
+ const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
361
324
  if (!response.ok)
362
325
  throw new Error(`JWKS endpoint returned ${response.status}`);
363
326
  const json = await response.json();
364
327
  return (0, import_jose.createLocalJWKSet)(json);
365
328
  } catch (err) {
366
329
  log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
367
- throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`, "jwks");
330
+ throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
368
331
  }
369
332
  }
370
333
  function assertAccessTokenTyp(token, allowed) {
@@ -381,13 +344,12 @@ function assertAccessTokenTyp(token, allowed) {
381
344
  throw new OAuthTokenError(401, "invalid_token", "Access token typ header is not accepted");
382
345
  }
383
346
  }
384
- async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
347
+ async function verifyJwtClaims(token, keySet, issuer, auth) {
385
348
  try {
386
349
  const { payload } = await (0, import_jose.jwtVerify)(token, keySet, {
387
350
  // `issuer` is trimmed of any trailing slash; accept both forms so a token whose
388
351
  // `iss` carries the slash the AS publishes still verifies.
389
352
  issuer: [issuer, `${issuer}/`],
390
- audience: [...audience],
391
353
  algorithms: auth.algorithms ? [...auth.algorithms] : DEFAULT_JWT_ALGORITHMS,
392
354
  requiredClaims: ["sub", "exp"],
393
355
  clockTolerance: auth.clockToleranceSeconds ?? DEFAULT_CLOCK_TOLERANCE_SECONDS
@@ -398,6 +360,22 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
398
360
  throw err;
399
361
  }
400
362
  }
363
+ function checkAcceptedAudience(claims, accepted, auth) {
364
+ const audPopulated = claims.aud !== void 0 && !(Array.isArray(claims.aud) && claims.aud.length === 0);
365
+ if (audPopulated) {
366
+ if (stringClaimList(claims.aud).some((audience) => accepted.includes(audience)))
367
+ return "aud";
368
+ log.debug("oauth.verify.bad_audience", { tokenAud: claims.aud, accepted, outcome: "401 invalid_token" });
369
+ } else if (auth.acceptResourceClaim !== false) {
370
+ const acceptedTrimmed = accepted.map(trimTrailingSlash);
371
+ if (stringClaimList(claims.resource).some((resource) => acceptedTrimmed.includes(trimTrailingSlash(resource))))
372
+ return "resource";
373
+ log.debug("oauth.verify.bad_resource", { tokenResource: claims.resource, accepted, outcome: "401 invalid_token" });
374
+ } else {
375
+ log.debug("oauth.verify.empty_audience", { accepted, outcome: "401 invalid_token" });
376
+ }
377
+ throw new OAuthTokenError(401, "invalid_token", "token audience is not accepted");
378
+ }
401
379
  function assertNonEmptySubject(claims) {
402
380
  const sub = claims["sub"];
403
381
  if (typeof sub !== "string" || sub.trim() === "") {
@@ -442,14 +420,16 @@ function createOAuthTokenVerifier(auth, discovery) {
442
420
  log.debug("oauth.jwks.fetch", { jwksUri });
443
421
  const keySet = await fetchVerificationKeySet(jwksUri);
444
422
  assertAccessTokenTyp(token, allowedTyps);
445
- const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
423
+ const claims = await verifyJwtClaims(token, keySet, issuer, auth);
424
+ const audienceVia = checkAcceptedAudience(claims, acceptedAudiences, auth);
446
425
  assertNonEmptySubject(claims);
447
426
  const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
448
427
  assertOAuthClientClaim(auth, context.principal.clientId);
449
428
  log.info("oauth.verify.ok", {
450
429
  sub: context.principal.sub,
451
430
  clientId: context.principal.clientId,
452
- scopes: context.principal.scopes
431
+ scopes: context.principal.scopes,
432
+ audienceVia
453
433
  });
454
434
  return context;
455
435
  };
@@ -498,12 +478,6 @@ function oauthConfigurationErrorResponse() {
498
478
  headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
499
479
  });
500
480
  }
501
- function oauthUpstreamUnreachableResponse() {
502
- return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
503
- status: 503,
504
- headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
505
- });
506
- }
507
481
  function parseBearerToken(request) {
508
482
  const header = request.headers.get("Authorization");
509
483
  if (!header)
@@ -568,22 +542,12 @@ function createRequestAuthorizer(mcp, options = {}) {
568
542
  assertRequiredScopes(runtime.auth, auth);
569
543
  return { ok: true, auth };
570
544
  } catch (err) {
571
- if (err instanceof OAuthUpstreamUnreachableError) {
572
- log.error("auth.upstream_unreachable", { ...describeError(err), stage: err.stage, outcome: "503" });
573
- await recorder?.emit({
574
- tool: null,
575
- method: "authorize",
576
- outcome: err.stage === "jwks" ? "auth_jwks_unreachable" : "auth_discovery_unreachable",
577
- durationMs: nowMs() - startedAt
578
- });
579
- return { ok: false, response: oauthUpstreamUnreachableResponse() };
580
- }
581
545
  if (err instanceof OAuthConfigurationError) {
582
- log.error("auth.config_error", { ...describeError(err), stage: err.stage, outcome: "500" });
546
+ log.error("auth.config_error", { ...describeError(err), outcome: "500" });
583
547
  await recorder?.emit({
584
548
  tool: null,
585
549
  method: "authorize",
586
- outcome: err.stage === "jwks" ? "auth_jwks_config_error" : "auth_discovery_config_error",
550
+ outcome: "auth_config_error",
587
551
  durationMs: nowMs() - startedAt
588
552
  });
589
553
  return { ok: false, response: oauthConfigurationErrorResponse() };
@@ -662,6 +626,14 @@ var ToolContext = class {
662
626
  }
663
627
  };
664
628
 
629
+ // src/core/content.ts
630
+ function extractTextContent(content) {
631
+ if (!content)
632
+ return void 0;
633
+ const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
634
+ return text.length > 0 ? text : void 0;
635
+ }
636
+
665
637
  // src/core/cors.ts
666
638
  var EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
667
639
  var ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
@@ -712,7 +684,8 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
712
684
  tool: tool.name,
713
685
  method: "tools/call",
714
686
  outcome: result.isError ? "tool_error" : "ok",
715
- durationMs: nowMs() - start
687
+ durationMs: nowMs() - start,
688
+ errorText: result.isError ? extractTextContent(result.content) : void 0
716
689
  });
717
690
  return { content: result.content ?? [], structuredContent: result.structuredContent, isError: result.isError };
718
691
  };
@@ -1,6 +1,6 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../../types-CPkhCRxc.js';
3
- import { M as MetricsRecorder } from '../../base-DTtaX5Rf.js';
2
+ import { d as McpDefinition } from '../../types-qqrmJ736.js';
3
+ import { M as MetricsRecorder } from '../../base-CFy5vZ5Z.js';
4
4
  import 'zod';
5
5
 
6
6
  type McpProtocolHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
@@ -1,6 +1,6 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../../types-CPkhCRxc.js';
3
- import { M as MetricsRecorder } from '../../base-DTtaX5Rf.js';
2
+ import { d as McpDefinition } from '../../types-qqrmJ736.js';
3
+ import { M as MetricsRecorder } from '../../base-CFy5vZ5Z.js';
4
4
  import 'zod';
5
5
 
6
6
  type McpProtocolHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  createMcpProtocolHandler
3
- } from "../../chunk-5W6JK6OS.js";
3
+ } from "../../chunk-CI7JYLV3.js";
4
+ import "../../chunk-6QFZYYUV.js";
4
5
  import "../../chunk-MA5H6PSF.js";
5
- import "../../chunk-CIAVINZ5.js";
6
+ import "../../chunk-YLU2JTHL.js";
6
7
  import "../../chunk-H37EB22A.js";
7
8
  import "../../chunk-6DXGZZA4.js";
8
- import "../../chunk-SIDWSSWV.js";
9
+ import "../../chunk-E3Q3FRKQ.js";
9
10
  export {
10
11
  createMcpProtocolHandler
11
12
  };
@@ -143,23 +143,9 @@ function parseSafeUrl(subject, raw, ErrorClass = Error) {
143
143
 
144
144
  // src/auth/discovery.ts
145
145
  var OAuthConfigurationError = class extends Error {
146
- constructor(message, stage) {
147
- super(message);
148
- this.stage = stage;
149
- this.name = "OAuthConfigurationError";
150
- }
151
- };
152
- var OAuthUpstreamUnreachableError = class extends Error {
153
- constructor(message, stage) {
154
- super(message);
155
- this.stage = stage;
156
- this.name = "OAuthUpstreamUnreachableError";
157
- }
158
- };
159
- var MetadataUnreachableError = class extends Error {
160
146
  constructor(message) {
161
147
  super(message);
162
- this.name = "MetadataUnreachableError";
148
+ this.name = "OAuthConfigurationError";
163
149
  }
164
150
  };
165
151
  var METADATA_FETCH_TIMEOUT_MS = 5e3;
@@ -191,28 +177,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
191
177
  try {
192
178
  return await fetchOAuthServerMetadata(url, expectedIssuer);
193
179
  } catch (err) {
194
- const error = err instanceof Error ? err : new Error(String(err));
195
- log.debug("oauth.discovery.attempt_failed", { url, ...describeError(error) });
196
- errors.push(error);
180
+ log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
181
+ errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
197
182
  }
198
183
  }
199
- const unreachable = errors.some((e) => e instanceof MetadataUnreachableError);
200
- const detail = errors.map((e) => e.message).join("; ");
201
- log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
202
- const message = `failed to discover OAuth server metadata (${detail})`;
203
- throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
184
+ log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
185
+ throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
204
186
  }
205
187
  async function fetchOAuthServerMetadata(url, expectedIssuer) {
206
188
  log.debug("oauth.discovery.fetch", { url });
207
- let response;
208
- try {
209
- response = await fetch(url, {
210
- signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS),
211
- redirect: "manual"
212
- });
213
- } catch (err) {
214
- throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
215
- }
189
+ const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
216
190
  if (!response.ok) {
217
191
  throw new Error(String(response.status));
218
192
  }
@@ -235,21 +209,14 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
235
209
  try {
236
210
  return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
237
211
  } catch (err) {
238
- const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
239
- if (err instanceof MetadataUnreachableError) {
240
- log.error("oauth.discovery.unreachable", {
241
- issuer,
242
- ...describeError(err),
243
- outcome: "503 authorization server unreachable"
244
- });
245
- throw new OAuthUpstreamUnreachableError(message, "discovery");
246
- }
247
212
  log.error("oauth.discovery.config_error", {
248
213
  issuer,
249
214
  ...describeError(err),
250
215
  outcome: "500 oauth configuration error"
251
216
  });
252
- throw new OAuthConfigurationError(message, "discovery");
217
+ throw new OAuthConfigurationError(
218
+ `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
219
+ );
253
220
  }
254
221
  }
255
222
  function createOAuthDiscoveryResolver(auth) {
@@ -306,13 +273,18 @@ function readString(value) {
306
273
  function splitScopes(value) {
307
274
  if (typeof value === "string")
308
275
  return value.split(/\s+/).filter(Boolean);
309
- if (Array.isArray(value))
310
- return value.filter((entry) => typeof entry === "string" && entry.length > 0);
311
- return [];
276
+ return stringClaimList(value);
312
277
  }
313
278
  function stringClaim(claims, name) {
314
279
  return readString(claims[name]);
315
280
  }
281
+ function stringClaimList(value) {
282
+ if (typeof value === "string")
283
+ return value === "" ? [] : [value];
284
+ if (Array.isArray(value))
285
+ return value.filter((entry) => typeof entry === "string" && entry !== "");
286
+ return [];
287
+ }
316
288
 
317
289
  // src/auth/verifier.ts
318
290
  var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
@@ -339,24 +311,15 @@ function tokenHeaderFields(token) {
339
311
  }
340
312
  }
341
313
  async function fetchVerificationKeySet(jwksUri) {
342
- let response;
343
- try {
344
- response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
345
- } catch (err) {
346
- log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
347
- throw new OAuthUpstreamUnreachableError(
348
- `JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`,
349
- "jwks"
350
- );
351
- }
352
314
  try {
315
+ const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
353
316
  if (!response.ok)
354
317
  throw new Error(`JWKS endpoint returned ${response.status}`);
355
318
  const json = await response.json();
356
319
  return (0, import_jose.createLocalJWKSet)(json);
357
320
  } catch (err) {
358
321
  log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
359
- throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`, "jwks");
322
+ throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
360
323
  }
361
324
  }
362
325
  function assertAccessTokenTyp(token, allowed) {
@@ -373,13 +336,12 @@ function assertAccessTokenTyp(token, allowed) {
373
336
  throw new OAuthTokenError(401, "invalid_token", "Access token typ header is not accepted");
374
337
  }
375
338
  }
376
- async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
339
+ async function verifyJwtClaims(token, keySet, issuer, auth) {
377
340
  try {
378
341
  const { payload } = await (0, import_jose.jwtVerify)(token, keySet, {
379
342
  // `issuer` is trimmed of any trailing slash; accept both forms so a token whose
380
343
  // `iss` carries the slash the AS publishes still verifies.
381
344
  issuer: [issuer, `${issuer}/`],
382
- audience: [...audience],
383
345
  algorithms: auth.algorithms ? [...auth.algorithms] : DEFAULT_JWT_ALGORITHMS,
384
346
  requiredClaims: ["sub", "exp"],
385
347
  clockTolerance: auth.clockToleranceSeconds ?? DEFAULT_CLOCK_TOLERANCE_SECONDS
@@ -390,6 +352,22 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
390
352
  throw err;
391
353
  }
392
354
  }
355
+ function checkAcceptedAudience(claims, accepted, auth) {
356
+ const audPopulated = claims.aud !== void 0 && !(Array.isArray(claims.aud) && claims.aud.length === 0);
357
+ if (audPopulated) {
358
+ if (stringClaimList(claims.aud).some((audience) => accepted.includes(audience)))
359
+ return "aud";
360
+ log.debug("oauth.verify.bad_audience", { tokenAud: claims.aud, accepted, outcome: "401 invalid_token" });
361
+ } else if (auth.acceptResourceClaim !== false) {
362
+ const acceptedTrimmed = accepted.map(trimTrailingSlash);
363
+ if (stringClaimList(claims.resource).some((resource) => acceptedTrimmed.includes(trimTrailingSlash(resource))))
364
+ return "resource";
365
+ log.debug("oauth.verify.bad_resource", { tokenResource: claims.resource, accepted, outcome: "401 invalid_token" });
366
+ } else {
367
+ log.debug("oauth.verify.empty_audience", { accepted, outcome: "401 invalid_token" });
368
+ }
369
+ throw new OAuthTokenError(401, "invalid_token", "token audience is not accepted");
370
+ }
393
371
  function assertNonEmptySubject(claims) {
394
372
  const sub = claims["sub"];
395
373
  if (typeof sub !== "string" || sub.trim() === "") {
@@ -434,14 +412,16 @@ function createOAuthTokenVerifier(auth, discovery) {
434
412
  log.debug("oauth.jwks.fetch", { jwksUri });
435
413
  const keySet = await fetchVerificationKeySet(jwksUri);
436
414
  assertAccessTokenTyp(token, allowedTyps);
437
- const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
415
+ const claims = await verifyJwtClaims(token, keySet, issuer, auth);
416
+ const audienceVia = checkAcceptedAudience(claims, acceptedAudiences, auth);
438
417
  assertNonEmptySubject(claims);
439
418
  const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
440
419
  assertOAuthClientClaim(auth, context.principal.clientId);
441
420
  log.info("oauth.verify.ok", {
442
421
  sub: context.principal.sub,
443
422
  clientId: context.principal.clientId,
444
- scopes: context.principal.scopes
423
+ scopes: context.principal.scopes,
424
+ audienceVia
445
425
  });
446
426
  return context;
447
427
  };
@@ -1,5 +1,5 @@
1
1
  import { M as McpRuntimeOptions } from '../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../types-CPkhCRxc.js';
2
+ import { d as McpDefinition } from '../types-qqrmJ736.js';
3
3
  import 'zod';
4
4
 
5
5
  type OAuthProtectedResourceMetadataHandler = (request: Request) => Promise<Response>;
@@ -1,5 +1,5 @@
1
1
  import { M as McpRuntimeOptions } from '../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../types-CPkhCRxc.js';
2
+ import { d as McpDefinition } from '../types-qqrmJ736.js';
3
3
  import 'zod';
4
4
 
5
5
  type OAuthProtectedResourceMetadataHandler = (request: Request) => Promise<Response>;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createOAuthProtectedResourceMetadataHandler
3
- } from "../chunk-NENAFXZJ.js";
4
- import "../chunk-CIAVINZ5.js";
3
+ } from "../chunk-ENJDNFEB.js";
4
+ import "../chunk-YLU2JTHL.js";
5
5
  import "../chunk-H37EB22A.js";
6
6
  import "../chunk-6DXGZZA4.js";
7
- import "../chunk-SIDWSSWV.js";
7
+ import "../chunk-E3Q3FRKQ.js";
8
8
  export {
9
9
  createOAuthProtectedResourceMetadataHandler
10
10
  };