@lovable.dev/mcp-js 0.6.0 → 0.7.1

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.
@@ -31,17 +31,71 @@ var import_webStandardStreamableHttp = require("@modelcontextprotocol/sdk/server
31
31
  // src/core/http.ts
32
32
  var JSON_HEADERS = { "Content-Type": "application/json" };
33
33
 
34
+ // src/core/logger.ts
35
+ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
36
+ function isLogLevel(value) {
37
+ return typeof value === "string" && value in LEVEL_RANK;
38
+ }
39
+ function readEnvLevel() {
40
+ try {
41
+ const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
42
+ const normalized = raw?.trim().toLowerCase();
43
+ return isLogLevel(normalized) ? normalized : void 0;
44
+ } catch {
45
+ return void 0;
46
+ }
47
+ }
48
+ var currentLevel = readEnvLevel() ?? "silent";
49
+ function enabled(level) {
50
+ return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
51
+ }
52
+ function emit(level, method, event, fields) {
53
+ if (!enabled(level))
54
+ return;
55
+ const message = `[mcp-js] ${event}`;
56
+ if (fields)
57
+ console[method](message, fields);
58
+ else
59
+ console[method](message);
60
+ }
61
+ var log = {
62
+ error: (event, fields) => emit("error", "error", event, fields),
63
+ warn: (event, fields) => emit("warn", "warn", event, fields),
64
+ info: (event, fields) => emit("info", "info", event, fields),
65
+ debug: (event, fields) => emit("debug", "debug", event, fields)
66
+ };
67
+ function describeError(err) {
68
+ if (err instanceof Error) {
69
+ const code = err.code;
70
+ return { name: err.name, message: err.message, ...typeof code === "string" ? { code } : {} };
71
+ }
72
+ return { value: String(err) };
73
+ }
74
+
34
75
  // src/core/promise.ts
35
- function cachedPromise(load) {
76
+ function cachedPromise(load, label) {
36
77
  let settled = false;
37
78
  let value;
38
79
  return async () => {
39
- if (settled)
80
+ if (settled) {
81
+ if (label)
82
+ log.debug(`${label}.cache_hit`);
40
83
  return value;
41
- const loaded = await load();
42
- settled = true;
43
- value = loaded;
44
- return loaded;
84
+ }
85
+ if (label)
86
+ log.debug(`${label}.load_start`);
87
+ try {
88
+ const loaded = await load();
89
+ settled = true;
90
+ value = loaded;
91
+ if (label)
92
+ log.debug(`${label}.settled`);
93
+ return loaded;
94
+ } catch (err) {
95
+ if (label)
96
+ log.debug(`${label}.load_failed`, describeError(err));
97
+ throw err;
98
+ }
45
99
  };
46
100
  }
47
101
 
@@ -117,13 +171,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
117
171
  try {
118
172
  return await fetchOAuthServerMetadata(url, expectedIssuer);
119
173
  } catch (err) {
174
+ log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
120
175
  errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
121
176
  }
122
177
  }
178
+ log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
123
179
  throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
124
180
  }
125
181
  async function fetchOAuthServerMetadata(url, expectedIssuer) {
126
- const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "error" });
182
+ log.debug("oauth.discovery.fetch", { url });
183
+ const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
127
184
  if (!response.ok) {
128
185
  throw new Error(String(response.status));
129
186
  }
@@ -133,6 +190,7 @@ async function fetchOAuthServerMetadata(url, expectedIssuer) {
133
190
  }
134
191
  parseSafeUrl("discovered issuer", json.issuer);
135
192
  if (trimTrailingSlash(json.issuer) !== expectedIssuer) {
193
+ log.warn("oauth.discovery.issuer_mismatch", { url, expectedIssuer, published: json.issuer });
136
194
  throw new Error("issuer mismatch");
137
195
  }
