@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
@@ -158,23 +158,9 @@ function parseSafeUrl(subject, raw, ErrorClass = Error) {
158
158
 
159
159
  // src/auth/discovery.ts
160
160
  var OAuthConfigurationError = class extends Error {
161
- constructor(message, stage) {
162
- super(message);
163
- this.stage = stage;
164
- this.name = "OAuthConfigurationError";
165
- }
166
- };
167
- var OAuthUpstreamUnreachableError = class extends Error {
168
- constructor(message, stage) {
169
- super(message);
170
- this.stage = stage;
171
- this.name = "OAuthUpstreamUnreachableError";
172
- }
173
- };
174
- var MetadataUnreachableError = class extends Error {
175
161
  constructor(message) {
176
162
  super(message);
177
- this.name = "MetadataUnreachableError";
163
+ this.name = "OAuthConfigurationError";
178
164
  }
179
165
  };
180
166
  var METADATA_FETCH_TIMEOUT_MS = 5e3;
@@ -206,28 +192,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
206
192
  try {
207
193
  return await fetchOAuthServerMetadata(url, expectedIssuer);
208
194
  } catch (err) {
209
- const error = err instanceof Error ? err : new Error(String(err));
210
- log.debug("oauth.discovery.attempt_failed", { url, ...describeError(error) });
211
- errors.push(error);
195
+ log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
196
+ errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
212
197
  }
213
198
  }
214
- const unreachable = errors.some((e) => e instanceof MetadataUnreachableError);
215
- const detail = errors.map((e) => e.message).join("; ");
216
- log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
217
- const message = `failed to discover OAuth server metadata (${detail})`;
218
- throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
199
+ log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
200
+ throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
219
201
  }
220
202
  async function fetchOAuthServerMetadata(url, expectedIssuer) {
221
203
  log.debug("oauth.discovery.fetch", { url });
222
- let response;
223
- try {
224
- response = await fetch(url, {
225
- signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS),
226
- redirect: "manual"
227
- });
228
- } catch (err) {
229
- throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
230
- }
204
+ const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
231
205
  if (!response.ok) {
232
206
  throw new Error(String(response.status));
233
207
  }
@@ -250,21 +224,14 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
250
224
  try {
251
225
  return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
252
226
  } catch (err) {
253
- const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
254
- if (err instanceof MetadataUnreachableError) {
255
- log.error("oauth.discovery.unreachable", {
256
- issuer,
257
- ...describeError(err),
258
- outcome: "503 authorization server unreachable"
259
- });
260
- throw new OAuthUpstreamUnreachableError(message, "discovery");
261
- }
262
227
  log.error("oauth.discovery.config_error", {
263
228
  issuer,
264
229
  ...describeError(err),
265
230
  outcome: "500 oauth configuration error"
266
231
  });
267
- throw new OAuthConfigurationError(message, "discovery");
232
+ throw new OAuthConfigurationError(
233
+ `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
234
+ );
268
235
  }
269
236
  }
270
237
  function createOAuthDiscoveryResolver(auth) {
@@ -324,13 +291,18 @@ function readString(value) {
324
291
  function splitScopes(value) {
325
292
  if (typeof value === "string")
326
293
  return value.split(/\s+/).filter(Boolean);
327
- if (Array.isArray(value))
328
- return value.filter((entry) => typeof entry === "string" && entry.length > 0);
329
- return [];
294
+ return stringClaimList(value);
330
295
  }
331
296
  function stringClaim(claims, name) {
332
297
  return readString(claims[name]);
333
298
  }
299
+ function stringClaimList(value) {
300
+ if (typeof value === "string")
301
+ return value === "" ? [] : [value];
302
+ if (Array.isArray(value))
303
+ return value.filter((entry) => typeof entry === "string" && entry !== "");
304
+ return [];
305
+ }
334
306
 
335
307
  // src/auth/verifier.ts
336
308
  var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
@@ -357,24 +329,15 @@ function tokenHeaderFields(token) {
357
329
  }
358
330
  }
359
331
  async function fetchVerificationKeySet(jwksUri) {
360
- let response;
361
- try {
362
- response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
363
- } catch (err) {
364
- log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
365
- throw new OAuthUpstreamUnreachableError(
366
- `JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`,
367
- "jwks"
368
- );
369
- }
370
332
  try {
333
+ const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
371
334
  if (!response.ok)
372
335
  throw new Error(`JWKS endpoint returned ${response.status}`);
373
336
  const json = await response.json();
374
337
  return (0, import_jose.createLocalJWKSet)(json);
375
338
  } catch (err) {
376
339
  log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
377
- throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`, "jwks");
340
+ throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
378
341
  }
