History

fix url + mcp oauth

Rhys Sullivan committed 8 months ago

Commit a0347d61 parent 60b3c7111 files · +482 −19Browse files

Unified · Split

11 files
  1. apps/web/README.md +9 −0
  2. apps/web/app/.well-known/oauth-authorization-server/route.ts +43 −0
  3. apps/web/app/.well-known/oauth-protected-resource/route.ts +62 −0
  4. apps/web/app/v1/mcp/route.ts +66 −0
  5. apps/web/components/tools/tools-view.tsx +1 −12
  6. apps/web/lib/control-plane/server.ts +99 −1
  7. apps/web/lib/mcp/resource-auth.ts +181 −0
  8. apps/web/package.json +5 −0
  9. bun.lock +9 −4
  10. packages/management-api/src/openapi-extractor-wasm.ts +6 −1
  11. packages/runtime-deno-subprocess/src/index.ts +1 −1
apps/web/README.md+9−0
@@ -27,6 +27,7 @@ MCP install URL generation: · 26 unmodified lines
2727
2828 - Derives from existing frontend origin and deployment metadata.
2929 - In local dev with no DB URL, defaults to local PGlite-backed control-plane in this Next.js app.
30+- MCP endpoint is served from this app at `GET/POST/DELETE /v1/mcp`.
3031
3132 WorkOS auth setup (optional but recommended):
3233
@@ -36,3 +37,11 @@ WorkOS auth setup (optional but recommended): · 3 unmodified lines
3637 - `WORKOS_REDIRECT_URI` or `NEXT_PUBLIC_WORKOS_REDIRECT_URI` (for example `http://localhost:4312/callback`)
3738
3839 When WorkOS is configured, the app requires sign-in and the server proxy forwards the authenticated WorkOS access token to control-plane as a bearer token (`Authorization: Bearer ...`).
40+
41+MCP OAuth setup (for external MCP clients):
42+
43+- `MCP_AUTHORIZATION_SERVER` (or `MCP_AUTHORIZATION_SERVER_URL`, `WORKOS_AUTHKIT_ISSUER`)
44+- `/.well-known/oauth-protected-resource`
45+- `/.well-known/oauth-authorization-server`
46+
47+Unauthenticated requests to `/v1/mcp` return `401` with `WWW-Authenticate` and `resource_metadata` for OAuth discovery.
apps/web/app/.well-known/oauth-authorization-server/route.ts+43−0
@@ -0,0 +1,43 @@
1+import { getMcpAuthConfig } from "../../../lib/mcp/resource-auth";
2+
3+export const dynamic = "force-dynamic";
4+export const runtime = "nodejs";
5+
6+export async function GET(): Promise<Response> {
7+ const authConfig = getMcpAuthConfig();
8+ if (!authConfig.enabled || !authConfig.authorizationServer) {
9+ return Response.json(
10+ { error: "MCP OAuth is not configured" },
11+ { status: 404 },
12+ );
13+ }
14+
15+ const upstream = new URL(
16+ "/.well-known/oauth-authorization-server",
17+ authConfig.authorizationServer,
18+ );
19+
20+ try {
21+ const response = await fetch(upstream.toString(), {
22+ headers: { accept: "application/json" },
23+ });
24+
25+ const text = await response.text();
26+ return new Response(text, {
27+ status: response.status,
28+ headers: {
29+ "content-type": response.headers.get("content-type") ?? "application/json",
30+ },
31+ });
32+ } catch (error) {
33+ return Response.json(
34+ {
35+ error:
36+ error instanceof Error
37+ ? `Failed to fetch upstream auth metadata: ${error.message}`
38+ : "Failed to fetch upstream auth metadata",
39+ },
40+ { status: 502 },
41+ );
42+ }
43+}
apps/web/app/.well-known/oauth-protected-resource/route.ts+62−0
@@ -0,0 +1,62 @@
1+import { externalOriginFromRequest } from "../../../lib/workos";
2+import { getMcpAuthConfig } from "../../../lib/mcp/resource-auth";
3+
4+export const dynamic = "force-dynamic";
5+export const runtime = "nodejs";
6+
7+const MCP_PATH = "/v1/mcp";
8+
9+const parseResourceHint = (
10+ requestUrl: URL,
11+ origin: string,
12+): { resourceUrl: URL } | { error: string } => {
13+ const resourceHint = requestUrl.searchParams.get("resource")?.trim();
14+ if (!resourceHint) {
15+ const fallback = new URL(MCP_PATH, origin);
16+ for (const [key, value] of requestUrl.searchParams.entries()) {
17+ if (key === "resource") continue;
18+ fallback.searchParams.set(key, value);
19+ }
20+ return { resourceUrl: fallback };
21+ }
22+
23+ let parsed: URL;
24+ try {
25+ parsed = new URL(resourceHint);
26+ } catch {
27+ return { error: "Invalid resource hint" };
28+ }
29+
30+ if (parsed.origin !== origin) {
31+ return { error: "resource hint origin must match this server" };
32+ }
33+
34+ if (parsed.pathname !== MCP_PATH) {
35+ return { error: `resource hint path must be ${MCP_PATH}` };
36+ }
37+
38+ return { resourceUrl: parsed };
39+};
40+
41+export async function GET(request: Request): Promise<Response> {
42+ const authConfig = getMcpAuthConfig();
43+ if (!authConfig.enabled || !authConfig.authorizationServer) {
44+ return Response.json(
45+ { error: "MCP OAuth is not configured" },
46+ { status: 404 },
47+ );
48+ }
49+
50+ const origin = externalOriginFromRequest(request);
51+ const requestUrl = new URL(request.url);
52+ const parsedResource = parseResourceHint(requestUrl, origin);
53+ if ("error" in parsedResource) {
54+ return Response.json({ error: parsedResource.error }, { status: 400 });
55+ }
56+
57+ return Response.json({
58+ resource: parsedResource.resourceUrl.toString(),
59+ authorization_servers: [authConfig.authorizationServer],
60+ bearer_methods_supported: ["header"],
61+ });
62+}
apps/web/app/v1/mcp/route.ts+66−0
@@ -0,0 +1,66 @@
1+import {
2+ createWorkosPrincipal,
3+ getControlPlaneRuntime,
4+ provisionPrincipal,
5+} from "../../../lib/control-plane/server";
6+import {
7+ getMcpAuthConfig,
8+ unauthorizedMcpResponse,
9+ verifyMcpBearerToken,
10+} from "../../../lib/mcp/resource-auth";
11+
12+export const dynamic = "force-dynamic";
13+export const runtime = "nodejs";
14+
15+const parseWorkspaceId = (request: Request): string | null => {
16+ const value = new URL(request.url).searchParams.get("workspaceId")?.trim();
17+ return value && value.length > 0 ? value : null;
18+};
19+
20+const handler = async (request: Request): Promise<Response> => {
21+ const authConfig = getMcpAuthConfig();
22+ if (!authConfig.enabled) {
23+ return Response.json(
24+ {
25+ error:
26+ "MCP OAuth is not configured. Set MCP_AUTHORIZATION_SERVER (or WORKOS_AUTHKIT_ISSUER).",
27+ },
28+ { status: 503 },
29+ );
30+ }
31+
32+ const verified = await verifyMcpBearerToken(request, authConfig);
33+ if (!verified) {
34+ return unauthorizedMcpResponse(request, "No valid bearer token provided.");
35+ }
36+
37+ const workspaceId = parseWorkspaceId(request);
38+ if (!workspaceId) {
39+ return Response.json(
40+ { error: "workspaceId query parameter is required" },
41+ { status: 400 },
42+ );
43+ }
44+
45+ const principal = createWorkosPrincipal({
46+ subject: verified.subject,
47+ email: verified.email,
48+ displayName: verified.displayName,
49+ });
50+
51+ if (workspaceId !== principal.workspaceId) {
52+ return Response.json(
53+ { error: "workspaceId does not match authenticated subject" },
54+ { status: 403 },
55+ );
56+ }
57+
58+ const controlPlaneRuntime = await getControlPlaneRuntime();
59+ await provisionPrincipal(controlPlaneRuntime, principal);
60+
61+ return controlPlaneRuntime.handleMcp(request, workspaceId);
62+};
63+
64+export const GET = handler;
65+export const POST = handler;
66+export const DELETE = handler;
apps/web/components/tools/tools-view.tsx+1−12
@@ -65,13 +65,6 @@ type SourceGroup = { · 64 unmodified lines
6565 toolCount: number;
6666 };
6767
68−const isLocalHost = (hostname: string): boolean =>
69− hostname === "localhost"
70− || hostname === "127.0.0.1"
71− || hostname === "0.0.0.0"
72− || hostname === "::1"
73− || hostname === "[::1]";
74−
7568 // ---------------------------------------------------------------------------
7669 // Search helpers (ported from old executor explorer-derived.ts)
7770 // ---------------------------------------------------------------------------
@@ -407,11 +400,7 @@ export function ToolsView(props: { · 329 unmodified lines
407400 return `http://127.0.0.1:8788/v1/mcp?workspaceId=${encodeURIComponent(workspaceId)}`;
408401 }
409402
410− const baseOrigin = isLocalHost(window.location.hostname)
411− ? "http://127.0.0.1:8788"
412− : window.location.origin;
413−
414− const url = new URL("/v1/mcp", baseOrigin);
403+ const url = new URL("/v1/mcp", window.location.origin);
415404 url.searchParams.set("workspaceId", workspaceId);
416405 return url.toString();
417406 }, [props.mcpBaseUrl, workspaceId]);
apps/web/lib/control-plane/server.ts+99−1
@@ -8,6 +8,18 @@ import { · 7 unmodified lines
88 makeSourceCatalogService,
99 makeSourceManagerService,
1010 } from "@executor-v2/management-api";
11+import {
12+ createRunExecutor,
13+ createSourceToolRegistry,
14+ defaultExecuteToolExposureMode,
15+ makeGraphqlToolProvider,
16+ makeMcpToolProvider,
17+ makeOpenApiToolProvider,
18+ makeRuntimeAdapterRegistry,
19+ makeToolProviderRegistry,
20+ parseExecuteToolExposureMode,
21+ type ExecuteToolExposureMode,
22+} from "@executor-v2/engine";
1123 import {
1224 makeSqlControlPlanePersistence,
1325 type SqlControlPlanePersistence,
@@ -22,14 +34,22 @@ import { · 8 unmodified lines
2234 type OrganizationMembership,
2335 type Workspace,
2436 } from "@executor-v2/schema";
37+import { makeCloudflareWorkerLoaderRuntimeAdapter } from "@executor-v2/runtime-cloudflare-worker-loader";
38+import { makeDenoSubprocessRuntimeAdapter } from "@executor-v2/runtime-deno-subprocess";
39+import { makeLocalInProcessRuntimeAdapter } from "@executor-v2/runtime-local-inproc";
2540 import * as Effect from "effect/Effect";
2641 import * as Layer from "effect/Layer";
2742
2843 import { PmActorLive } from "../../../pm/src/actor";
29−import { createPmApprovalsService } from "../../../pm/src/approvals-service";
44+import {
45+ createPmApprovalsService,
46+ createPmPersistentToolApprovalPolicy,
47+} from "../../../pm/src/approvals-service";
3048 import { createPmCredentialsService } from "../../../pm/src/credentials-service";
49+import { createPmMcpHandler } from "../../../pm/src/mcp-handler";
3150 import { createPmOrganizationsService } from "../../../pm/src/organizations-service";
3251 import { createPmPoliciesService } from "../../../pm/src/policies-service";
52+import { createPmExecuteRuntimeRun } from "../../../pm/src/runtime-execution-port";
3353 import { createPmStorageService } from "../../../pm/src/storage-service";
3454 import { createPmToolsService } from "../../../pm/src/tools-service";
3555 import { createPmWorkspacesService } from "../../../pm/src/workspaces-service";
@@ -90,6 +110,7 @@ type ControlPlaneRuntime = { · 54 unmodified lines
90110 toolArtifactStore: ToolArtifactStore;
91111 fetchOpenApiDocument: typeof fetchOpenApiDocument;
92112 handleControlPlane: (request: Request) => Promise<Response>;
113+ handleMcp: (request: Request, workspaceId: string) => Promise<Response>;
93114 dispose: () => Promise<void>;
94115 };
95116
@@ -127,6 +148,22 @@ const resolveControlPlaneDataDir = (): string => · 31 unmodified lines
127148 const resolveStateRootDir = (): string =>
128149 defaultControlPlaneStateRootDir;
129150
151+const readConfiguredRuntimeKind = (value: string | undefined): string | undefined => {
152+ const normalized = value?.trim();
153+ return normalized && normalized.length > 0 ? normalized : undefined;
154+};
155+
156+const readBooleanFlag = (value: string | undefined): boolean => {
157+ const normalized = value?.trim().toLowerCase();
158+ return normalized === "1" || normalized === "true" || normalized === "yes";
159+};
160+
161+const readConfiguredToolExposureMode = (
162+ value: string | undefined,
163+): ExecuteToolExposureMode =>
164+ parseExecuteToolExposureMode(value ?? undefined) ??
165+ defaultExecuteToolExposureMode;
166+
130167 const openApiSyncRetryDelayMs = 300;
131168
132169 const formatCause = (cause: unknown): string => {
@@ -256,6 +293,35 @@ const createControlPlaneRuntime = async (): Promise<ControlPlaneRuntime> => { · 123 unmodified lines
256293
257294 const sourceStore = persistence.sourceStore;
258295 const toolArtifactStore = persistence.toolArtifactStore;
296+ const runtimeAdapterList = [
297+ makeLocalInProcessRuntimeAdapter(),
298+ makeDenoSubprocessRuntimeAdapter(),
299+ makeCloudflareWorkerLoaderRuntimeAdapter(),
300+ ];
301+ const runtimeAdapters = makeRuntimeAdapterRegistry(runtimeAdapterList);
302+ const defaultRuntimeKind =
303+ readConfiguredRuntimeKind(process.env.PM_RUNTIME_KIND)
304+ ?? runtimeAdapterList[0]?.kind
305+ ?? "local-inproc";
306+ const requireToolApprovals = readBooleanFlag(process.env.PM_REQUIRE_TOOL_APPROVALS);
307+ const defaultToolExposureMode = readConfiguredToolExposureMode(
308+ process.env.PM_TOOL_EXPOSURE_MODE,
309+ );
310+ const toolProviderRegistry = makeToolProviderRegistry([
311+ makeOpenApiToolProvider(),
312+ makeMcpToolProvider(),
313+ makeGraphqlToolProvider(),
314+ ]);
315+ const persistentApprovalPolicy = createPmPersistentToolApprovalPolicy(
316+ persistence.rows,
317+ {
318+ requireApprovals: requireToolApprovals,
319+ },
320+ );
321+ const mcpHandlers = new Map<
322+ string,
323+ (request: Request) => Promise<Response>
324+ >();
259325
260326 const sourceCatalog = makeSourceCatalogService(sourceStore);
261327 const sourceManager = makeSourceManagerService(toolArtifactStore);
@@ -373,12 +439,44 @@ const createControlPlaneRuntime = async (): Promise<ControlPlaneRuntime> => { · 111 unmodified lines
373439 PmActorLive(persistence.rows),
374440 );
375441
442+ const resolveMcpHandler = (workspaceId: string): ((request: Request) => Promise<Response>) => {
443+ const existing = mcpHandlers.get(workspaceId);
444+ if (existing) {
445+ return existing;
446+ }
447+
448+ const toolRegistry = createSourceToolRegistry({
449+ workspaceId,
450+ sourceStore,
451+ toolArtifactStore,
452+ toolProviderRegistry,
453+ approvalPolicy: persistentApprovalPolicy,
454+ });
455+ const executeRuntimeRun = createPmExecuteRuntimeRun({
456+ defaultRuntimeKind,
457+ runtimeAdapters,
458+ toolRegistry,
459+ });
460+ const runExecutor = createRunExecutor(executeRuntimeRun);
461+ const next = createPmMcpHandler(runExecutor.executeRun, {
462+ toolRegistry,
463+ defaultToolExposureMode,
464+ });
465+
466+ mcpHandlers.set(workspaceId, next);
467+ return next;
468+ };
469+
376470 return {
377471 persistence,
378472 sourceStore,
379473 toolArtifactStore,
380474 fetchOpenApiDocument,
381475 handleControlPlane: controlPlaneWebHandler.handler,
476+ handleMcp: async (request, workspaceId) => {
477+ const handler = resolveMcpHandler(workspaceId);
478+ return handler(request);
479+ },
382480 dispose: async () => {
383481 await controlPlaneWebHandler.dispose();
384482 await persistence.close();
apps/web/lib/mcp/resource-auth.ts+181−0
@@ -0,0 +1,181 @@
1+import { createRemoteJWKSet, jwtVerify } from "jose";
2+
3+import { externalOriginFromRequest } from "../workos";
4+
5+type McpAuthConfig = {
6+ enabled: boolean;
7+ authorizationServer: string | null;
8+ jwks: ReturnType<typeof createRemoteJWKSet> | null;
9+};
10+
11+export type VerifiedMcpToken = {
12+ subject: string;
13+ email: string | null;
14+ displayName: string | null;
15+};
16+
17+const trim = (value: string | undefined): string | undefined => {
18+ const candidate = value?.trim();
19+ return candidate && candidate.length > 0 ? candidate : undefined;
20+};
21+
22+const toAuthorizationServerUrl = (value: string): string | null => {
23+ const candidate =
24+ value.startsWith("http://") || value.startsWith("https://")
25+ ? value
26+ : `https://${value}`;
27+
28+ try {
29+ return new URL(candidate).origin;
30+ } catch {
31+ return null;
32+ }
33+};
34+
35+const resolveAuthorizationServer = (): string | null => {
36+ const candidates = [
37+ process.env.MCP_AUTHORIZATION_SERVER,
38+ process.env.MCP_AUTHORIZATION_SERVER_URL,
39+ process.env.WORKOS_AUTHKIT_ISSUER,
40+ process.env.WORKOS_AUTHKIT_DOMAIN,
41+ ];
42+
43+ for (const candidate of candidates) {
44+ const normalized = trim(candidate);
45+ if (!normalized) {
46+ continue;
47+ }
48+
49+ const parsed = toAuthorizationServerUrl(normalized);
50+ if (parsed) {
51+ return parsed;
52+ }
53+ }
54+
55+ return null;
56+};
57+
58+let cachedAuthConfig: McpAuthConfig | null = null;
59+
60+export const getMcpAuthConfig = (): McpAuthConfig => {
61+ const authorizationServer = resolveAuthorizationServer();
62+
63+ if (
64+ cachedAuthConfig
65+ && cachedAuthConfig.authorizationServer === authorizationServer
66+ ) {
67+ return cachedAuthConfig;
68+ }
69+
70+ if (!authorizationServer) {
71+ cachedAuthConfig = {
72+ enabled: false,
73+ authorizationServer: null,
74+ jwks: null,
75+ };
76+ return cachedAuthConfig;
77+ }
78+
79+ cachedAuthConfig = {
80+ enabled: true,
81+ authorizationServer,
82+ jwks: createRemoteJWKSet(new URL("/oauth2/jwks", authorizationServer)),
83+ };
84+
85+ return cachedAuthConfig;
86+};
87+
88+const parseBearerToken = (request: Request): string | null => {
89+ const header = request.headers.get("authorization");
90+ if (!header || !header.startsWith("Bearer ")) {
91+ return null;
92+ }
93+
94+ const token = header.slice("Bearer ".length).trim();
95+ return token.length > 0 ? token : null;
96+};
97+
98+export const resourceMetadataUrl = (request: Request): string => {
99+ const url = new URL(request.url);
100+ const origin = externalOriginFromRequest(request);
101+ const metadata = new URL(
102+ "/.well-known/oauth-protected-resource",
103+ origin,
104+ );
105+ metadata.search = url.search;
106+
107+ const resource = new URL(url.pathname, origin);
108+ resource.search = url.search;
109+ metadata.searchParams.set("resource", resource.toString());
110+
111+ return metadata.toString();
112+};
113+
114+export const unauthorizedMcpResponse = (
115+ request: Request,
116+ message: string,
117+): Response => {
118+ const challenge = [
119+ 'Bearer error="unauthorized"',
120+ 'error_description="Authorization needed"',
121+ `resource_metadata="${resourceMetadataUrl(request)}"`,
122+ ].join(", ");
123+
124+ return Response.json(
125+ { error: message },
126+ {
127+ status: 401,
128+ headers: {
129+ "WWW-Authenticate": challenge,
130+ },
131+ },
132+ );
133+};
134+
135+const readDisplayName = (payload: Record<string, unknown>): string | null => {
136+ const direct = payload.name;
137+ if (typeof direct === "string" && direct.trim().length > 0) {
138+ return direct.trim();
139+ }
140+
141+ const given = typeof payload.given_name === "string" ? payload.given_name.trim() : "";
142+ const family = typeof payload.family_name === "string" ? payload.family_name.trim() : "";
143+ const combined = [given, family].filter((part) => part.length > 0).join(" ");
144+
145+ return combined.length > 0 ? combined : null;
146+};
147+
148+export const verifyMcpBearerToken = async (
149+ request: Request,
150+ config: McpAuthConfig,
151+): Promise<VerifiedMcpToken | null> => {
152+ if (!config.enabled || !config.authorizationServer || !config.jwks) {
153+ return null;
154+ }
155+
156+ const token = parseBearerToken(request);
157+ if (!token) {
158+ return null;
159+ }
160+
161+ try {
162+ const { payload } = await jwtVerify(token, config.jwks, {
163+ issuer: config.authorizationServer,
164+ });
165+
166+ if (typeof payload.sub !== "string" || payload.sub.length === 0) {
167+ return null;
168+ }
169+
170+ const email = typeof payload.email === "string" ? payload.email : null;
171+ const displayName = readDisplayName(payload);
172+
173+ return {
174+ subject: payload.sub,
175+ email,
176+ displayName,
177+ };
178+ } catch {
179+ return null;
180+ }
181+};
apps/web/package.json+5−0
@@ -11,9 +11,13 @@ · 10 unmodified lines
1111 "dependencies": {
1212 "@effect-atom/atom": "^0.4.11",
1313 "@effect-atom/atom-react": "^0.4.6",
14+ "@executor-v2/engine": "workspace:*",
1415 "@executor-v2/management-api": "workspace:*",
1516 "@executor-v2/persistence-ports": "workspace:*",
1617 "@executor-v2/persistence-sql": "workspace:*",
18+ "@executor-v2/runtime-cloudflare-worker-loader": "workspace:*",
19+ "@executor-v2/runtime-deno-subprocess": "workspace:*",
20+ "@executor-v2/runtime-local-inproc": "workspace:*",
1721 "@executor-v2/schema": "workspace:*",
1822 "@modelcontextprotocol/sdk": "^1.27.1",
1923 "@streamdown/code": "^1.0.3",
@@ -21,6 +25,7 @@ · 1 unmodified line
2125 "class-variance-authority": "^0.7.1",
2226 "clsx": "^2.1.1",
2327 "effect": "catalog:",
28+ "jose": "^6.1.3",
2429 "next": "^15.4.6",
2530 "next-themes": "^0.4.6",
2631 "react": "^19.1.1",
bun.lock+9−4
@@ -47,9 +47,13 @@ · 46 unmodified lines
4747 "dependencies": {
4848 "@effect-atom/atom": "^0.4.11",
4949 "@effect-atom/atom-react": "^0.4.6",
50+ "@executor-v2/engine": "workspace:*",
5051 "@executor-v2/management-api": "workspace:*",
5152 "@executor-v2/persistence-ports": "workspace:*",
5253 "@executor-v2/persistence-sql": "workspace:*",
54+ "@executor-v2/runtime-cloudflare-worker-loader": "workspace:*",
55+ "@executor-v2/runtime-deno-subprocess": "workspace:*",
56+ "@executor-v2/runtime-local-inproc": "workspace:*",
5357 "@executor-v2/schema": "workspace:*",
5458 "@modelcontextprotocol/sdk": "^1.27.1",
5559 "@streamdown/code": "^1.0.3",
@@ -57,6 +61,7 @@ · 1 unmodified line
5761 "class-variance-authority": "^0.7.1",
5862 "clsx": "^2.1.1",
5963 "effect": "catalog:",
64+ "jose": "^6.1.3",
6065 "next": "^15.4.6",
6166 "next-themes": "^0.4.6",
6267 "react": "^19.1.1",
@@ -1472,7 +1477,7 @@ · 1409 unmodified lines
14721477
14731478 "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
14741479
1475− "jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
1480+ "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
14761481
14771482 "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
14781483
@@ -2348,8 +2353,6 @@ · 869 unmodified lines
23482353
23492354 "@mapbox/node-pre-gyp/tar": ["tar@7.5.9", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg=="],
23502355
2351− "@modelcontextprotocol/sdk/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
2352−
23532356 "@neondatabase/serverless/@types/node": ["@types/node@22.19.13", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw=="],
23542357
23552358 "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
@@ -2430,7 +2433,7 @@ · 74 unmodified lines
24302433
24312434 "@vercel/static-config/ajv": ["ajv@8.6.3", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw=="],
24322435
2433− "@workos-inc/node/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
2436+ "@workos-inc/authkit-nextjs/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
24342437
24352438 "accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
24362439
@@ -2522,6 +2525,8 @@ · 85 unmodified lines
25222525
25232526 "unstorage/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
25242527
2528+ "vercel/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
2529+
25252530 "vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
25262531
25272532 "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
packages/management-api/src/openapi-extractor-wasm.ts+6−1
@@ -12,8 +12,13 @@ const readWasmBytes = async (): Promise<Uint8Array> => { · 11 unmodified lines
1212 const candidates: string[] = [];
1313
1414 try {
15+ const wasmModuleUrl = new URL(
16+ "./openapi-extractor-wasm/openapi_extractor_bg.wasm",
17+ import.meta.url,
18+ ).toString();
19+
1520 candidates.push(
16− fileURLToPath(new URL("./openapi-extractor-wasm/openapi_extractor_bg.wasm", import.meta.url)),
21+ fileURLToPath(wasmModuleUrl),
1722 );
1823 } catch {
1924 // Next.js serverless bundling can provide non-URL import.meta.url values.
packages/runtime-deno-subprocess/src/index.ts+1−1
@@ -131,7 +131,7 @@ const defaultDenoExecutable = (): string => { · 130 unmodified lines
131131 };
132132
133133 const workerScriptPath = fileURLToPath(
134− new URL("./deno-subprocess-worker.mjs", import.meta.url),
134+ new URL("./deno-subprocess-worker.mjs", import.meta.url).toString(),
135135 );
136136
137137 const writeMessage = (