138
196
  if (typeof json.jwks_uri !== "string") {
@@ -145,6 +203,11 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
145
203
  try {
146
204
  return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
147
205
  } catch (err) {
206
+ log.error("oauth.discovery.config_error", {
207
+ issuer,
208
+ ...describeError(err),
209
+ outcome: "500 oauth configuration error"
210
+ });
148
211
  throw new OAuthConfigurationError(
149
212
  `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
150
213
  );
@@ -152,10 +215,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
152
215
  }
153
216
  function createOAuthDiscoveryResolver(auth) {
154
217
  const configuredIssuer = trimTrailingSlash(auth.issuer);
155
- const oauthServerMetadata = cachedPromise(() => fetchIssuerOAuthServerMetadata(configuredIssuer));
218
+ const oauthServerMetadata = cachedPromise(
219
+ () => fetchIssuerOAuthServerMetadata(configuredIssuer),
220
+ "oauth.discovery.metadata"
221
+ );
156
222
  return {
157
223
  resolveIssuer: async () => configuredIssuer,
158
- resolveJwksUri: async () => auth.jwksUri ?? (await oauthServerMetadata()).jwks_uri
224
+ resolveJwksUri: async () => {
225
+ if (auth.jwksUri) {
226
+ log.debug("oauth.jwks.resolved", { jwksUri: auth.jwksUri, source: "configured" });
227
+ return auth.jwksUri;
228
+ }
229
+ const jwksUri = (await oauthServerMetadata()).jwks_uri;
230
+ log.debug("oauth.jwks.resolved", { jwksUri, source: "discovered" });
231
+ return jwksUri;
232
+ }
159
233
  };
160
234
  }
161
235
 
@@ -207,6 +281,7 @@ function stringClaim(claims, name) {
207
281
  // src/auth/verifier.ts
208
282
  var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
209
283
  var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
284
+ var JWKS_FETCH_TIMEOUT_MS = 5e3;
210
285
  var OAuthTokenError = class extends Error {
211
286
  constructor(status, oauthError, message) {
212
287
  super(message);
@@ -218,12 +293,25 @@ var OAuthTokenError = class extends Error {
218
293
  function resolveAcceptedAudiences(auth, resource) {
219
294
  return auth.acceptedAudiences ?? [resource];
220
295
  }
221
- function isJwksFetchFailure(err) {
222
- if (err instanceof import_jose.errors.JWKSTimeout || err instanceof import_jose.errors.JWKSInvalid)
223
- return true;
224
- if (!(err instanceof import_jose.errors.JOSEError))
225
- return err instanceof Error;
226
- return err.code === "ERR_JOSE_GENERIC";
296
+ function tokenHeaderFields(token) {
297
+ try {
298
+ const header = (0, import_jose.decodeProtectedHeader)(token);
299
+ return { jwtAlg: header.alg, jwtKid: header.kid, tokenLength: token.length };
300
+ } catch {
301
+ return { tokenLength: token.length };
302
+ }
303
+ }
304
+ async function fetchVerificationKeySet(jwksUri) {
305
+ try {
306
+ const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
307
+ if (!response.ok)
308
+ throw new Error(`JWKS endpoint returned ${response.status}`);
309
+ const json = await response.json();
310
+ return (0, import_jose.createLocalJWKSet)(json);
311
+ } catch (err) {
312
+ log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
313
+ throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
314
+ }
227
315
  }
228
316
  async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
229
317
  try {
@@ -238,20 +326,20 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
238
326
  });
239
327
  return payload;
240
328
  } catch (err) {
241
- if (isJwksFetchFailure(err)) {
242
- throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
243
- }
329
+ log.debug("oauth.verify.rejected", { ...describeError(err), outcome: "401 invalid_token" });
244
330
  throw err;
245
331
  }
246
332
  }
247
333
  function assertNonEmptySubject(claims) {
248
334
  const sub = claims["sub"];
249
335
  if (typeof sub !== "string" || sub.trim() === "") {
336
+ log.debug("oauth.verify.bad_subject", { subType: typeof sub });
250
337
  throw new OAuthTokenError(401, "invalid_token", "token subject claim must be a non-empty string");
251
338
  }
252
339
  }
253
340
  function assertOAuthClientClaim(auth, clientId) {
254
341
  if (auth.requireOAuthClientClaim !== false && !clientId) {
342
+ log.debug("oauth.verify.missing_client_claim", { outcome: "401 invalid_token" });
255
343
  throw new OAuthTokenError(401, "invalid_token", "OAuth client claim is required");
256
344
  }
257
345
  }
@@ -276,17 +364,23 @@ function buildMcpAuthContext(args) {
276
364
  };
277
365
  }
278
366
  function createOAuthTokenVerifier(auth, discovery) {
279
- const loadRemoteJwksKeySet = cachedPromise(
280
- () => discovery.resolveJwksUri().then((jwksURI) => (0, import_jose.createRemoteJWKSet)(new URL(jwksURI)))
281
- );
282
367
  return async (token, request, options) => {
283
368
  const resource = resolveProtectedResource(auth, request, options);
284
369
  const issuer = await discovery.resolveIssuer();
285
370
  const acceptedAudiences = resolveAcceptedAudiences(auth, resource);
286
- const claims = await verifyJwtClaims(token, await loadRemoteJwksKeySet(), issuer, acceptedAudiences, auth);
371
+ log.debug("oauth.verify.start", { issuer, acceptedAudiences, resource, ...tokenHeaderFields(token) });
372
+ const jwksUri = await discovery.resolveJwksUri();
373
+ log.debug("oauth.jwks.fetch", { jwksUri });
374
+ const keySet = await fetchVerificationKeySet(jwksUri);
375
+ const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
287
376
  assertNonEmptySubject(claims);
288
377
  const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
289
378
  assertOAuthClientClaim(auth, context.principal.clientId);
379
+ log.info("oauth.verify.ok", {
380
+ sub: context.principal.sub,
381
+ clientId: context.principal.clientId,
382
+ scopes: context.principal.scopes
383
+ });
290
384
  return context;
291
385
  };
292
386
  }
@@ -378,22 +472,27 @@ function createRequestAuthorizer(mcp, options = {}) {
378
472
  if (runtime.kind === "unconfigured")
379
473
  return { ok: true };
380
474
  const token = parseBearerToken(request);
381
- if (!token)
475
+ if (!token) {
476
+ log.info("auth.no_bearer_token", { outcome: "401" });
382
477
  return { ok: false, response: challengeResponse(runtime.auth, request, 401) };
478
+ }
383
479
  try {
384
480
  const auth = await runtime.verify(token, request, runtime.options);
385
481
  assertRequiredScopes(runtime.auth, auth);
386
482
  return { ok: true, auth };
387
483
  } catch (err) {
388
484
  if (err instanceof OAuthConfigurationError) {
485
+ log.error("auth.config_error", { ...describeError(err), outcome: "500" });
389
486
  return { ok: false, response: oauthConfigurationErrorResponse() };
390
487
  }
391
488
  if (err instanceof OAuthTokenError) {
489
+ log.info("auth.token_rejected", { status: err.status, oauthError: err.oauthError });
392
490
  return {
393
491
  ok: false,
394
492
  response: challengeResponse(runtime.auth, request, err.status, err.oauthError, err.message)
395
493
  };
396
494
  }
495
+ log.error("auth.unexpected_error", { ...describeError(err), outcome: "401" });
397
496
  return {
398
497
  ok: false,
399
498
  response: challengeResponse(runtime.auth, request, 401, "invalid_token", "Invalid access token")
@@ -511,7 +610,8 @@ function createMcpProtocolHandler(mcp, options = {}) {
511
610
  });
512
611
  await server.connect(transport);
513
612
  return await transport.handleRequest(request);
514
- } catch {
613
+ } catch (err) {
614
+ log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
515
615
  return Response.json(
516
616
  { jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
517
617
  { status: 500 }
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createMcpProtocolHandler
3
- } from "../../chunk-NJ5WBRYI.js";
3
+ } from "../../chunk-3XPKR4G6.js";
4
4
  import "../../chunk-MA5H6PSF.js";
5
- import "../../chunk-ZKKLOL2C.js";
6
- import "../../chunk-QA3FWDUV.js";
5
+ import "../../chunk-HRMLGCXV.js";
6
+ import "../../chunk-QC3DXQTH.js";
7
7
  import "../../chunk-6DXGZZA4.js";
8
8
  export {
9
9
  createMcpProtocolHandler
@@ -36,17 +36,71 @@ function methodNotAllowed(allow) {
36
36
  });
37
37
  }
38
38
 
39
+ // src/core/logger.ts
40
+ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
41
+ function isLogLevel(value) {
42
+ return typeof value === "string" && value in LEVEL_RANK;
43
+ }
44
+ function readEnvLevel() {
45
+ try {
46
+ const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
47
+ const normalized = raw?.trim().toLowerCase();
48
+ return isLogLevel(normalized) ? normalized : void 0;
49
+ } catch {
50
+ return void 0;
51
+ }
52
+ }
53
+ var currentLevel = readEnvLevel() ?? "silent";
54
+ function enabled(level) {
55
+ return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
56
+ }
57
+ function emit(level, method, event, fields) {
58
+ if (!enabled(level))
59
+ return;
60
+ const message = `[mcp-js] ${event}`;
61
+ if (fields)
62
+ console[method](message, fields);
63
+ else
64
+ console[method](message);
65
+ }
66
+ var log = {
67
+ error: (event, fields) => emit("error", "error", event, fields),
68
+ warn: (event, fields) => emit("warn", "warn", event, fields),
69
+ info: (event, fields) => emit("info", "info", event, fields),
70
+ debug: (event, fields) => emit("debug", "debug", event, fields)
71
+ };
72
+ function describeError(err) {
73
+ if (err instanceof Error) {
74
+ const code = err.code;
75
+ return { name: err.name, message: err.message, ...typeof code === "string" ? { code } : {} };
76
+ }
77
+ return { value: String(err) };
78
+ }
79
+
39
80
  // src/core/promise.ts
40
- function cachedPromise(load) {
81
+ function cachedPromise(load, label) {
41
82
  let settled = false;
42
83
  let value;
43
84
  return async () => {
44
- if (settled)
85
+ if (settled) {
86
+ if (label)
87
+ log.debug(`${label}.cache_hit`);
45
88
  return value;
46
- const loaded = await load();
47
- settled = true;
48
- value = loaded;
49
- return loaded;
89
+ }
90
+ if (label)
91
+ log.debug(`${label}.load_start`);
92
+ try {
93
+ const loaded = await load();
94
+ settled = true;
95
+ value = loaded;
96
+ if (label)
97
+ log.debug(`${label}.settled`);
98
+ return loaded;
99
+ } catch (err) {
100
+ if (label)
101
+ log.debug(`${label}.load_failed`, describeError(err));
102
+ throw err;
103
+ }
50
104
  };
51
105
  }
52
106
 
@@ -122,13 +176,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
122
176
  try {
123
177
  return await fetchOAuthServerMetadata(url, expectedIssuer);
124
178
  } catch (err) {
179
+ log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
125
180
  errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
126
181
  }
127
182
  }
183
+ log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
128
184
  throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
129
185
  }
130
186
  async function fetchOAuthServerMetadata(url, expectedIssuer) {
131
- const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "error" });
187
+ log.debug("oauth.discovery.fetch", { url });
188
+ const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
132
189
  if (!response.ok) {
133
190
  throw new Error(String(response.status));
134
191
  }
@@ -138,6 +195,7 @@ async function fetchOAuthServerMetadata(url, expectedIssuer) {
138
195
  }
139
196
  parseSafeUrl("discovered issuer", json.issuer);
140
197
  if (trimTrailingSlash(json.issuer) !== expectedIssuer) {
198
+ log.warn("oauth.discovery.issuer_mismatch", { url, expectedIssuer, published: json.issuer });
141
199
  throw new Error("issuer mismatch");
142
200
  }
143
201
  if (typeof json.jwks_uri !== "string") {
@@ -150,6 +208,11 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
150
208
  try {
151
209
  return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
152
210
  } catch (err) {
211
+ log.error("oauth.discovery.config_error", {
212
+ issuer,
213
+ ...describeError(err),
214
+ outcome: "500 oauth configuration error"
215
+ });
153
216
  throw new OAuthConfigurationError(
154
217
  `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
155
218
  );
@@ -157,10 +220,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
157
220
  }
158
221
  function createOAuthDiscoveryResolver(auth) {
159
222
  const configuredIssuer = trimTrailingSlash(auth.issuer);
160
- const oauthServerMetadata = cachedPromise(() => fetchIssuerOAuthServerMetadata(configuredIssuer));
223
+ const oauthServerMetadata = cachedPromise(
224
+ () => fetchIssuerOAuthServerMetadata(configuredIssuer),
225
+ "oauth.discovery.metadata"
226
+ );
161
227
  return {
162
228
  resolveIssuer: async () => configuredIssuer,
163
- resolveJwksUri: async () => auth.jwksUri ?? (await oauthServerMetadata()).jwks_uri
229
+ resolveJwksUri: async () => {
230
+ if (auth.jwksUri) {
231
+ log.debug("oauth.jwks.resolved", { jwksUri: auth.jwksUri, source: "configured" });
232
+ return auth.jwksUri;
233
+ }
234
+ const jwksUri = (await oauthServerMetadata()).jwks_uri;
235
+ log.debug("oauth.jwks.resolved", { jwksUri, source: "discovered" });
236
+ return jwksUri;
237
+ }
164
238
  };
165
239
  }
166
240
 
@@ -209,6 +283,7 @@ function stringClaim(claims, name) {
209
283
  // src/auth/verifier.ts
210
284
  var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
211
285
  var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
286
+ var JWKS_FETCH_TIMEOUT_MS = 5e3;
212
287
  var OAuthTokenError = class extends Error {
213
288
  constructor(status, oauthError, message) {
214
289
  super(message);
@@ -220,12 +295,25 @@ var OAuthTokenError = class extends Error {
220
295
  function resolveAcceptedAudiences(auth, resource) {
221
296
  return auth.acceptedAudiences ?? [resource];
222
297
  }
223
- function isJwksFetchFailure(err) {
224
- if (err instanceof import_jose.errors.JWKSTimeout || err instanceof import_jose.errors.JWKSInvalid)
225
- return true;
226
- if (!(err instanceof import_jose.errors.JOSEError))
227
- return err instanceof Error;
228
- return err.code === "ERR_JOSE_GENERIC";
298
+ function tokenHeaderFields(token) {
299
+ try {
300
+ const header = (0, import_jose.decodeProtectedHeader)(token);
301
+ return { jwtAlg: header.alg, jwtKid: header.kid, tokenLength: token.length };
302
+ } catch {
303
+ return { tokenLength: token.length };
304
+ }
305
+ }
306
+ async function fetchVerificationKeySet(jwksUri) {
307
+ try {
308
+ const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
309
+ if (!response.ok)
310
+ throw new Error(`JWKS endpoint returned ${response.status}`);
311
+ const json = await response.json();
312
+ return (0, import_jose.createLocalJWKSet)(json);
313
+ } catch (err) {
314
+ log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
315
+ throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
316
+ }
229
317
  }
230
318
  async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
231
319
  try {
@@ -240,20 +328,20 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
240
328
  });
241
329
  return payload;
242
330
  } catch (err) {
243
- if (isJwksFetchFailure(err)) {
244
- throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
245
- }
331
+ log.debug("oauth.verify.rejected", { ...describeError(err), outcome: "401 invalid_token" });
246
332
  throw err;
247
333
  }
248
334
  }
249
335
  function assertNonEmptySubject(claims) {
250
336
  const sub = claims["sub"];
251
337
  if (typeof sub !== "string" || sub.trim() === "") {
338
+ log.debug("oauth.verify.bad_subject", { subType: typeof sub });
252
339
  throw new OAuthTokenError(401, "invalid_token", "token subject claim must be a non-empty string");
253
340
  }
254
341
  }
255
342
  function assertOAuthClientClaim(auth, clientId) {
256
343
  if (auth.requireOAuthClientClaim !== false && !clientId) {
344
+ log.debug("oauth.verify.missing_client_claim", { outcome: "401 invalid_token" });
257
345
  throw new OAuthTokenError(401, "invalid_token", "OAuth client claim is required");
258
346
  }
259
347
  }
@@ -278,17 +366,23 @@ function buildMcpAuthContext(args) {
278
366
  };
279
367
  }
280
368
  function createOAuthTokenVerifier(auth, discovery) {
281
- const loadRemoteJwksKeySet = cachedPromise(
282
- () => discovery.resolveJwksUri().then((jwksURI) => (0, import_jose.createRemoteJWKSet)(new URL(jwksURI)))
283
- );
284
369
  return async (token, request, options) => {
285
370
  const resource = resolveProtectedResource(auth, request, options);
286
371
  const issuer = await discovery.resolveIssuer();
287
372
  const acceptedAudiences = resolveAcceptedAudiences(auth, resource);
288
- const claims = await verifyJwtClaims(token, await loadRemoteJwksKeySet(), issuer, acceptedAudiences, auth);
373
+ log.debug("oauth.verify.start", { issuer, acceptedAudiences, resource, ...tokenHeaderFields(token) });
374
+ const jwksUri = await discovery.resolveJwksUri();
375
+ log.debug("oauth.jwks.fetch", { jwksUri });
376
+ const keySet = await fetchVerificationKeySet(jwksUri);
377
+ const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
289
378
  assertNonEmptySubject(claims);
290
379
  const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
291
380
  assertOAuthClientClaim(auth, context.principal.clientId);
381
+ log.info("oauth.verify.ok", {
382
+ sub: context.principal.sub,
383
+ clientId: context.principal.clientId,
384
+ scopes: context.principal.scopes
385
+ });
292
386
  return context;
293
387
  };
294
388
  }
@@ -390,7 +484,8 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
390
484
  );
391
485
  const response = withCors(Response.json(metadata, { headers }));
392
486
  return request.method === "HEAD" ? headResponse(response) : response;
393
- } catch {
487
+ } catch (err) {
488
+ log.error("oauth.metadata.config_error", { ...describeError(err), outcome: "500 oauth configuration error" });
394
489
  const response = withCors(oauthConfigurationErrorResponse());
395
490
  return request.method === "HEAD" ? headResponse(response) : response;
396
491
  }
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createOAuthProtectedResourceMetadataHandler
3
- } from "../chunk-K2T4WTKX.js";
4
- import "../chunk-ZKKLOL2C.js";
5
- import "../chunk-QA3FWDUV.js";
3
+ } from "../chunk-W7F6JRDB.js";
4
+ import "../chunk-HRMLGCXV.js";
5
+ import "../chunk-QC3DXQTH.js";
6
6
  import "../chunk-6DXGZZA4.js";
7
7
  export {
8
8
  createOAuthProtectedResourceMetadataHandler