379
342
  }
380
343
  function assertAccessTokenTyp(token, allowed) {
@@ -391,13 +354,12 @@ function assertAccessTokenTyp(token, allowed) {
391
354
  throw new OAuthTokenError(401, "invalid_token", "Access token typ header is not accepted");
392
355
  }
393
356
  }
394
- async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
357
+ async function verifyJwtClaims(token, keySet, issuer, auth) {
395
358
  try {
396
359
  const { payload } = await (0, import_jose.jwtVerify)(token, keySet, {
397
360
  // `issuer` is trimmed of any trailing slash; accept both forms so a token whose
398
361
  // `iss` carries the slash the AS publishes still verifies.
399
362
  issuer: [issuer, `${issuer}/`],
400
- audience: [...audience],
401
363
  algorithms: auth.algorithms ? [...auth.algorithms] : DEFAULT_JWT_ALGORITHMS,
402
364
  requiredClaims: ["sub", "exp"],
403
365
  clockTolerance: auth.clockToleranceSeconds ?? DEFAULT_CLOCK_TOLERANCE_SECONDS
@@ -408,6 +370,22 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
408
370
  throw err;
409
371
  }
410
372
  }
373
+ function checkAcceptedAudience(claims, accepted, auth) {
374
+ const audPopulated = claims.aud !== void 0 && !(Array.isArray(claims.aud) && claims.aud.length === 0);
375
+ if (audPopulated) {
376
+ if (stringClaimList(claims.aud).some((audience) => accepted.includes(audience)))
377
+ return "aud";
378
+ log.debug("oauth.verify.bad_audience", { tokenAud: claims.aud, accepted, outcome: "401 invalid_token" });
379
+ } else if (auth.acceptResourceClaim !== false) {
380
+ const acceptedTrimmed = accepted.map(trimTrailingSlash);
381
+ if (stringClaimList(claims.resource).some((resource) => acceptedTrimmed.includes(trimTrailingSlash(resource))))
382
+ return "resource";
383
+ log.debug("oauth.verify.bad_resource", { tokenResource: claims.resource, accepted, outcome: "401 invalid_token" });
384
+ } else {
385
+ log.debug("oauth.verify.empty_audience", { accepted, outcome: "401 invalid_token" });
386
+ }
387
+ throw new OAuthTokenError(401, "invalid_token", "token audience is not accepted");
388
+ }
411
389
  function assertNonEmptySubject(claims) {
412
390
  const sub = claims["sub"];
413
391
  if (typeof sub !== "string" || sub.trim() === "") {
@@ -452,14 +430,16 @@ function createOAuthTokenVerifier(auth, discovery) {
452
430
  log.debug("oauth.jwks.fetch", { jwksUri });
453
431
  const keySet = await fetchVerificationKeySet(jwksUri);
454
432
  assertAccessTokenTyp(token, allowedTyps);
455
- const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
433
+ const claims = await verifyJwtClaims(token, keySet, issuer, auth);
434
+ const audienceVia = checkAcceptedAudience(claims, acceptedAudiences, auth);
456
435
  assertNonEmptySubject(claims);
457
436
  const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
458
437
  assertOAuthClientClaim(auth, context.principal.clientId);
459
438
  log.info("oauth.verify.ok", {
460
439
  sub: context.principal.sub,
461
440
  clientId: context.principal.clientId,
462
- scopes: context.principal.scopes
441
+ scopes: context.principal.scopes,
442
+ audienceVia
463
443
  });
464
444
  return context;
465
445
  };
@@ -508,12 +488,6 @@ function oauthConfigurationErrorResponse() {
508
488
  headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
509
489
  });
510
490
  }
511
- function oauthUpstreamUnreachableResponse() {
512
- return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
513
- status: 503,
514
- headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
515
- });
516
- }
517
491
  function parseBearerToken(request) {
518
492
  const header = request.headers.get("Authorization");
519
493
  if (!header)
@@ -586,22 +560,12 @@ function createRequestAuthorizer(mcp, options = {}) {
586
560
  assertRequiredScopes(runtime.auth, auth);
587
561
  return { ok: true, auth };
588
562
  } catch (err) {
589
- if (err instanceof OAuthUpstreamUnreachableError) {
590
- log.error("auth.upstream_unreachable", { ...describeError(err), stage: err.stage, outcome: "503" });
591
- await recorder?.emit({
592
- tool: null,
593
- method: "authorize",
594
- outcome: err.stage === "jwks" ? "auth_jwks_unreachable" : "auth_discovery_unreachable",
595
- durationMs: nowMs() - startedAt
596
- });
597
- return { ok: false, response: oauthUpstreamUnreachableResponse() };
598
- }
599
563
  if (err instanceof OAuthConfigurationError) {
600
- log.error("auth.config_error", { ...describeError(err), stage: err.stage, outcome: "500" });
564
+ log.error("auth.config_error", { ...describeError(err), outcome: "500" });
601
565
  await recorder?.emit({
602
566
  tool: null,
603
567
  method: "authorize",
604
- outcome: err.stage === "jwks" ? "auth_jwks_config_error" : "auth_discovery_config_error",
568
+ outcome: "auth_config_error",
605
569
  durationMs: nowMs() - startedAt
606
570
  });
607
571
  return { ok: false, response: oauthConfigurationErrorResponse() };
@@ -745,6 +709,14 @@ var ToolContext = class {
745
709
  }
746
710
  };
747
711
 
712
+ // src/core/content.ts
713
+ function extractTextContent(content) {
714
+ if (!content)
715
+ return void 0;
716
+ const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
717
+ return text.length > 0 ? text : void 0;
718
+ }
719
+
748
720
  // src/protocols/rest/invoke-tool.ts
749
721
  var MAX_REFLECTED_TOOL_NAME = 256;
750
722
  function safeReflectName(name) {
@@ -845,7 +817,8 @@ function createInvokeToolHandler(mcp, options = {}) {
845
817
  tool: tool.name,
846
818
  method: "tools/call",
847
819
  outcome: result.isError ? "tool_error" : "ok",
848
- durationMs: nowMs() - start
820
+ durationMs: nowMs() - start,
821
+ errorText: result.isError ? extractTextContent(result.content) : void 0
849
822
  });
850
823
  return Response.json({
851
824
  content: result.content ?? [],
@@ -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 RestListToolsHandler = (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 RestListToolsHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  createInvokeToolHandler
3
- } from "../../chunk-JIOOUIDX.js";
3
+ } from "../../chunk-QPKPYDPB.js";
4
4
  import {
5
5
  createListToolsHandler
6
- } from "../../chunk-TVU5FL2T.js";
6
+ } from "../../chunk-LFXK3FNF.js";
7
+ import "../../chunk-6QFZYYUV.js";
7
8
  import "../../chunk-MA5H6PSF.js";
8
- import "../../chunk-CIAVINZ5.js";
9
+ import "../../chunk-YLU2JTHL.js";
9
10
  import "../../chunk-H37EB22A.js";
10
11
  import "../../chunk-6DXGZZA4.js";
11
- import "../../chunk-SIDWSSWV.js";
12
+ import "../../chunk-E3Q3FRKQ.js";
12
13
  export {
13
14
  createInvokeToolHandler,
14
15
  createListToolsHandler
@@ -174,7 +174,7 @@ function describeError(err) {
174
174
  }
175
175
 
176
176
  // package.json
177
- var version = "0.21.0-rc.3";
177
+ var version = "0.21.0";
178
178
 
179
179
  // src/metrics/otlp.ts
180
180
  var SCOPE_NAME = "@lovable.dev/mcp-js";
@@ -281,7 +281,8 @@ var BaseMetricRecorder = class {
281
281
  method: ev.method,
282
282
  outcome: ev.outcome,
283
283
  durationMs: ev.durationMs,
284
- stack: this.stack
284
+ stack: this.stack,
285
+ ...ev.errorText !== void 0 && { errorText: ev.errorText }
285
286
  });
286
287
  if (!this.config.enabled)
287
288
  return;
@@ -440,23 +441,9 @@ function parseSafeUrl(subject, raw, ErrorClass = Error) {
440
441
 
441
442
  // src/auth/discovery.ts
442
443
  var OAuthConfigurationError = class extends Error {
443
- constructor(message, stage) {
444
- super(message);
445
- this.stage = stage;
446
- this.name = "OAuthConfigurationError";
447
- }
448
- };
449
- var OAuthUpstreamUnreachableError = class extends Error {
450
- constructor(message, stage) {
451
- super(message);
452
- this.stage = stage;
453
- this.name = "OAuthUpstreamUnreachableError";
454
- }
455
- };
456
- var MetadataUnreachableError = class extends Error {
457
444
  constructor(message) {
458
445
  super(message);
459
- this.name = "MetadataUnreachableError";
446
+ this.name = "OAuthConfigurationError";
460
447
  }
461
448
  };
462
449
  var METADATA_FETCH_TIMEOUT_MS = 5e3;
@@ -488,28 +475,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
488
475
  try {
489
476
  return await fetchOAuthServerMetadata(url, expectedIssuer);
490
477
  } catch (err) {
491
- const error = err instanceof Error ? err : new Error(String(err));
492
- log.debug("oauth.discovery.attempt_failed", { url, ...describeError(error) });
493
- errors.push(error);
478
+ log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
479
+ errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
494
480
  }
495
481
  }
496
- const unreachable = errors.some((e) => e instanceof MetadataUnreachableError);
497
- const detail = errors.map((e) => e.message).join("; ");
498
- log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
499
- const message = `failed to discover OAuth server metadata (${detail})`;
500
- throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
482
+ log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
483
+ throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
501
484
  }
502
485
  async function fetchOAuthServerMetadata(url, expectedIssuer) {
503
486
  log.debug("oauth.discovery.fetch", { url });
504
- let response;
505
- try {
506
- response = await fetch(url, {
507
- signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS),
508
- redirect: "manual"
509
- });
510
- } catch (err) {
511
- throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
512
- }
487
+ const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
513
488
  if (!response.ok) {
514
489
  throw new Error(String(response.status));
515
490
  }
@@ -532,21 +507,14 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
532
507
  try {
533
508
  return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
534
509
  } catch (err) {
535
- const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
536
- if (err instanceof MetadataUnreachableError) {
537
- log.error("oauth.discovery.unreachable", {
538
- issuer,
539
- ...describeError(err),
540
- outcome: "503 authorization server unreachable"
541
- });
542
- throw new OAuthUpstreamUnreachableError(message, "discovery");
543
- }
544
510
  log.error("oauth.discovery.config_error", {
545
511
  issuer,
546
512
  ...describeError(err),
547
513
  outcome: "500 oauth configuration error"
548
514
  });
549
- throw new OAuthConfigurationError(message, "discovery");
515
+ throw new OAuthConfigurationError(
516
+ `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
517
+ );
550
518
  }
551
519
  }
552
520
  function createOAuthDiscoveryResolver(auth) {
@@ -579,13 +547,18 @@ function readString(value) {
579
547
  function splitScopes(value) {
580
548
  if (typeof value === "string")
581
549
  return value.split(/\s+/).filter(Boolean);
582
- if (Array.isArray(value))
583
- return value.filter((entry) => typeof entry === "string" && entry.length > 0);
584
- return [];
550
+ return stringClaimList(value);
585
551
  }
586
552
  function stringClaim(claims, name) {
587
553
  return readString(claims[name]);
588
554
  }
555
+ function stringClaimList(value) {
556
+ if (typeof value === "string")
557
+ return value === "" ? [] : [value];
558
+ if (Array.isArray(value))
559
+ return value.filter((entry) => typeof entry === "string" && entry !== "");
560
+ return [];
561
+ }
589
562
 
590
563
  // src/auth/verifier.ts
591
564
  var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
@@ -612,24 +585,15 @@ function tokenHeaderFields(token) {
612
585
  }
613
586
  }
614
587
  async function fetchVerificationKeySet(jwksUri) {
615
- let response;
616
- try {
617
- response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
618
- } catch (err) {
619
- log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
620
- throw new OAuthUpstreamUnreachableError(
621
- `JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`,
622
- "jwks"
623
- );
624
- }
625
588
  try {
589
+ const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
626
590
  if (!response.ok)
627
591
  throw new Error(`JWKS endpoint returned ${response.status}`);
628
592
  const json = await response.json();
629
593
  return (0, import_jose.createLocalJWKSet)(json);
630
594
  } catch (err) {
631
595
  log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
632
- throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`, "jwks");
596
+ throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
633
597
  }
634
598
  }
