@lovable.dev/mcp-js 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.internal.md +6 -2
  2. package/README.md +13 -1
  3. package/dist/authorize-HTd0GKmB.d.ts +18 -0
  4. package/dist/{chunk-W7F6JRDB.js → chunk-3GQXQBYQ.js} +1 -1
  5. package/dist/chunk-BG3HOZXZ.js +57 -0
  6. package/dist/{chunk-3XPKR4G6.js → chunk-CJMHLE7F.js} +1 -1
  7. package/dist/{chunk-HRMLGCXV.js → chunk-FQLM4JDU.js} +36 -12
  8. package/dist/chunk-QJ7XEKT3.js +8 -0
  9. package/dist/{chunk-LMT6RXUF.js → chunk-SBKIKJNB.js} +3 -47
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/protocols/mcp/index.cjs +36 -12
  13. package/dist/protocols/mcp/index.d.cts +2 -2
  14. package/dist/protocols/mcp/index.d.ts +2 -2
  15. package/dist/protocols/mcp/index.js +2 -2
  16. package/dist/protocols/oauth-metadata.cjs +10 -3
  17. package/dist/protocols/oauth-metadata.d.cts +2 -2
  18. package/dist/protocols/oauth-metadata.d.ts +2 -2
  19. package/dist/protocols/oauth-metadata.js +2 -2
  20. package/dist/protocols/rest/index.cjs +50 -24
  21. package/dist/protocols/rest/index.d.cts +2 -2
  22. package/dist/protocols/rest/index.d.ts +2 -2
  23. package/dist/protocols/rest/index.js +6 -4
  24. package/dist/stacks/tanstack/cli/extract-manifest.cjs +214 -0
  25. package/dist/stacks/tanstack/cli/extract-manifest.d.cts +1 -0
  26. package/dist/stacks/tanstack/cli/extract-manifest.d.ts +1 -0
  27. package/dist/stacks/tanstack/cli/extract-manifest.js +170 -0
  28. package/dist/stacks/tanstack/index.cjs +50 -24
  29. package/dist/stacks/tanstack/index.d.cts +2 -2
  30. package/dist/stacks/tanstack/index.d.ts +2 -2
  31. package/dist/stacks/tanstack/index.js +8 -6
  32. package/dist/stacks/tanstack/vite.cjs +67 -19
  33. package/dist/stacks/tanstack/vite.d.cts +26 -2
  34. package/dist/stacks/tanstack/vite.d.ts +26 -2
  35. package/dist/stacks/tanstack/vite.js +65 -20
  36. package/dist/{types-zMlr1mOK.d.ts → types-C5SSORxe.d.ts} +4 -0
  37. package/package.json +6 -3
  38. package/dist/authorize-DpTEIFvs.d.ts +0 -9
@@ -256,10 +256,10 @@ function resolveProtectedResource(auth, request, options) {
256
256
  resourceURL.hash = "";
257
257
  return trimTrailingSlash(resourceURL.toString());
258
258
  }
