@lovable.dev/mcp-js 0.19.0 → 0.20.1-rc.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.
- package/dist/{base-C9rhAHZ0.d.ts → base-RSqYamjy.d.ts} +1 -1
- package/dist/{chunk-UEOBGHXF.js → chunk-A2GUTEAK.js} +1 -1
- package/dist/{chunk-5FX6IIQ6.js → chunk-E7CPOJN6.js} +74 -17
- package/dist/{chunk-CLXKPZZE.js → chunk-ID3YWZBR.js} +1 -1
- package/dist/{chunk-CDZ7MBXC.js → chunk-OMEL6F6I.js} +1 -1
- package/dist/{chunk-57ZH2WW5.js → chunk-RT2I26ZN.js} +1 -1
- package/dist/{chunk-OMWXY6WY.js → chunk-YAXKWTJK.js} +1 -1
- package/dist/cli/extract-manifest.cjs +1 -1
- package/dist/cli/extract-manifest.js +3 -3
- package/dist/protocols/mcp/index.cjs +74 -17
- package/dist/protocols/mcp/index.d.cts +1 -1
- package/dist/protocols/mcp/index.d.ts +1 -1
- package/dist/protocols/mcp/index.js +3 -3
- package/dist/protocols/oauth-metadata.cjs +56 -15
- package/dist/protocols/oauth-metadata.js +3 -3
- package/dist/protocols/rest/index.cjs +74 -17
- package/dist/protocols/rest/index.d.cts +1 -1
- package/dist/protocols/rest/index.d.ts +1 -1
- package/dist/protocols/rest/index.js +4 -4
- package/dist/stacks/supabase/index.cjs +74 -17
- package/dist/stacks/supabase/index.js +6 -6
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +74 -17
- package/dist/stacks/tanstack/index.js +6 -6
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_config_error";
|
|
1
|
+
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_config_error" | "auth_upstream_unreachable";
|
|
2
2
|
/** One recorded MCP call. Carries no arguments or response payloads — only the
|
|
3
3
|
* tool name, the JSON-RPC method, the result class, timing, and byte sizes. */
|
|
4
4
|
interface InvocationRecord {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-6DXGZZA4.js";
|
|
14
14
|
import {
|
|
15
15
|
version
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-OMEL6F6I.js";
|
|
17
17
|
|
|
18
18
|
// src/core/http.ts
|
|
19
19
|
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
@@ -132,7 +132,7 @@ var BaseMetricRecorder = class {
|
|
|
132
132
|
this.config = config;
|
|
133
133
|
this.server = server;
|
|
134
134
|
this.deps = deps;
|
|
135
|
-
this.doFetch = deps.fetch ?? globalThis.fetch;
|
|
135
|
+
this.doFetch = deps.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
|
|
136
136
|
this.usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
137
137
|
const headers = {};
|
|
138
138
|
if (!this.usesLovableEndpoint) {
|
|
@@ -289,6 +289,18 @@ var OAuthConfigurationError = class extends Error {
|
|
|
289
289
|
this.name = "OAuthConfigurationError";
|
|
290
290
|
}
|
|
291
291
|
};
|
|
292
|
+
var OAuthUpstreamUnreachableError = class extends Error {
|
|
293
|
+
constructor(message) {
|
|
294
|
+
super(message);
|
|
295
|
+
this.name = "OAuthUpstreamUnreachableError";
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var MetadataUnreachableError = class extends Error {
|
|
299
|
+
constructor(message) {
|
|
300
|
+
super(message);
|
|
301
|
+
this.name = "MetadataUnreachableError";
|
|
302
|
+
}
|
|
303
|
+
};
|
|
292
304
|
var METADATA_FETCH_TIMEOUT_MS = 5e3;
|
|
293
305
|
function issuerPath(url) {
|
|
294
306
|
const path = trimTrailingSlash(url.pathname);
|
|
@@ -313,21 +325,37 @@ function oauthMetadataUrlsForIssuer(issuer) {
|
|
|
313
325
|
return [...pathInsertedOAuthMetadataUrls(url, path), `${normalizedIssuer}/.well-known/openid-configuration`];
|
|
314
326
|
}
|
|
315
327
|
async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer) {
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
320
|
-
} catch (err) {
|
|
328
|
+
const controller = new AbortController();
|
|
329
|
+
const attempts = metadataUrls.map(
|
|
330
|
+
(url) => fetchOAuthServerMetadata(url, expectedIssuer, controller.signal).catch((err) => {
|
|
321
331
|
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
322
|
-
|
|
323
|
-
}
|
|
332
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
333
|
+
})
|
|
334
|
+
);
|
|
335
|
+
try {
|
|
336
|
+
const metadata = await Promise.any(attempts);
|
|
337
|
+
controller.abort();
|
|
338
|
+
return metadata;
|
|
339
|
+
} catch (aggregate) {
|
|
340
|
+
const causes = aggregate instanceof AggregateError ? aggregate.errors : [aggregate];
|
|
341
|
+
const unreachable = causes.some((c) => c instanceof MetadataUnreachableError);
|
|
342
|
+
const detail = causes.map((c) => c instanceof Error ? c.message : String(c)).join("; ");
|
|
343
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
|
|
344
|
+
const message = `failed to discover OAuth server metadata (${detail})`;
|
|
345
|
+
throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
|
|
324
346
|
}
|
|
325
|
-
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
|
|
326
|
-
throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
|
|
327
347
|
}
|
|
328
|
-
async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
348
|
+
async function fetchOAuthServerMetadata(url, expectedIssuer, signal) {
|
|
329
349
|
log.debug("oauth.discovery.fetch", { url });
|
|
330
|
-
|
|
350
|
+
let response;
|
|
351
|
+
try {
|
|
352
|
+
response = await fetch(url, {
|
|
353
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)]),
|
|
354
|
+
redirect: "manual"
|
|
355
|
+
});
|
|
356
|
+
} catch (err) {
|
|
357
|
+
throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
|
|
358
|
+
}
|
|
331
359
|
if (!response.ok) {
|
|
332
360
|
throw new Error(String(response.status));
|
|
333
361
|
}
|
|
@@ -350,14 +378,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
350
378
|
try {
|
|
351
379
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
352
380
|
} catch (err) {
|
|
381
|
+
const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
382
|
+
if (err instanceof MetadataUnreachableError) {
|
|
383
|
+
log.error("oauth.discovery.unreachable", {
|
|
384
|
+
issuer,
|
|
385
|
+
...describeError(err),
|
|
386
|
+
outcome: "503 authorization server unreachable"
|
|
387
|
+
});
|
|
388
|
+
throw new OAuthUpstreamUnreachableError(message);
|
|
389
|
+
}
|
|
353
390
|
log.error("oauth.discovery.config_error", {
|
|
354
391
|
issuer,
|
|
355
392
|
...describeError(err),
|
|
356
393
|
outcome: "500 oauth configuration error"
|
|
357
394
|
});
|
|
358
|
-
throw new OAuthConfigurationError(
|
|
359
|
-
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
360
|
-
);
|
|
395
|
+
throw new OAuthConfigurationError(message);
|
|
361
396
|
}
|
|
362
397
|
}
|
|
363
398
|
function createOAuthDiscoveryResolver(auth) {
|
|
@@ -423,8 +458,14 @@ function tokenHeaderFields(token) {
|
|
|
423
458
|
}
|
|
424
459
|
}
|
|
425
460
|
async function fetchVerificationKeySet(jwksUri) {
|
|
461
|
+
let response;
|
|
462
|
+
try {
|
|
463
|
+
response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
464
|
+
} catch (err) {
|
|
465
|
+
log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
|
|
466
|
+
throw new OAuthUpstreamUnreachableError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
467
|
+
}
|
|
426
468
|
try {
|
|
427
|
-
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
428
469
|
if (!response.ok)
|
|
429
470
|
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
430
471
|
const json = await response.json();
|
|
@@ -565,6 +606,12 @@ function oauthConfigurationErrorResponse() {
|
|
|
565
606
|
headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
|
|
566
607
|
});
|
|
567
608
|
}
|
|
609
|
+
function oauthUpstreamUnreachableResponse() {
|
|
610
|
+
return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
|
|
611
|
+
status: 503,
|
|
612
|
+
headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
|
|
613
|
+
});
|
|
614
|
+
}
|
|
568
615
|
function parseBearerToken(request) {
|
|
569
616
|
const header = request.headers.get("Authorization");
|
|
570
617
|
if (!header)
|
|
@@ -637,6 +684,16 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
637
684
|
assertRequiredScopes(runtime.auth, auth);
|
|
638
685
|
return { ok: true, auth };
|
|
639
686
|
} catch (err) {
|
|
687
|
+
if (err instanceof OAuthUpstreamUnreachableError) {
|
|
688
|
+
log.error("auth.upstream_unreachable", { ...describeError(err), outcome: "503" });
|
|
689
|
+
await recorder?.emit({
|
|
690
|
+
tool: null,
|
|
691
|
+
method: "authorize",
|
|
692
|
+
outcome: "auth_upstream_unreachable",
|
|
693
|
+
durationMs: nowMs() - startedAt
|
|
694
|
+
});
|
|
695
|
+
return { ok: false, response: oauthUpstreamUnreachableResponse() };
|
|
696
|
+
}
|
|
640
697
|
if (err instanceof OAuthConfigurationError) {
|
|
641
698
|
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
642
699
|
await recorder?.emit({
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
methodNotAllowed,
|
|
11
11
|
nowMs,
|
|
12
12
|
withCors
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-E7CPOJN6.js";
|
|
14
14
|
|
|
15
15
|
// src/protocols/rest/invoke-tool.ts
|
|
16
16
|
import { getParseErrorMessage, objectFromShape, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildMcpListing
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-YAXKWTJK.js";
|
|
5
|
+
import "../chunk-E7CPOJN6.js";
|
|
6
6
|
import "../chunk-H37EB22A.js";
|
|
7
7
|
import "../chunk-6DXGZZA4.js";
|
|
8
8
|
import {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "../chunk-Y3ZFPEQH.js";
|
|
11
11
|
import {
|
|
12
12
|
version
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-OMEL6F6I.js";
|
|
14
14
|
|
|
15
15
|
// src/manifest/io.ts
|
|
16
16
|
import { randomUUID } from "crypto";
|
|
@@ -153,6 +153,18 @@ var OAuthConfigurationError = class extends Error {
|
|
|
153
153
|
this.name = "OAuthConfigurationError";
|
|
154
154
|
}
|
|
155
155
|
};
|
|
156
|
+
var OAuthUpstreamUnreachableError = class extends Error {
|
|
157
|
+
constructor(message) {
|
|
158
|
+
super(message);
|
|
159
|
+
this.name = "OAuthUpstreamUnreachableError";
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var MetadataUnreachableError = class extends Error {
|
|
163
|
+
constructor(message) {
|
|
164
|
+
super(message);
|
|
165
|
+
this.name = "MetadataUnreachableError";
|
|
166
|
+
}
|
|
167
|
+
};
|
|
156
168
|
var METADATA_FETCH_TIMEOUT_MS = 5e3;
|
|
157
169
|
function issuerPath(url) {
|
|
158
170
|
const path = trimTrailingSlash(url.pathname);
|
|
@@ -177,21 +189,37 @@ function oauthMetadataUrlsForIssuer(issuer) {
|
|
|
177
189
|
return [...pathInsertedOAuthMetadataUrls(url, path), `${normalizedIssuer}/.well-known/openid-configuration`];
|
|
178
190
|
}
|
|
179
191
|
async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer) {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
184
|
-
} catch (err) {
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
const attempts = metadataUrls.map(
|
|
194
|
+
(url) => fetchOAuthServerMetadata(url, expectedIssuer, controller.signal).catch((err) => {
|
|
185
195
|
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
196
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
try {
|
|
200
|
+
const metadata = await Promise.any(attempts);
|
|
201
|
+
controller.abort();
|
|
202
|
+
return metadata;
|
|
203
|
+
} catch (aggregate) {
|
|
204
|
+
const causes = aggregate instanceof AggregateError ? aggregate.errors : [aggregate];
|
|
205
|
+
const unreachable = causes.some((c) => c instanceof MetadataUnreachableError);
|
|
206
|
+
const detail = causes.map((c) => c instanceof Error ? c.message : String(c)).join("; ");
|
|
207
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
|
|
208
|
+
const message = `failed to discover OAuth server metadata (${detail})`;
|
|
209
|
+
throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function fetchOAuthServerMetadata(url, expectedIssuer, signal) {
|
|
193
213
|
log.debug("oauth.discovery.fetch", { url });
|
|
194
|
-
|
|
214
|
+
let response;
|
|
215
|
+
try {
|
|
216
|
+
response = await fetch(url, {
|
|
217
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)]),
|
|
218
|
+
redirect: "manual"
|
|
219
|
+
});
|
|
220
|
+
} catch (err) {
|
|
221
|
+
throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
|
|
222
|
+
}
|
|
195
223
|
if (!response.ok) {
|
|
196
224
|
throw new Error(String(response.status));
|
|
197
225
|
}
|
|
@@ -214,14 +242,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
214
242
|
try {
|
|
215
243
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
216
244
|
} catch (err) {
|
|
245
|
+
const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
246
|
+
if (err instanceof MetadataUnreachableError) {
|
|
247
|
+
log.error("oauth.discovery.unreachable", {
|
|
248
|
+
issuer,
|
|
249
|
+
...describeError(err),
|
|
250
|
+
outcome: "503 authorization server unreachable"
|
|
251
|
+
});
|
|
252
|
+
throw new OAuthUpstreamUnreachableError(message);
|
|
253
|
+
}
|
|
217
254
|
log.error("oauth.discovery.config_error", {
|
|
218
255
|
issuer,
|
|
219
256
|
...describeError(err),
|
|
220
257
|
outcome: "500 oauth configuration error"
|
|
221
258
|
});
|
|
222
|
-
throw new OAuthConfigurationError(
|
|
223
|
-
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
224
|
-
);
|
|
259
|
+
throw new OAuthConfigurationError(message);
|
|
225
260
|
}
|
|
226
261
|
}
|
|
227
262
|
function createOAuthDiscoveryResolver(auth) {
|
|
@@ -314,8 +349,14 @@ function tokenHeaderFields(token) {
|
|
|
314
349
|
}
|
|
315
350
|
}
|
|
316
351
|
async function fetchVerificationKeySet(jwksUri) {
|
|
352
|
+
let response;
|
|
353
|
+
try {
|
|
354
|
+
response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
355
|
+
} catch (err) {
|
|
356
|
+
log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
|
|
357
|
+
throw new OAuthUpstreamUnreachableError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
358
|
+
}
|
|
317
359
|
try {
|
|
318
|
-
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
319
360
|
if (!response.ok)
|
|
320
361
|
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
321
362
|
const json = await response.json();
|
|
@@ -456,6 +497,12 @@ function oauthConfigurationErrorResponse() {
|
|
|
456
497
|
headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
|
|
457
498
|
});
|
|
458
499
|
}
|
|
500
|
+
function oauthUpstreamUnreachableResponse() {
|
|
501
|
+
return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
|
|
502
|
+
status: 503,
|
|
503
|
+
headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
|
|
504
|
+
});
|
|
505
|
+
}
|
|
459
506
|
function parseBearerToken(request) {
|
|
460
507
|
const header = request.headers.get("Authorization");
|
|
461
508
|
if (!header)
|
|
@@ -520,6 +567,16 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
520
567
|
assertRequiredScopes(runtime.auth, auth);
|
|
521
568
|
return { ok: true, auth };
|
|
522
569
|
} catch (err) {
|
|
570
|
+
if (err instanceof OAuthUpstreamUnreachableError) {
|
|
571
|
+
log.error("auth.upstream_unreachable", { ...describeError(err), outcome: "503" });
|
|
572
|
+
await recorder?.emit({
|
|
573
|
+
tool: null,
|
|
574
|
+
method: "authorize",
|
|
575
|
+
outcome: "auth_upstream_unreachable",
|
|
576
|
+
durationMs: nowMs() - startedAt
|
|
577
|
+
});
|
|
578
|
+
return { ok: false, response: oauthUpstreamUnreachableResponse() };
|
|
579
|
+
}
|
|
523
580
|
if (err instanceof OAuthConfigurationError) {
|
|
524
581
|
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
525
582
|
await recorder?.emit({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
|
|
2
2
|
import { d as McpDefinition } from '../../types-CPkhCRxc.js';
|
|
3
|
-
import { M as MetricsRecorder } from '../../base-
|
|
3
|
+
import { M as MetricsRecorder } from '../../base-RSqYamjy.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
2
|
import { d as McpDefinition } from '../../types-CPkhCRxc.js';
|
|
3
|
-
import { M as MetricsRecorder } from '../../base-
|
|
3
|
+
import { M as MetricsRecorder } from '../../base-RSqYamjy.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
6
6
|
type McpProtocolHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMcpProtocolHandler
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-ID3YWZBR.js";
|
|
4
4
|
import "../../chunk-MA5H6PSF.js";
|
|
5
|
-
import "../../chunk-
|
|
5
|
+
import "../../chunk-E7CPOJN6.js";
|
|
6
6
|
import "../../chunk-H37EB22A.js";
|
|
7
7
|
import "../../chunk-6DXGZZA4.js";
|
|
8
|
-
import "../../chunk-
|
|
8
|
+
import "../../chunk-OMEL6F6I.js";
|
|
9
9
|
export {
|
|
10
10
|
createMcpProtocolHandler
|
|
11
11
|
};
|
|
@@ -148,6 +148,18 @@ var OAuthConfigurationError = class extends Error {
|
|
|
148
148
|
this.name = "OAuthConfigurationError";
|
|
149
149
|
}
|
|
150
150
|
};
|
|
151
|
+
var OAuthUpstreamUnreachableError = class extends Error {
|
|
152
|
+
constructor(message) {
|
|
153
|
+
super(message);
|
|
154
|
+
this.name = "OAuthUpstreamUnreachableError";
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var MetadataUnreachableError = class extends Error {
|
|
158
|
+
constructor(message) {
|
|
159
|
+
super(message);
|
|
160
|
+
this.name = "MetadataUnreachableError";
|
|
161
|
+
}
|
|
162
|
+
};
|
|
151
163
|
var METADATA_FETCH_TIMEOUT_MS = 5e3;
|
|
152
164
|
function issuerPath(url) {
|
|
153
165
|
const path = trimTrailingSlash(url.pathname);
|
|
@@ -172,21 +184,37 @@ function oauthMetadataUrlsForIssuer(issuer) {
|
|
|
172
184
|
return [...pathInsertedOAuthMetadataUrls(url, path), `${normalizedIssuer}/.well-known/openid-configuration`];
|
|
173
185
|
}
|
|
174
186
|
async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer) {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
179
|
-
} catch (err) {
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
const attempts = metadataUrls.map(
|
|
189
|
+
(url) => fetchOAuthServerMetadata(url, expectedIssuer, controller.signal).catch((err) => {
|
|
180
190
|
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
181
|
-
|
|
182
|
-
}
|
|
191
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
192
|
+
})
|
|
193
|
+
);
|
|
194
|
+
try {
|
|
195
|
+
const metadata = await Promise.any(attempts);
|
|
196
|
+
controller.abort();
|
|
197
|
+
return metadata;
|
|
198
|
+
} catch (aggregate) {
|
|
199
|
+
const causes = aggregate instanceof AggregateError ? aggregate.errors : [aggregate];
|
|
200
|
+
const unreachable = causes.some((c) => c instanceof MetadataUnreachableError);
|
|
201
|
+
const detail = causes.map((c) => c instanceof Error ? c.message : String(c)).join("; ");
|
|
202
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
|
|
203
|
+
const message = `failed to discover OAuth server metadata (${detail})`;
|
|
204
|
+
throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
|
|
183
205
|
}
|
|
184
|
-
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
|
|
185
|
-
throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
|
|
186
206
|
}
|
|
187
|
-
async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
207
|
+
async function fetchOAuthServerMetadata(url, expectedIssuer, signal) {
|
|
188
208
|
log.debug("oauth.discovery.fetch", { url });
|
|
189
|
-
|
|
209
|
+
let response;
|
|
210
|
+
try {
|
|
211
|
+
response = await fetch(url, {
|
|
212
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)]),
|
|
213
|
+
redirect: "manual"
|
|
214
|
+
});
|
|
215
|
+
} catch (err) {
|
|
216
|
+
throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
|
|
217
|
+
}
|
|
190
218
|
if (!response.ok) {
|
|
191
219
|
throw new Error(String(response.status));
|
|
192
220
|
}
|
|
@@ -209,14 +237,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
209
237
|
try {
|
|
210
238
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
211
239
|
} catch (err) {
|
|
240
|
+
const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
241
|
+
if (err instanceof MetadataUnreachableError) {
|
|
242
|
+
log.error("oauth.discovery.unreachable", {
|
|
243
|
+
issuer,
|
|
244
|
+
...describeError(err),
|
|
245
|
+
outcome: "503 authorization server unreachable"
|
|
246
|
+
});
|
|
247
|
+
throw new OAuthUpstreamUnreachableError(message);
|
|
248
|
+
}
|
|
212
249
|
log.error("oauth.discovery.config_error", {
|
|
213
250
|
issuer,
|
|
214
251
|
...describeError(err),
|
|
215
252
|
outcome: "500 oauth configuration error"
|
|
216
253
|
});
|
|
217
|
-
throw new OAuthConfigurationError(
|
|
218
|
-
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
219
|
-
);
|
|
254
|
+
throw new OAuthConfigurationError(message);
|
|
220
255
|
}
|
|
221
256
|
}
|
|
222
257
|
function createOAuthDiscoveryResolver(auth) {
|
|
@@ -306,8 +341,14 @@ function tokenHeaderFields(token) {
|
|
|
306
341
|
}
|
|
307
342
|
}
|
|
308
343
|
async function fetchVerificationKeySet(jwksUri) {
|
|
344
|
+
let response;
|
|
345
|
+
try {
|
|
346
|
+
response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
347
|
+
} catch (err) {
|
|
348
|
+
log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
|
|
349
|
+
throw new OAuthUpstreamUnreachableError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
350
|
+
}
|
|
309
351
|
try {
|
|
310
|
-
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
311
352
|
if (!response.ok)
|
|
312
353
|
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
313
354
|
const json = await response.json();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createOAuthProtectedResourceMetadataHandler
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-A2GUTEAK.js";
|
|
4
|
+
import "../chunk-E7CPOJN6.js";
|
|
5
5
|
import "../chunk-H37EB22A.js";
|
|
6
6
|
import "../chunk-6DXGZZA4.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-OMEL6F6I.js";
|
|
8
8
|
export {
|
|
9
9
|
createOAuthProtectedResourceMetadataHandler
|
|
10
10
|
};
|
|
@@ -163,6 +163,18 @@ var OAuthConfigurationError = class extends Error {
|
|
|
163
163
|
this.name = "OAuthConfigurationError";
|
|
164
164
|
}
|
|
165
165
|
};
|
|
166
|
+
var OAuthUpstreamUnreachableError = class extends Error {
|
|
167
|
+
constructor(message) {
|
|
168
|
+
super(message);
|
|
169
|
+
this.name = "OAuthUpstreamUnreachableError";
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
var MetadataUnreachableError = class extends Error {
|
|
173
|
+
constructor(message) {
|
|
174
|
+
super(message);
|
|
175
|
+
this.name = "MetadataUnreachableError";
|
|
176
|
+
}
|
|
177
|
+
};
|
|
166
178
|
var METADATA_FETCH_TIMEOUT_MS = 5e3;
|
|
167
179
|
function issuerPath(url) {
|
|
168
180
|
const path = trimTrailingSlash(url.pathname);
|
|
@@ -187,21 +199,37 @@ function oauthMetadataUrlsForIssuer(issuer) {
|
|
|
187
199
|
return [...pathInsertedOAuthMetadataUrls(url, path), `${normalizedIssuer}/.well-known/openid-configuration`];
|
|
188
200
|
}
|
|
189
201
|
async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer) {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
194
|
-
} catch (err) {
|
|
202
|
+
const controller = new AbortController();
|
|
203
|
+
const attempts = metadataUrls.map(
|
|
204
|
+
(url) => fetchOAuthServerMetadata(url, expectedIssuer, controller.signal).catch((err) => {
|
|
195
205
|
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
206
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
try {
|
|
210
|
+
const metadata = await Promise.any(attempts);
|
|
211
|
+
controller.abort();
|
|
212
|
+
return metadata;
|
|
213
|
+
} catch (aggregate) {
|
|
214
|
+
const causes = aggregate instanceof AggregateError ? aggregate.errors : [aggregate];
|
|
215
|
+
const unreachable = causes.some((c) => c instanceof MetadataUnreachableError);
|
|
216
|
+
const detail = causes.map((c) => c instanceof Error ? c.message : String(c)).join("; ");
|
|
217
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
|
|
218
|
+
const message = `failed to discover OAuth server metadata (${detail})`;
|
|
219
|
+
throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function fetchOAuthServerMetadata(url, expectedIssuer, signal) {
|
|
203
223
|
log.debug("oauth.discovery.fetch", { url });
|
|
204
|
-
|
|
224
|
+
let response;
|
|
225
|
+
try {
|
|
226
|
+
response = await fetch(url, {
|
|
227
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)]),
|
|
228
|
+
redirect: "manual"
|
|
229
|
+
});
|
|
230
|
+
} catch (err) {
|
|
231
|
+
throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
|
|
232
|
+
}
|
|
205
233
|
if (!response.ok) {
|
|
206
234
|
throw new Error(String(response.status));
|
|
207
235
|
}
|
|
@@ -224,14 +252,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
224
252
|
try {
|
|
225
253
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
226
254
|
} catch (err) {
|
|
255
|
+
const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
256
|
+
if (err instanceof MetadataUnreachableError) {
|
|
257
|
+
log.error("oauth.discovery.unreachable", {
|
|
258
|
+
issuer,
|
|
259
|
+
...describeError(err),
|
|
260
|
+
outcome: "503 authorization server unreachable"
|
|
261
|
+
});
|
|
262
|
+
throw new OAuthUpstreamUnreachableError(message);
|
|
263
|
+
}
|
|
227
264
|
log.error("oauth.discovery.config_error", {
|
|
228
265
|
issuer,
|
|
229
266
|
...describeError(err),
|
|
230
267
|
outcome: "500 oauth configuration error"
|
|
231
268
|
});
|
|
232
|
-
throw new OAuthConfigurationError(
|
|
233
|
-
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
234
|
-
);
|
|
269
|
+
throw new OAuthConfigurationError(message);
|
|
235
270
|
}
|
|
236
271
|
}
|
|
237
272
|
function createOAuthDiscoveryResolver(auth) {
|
|
@@ -324,8 +359,14 @@ function tokenHeaderFields(token) {
|
|
|
324
359
|
}
|
|
325
360
|
}
|
|
326
361
|
async function fetchVerificationKeySet(jwksUri) {
|
|
362
|
+
let response;
|
|
363
|
+
try {
|
|
364
|
+
response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
365
|
+
} catch (err) {
|
|
366
|
+
log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
|
|
367
|
+
throw new OAuthUpstreamUnreachableError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
368
|
+
}
|
|
327
369
|
try {
|
|
328
|
-
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
329
370
|
if (!response.ok)
|
|
330
371
|
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
331
372
|
const json = await response.json();
|
|
@@ -466,6 +507,12 @@ function oauthConfigurationErrorResponse() {
|
|
|
466
507
|
headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
|
|
467
508
|
});
|
|
468
509
|
}
|
|
510
|
+
function oauthUpstreamUnreachableResponse() {
|
|
511
|
+
return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
|
|
512
|
+
status: 503,
|
|
513
|
+
headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
|
|
514
|
+
});
|
|
515
|
+
}
|
|
469
516
|
function parseBearerToken(request) {
|
|
470
517
|
const header = request.headers.get("Authorization");
|
|
471
518
|
if (!header)
|
|
@@ -538,6 +585,16 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
538
585
|
assertRequiredScopes(runtime.auth, auth);
|
|
539
586
|
return { ok: true, auth };
|
|
540
587
|
} catch (err) {
|
|
588
|
+
if (err instanceof OAuthUpstreamUnreachableError) {
|
|
589
|
+
log.error("auth.upstream_unreachable", { ...describeError(err), outcome: "503" });
|
|
590
|
+
await recorder?.emit({
|
|
591
|
+
tool: null,
|
|
592
|
+
method: "authorize",
|
|
593
|
+
outcome: "auth_upstream_unreachable",
|
|
594
|
+
durationMs: nowMs() - startedAt
|
|
595
|
+
});
|
|
596
|
+
return { ok: false, response: oauthUpstreamUnreachableResponse() };
|
|
597
|
+
}
|
|
541
598
|
if (err instanceof OAuthConfigurationError) {
|
|
542
599
|
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
543
600
|
await recorder?.emit({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
|
|
2
2
|
import { d as McpDefinition } from '../../types-CPkhCRxc.js';
|
|
3
|
-
import { M as MetricsRecorder } from '../../base-
|
|
3
|
+
import { M as MetricsRecorder } from '../../base-RSqYamjy.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
2
|
import { d as McpDefinition } from '../../types-CPkhCRxc.js';
|
|
3
|
-
import { M as MetricsRecorder } from '../../base-
|
|
3
|
+
import { M as MetricsRecorder } from '../../base-RSqYamjy.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
6
6
|
type RestListToolsHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createInvokeToolHandler
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-RT2I26ZN.js";
|
|
4
4
|
import {
|
|
5
5
|
createListToolsHandler
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-YAXKWTJK.js";
|
|
7
7
|
import "../../chunk-MA5H6PSF.js";
|
|
8
|
-
import "../../chunk-
|
|
8
|
+
import "../../chunk-E7CPOJN6.js";
|
|
9
9
|
import "../../chunk-H37EB22A.js";
|
|
10
10
|
import "../../chunk-6DXGZZA4.js";
|
|
11
|
-
import "../../chunk-
|
|
11
|
+
import "../../chunk-OMEL6F6I.js";
|
|
12
12
|
export {
|
|
13
13
|
createInvokeToolHandler,
|
|
14
14
|
createListToolsHandler
|
|
@@ -174,7 +174,7 @@ function describeError(err) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// package.json
|
|
177
|
-
var version = "0.
|
|
177
|
+
var version = "0.20.1-rc.0";
|
|
178
178
|
|
|
179
179
|
// src/metrics/otlp.ts
|
|
180
180
|
var SCOPE_NAME = "@lovable.dev/mcp-js";
|
|
@@ -257,7 +257,7 @@ var BaseMetricRecorder = class {
|
|
|
257
257
|
this.config = config;
|
|
258
258
|
this.server = server;
|
|
259
259
|
this.deps = deps;
|
|
260
|
-
this.doFetch = deps.fetch ?? globalThis.fetch;
|
|
260
|
+
this.doFetch = deps.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
|
|
261
261
|
this.usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
262
262
|
const headers = {};
|
|
263
263
|
if (!this.usesLovableEndpoint) {
|
|
@@ -445,6 +445,18 @@ var OAuthConfigurationError = class extends Error {
|
|
|
445
445
|
this.name = "OAuthConfigurationError";
|
|
446
446
|
}
|
|
447
447
|
};
|
|
448
|
+
var OAuthUpstreamUnreachableError = class extends Error {
|
|
449
|
+
constructor(message) {
|
|
450
|
+
super(message);
|
|
451
|
+
this.name = "OAuthUpstreamUnreachableError";
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
var MetadataUnreachableError = class extends Error {
|
|
455
|
+
constructor(message) {
|
|
456
|
+
super(message);
|
|
457
|
+
this.name = "MetadataUnreachableError";
|
|
458
|
+
}
|
|
459
|
+
};
|
|
448
460
|
var METADATA_FETCH_TIMEOUT_MS = 5e3;
|
|
449
461
|
function issuerPath(url) {
|
|
450
462
|
const path = trimTrailingSlash(url.pathname);
|
|
@@ -469,21 +481,37 @@ function oauthMetadataUrlsForIssuer(issuer) {
|
|
|
469
481
|
return [...pathInsertedOAuthMetadataUrls(url, path), `${normalizedIssuer}/.well-known/openid-configuration`];
|
|
470
482
|
}
|
|
471
483
|
async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer) {
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
476
|
-
} catch (err) {
|
|
484
|
+
const controller = new AbortController();
|
|
485
|
+
const attempts = metadataUrls.map(
|
|
486
|
+
(url) => fetchOAuthServerMetadata(url, expectedIssuer, controller.signal).catch((err) => {
|
|
477
487
|
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
478
|
-
|
|
479
|
-
}
|
|
488
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
489
|
+
})
|
|
490
|
+
);
|
|
491
|
+
try {
|
|
492
|
+
const metadata = await Promise.any(attempts);
|
|
493
|
+
controller.abort();
|
|
494
|
+
return metadata;
|
|
495
|
+
} catch (aggregate) {
|
|
496
|
+
const causes = aggregate instanceof AggregateError ? aggregate.errors : [aggregate];
|
|
497
|
+
const unreachable = causes.some((c) => c instanceof MetadataUnreachableError);
|
|
498
|
+
const detail = causes.map((c) => c instanceof Error ? c.message : String(c)).join("; ");
|
|
499
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
|
|
500
|
+
const message = `failed to discover OAuth server metadata (${detail})`;
|
|
501
|
+
throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
|
|
480
502
|
}
|
|
481
|
-
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
|
|
482
|
-
throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
|
|
483
503
|
}
|
|
484
|
-
async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
504
|
+
async function fetchOAuthServerMetadata(url, expectedIssuer, signal) {
|
|
485
505
|
log.debug("oauth.discovery.fetch", { url });
|
|
486
|
-
|
|
506
|
+
let response;
|
|
507
|
+
try {
|
|
508
|
+
response = await fetch(url, {
|
|
509
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)]),
|
|
510
|
+
redirect: "manual"
|
|
511
|
+
});
|
|
512
|
+
} catch (err) {
|
|
513
|
+
throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
|
|
514
|
+
}
|
|
487
515
|
if (!response.ok) {
|
|
488
516
|
throw new Error(String(response.status));
|
|
489
517
|
}
|
|
@@ -506,14 +534,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
506
534
|
try {
|
|
507
535
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
508
536
|
} catch (err) {
|
|
537
|
+
const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
538
|
+
if (err instanceof MetadataUnreachableError) {
|
|
539
|
+
log.error("oauth.discovery.unreachable", {
|
|
540
|
+
issuer,
|
|
541
|
+
...describeError(err),
|
|
542
|
+
outcome: "503 authorization server unreachable"
|
|
543
|
+
});
|
|
544
|
+
throw new OAuthUpstreamUnreachableError(message);
|
|
545
|
+
}
|
|
509
546
|
log.error("oauth.discovery.config_error", {
|
|
510
547
|
issuer,
|
|
511
548
|
...describeError(err),
|
|
512
549
|
outcome: "500 oauth configuration error"
|
|
513
550
|
});
|
|
514
|
-
throw new OAuthConfigurationError(
|
|
515
|
-
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
516
|
-
);
|
|
551
|
+
throw new OAuthConfigurationError(message);
|
|
517
552
|
}
|
|
518
553
|
}
|
|
519
554
|
function createOAuthDiscoveryResolver(auth) {
|
|
@@ -579,8 +614,14 @@ function tokenHeaderFields(token) {
|
|
|
579
614
|
}
|
|
580
615
|
}
|
|
581
616
|
async function fetchVerificationKeySet(jwksUri) {
|
|
617
|
+
let response;
|
|
618
|
+
try {
|
|
619
|
+
response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
620
|
+
} catch (err) {
|
|
621
|
+
log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
|
|
622
|
+
throw new OAuthUpstreamUnreachableError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
623
|
+
}
|
|
582
624
|
try {
|
|
583
|
-
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
584
625
|
if (!response.ok)
|
|
585
626
|
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
586
627
|
const json = await response.json();
|
|
@@ -721,6 +762,12 @@ function oauthConfigurationErrorResponse() {
|
|
|
721
762
|
headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
|
|
722
763
|
});
|
|
723
764
|
}
|
|
765
|
+
function oauthUpstreamUnreachableResponse() {
|
|
766
|
+
return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
|
|
767
|
+
status: 503,
|
|
768
|
+
headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
|
|
769
|
+
});
|
|
770
|
+
}
|
|
724
771
|
function parseBearerToken(request) {
|
|
725
772
|
const header = request.headers.get("Authorization");
|
|
726
773
|
if (!header)
|
|
@@ -793,6 +840,16 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
793
840
|
assertRequiredScopes(runtime.auth, auth);
|
|
794
841
|
return { ok: true, auth };
|
|
795
842
|
} catch (err) {
|
|
843
|
+
if (err instanceof OAuthUpstreamUnreachableError) {
|
|
844
|
+
log.error("auth.upstream_unreachable", { ...describeError(err), outcome: "503" });
|
|
845
|
+
await recorder?.emit({
|
|
846
|
+
tool: null,
|
|
847
|
+
method: "authorize",
|
|
848
|
+
outcome: "auth_upstream_unreachable",
|
|
849
|
+
durationMs: nowMs() - startedAt
|
|
850
|
+
});
|
|
851
|
+
return { ok: false, response: oauthUpstreamUnreachableResponse() };
|
|
852
|
+
}
|
|
796
853
|
if (err instanceof OAuthConfigurationError) {
|
|
797
854
|
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
798
855
|
await recorder?.emit({
|
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
} from "../../chunk-UQK5UO6C.js";
|
|
4
4
|
import {
|
|
5
5
|
createMcpProtocolHandler
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-ID3YWZBR.js";
|
|
7
7
|
import {
|
|
8
8
|
createOAuthProtectedResourceMetadataHandler
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-A2GUTEAK.js";
|
|
10
10
|
import {
|
|
11
11
|
createInvokeToolHandler
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-RT2I26ZN.js";
|
|
13
13
|
import {
|
|
14
14
|
createListToolsHandler
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-YAXKWTJK.js";
|
|
16
16
|
import "../../chunk-MA5H6PSF.js";
|
|
17
17
|
import {
|
|
18
18
|
assertResourcePathShape,
|
|
19
19
|
createRecorderForRuntime
|
|
20
|
-
} from "../../chunk-
|
|
20
|
+
} from "../../chunk-E7CPOJN6.js";
|
|
21
21
|
import {
|
|
22
22
|
trimTrailingSlash
|
|
23
23
|
} from "../../chunk-H37EB22A.js";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
FUNCTIONS_MOUNT_PREFIX,
|
|
29
29
|
assertFunctionName
|
|
30
30
|
} from "../../chunk-XQWJN6DC.js";
|
|
31
|
-
import "../../chunk-
|
|
31
|
+
import "../../chunk-OMEL6F6I.js";
|
|
32
32
|
|
|
33
33
|
// src/stacks/supabase/handler.ts
|
|
34
34
|
function deriveResourcePath(options) {
|
|
@@ -129,7 +129,7 @@ function describeError(err) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// package.json
|
|
132
|
-
var version = "0.
|
|
132
|
+
var version = "0.20.1-rc.0";
|
|
133
133
|
|
|
134
134
|
// src/metrics/otlp.ts
|
|
135
135
|
var SCOPE_NAME = "@lovable.dev/mcp-js";
|
|
@@ -212,7 +212,7 @@ var BaseMetricRecorder = class {
|
|
|
212
212
|
this.config = config;
|
|
213
213
|
this.server = server;
|
|
214
214
|
this.deps = deps;
|
|
215
|
-
this.doFetch = deps.fetch ?? globalThis.fetch;
|
|
215
|
+
this.doFetch = deps.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
|
|
216
216
|
this.usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
217
217
|
const headers = {};
|
|
218
218
|
if (!this.usesLovableEndpoint) {
|
|
@@ -421,6 +421,18 @@ var OAuthConfigurationError = class extends Error {
|
|
|
421
421
|
this.name = "OAuthConfigurationError";
|
|
422
422
|
}
|
|
423
423
|
};
|
|
424
|
+
var OAuthUpstreamUnreachableError = class extends Error {
|
|
425
|
+
constructor(message) {
|
|
426
|
+
super(message);
|
|
427
|
+
this.name = "OAuthUpstreamUnreachableError";
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var MetadataUnreachableError = class extends Error {
|
|
431
|
+
constructor(message) {
|
|
432
|
+
super(message);
|
|
433
|
+
this.name = "MetadataUnreachableError";
|
|
434
|
+
}
|
|
435
|
+
};
|
|
424
436
|
var METADATA_FETCH_TIMEOUT_MS = 5e3;
|
|
425
437
|
function issuerPath(url) {
|
|
426
438
|
const path = trimTrailingSlash(url.pathname);
|
|
@@ -445,21 +457,37 @@ function oauthMetadataUrlsForIssuer(issuer) {
|
|
|
445
457
|
return [...pathInsertedOAuthMetadataUrls(url, path), `${normalizedIssuer}/.well-known/openid-configuration`];
|
|
446
458
|
}
|
|
447
459
|
async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer) {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
452
|
-
} catch (err) {
|
|
460
|
+
const controller = new AbortController();
|
|
461
|
+
const attempts = metadataUrls.map(
|
|
462
|
+
(url) => fetchOAuthServerMetadata(url, expectedIssuer, controller.signal).catch((err) => {
|
|
453
463
|
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
454
|
-
|
|
455
|
-
}
|
|
464
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
465
|
+
})
|
|
466
|
+
);
|
|
467
|
+
try {
|
|
468
|
+
const metadata = await Promise.any(attempts);
|
|
469
|
+
controller.abort();
|
|
470
|
+
return metadata;
|
|
471
|
+
} catch (aggregate) {
|
|
472
|
+
const causes = aggregate instanceof AggregateError ? aggregate.errors : [aggregate];
|
|
473
|
+
const unreachable = causes.some((c) => c instanceof MetadataUnreachableError);
|
|
474
|
+
const detail = causes.map((c) => c instanceof Error ? c.message : String(c)).join("; ");
|
|
475
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors: detail, unreachable });
|
|
476
|
+
const message = `failed to discover OAuth server metadata (${detail})`;
|
|
477
|
+
throw unreachable ? new MetadataUnreachableError(message) : new Error(message);
|
|
456
478
|
}
|
|
457
|
-
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
|
|
458
|
-
throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
|
|
459
479
|
}
|
|
460
|
-
async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
480
|
+
async function fetchOAuthServerMetadata(url, expectedIssuer, signal) {
|
|
461
481
|
log.debug("oauth.discovery.fetch", { url });
|
|
462
|
-
|
|
482
|
+
let response;
|
|
483
|
+
try {
|
|
484
|
+
response = await fetch(url, {
|
|
485
|
+
signal: AbortSignal.any([signal, AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)]),
|
|
486
|
+
redirect: "manual"
|
|
487
|
+
});
|
|
488
|
+
} catch (err) {
|
|
489
|
+
throw new MetadataUnreachableError(err instanceof Error ? err.message : String(err));
|
|
490
|
+
}
|
|
463
491
|
if (!response.ok) {
|
|
464
492
|
throw new Error(String(response.status));
|
|
465
493
|
}
|
|
@@ -482,14 +510,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
482
510
|
try {
|
|
483
511
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
484
512
|
} catch (err) {
|
|
513
|
+
const message = `OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
514
|
+
if (err instanceof MetadataUnreachableError) {
|
|
515
|
+
log.error("oauth.discovery.unreachable", {
|
|
516
|
+
issuer,
|
|
517
|
+
...describeError(err),
|
|
518
|
+
outcome: "503 authorization server unreachable"
|
|
519
|
+
});
|
|
520
|
+
throw new OAuthUpstreamUnreachableError(message);
|
|
521
|
+
}
|
|
485
522
|
log.error("oauth.discovery.config_error", {
|
|
486
523
|
issuer,
|
|
487
524
|
...describeError(err),
|
|
488
525
|
outcome: "500 oauth configuration error"
|
|
489
526
|
});
|
|
490
|
-
throw new OAuthConfigurationError(
|
|
491
|
-
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
492
|
-
);
|
|
527
|
+
throw new OAuthConfigurationError(message);
|
|
493
528
|
}
|
|
494
529
|
}
|
|
495
530
|
function createOAuthDiscoveryResolver(auth) {
|
|
@@ -582,8 +617,14 @@ function tokenHeaderFields(token) {
|
|
|
582
617
|
}
|
|
583
618
|
}
|
|
584
619
|
async function fetchVerificationKeySet(jwksUri) {
|
|
620
|
+
let response;
|
|
621
|
+
try {
|
|
622
|
+
response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
623
|
+
} catch (err) {
|
|
624
|
+
log.error("oauth.jwks.unreachable", { ...describeError(err), outcome: "503 authorization server unreachable" });
|
|
625
|
+
throw new OAuthUpstreamUnreachableError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
626
|
+
}
|
|
585
627
|
try {
|
|
586
|
-
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
587
628
|
if (!response.ok)
|
|
588
629
|
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
589
630
|
const json = await response.json();
|
|
@@ -724,6 +765,12 @@ function oauthConfigurationErrorResponse() {
|
|
|
724
765
|
headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
|
|
725
766
|
});
|
|
726
767
|
}
|
|
768
|
+
function oauthUpstreamUnreachableResponse() {
|
|
769
|
+
return new Response(JSON.stringify({ error: "authorization server unreachable" }), {
|
|
770
|
+
status: 503,
|
|
771
|
+
headers: { ...JSON_HEADERS, "Cache-Control": "no-store", "Retry-After": "30" }
|
|
772
|
+
});
|
|
773
|
+
}
|
|
727
774
|
function parseBearerToken(request) {
|
|
728
775
|
const header = request.headers.get("Authorization");
|
|
729
776
|
if (!header)
|
|
@@ -796,6 +843,16 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
796
843
|
assertRequiredScopes(runtime.auth, auth);
|
|
797
844
|
return { ok: true, auth };
|
|
798
845
|
} catch (err) {
|
|
846
|
+
if (err instanceof OAuthUpstreamUnreachableError) {
|
|
847
|
+
log.error("auth.upstream_unreachable", { ...describeError(err), outcome: "503" });
|
|
848
|
+
await recorder?.emit({
|
|
849
|
+
tool: null,
|
|
850
|
+
method: "authorize",
|
|
851
|
+
outcome: "auth_upstream_unreachable",
|
|
852
|
+
durationMs: nowMs() - startedAt
|
|
853
|
+
});
|
|
854
|
+
return { ok: false, response: oauthUpstreamUnreachableResponse() };
|
|
855
|
+
}
|
|
799
856
|
if (err instanceof OAuthConfigurationError) {
|
|
800
857
|
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
801
858
|
await recorder?.emit({
|
|
@@ -3,23 +3,23 @@ import {
|
|
|
3
3
|
} from "../../chunk-UQK5UO6C.js";
|
|
4
4
|
import {
|
|
5
5
|
createMcpProtocolHandler
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-ID3YWZBR.js";
|
|
7
7
|
import {
|
|
8
8
|
createOAuthProtectedResourceMetadataHandler
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-A2GUTEAK.js";
|
|
10
10
|
import {
|
|
11
11
|
createInvokeToolHandler
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-RT2I26ZN.js";
|
|
13
13
|
import {
|
|
14
14
|
createListToolsHandler
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-YAXKWTJK.js";
|
|
16
16
|
import "../../chunk-MA5H6PSF.js";
|
|
17
17
|
import {
|
|
18
18
|
createRecorderForRuntime
|
|
19
|
-
} from "../../chunk-
|
|
19
|
+
} from "../../chunk-E7CPOJN6.js";
|
|
20
20
|
import "../../chunk-H37EB22A.js";
|
|
21
21
|
import "../../chunk-6DXGZZA4.js";
|
|
22
|
-
import "../../chunk-
|
|
22
|
+
import "../../chunk-OMEL6F6I.js";
|
|
23
23
|
|
|
24
24
|
// src/stacks/tanstack/handlers.ts
|
|
25
25
|
var STACK = "tanstack";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/mcp-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1-rc.0",
|
|
4
4
|
"description": "Author MCP servers for Lovable apps. Declare tools with defineTool, register them in defineMcp, and a framework adapter (TanStack or Supabase Edge Functions) emits the route(s) at build time.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|