635
599
  function assertAccessTokenTyp(token, allowed) {
@@ -646,13 +610,12 @@ function assertAccessTokenTyp(token, allowed) {
646
610
  throw new OAuthTokenError(401, "invalid_token", "Access token typ header is not accepted");
647
611
  }
648
612
  }
649
- async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
613
+ async function verifyJwtClaims(token, keySet, issuer, auth) {
650
614
  try {
651
615
  const { payload } = await (0, import_jose.jwtVerify)(token, keySet, {
652
616
  // `issuer` is trimmed of any trailing slash; accept both forms so a token whose
653
617
  // `iss` carries the slash the AS publishes still verifies.
654
618
  issuer: [issuer, `${issuer}/`],
655
- audience: [...audience],
656
619
  algorithms: auth.algorithms ? [...auth.algorithms] : DEFAULT_JWT_ALGORITHMS,
657
620
  requiredClaims: ["sub", "exp"],
658
621
  clockTolerance: auth.clockToleranceSeconds ?? DEFAULT_CLOCK_TOLERANCE_SECONDS
@@ -663,6 +626,22 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
663
626
  throw err;
664
627
  }
665
628
  }
629
+ function checkAcceptedAudience(claims, accepted, auth) {
630
+ const audPopulated = claims.aud !== void 0 && !(Array.isArray(claims.aud) && claims.aud.length === 0);
631
+ if (audPopulated) {
632
+ if (stringClaimList(claims.aud).some((audience) => accepted.includes(audience)))
633
+ return "aud";
634
+ log.debug("oauth.verify.bad_audience", { tokenAud: claims.aud, accepted, outcome: "401 invalid_token" });
635
+ } else if (auth.acceptResourceClaim !== false) {
636
+ const acceptedTrimmed = accepted.map(trimTrailingSlash);
637
+ if (stringClaimList(claims.resource).some((resource) => acceptedTrimmed.includes(trimTrailingSlash(resource))))
638
+ return "resource";
639
+ log.debug("oauth.verify.bad_resource", { tokenResource: claims.resource, accepted, outcome: "401 invalid_token" });
640
+ } else {
641
+ log.debug("oauth.verify.empty_audience", { accepted, outcome: "401 invalid_token" });
642
+ }
643
+ throw new OAuthTokenError(401, "invalid_token", "token audience is not accepted");
644
+ }
666
645
  function assertNonEmptySubject(claims) {
667
646
  const sub = claims["sub"];
668
647
  if (typeof sub !== "string" || sub.trim() === "") {
@@ -707,14 +686,16 @@ function createOAuthTokenVerifier(auth, discovery) {
707
686
  log.debug("oauth.jwks.fetch", { jwksUri });
708
687
  const keySet = await fetchVerificationKeySet(jwksUri);
709
688
  assertAccessTokenTyp(token, allowedTyps);
710
- const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
689
+ const claims = await verifyJwtClaims(token, keySet, issuer, auth);
690
+ const audienceVia = checkAcceptedAudience(claims, acceptedAudiences, auth);
711
691
  assertNonEmptySubject(claims);
712
692
  const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
713
693
  assertOAuthClientClaim(auth, context.principal.clientId);
714
694
  log.info("oauth.verify.ok", {
715
695
  sub: context.principal.sub,
716
696
  clientId: context.principal.clientId,
717
- scopes: context.principal.scopes
697
+ scopes: context.principal.scopes,
698
+ audienceVia
718
699
  });
719
700
  return context;
720
701
  };
@@ -763,12 +744,6 @@ function oauthConfigurationErrorResponse() {
763
744
  headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
764
745
  });
765
746
  }
766
- function oauthUpstreamUnreachableResponse() {
767
- return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
768
- status: 503,
769
- headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
770
- });
771
- }
772
747
  function parseBearerToken(request) {
773
748
  const header = request.headers.get("Authorization");
774
749
  if (!header)
@@ -841,22 +816,12 @@ function createRequestAuthorizer(mcp, options = {}) {
841
816
  assertRequiredScopes(runtime.auth, auth);
842
817
  return { ok: true, auth };
843
818
  } catch (err) {
844
- if (err instanceof OAuthUpstreamUnreachableError) {
845
- log.error("auth.upstream_unreachable", { ...describeError(err), stage: err.stage, outcome: "503" });
846
- await recorder?.emit({
847
- tool: null,
848
- method: "authorize",
849
- outcome: err.stage === "jwks" ? "auth_jwks_unreachable" : "auth_discovery_unreachable",
850
- durationMs: nowMs() - startedAt
851
- });
852
- return { ok: false, response: oauthUpstreamUnreachableResponse() };
853
- }
854
819
  if (err instanceof OAuthConfigurationError) {
855
- log.error("auth.config_error", { ...describeError(err), stage: err.stage, outcome: "500" });
820
+ log.error("auth.config_error", { ...describeError(err), outcome: "500" });
856
821
  await recorder?.emit({
857
822
  tool: null,
858
823
  method: "authorize",
859
- outcome: err.stage === "jwks" ? "auth_jwks_config_error" : "auth_discovery_config_error",
824
+ outcome: "auth_config_error",
860
825
  durationMs: nowMs() - startedAt
861
826
  });
862
827
  return { ok: false, response: oauthConfigurationErrorResponse() };
@@ -935,6 +900,14 @@ var ToolContext = class {
935
900
  }
936
901
  };
937
902
 
903
+ // src/core/content.ts
904
+ function extractTextContent(content) {
905
+ if (!content)
906
+ return void 0;
907
+ const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
908
+ return text.length > 0 ? text : void 0;
909
+ }
910
+
938
911
  // src/core/cors.ts
939
912
  var EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
940
913
  var ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
@@ -985,7 +958,8 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
985
958
  tool: tool.name,
986
959
  method: "tools/call",
987
960
  outcome: result.isError ? "tool_error" : "ok",
988
- durationMs: nowMs() - start
961
+ durationMs: nowMs() - start,
962
+ errorText: result.isError ? extractTextContent(result.content) : void 0
989
963
  });