259
- function assertResourcePathShape(resourcePath) {
259
+ function assertResourcePathShape(resourcePath, label = "resourcePath") {
260
260
  if (!resourcePath.startsWith("/") || resourcePath.startsWith("//") || resourcePath.includes("\\") || /(^|\/)\.\.(\/|$)/.test(resourcePath)) {
261
261
  throw new Error(
262
- `@lovable.dev/mcp-js: resourcePath must be an absolute path beginning with "/" without ".." segments or backslashes (got ${JSON.stringify(resourcePath)})`
262
+ `@lovable.dev/mcp-js: ${label} must be an absolute path beginning with "/" without ".." segments or backslashes (got ${JSON.stringify(resourcePath)})`
263
263
  );
264
264
  }
265
265
  }
@@ -400,16 +400,16 @@ function quoteAuthenticateParam(value) {
400
400
  const sanitized = value.replace(/[\u0000-\u001F\u007F]/g, "");
401
401
  return `"${sanitized.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
402
402
  }
403
- function resolveProtectedResourceMetadataUrl(auth, request) {
403
+ function resolveProtectedResourceMetadataUrl(auth, request, options) {
404
404
  if (auth.protectedResourceMetadataUrl !== void 0)
405
405
  return auth.protectedResourceMetadataUrl;
406
406
  const base = auth.resource ?? request.url;
407
- return new URL(OAUTH_PROTECTED_RESOURCE_METADATA_PATH, base).toString();
407
+ return new URL(options.metadataPath ?? OAUTH_PROTECTED_RESOURCE_METADATA_PATH, base).toString();
408
408
  }
409
- function wwwAuthenticateHeader(auth, request, params = {}) {
409
+ function wwwAuthenticateHeader(auth, request, options, params = {}) {
410
410
  const values = [
411
411
  `realm=${quoteAuthenticateParam("mcp")}`,
412
- `resource_metadata=${quoteAuthenticateParam(resolveProtectedResourceMetadataUrl(auth, request))}`
412
+ `resource_metadata=${quoteAuthenticateParam(resolveProtectedResourceMetadataUrl(auth, request, options))}`
413
413
  ];
414
414
  if (auth.requiredScopes && auth.requiredScopes.length > 0) {
415
415
  values.push(`scope=${quoteAuthenticateParam(auth.requiredScopes.join(" "))}`);
@@ -420,9 +420,12 @@ function wwwAuthenticateHeader(auth, request, params = {}) {
420
420
  values.push(`error_description=${quoteAuthenticateParam(params.errorDescription)}`);
421
421
  return `Bearer ${values.join(", ")}`;
422
422
  }
423
- function challengeResponse(auth, request, status, oauthError, errorDescription) {
423
+ function challengeResponse(auth, request, options, status, oauthError, errorDescription) {
424
424
  const headers = new Headers(JSON_HEADERS);
425
- headers.set("WWW-Authenticate", wwwAuthenticateHeader(auth, request, { error: oauthError, errorDescription }));
425
+ headers.set(
426
+ "WWW-Authenticate",
427
+ wwwAuthenticateHeader(auth, request, options, { error: oauthError, errorDescription })
428
+ );
426
429
  headers.set("Cache-Control", "no-store");
427
430
  return new Response(JSON.stringify({ error: status === 403 ? "forbidden" : "unauthorized" }), {
428
431
  status,
@@ -450,10 +453,17 @@ function parseBearerToken(request) {
450
453
  function getOAuthRuntime(mcp, options = {}) {
451
454
  if (options.resourcePath !== void 0)
452
455
  assertResourcePathShape(options.resourcePath);
453
- const stableOptions = { resourcePath: options.resourcePath };
456
+ if (options.metadataPath !== void 0)
457
+ assertResourcePathShape(options.metadataPath, "metadataPath");
458
+ const stableOptions = { resourcePath: options.resourcePath, metadataPath: options.metadataPath };
454
459
  const auth = mcp.auth?.type === "oauth" ? mcp.auth : void 0;
455
460
  if (!auth)
456
461
  return { kind: "unconfigured", options: stableOptions };
462
+ if (options.metadataPath !== void 0 && auth.protectedResourceMetadataUrl !== void 0) {
463
+ throw new Error(
464
+ `@lovable.dev/mcp-js: the Vite plugin generates protected-resource metadata (metadataPath set), so auth.protectedResourceMetadataUrl must not also be set. Drop protectedResourceMetadataUrl, or set protectedResourceMetadataRoute: false in mcpPlugin(...) to host the document yourself.`
465
+ );
466
+ }
457
467
  const discovery = createOAuthDiscoveryResolver(auth);
458
468
  return {
459
469
  kind: "configured",
@@ -492,7 +502,7 @@ function createRequestAuthorizer(mcp, options = {}) {
492
502
  const token = parseBearerToken(request);
493
503
  if (!token) {
494
504
  log.info("auth.no_bearer_token", { outcome: "401" });
495
- return { ok: false, response: challengeResponse(runtime.auth, request, 401) };
505
+ return { ok: false, response: challengeResponse(runtime.auth, request, runtime.options, 401) };
496
506
  }
497
507
  try {
498
508
  const auth = await runtime.verify(token, request, runtime.options);
@@ -507,13 +517,27 @@ function createRequestAuthorizer(mcp, options = {}) {
507
517
  log.info("auth.token_rejected", { status: err.status, oauthError: err.oauthError });
508
518
  return {
509
519
  ok: false,
510
- response: challengeResponse(runtime.auth, request, err.status, err.oauthError, err.message)
520
+ response: challengeResponse(
521
+ runtime.auth,
522
+ request,
523
+ runtime.options,
524
+ err.status,
525
+ err.oauthError,
526
+ err.message
527
+ )
511
528
  };
512
529
  }
513
530
  log.error("auth.unexpected_error", { ...describeError(err), outcome: "401" });
514
531
  return {
515
532
  ok: false,
516
- response: challengeResponse(runtime.auth, request, 401, "invalid_token", "Invalid access token")
533
+ response: challengeResponse(
534
+ runtime.auth,
535
+ request,
536
+ runtime.options,
537
+ 401,
538
+ "invalid_token",
539
+ "Invalid access token"
540
+ )
517
541
  };
518
542
  }
519
543
  }
@@ -550,6 +574,19 @@ function shapeToJsonSchema(shape) {
550
574
  return null;
551
575
  }
552
576
  }
577
+ function buildMcpListing(mcp) {
578
+ return {
579
+ server: { name: mcp.name, version: mcp.version, title: mcp.title },
580
+ tools: mcp.tools.map((tool) => ({
581
+ name: tool.name,
582
+ title: tool.title,
583
+ description: tool.description,
584
+ annotations: tool.annotations,
585
+ inputSchema: shapeToJsonSchema(tool.inputSchema),
586
+ outputSchema: shapeToJsonSchema(tool.outputSchema)
587
+ }))
588
+ };
589
+ }
553
590
  function createListToolsHandler(mcp, options = {}) {
554
591
  assertRestResourceBinding(mcp, options);
555
592
  const authorizer = createRequestAuthorizer(mcp, options);
@@ -559,18 +596,7 @@ function createListToolsHandler(mcp, options = {}) {
559
596
  return authResult.response;
560
597
  if (request.method !== "GET" && request.method !== "HEAD")
561
598
  return methodNotAllowed("GET, HEAD, OPTIONS");
562
- const body = {
563
- server: { name: mcp.name, version: mcp.version, title: mcp.title },
564
- tools: mcp.tools.map((tool) => ({
565
- name: tool.name,
566
- title: tool.title,
567
- description: tool.description,
568
- annotations: tool.annotations,
569
- inputSchema: shapeToJsonSchema(tool.inputSchema),
570
- outputSchema: shapeToJsonSchema(tool.outputSchema)
571
- }))
572
- };
573
- const response = Response.json(body);
599
+ const response = Response.json(buildMcpListing(mcp));
574
600
  return request.method === "HEAD" ? headResponse(response) : response;
575
601
  };
576
602
  return async (request) => {
@@ -1,5 +1,5 @@
1
- import { M as McpRuntimeOptions } from '../../authorize-DpTEIFvs.js';
2
- import { d as McpDefinition } from '../../types-zMlr1mOK.js';
1
+ import { M as McpRuntimeOptions } from '../../authorize-HTd0GKmB.js';
2
+ import { d as McpDefinition } from '../../types-C5SSORxe.js';
3
3
  import 'zod';
4
4
 
5
5
  type RestListToolsHandler = (request: Request) => Promise<Response>;
@@ -1,5 +1,5 @@
1
- import { M as McpRuntimeOptions } from '../../authorize-DpTEIFvs.js';
2
- import { d as McpDefinition } from '../../types-zMlr1mOK.js';
1
+ import { M as McpRuntimeOptions } from '../../authorize-HTd0GKmB.js';
2
+ import { d as McpDefinition } from '../../types-C5SSORxe.js';
3
3
  import 'zod';
4
4
 
5
5
  type RestListToolsHandler = (request: Request) => Promise<Response>;
@@ -1,9 +1,11 @@
1
1
  import {
2
- createInvokeToolHandler,
3
- createListToolsHandler
4
- } from "../../chunk-LMT6RXUF.js";
2
+ createInvokeToolHandler
3
+ } from "../../chunk-SBKIKJNB.js";
5
4
  import "../../chunk-MA5H6PSF.js";
6
- import "../../chunk-HRMLGCXV.js";
5
+ import {
6
+ createListToolsHandler
7
+ } from "../../chunk-BG3HOZXZ.js";
8
+ import "../../chunk-FQLM4JDU.js";
7
9
  import "../../chunk-QC3DXQTH.js";
8
10
  import "../../chunk-6DXGZZA4.js";
9
11
  export {
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/stacks/tanstack/manifest-io.ts
5
+ var import_node_crypto = require("crypto");
6
+ var import_node_fs = require("fs");
7
+ var import_node_path = require("path");
8
+ var import_vite2 = require("vite");
9
+
10
+ // package.json
11
+ var version = "0.9.0";
12
+
13
+ // src/protocols/rest/list-tools.ts
14
+ var import_zod_compat = require("@modelcontextprotocol/sdk/server/zod-compat.js");
15
+ var import_zod_json_schema_compat = require("@modelcontextprotocol/sdk/server/zod-json-schema-compat.js");
16
+
17
+ // src/core/logger.ts
18
+ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
19
+ function isLogLevel(value) {
20
+ return typeof value === "string" && value in LEVEL_RANK;
21
+ }
22
+ function readEnvLevel() {
23
+ try {
24
+ const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
25
+ const normalized = raw?.trim().toLowerCase();
26
+ return isLogLevel(normalized) ? normalized : void 0;
27
+ } catch {
28
+ return void 0;
29
+ }
30
+ }
31
+ var currentLevel = readEnvLevel() ?? "silent";
32
+
33
+ // src/auth/verifier.ts
34
+ var import_jose = require("jose");
35
+
36
+ // src/protocols/rest/list-tools.ts
37
+ function shapeToJsonSchema(shape) {
38
+ if (!shape)
39
+ return null;
40
+ try {
41
+ return (0, import_zod_json_schema_compat.toJsonSchemaCompat)((0, import_zod_compat.objectFromShape)(shape));
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ function buildMcpListing(mcp) {
47
+ return {
48
+ server: { name: mcp.name, version: mcp.version, title: mcp.title },
49
+ tools: mcp.tools.map((tool) => ({
50
+ name: tool.name,
51
+ title: tool.title,
52
+ description: tool.description,
53
+ annotations: tool.annotations,
54
+ inputSchema: shapeToJsonSchema(tool.inputSchema),
55
+ outputSchema: shapeToJsonSchema(tool.outputSchema)
56
+ }))
57
+ };
58
+ }
59
+
60
+ // src/stacks/tanstack/extract-manifest.ts
61
+ var MANIFEST_VERSION = 1;
62
+ var MANIFEST_RELATIVE_PATH = ".lovable/mcp/manifest.json";
63
+ function manifestAuth(auth) {
64
+ if (!auth)
65
+ return { type: "none" };
66
+ const oauth = { type: "oauth", issuer: auth.issuer };
67
+ if (auth.resource !== void 0)
68
+ oauth.resource = auth.resource;
69
+ if (auth.resourceName !== void 0)
70
+ oauth.resource_name = auth.resourceName;
71
+ if (auth.resourceDocumentation !== void 0)
72
+ oauth.resource_documentation = auth.resourceDocumentation;
73
+ if (auth.protectedResourceMetadataUrl !== void 0)
74
+ oauth.protected_resource_metadata_url = auth.protectedResourceMetadataUrl;
75
+ if (auth.requireOAuthClientClaim !== void 0)
76
+ oauth.require_oauth_client_claim = auth.requireOAuthClientClaim;
77
+ if (auth.acceptedAudiences !== void 0)
78
+ oauth.accepted_audiences = auth.acceptedAudiences;
79
+ if (auth.requiredScopes !== void 0)
80
+ oauth.required_scopes = auth.requiredScopes;
81
+ if (auth.jwksUri !== void 0)
82
+ oauth.jwks_uri = auth.jwksUri;
83
+ if (auth.algorithms !== void 0)
84
+ oauth.algorithms = auth.algorithms;
85
+ if (auth.clockToleranceSeconds !== void 0)
86
+ oauth.clock_tolerance_seconds = auth.clockToleranceSeconds;
87
+ return oauth;
88
+ }
89
+ function isToolShape(tool) {
90
+ if (typeof tool !== "object" || tool === null)
91
+ return false;
92
+ const t = tool;
93
+ return typeof t.name === "string" && typeof t.title === "string" && typeof t.description === "string";
94
+ }
95
+ function isAuthShape(auth) {
96
+ if (auth === void 0)
97
+ return true;
98
+ if (typeof auth !== "object" || auth === null)
99
+ return false;
100
+ const a = auth;
101
+ return a.type === "oauth" && typeof a.issuer === "string" && a.issuer.length > 0;
102
+ }
103
+ function manifestFromDefinition(definition, urlPath, source = "the MCP entry") {
104
+ const def = definition;
105
+ const validShape = !!def && typeof def === "object" && typeof def.name === "string" && typeof def.title === "string" && typeof def.version === "string" && Array.isArray(def.tools) && def.tools.every(isToolShape) && isAuthShape(def.auth);
106
+ if (!validShape) {
107
+ const got = definition == null ? "no default export" : typeof definition;
108
+ throw new Error(`@lovable.dev/mcp-js: ${source} must \`export default defineMcp(...)\` (got ${got}).`);
109
+ }
110
+ return {
111
+ version: MANIFEST_VERSION,
112
+ sdk_version: version,
113
+ path: urlPath,
114
+ auth: manifestAuth(def.auth),
115
+ mcp: buildMcpListing(def)
116
+ };
117
+ }
118
+
119
+ // src/stacks/tanstack/fs-errors.ts
120
+ function isFileMissing(err) {
121
+ return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
122
+ }
123
+
124
+ // src/stacks/tanstack/ssr-loader.ts
125
+ var import_vite = require("vite");
126
+ async function produceViaSsr(projectRoot2, alias, entryAbs, urlPath) {
127
+ const server = await (0, import_vite.createServer)({
128
+ configFile: false,
129
+ root: projectRoot2,
130
+ logLevel: "silent",
131
+ server: { middlewareMode: true, hmr: false, watch: null },
132
+ optimizeDeps: { noDiscovery: true },
133
+ resolve: { alias }
134
+ });
135
+ try {
136
+ const mod = await server.ssrLoadModule(entryAbs);
137
+ return manifestFromDefinition(mod.default, urlPath, entryAbs);
138
+ } finally {
139
+ await server.close();
140
+ }
141
+ }
142
+
143
+ // src/stacks/tanstack/manifest-io.ts
144
+ var MCP_PLUGIN_NAME = "@lovable.dev/mcp-js";
145
+ var DEFAULT_MCP_ENTRY = "src/lib/mcp/index.ts";
146
+ var DEFAULT_MCP_PATH = "/mcp";
147
+ function writeManifestIfChanged(file, manifest) {
148
+ const content = JSON.stringify(manifest, null, 2) + "\n";
149
+ let existing;
150
+ try {
151
+ existing = (0, import_node_fs.readFileSync)(file, "utf8");
152
+ } catch (err) {
153
+ if (!isFileMissing(err))
154
+ throw err;
155
+ }
156
+ if (existing === content)
157
+ return;
158
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(file), { recursive: true });
159
+ const tmp = `${file}.${(0, import_node_crypto.randomUUID)()}.tmp`;
160
+ try {
161
+ (0, import_node_fs.writeFileSync)(tmp, content, "utf8");
162
+ (0, import_node_fs.renameSync)(tmp, file);
163
+ } catch (err) {
164
+ try {
165
+ (0, import_node_fs.unlinkSync)(tmp);
166
+ } catch {
167
+ }
168
+ throw err;
169
+ }
170
+ }
171
+ function removeManifest(file) {
172
+ try {
173
+ (0, import_node_fs.unlinkSync)(file);
174
+ } catch (err) {
175
+ if (!isFileMissing(err))
176
+ throw err;
177
+ }
178
+ }
179
+ async function syncManifest(manifestFile, entryAbs, urlPath, produce) {
180
+ let entryExists = true;
181
+ try {
182
+ (0, import_node_fs.lstatSync)(entryAbs);
183
+ } catch (err) {
184
+ if (!isFileMissing(err))
185
+ throw err;
186
+ entryExists = false;
187
+ }
188
+ if (!entryExists) {
189
+ removeManifest(manifestFile);
190
+ return;
191
+ }
192
+ const manifest = await produce(entryAbs, urlPath);
193
+ writeManifestIfChanged(manifestFile, manifest);
194
+ }
195
+ async function runExtract(projectRoot2) {
196
+ const resolved = await (0, import_vite2.resolveConfig)({ root: projectRoot2, logLevel: "silent" }, "serve");
197
+ const alias = resolved.resolve?.alias ?? [];
198
+ const pluginApi = resolved.plugins.find((p) => p.name === MCP_PLUGIN_NAME)?.api;
199
+ const entryAbs = pluginApi?.mcpEntry ?? (0, import_node_path.resolve)(projectRoot2, DEFAULT_MCP_ENTRY);
200
+ const urlPath = pluginApi?.urlPath ?? DEFAULT_MCP_PATH;
201
+ await syncManifest(
202
+ (0, import_node_path.resolve)(projectRoot2, MANIFEST_RELATIVE_PATH),
203
+ entryAbs,
204
+ urlPath,
205
+ (entry, path) => produceViaSsr(projectRoot2, alias, entry, path)
206
+ );
207
+ }
208
+
209
+ // src/stacks/tanstack/cli/extract-manifest.ts
210
+ var projectRoot = process.argv[2] ?? process.cwd();
211
+ runExtract(projectRoot).catch((err) => {
212
+ console.error(err instanceof Error ? err.message : String(err));
213
+ process.exit(1);
214
+ });
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ buildMcpListing
4
+ } from "../../../chunk-BG3HOZXZ.js";
5
+ import "../../../chunk-FQLM4JDU.js";
6
+ import "../../../chunk-QC3DXQTH.js";
7
+ import {
8
+ isFileMissing
9
+ } from "../../../chunk-QJ7XEKT3.js";
10
+ import "../../../chunk-6DXGZZA4.js";
11
+
12
+ // src/stacks/tanstack/manifest-io.ts
13
+ import { randomUUID } from "crypto";
14
+ import { lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
15
+ import { dirname, resolve } from "path";
16
+ import { resolveConfig } from "vite";
17
+
18
+ // package.json
19
+ var version = "0.9.0";
20
+
21
+ // src/stacks/tanstack/extract-manifest.ts
22
+ var MANIFEST_VERSION = 1;
23
+ var MANIFEST_RELATIVE_PATH = ".lovable/mcp/manifest.json";
24
+ function manifestAuth(auth) {
25
+ if (!auth)
26
+ return { type: "none" };
27
+ const oauth = { type: "oauth", issuer: auth.issuer };
28
+ if (auth.resource !== void 0)
29
+ oauth.resource = auth.resource;
30
+ if (auth.resourceName !== void 0)
31
+ oauth.resource_name = auth.resourceName;
32
+ if (auth.resourceDocumentation !== void 0)
33
+ oauth.resource_documentation = auth.resourceDocumentation;
34
+ if (auth.protectedResourceMetadataUrl !== void 0)
35
+ oauth.protected_resource_metadata_url = auth.protectedResourceMetadataUrl;
36
+ if (auth.requireOAuthClientClaim !== void 0)
37
+ oauth.require_oauth_client_claim = auth.requireOAuthClientClaim;
38
+ if (auth.acceptedAudiences !== void 0)
39
+ oauth.accepted_audiences = auth.acceptedAudiences;
40
+ if (auth.requiredScopes !== void 0)
41
+ oauth.required_scopes = auth.requiredScopes;
42
+ if (auth.jwksUri !== void 0)
43
+ oauth.jwks_uri = auth.jwksUri;
44
+ if (auth.algorithms !== void 0)
45
+ oauth.algorithms = auth.algorithms;
46
+ if (auth.clockToleranceSeconds !== void 0)
47
+ oauth.clock_tolerance_seconds = auth.clockToleranceSeconds;
48
+ return oauth;
49
+ }
50
+ function isToolShape(tool) {
51
+ if (typeof tool !== "object" || tool === null)
52
+ return false;
53
+ const t = tool;
54
+ return typeof t.name === "string" && typeof t.title === "string" && typeof t.description === "string";
55
+ }
56
+ function isAuthShape(auth) {
57
+ if (auth === void 0)
58
+ return true;
59
+ if (typeof auth !== "object" || auth === null)
60
+ return false;
61
+ const a = auth;
62
+ return a.type === "oauth" && typeof a.issuer === "string" && a.issuer.length > 0;
63
+ }
64
+ function manifestFromDefinition(definition, urlPath, source = "the MCP entry") {
65
+ const def = definition;
66
+ const validShape = !!def && typeof def === "object" && typeof def.name === "string" && typeof def.title === "string" && typeof def.version === "string" && Array.isArray(def.tools) && def.tools.every(isToolShape) && isAuthShape(def.auth);
67
+ if (!validShape) {
68
+ const got = definition == null ? "no default export" : typeof definition;
69
+ throw new Error(`@lovable.dev/mcp-js: ${source} must \`export default defineMcp(...)\` (got ${got}).`);
70
+ }
71
+ return {
72
+ version: MANIFEST_VERSION,
73
+ sdk_version: version,
74
+ path: urlPath,
75
+ auth: manifestAuth(def.auth),
76
+ mcp: buildMcpListing(def)
77
+ };
78
+ }
79
+
80
+ // src/stacks/tanstack/ssr-loader.ts
81
+ import { createServer } from "vite";
82
+ async function produceViaSsr(projectRoot2, alias, entryAbs, urlPath) {
83
+ const server = await createServer({
84
+ configFile: false,
85
+ root: projectRoot2,
86
+ logLevel: "silent",
87
+ server: { middlewareMode: true, hmr: false, watch: null },
88
+ optimizeDeps: { noDiscovery: true },
89
+ resolve: { alias }
90
+ });
91
+ try {
92
+ const mod = await server.ssrLoadModule(entryAbs);
93
+ return manifestFromDefinition(mod.default, urlPath, entryAbs);
94
+ } finally {
95
+ await server.close();
96
+ }
97
+ }
98
+
99
+ // src/stacks/tanstack/manifest-io.ts
100
+ var MCP_PLUGIN_NAME = "@lovable.dev/mcp-js";
101
+ var DEFAULT_MCP_ENTRY = "src/lib/mcp/index.ts";
102
+ var DEFAULT_MCP_PATH = "/mcp";
103
+ function writeManifestIfChanged(file, manifest) {
104
+ const content = JSON.stringify(manifest, null, 2) + "\n";
105
+ let existing;
106
+ try {
107
+ existing = readFileSync(file, "utf8");
108
+ } catch (err) {
109
+ if (!isFileMissing(err))
110
+ throw err;
111
+ }
112
+ if (existing === content)
113
+ return;
114
+ mkdirSync(dirname(file), { recursive: true });
115
+ const tmp = `${file}.${randomUUID()}.tmp`;
116
+ try {
117
+ writeFileSync(tmp, content, "utf8");
118
+ renameSync(tmp, file);
119
+ } catch (err) {
120
+ try {
121
+ unlinkSync(tmp);
122
+ } catch {
123
+ }
124
+ throw err;
125
+ }
126
+ }
127
+ function removeManifest(file) {
128
+ try {
129
+ unlinkSync(file);
130
+ } catch (err) {
131
+ if (!isFileMissing(err))
132
+ throw err;
133
+ }
134
+ }
135
+ async function syncManifest(manifestFile, entryAbs, urlPath, produce) {
136
+ let entryExists = true;
137
+ try {
138
+ lstatSync(entryAbs);
139
+ } catch (err) {
140
+ if (!isFileMissing(err))
141
+ throw err;
142
+ entryExists = false;
143
+ }
144
+ if (!entryExists) {
145
+ removeManifest(manifestFile);
146
+ return;
147
+ }
148
+ const manifest = await produce(entryAbs, urlPath);
149
+ writeManifestIfChanged(manifestFile, manifest);
150
+ }
151
+ async function runExtract(projectRoot2) {
152
+ const resolved = await resolveConfig({ root: projectRoot2, logLevel: "silent" }, "serve");
153
+ const alias = resolved.resolve?.alias ?? [];
154
+ const pluginApi = resolved.plugins.find((p) => p.name === MCP_PLUGIN_NAME)?.api;
155
+ const entryAbs = pluginApi?.mcpEntry ?? resolve(projectRoot2, DEFAULT_MCP_ENTRY);
156
+ const urlPath = pluginApi?.urlPath ?? DEFAULT_MCP_PATH;
157
+ await syncManifest(
158
+ resolve(projectRoot2, MANIFEST_RELATIVE_PATH),
159
+ entryAbs,
160
+ urlPath,
161
+ (entry, path) => produceViaSsr(projectRoot2, alias, entry, path)
162
+ );
163
+ }
164
+
165
+ // src/stacks/tanstack/cli/extract-manifest.ts
166
+ var projectRoot = process.argv[2] ?? process.cwd();
167
+ runExtract(projectRoot).catch((err) => {
168
+ console.error(err instanceof Error ? err.message : String(err));
169
+ process.exit(1);
170
+ });