990
964
  return { content: result.content ?? [], structuredContent: result.structuredContent, isError: result.isError };
991
965
  };
@@ -1241,7 +1215,8 @@ function createInvokeToolHandler(mcp, options = {}) {
1241
1215
  tool: tool.name,
1242
1216
  method: "tools/call",
1243
1217
  outcome: result.isError ? "tool_error" : "ok",
1244
- durationMs: nowMs() - start
1218
+ durationMs: nowMs() - start,
1219
+ errorText: result.isError ? extractTextContent(result.content) : void 0
1245
1220
  });
1246
1221
  return Response.json({
1247
1222
  content: result.content ?? [],
@@ -1,4 +1,4 @@
1
- import { d as McpDefinition } from '../../types-CPkhCRxc.js';
1
+ import { d as McpDefinition } from '../../types-qqrmJ736.js';
2
2
  import 'zod';
3
3
 
4
4
  type SupabaseHandler = (request: Request) => Promise<Response>;
@@ -1,4 +1,4 @@
1
- import { d as McpDefinition } from '../../types-CPkhCRxc.js';
1
+ import { d as McpDefinition } from '../../types-qqrmJ736.js';
2
2
  import 'zod';
3
3
 
4
4
  type SupabaseHandler = (request: Request) => Promise<Response>;
@@ -3,21 +3,22 @@ import {
3
3
  } from "../../chunk-UQK5UO6C.js";
4
4
  import {
5
5
  createMcpProtocolHandler
6
- } from "../../chunk-5W6JK6OS.js";
6
+ } from "../../chunk-CI7JYLV3.js";
7
7
  import {
8
8
  createOAuthProtectedResourceMetadataHandler
9
- } from "../../chunk-NENAFXZJ.js";
9
+ } from "../../chunk-ENJDNFEB.js";
10
10
  import {
11
11
  createInvokeToolHandler
12
- } from "../../chunk-JIOOUIDX.js";
12
+ } from "../../chunk-QPKPYDPB.js";
13
13
  import {
14
14
  createListToolsHandler
15
- } from "../../chunk-TVU5FL2T.js";
15
+ } from "../../chunk-LFXK3FNF.js";
16
+ import "../../chunk-6QFZYYUV.js";
16
17
  import "../../chunk-MA5H6PSF.js";
17
18
  import {
18
19
  assertResourcePathShape,
19
20
  createRecorderForRuntime
20
- } from "../../chunk-CIAVINZ5.js";
21
+ } from "../../chunk-YLU2JTHL.js";
21
22
  import {
22
23
  trimTrailingSlash
23
24
  } from "../../chunk-H37EB22A.js";
@@ -28,7 +29,7 @@ import {
28
29
  FUNCTIONS_MOUNT_PREFIX,
29
30
  assertFunctionName
30
31
  } from "../../chunk-XQWJN6DC.js";
31
- import "../../chunk-SIDWSSWV.js";
32
+ import "../../chunk-E3Q3FRKQ.js";
32
33
 
33
34
  // src/stacks/supabase/handler.ts
34
35
  function deriveResourcePath(options) {
@@ -33,7 +33,7 @@ var import_node_fs = require("fs");
33
33
  var import_node_path = require("path");
34
34
 
35
35
  // package.json
36
- var version = "0.21.0-rc.3";
36
+ var version = "0.21.0";
37
37
 
38
38
  // src/core/fs-errors.ts
39
39
  function isFileMissing(err) {
@@ -7,7 +7,7 @@ import {
7
7
  } from "../../chunk-XQWJN6DC.js";
8
8
  import {
9
9
  version
10
- } from "../../chunk-SIDWSSWV.js";
10
+ } from "../../chunk-E3Q3FRKQ.js";
11
11
 
12
12
  // src/stacks/supabase/vite.ts
13
13
  import { resolve as resolve2, sep as sep2 } from "path";