@lovable.dev/mcp-js 0.22.1 → 0.22.2-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authorize-CumVyk7p.d.cts +27 -0
- package/dist/authorize-D6p4-9_D.d.ts +27 -0
- package/dist/base-DCe0kP79.d.cts +29 -0
- package/dist/base-DCe0kP79.d.ts +29 -0
- package/dist/cli/extract-manifest.cjs +116 -201
- package/dist/cli/extract-manifest.d.cts +1 -1
- package/dist/cli/extract-manifest.d.ts +1 -1
- package/dist/cli/extract-manifest.js +117 -156
- package/dist/content-D5LJAHyM.js +12 -0
- package/dist/content-NCYIGgOb.cjs +17 -0
- package/dist/context-BweeEcoA.cjs +56 -0
- package/dist/context-CIpPzN7P.js +51 -0
- package/dist/cors-C2n2LxrC.js +830 -0
- package/dist/cors-Cga2PWEg.cjs +913 -0
- package/dist/forwarded--h4efJy-.js +43 -0
- package/dist/forwarded-Pi8Hs5Ps.cjs +48 -0
- package/dist/fs-errors-CWNzOV75.cjs +11 -0
- package/dist/fs-errors-PA2t1TIp.js +6 -0
- package/dist/index.cjs +129 -329
- package/dist/index.d.cts +26 -26
- package/dist/index.d.ts +26 -26
- package/dist/index.js +118 -177
- package/dist/{io-D4srzRKc.d.ts → io-CfSPxeBN.d.ts} +6 -4
- package/dist/io-DPYvBqET.d.cts +15 -0
- package/dist/list-tools-07OV14rK.cjs +57 -0
- package/dist/list-tools-B4TTPEl1.d.cts +8 -0
- package/dist/list-tools-I8Hvub9P.js +46 -0
- package/dist/list-tools-bh6-eqZM.d.ts +8 -0
- package/dist/logger-2OXtfiIW.js +141 -0
- package/dist/logger-BqPOa_E1.cjs +194 -0
- package/dist/mcp-B6z5Givq.js +123 -0
- package/dist/mcp-DLv_c1O5.cjs +128 -0
- package/dist/metadata-path-CAkNcfyY.js +12 -0
- package/dist/metadata-path-zd7NSNoa.cjs +17 -0
- package/dist/package-CxQcGWdU.cjs +9 -0
- package/dist/package-xS6mk-b_.js +4 -0
- package/dist/paths-IS65L6TA.js +7 -0
- package/dist/paths-OP318a6c.cjs +18 -0
- package/dist/protocols/mcp/index.cjs +3 -750
- package/dist/protocols/mcp/index.d.cts +6 -7
- package/dist/protocols/mcp/index.d.ts +6 -7
- package/dist/protocols/mcp/index.js +2 -12
- package/dist/protocols/oauth-metadata.cjs +48 -539
- package/dist/protocols/oauth-metadata.d.cts +5 -6
- package/dist/protocols/oauth-metadata.d.ts +5 -6
- package/dist/protocols/oauth-metadata.js +52 -10
- package/dist/protocols/rest/index.cjs +5 -843
- package/dist/protocols/rest/index.d.cts +7 -10
- package/dist/protocols/rest/index.d.ts +7 -10
- package/dist/protocols/rest/index.js +3 -16
- package/dist/rest-CggWx00c.js +117 -0
- package/dist/rest-DxhK7-3b.cjs +122 -0
- package/dist/stacks/supabase/index.cjs +76 -1318
- package/dist/stacks/supabase/index.d.cts +29 -30
- package/dist/stacks/supabase/index.d.ts +29 -30
- package/dist/stacks/supabase/index.js +73 -89
- package/dist/stacks/supabase/vite.cjs +258 -287
- package/dist/stacks/supabase/vite.d.cts +24 -24
- package/dist/stacks/supabase/vite.d.ts +24 -24
- package/dist/stacks/supabase/vite.js +252 -252
- package/dist/stacks/tanstack/index.cjs +42 -1272
- package/dist/stacks/tanstack/index.d.cts +8 -9
- package/dist/stacks/tanstack/index.d.ts +8 -9
- package/dist/stacks/tanstack/index.js +36 -41
- package/dist/stacks/tanstack/vite.cjs +223 -312
- package/dist/stacks/tanstack/vite.d.cts +80 -81
- package/dist/stacks/tanstack/vite.d.ts +80 -81
- package/dist/stacks/tanstack/vite.js +216 -282
- package/dist/types-BYdYYTjk.d.cts +321 -0
- package/dist/types-BYdYYTjk.d.ts +321 -0
- package/package.json +4 -4
- package/dist/authorize-BMeXd_Sh.d.ts +0 -25
- package/dist/base-CM-KCpp6.d.ts +0 -28
- package/dist/chunk-5HVSJCKE.js +0 -114
- package/dist/chunk-6DXGZZA4.js +0 -6
- package/dist/chunk-6QFZYYUV.js +0 -11
- package/dist/chunk-6SZOFKGH.js +0 -58
- package/dist/chunk-H37EB22A.js +0 -125
- package/dist/chunk-IFDWUM7L.js +0 -741
- package/dist/chunk-IXZIFYO6.js +0 -79
- package/dist/chunk-MA5H6PSF.js +0 -46
- package/dist/chunk-TJN33K5U.js +0 -141
- package/dist/chunk-UQK5UO6C.js +0 -26
- package/dist/chunk-XQWJN6DC.js +0 -14
- package/dist/chunk-Y3ZFPEQH.js +0 -8
- package/dist/chunk-ZQXSWTPU.js +0 -6
- package/dist/types-qqrmJ736.d.ts +0 -321
|
@@ -1,109 +1,85 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
} from "
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// src/stacks/tanstack/vite.ts
|
|
9
|
-
import { lstatSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "fs";
|
|
10
|
-
import { dirname, join, relative, resolve, sep } from "path";
|
|
11
|
-
var GENERATED_BANNER = "// AUTO-GENERATED by @lovable.dev/mcp-js \u2014 do not edit. Regenerated by the Vite plugin.\n// To take ownership, delete this banner line; the plugin then leaves the file alone.";
|
|
1
|
+
import "../../metadata-path-CAkNcfyY.js";
|
|
2
|
+
import { t as isFileMissing } from "../../fs-errors-PA2t1TIp.js";
|
|
3
|
+
import { lstatSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
//#region src/stacks/tanstack/vite.ts
|
|
6
|
+
const GENERATED_BANNER = "// AUTO-GENERATED by @lovable.dev/mcp-js — do not edit. Regenerated by the Vite plugin.\n// To take ownership, delete this banner line; the plugin then leaves the file alone.";
|
|
12
7
|
function normalizePath(p) {
|
|
13
|
-
|
|
8
|
+
return p.split(sep).join("/");
|
|
14
9
|
}
|
|
15
10
|
function assertContains(parent, child, label) {
|
|
16
|
-
|
|
17
|
-
throw new Error(`@lovable.dev/mcp-js: ${label} must resolve under ${parent}, got ${child}`);
|
|
18
|
-
}
|
|
11
|
+
if (child !== parent && !child.startsWith(parent + sep)) throw new Error(`@lovable.dev/mcp-js: ${label} must resolve under ${parent}, got ${child}`);
|
|
19
12
|
}
|
|
20
13
|
function wellKnownRouteFile(routesDir, urlPath) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return resolve(routesDir, ...fileSegments);
|
|
14
|
+
const segments = urlPath.replace(/^\//, "").split("/");
|
|
15
|
+
return resolve(routesDir, ...segments.map((segment, index) => {
|
|
16
|
+
const escaped = segment.startsWith(".") ? `[${segment}]` : segment;
|
|
17
|
+
return index === segments.length - 1 ? `${escaped}.ts` : escaped;
|
|
18
|
+
}));
|
|
27
19
|
}
|
|
28
20
|
function resolveRestRoutes(routesDir, mcpUrlPath) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
const parentSegments = mcpUrlPath.replace(/^\/+/, "").replace(/\/+$/, "").split("/").slice(0, -1);
|
|
22
|
+
const restDir = resolve(routesDir, ...parentSegments, "[.mcp]");
|
|
23
|
+
const restUrlBase = `/${[...parentSegments, ".mcp"].join("/")}`;
|
|
24
|
+
return {
|
|
25
|
+
listToolsRouteFile: resolve(restDir, "list-tools.ts"),
|
|
26
|
+
invokeToolRouteFile: resolve(restDir, "invoke-tool", "$tool.ts"),
|
|
27
|
+
listToolsLiteral: `${restUrlBase}/list-tools`,
|
|
28
|
+
invokeToolLiteral: `${restUrlBase}/invoke-tool/$tool`
|
|
29
|
+
};
|
|
38
30
|
}
|
|
39
31
|
function resolveAllRoutes(projectRoot, routesDirOption, routeFileName, mcpUrlPath, metadataUrlPath) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
const routesDir = resolve(projectRoot, routesDirOption);
|
|
33
|
+
assertContains(projectRoot, routesDir, `routesDir "${routesDirOption}"`);
|
|
34
|
+
const mcpRouteFile = resolve(routesDir, routeFileName);
|
|
35
|
+
assertContains(routesDir, mcpRouteFile, `routeFileName "${routeFileName}"`);
|
|
36
|
+
const metadataRouteFile = wellKnownRouteFile(routesDir, metadataUrlPath);
|
|
37
|
+
assertContains(routesDir, metadataRouteFile, `metadataPath "${metadataUrlPath}"`);
|
|
38
|
+
const rest = resolveRestRoutes(routesDir, mcpUrlPath);
|
|
39
|
+
assertContains(routesDir, rest.listToolsRouteFile, `REST routes for path "${mcpUrlPath}"`);
|
|
40
|
+
assertContains(routesDir, rest.invokeToolRouteFile, `REST routes for path "${mcpUrlPath}"`);
|
|
41
|
+
return {
|
|
42
|
+
routesDir,
|
|
43
|
+
mcpRouteFile,
|
|
44
|
+
metadataRouteFile,
|
|
45
|
+
...rest
|
|
46
|
+
};
|
|
50
47
|
}
|
|
51
48
|
function assertUrlPathShape(urlPath) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const trimmed = urlPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
59
|
-
if (!trimmed) {
|
|
60
|
-
throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" has no route segment`);
|
|
61
|
-
}
|
|
62
|
-
if (!/^[a-zA-Z0-9_\-/]+$/.test(trimmed)) {
|
|
63
|
-
throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" contains unsupported characters`);
|
|
64
|
-
}
|
|
65
|
-
return trimmed;
|
|
49
|
+
if (!urlPath.startsWith("/")) throw new Error(`@lovable.dev/mcp-js: path must start with "/", got "${urlPath}"`);
|
|
50
|
+
if (/\/{2,}/.test(urlPath)) throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" contains empty segments ("//")`);
|
|
51
|
+
const trimmed = urlPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
52
|
+
if (!trimmed) throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" has no route segment`);
|
|
53
|
+
if (!/^[a-zA-Z0-9_\-/]+$/.test(trimmed)) throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" contains unsupported characters`);
|
|
54
|
+
return trimmed;
|
|
66
55
|
}
|
|
67
56
|
function deriveRouteFileName(urlPath) {
|
|
68
|
-
|
|
57
|
+
return `${assertUrlPathShape(urlPath)}.ts`;
|
|
69
58
|
}
|
|
70
59
|
function assertMetadataPathShape(metadataPath) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (/(^|\/)\.\.(\/|$)/.test(metadataPath)) {
|
|
78
|
-
throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" must not contain ".." segments`);
|
|
79
|
-
}
|
|
80
|
-
const trimmed = metadataPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
81
|
-
if (!trimmed) {
|
|
82
|
-
throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" has no route segment`);
|
|
83
|
-
}
|
|
84
|
-
if (!/^[a-zA-Z0-9_\-./]+$/.test(trimmed)) {
|
|
85
|
-
throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" contains unsupported characters`);
|
|
86
|
-
}
|
|
60
|
+
if (!metadataPath.startsWith("/")) throw new Error(`@lovable.dev/mcp-js: metadataPath must start with "/", got "${metadataPath}"`);
|
|
61
|
+
if (/\/{2,}/.test(metadataPath)) throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" contains empty segments ("//")`);
|
|
62
|
+
if (/(^|\/)\.\.(\/|$)/.test(metadataPath)) throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" must not contain ".." segments`);
|
|
63
|
+
const trimmed = metadataPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
64
|
+
if (!trimmed) throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" has no route segment`);
|
|
65
|
+
if (!/^[a-zA-Z0-9_\-./]+$/.test(trimmed)) throw new Error(`@lovable.dev/mcp-js: metadataPath "${metadataPath}" contains unsupported characters`);
|
|
87
66
|
}
|
|
88
67
|
function canonicalUrlPath(urlPath) {
|
|
89
|
-
|
|
68
|
+
return `/${urlPath.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
90
69
|
}
|
|
91
70
|
function relativeImportSpecifier(routeFile, target) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return withoutExt.startsWith(".") ? withoutExt : `./${withoutExt}`;
|
|
71
|
+
const withoutExt = normalizePath(relative(dirname(routeFile), target)).replace(/\.ts$/, "");
|
|
72
|
+
return withoutExt.startsWith(".") ? withoutExt : `./${withoutExt}`;
|
|
95
73
|
}
|
|
96
74
|
function buildRouteSource(route, resourcePath, metadataPath, trustForwardedHost, mcpEntry, projectRoot) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const runtimeOptions = `{ ${optionEntries.join(", ")} }`;
|
|
106
|
-
return `${GENERATED_BANNER}
|
|
75
|
+
const mcpImport = relativeImportSpecifier(route.file, mcpEntry);
|
|
76
|
+
const routeRel = normalizePath(relative(projectRoot, route.file));
|
|
77
|
+
const note = route.spaFallbackNote ? " // ANY: TanStack returns SPA HTML for methods not in `handlers`; the SDK 405s instead.\n" : "";
|
|
78
|
+
const optionEntries = [`resourcePath: "${resourcePath}"`];
|
|
79
|
+
if (metadataPath !== void 0) optionEntries.push(`metadataPath: "${metadataPath}"`);
|
|
80
|
+
if (trustForwardedHost) optionEntries.push("trustForwardedHost: true");
|
|
81
|
+
const runtimeOptions = `{ ${optionEntries.join(", ")} }`;
|
|
82
|
+
return `${GENERATED_BANNER}
|
|
107
83
|
// route: ${route.routeLiteral}
|
|
108
84
|
// emitted to: ${routeRel}
|
|
109
85
|
|
|
@@ -122,210 +98,168 @@ ${note} ANY: ${route.handlerFactory}(mcp, ${runtimeOptions}),
|
|
|
122
98
|
});
|
|
123
99
|
`;
|
|
124
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Writes `content` to `file` if it differs from what's there. Refuses to
|
|
103
|
+
* overwrite files without `GENERATED_BANNER` — the banner is the consent token.
|
|
104
|
+
*/
|
|
125
105
|
function hasGeneratedBanner(content) {
|
|
126
|
-
|
|
106
|
+
return content.replace(/\r\n/g, "\n").startsWith(GENERATED_BANNER);
|
|
127
107
|
}
|
|
128
108
|
function writeIfChanged(file, content) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (existing === content)
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
146
|
-
writeFileSync(file, content, "utf8");
|
|
147
|
-
return true;
|
|
109
|
+
let existing;
|
|
110
|
+
try {
|
|
111
|
+
existing = readFileSync(file, "utf8");
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (!isFileMissing(err)) throw err;
|
|
114
|
+
}
|
|
115
|
+
if (existing !== void 0) {
|
|
116
|
+
if (!hasGeneratedBanner(existing)) throw new Error(`@lovable.dev/mcp-js: refusing to overwrite user-authored route at ${file}. Delete it or move it first.`);
|
|
117
|
+
if (existing === content) return false;
|
|
118
|
+
}
|
|
119
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
120
|
+
writeFileSync(file, content, "utf8");
|
|
121
|
+
return true;
|
|
148
122
|
}
|
|
149
123
|
function fileStartsWithBanner(file) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
124
|
+
try {
|
|
125
|
+
return hasGeneratedBanner(readFileSync(file, "utf8"));
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (isFileMissing(err)) return false;
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
157
130
|
}
|
|
158
131
|
function findAdoptedFiles(routesDir) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
visit(routesDir);
|
|
187
|
-
return adopted;
|
|
132
|
+
const adopted = [];
|
|
133
|
+
const visit = (dir) => {
|
|
134
|
+
let entries;
|
|
135
|
+
try {
|
|
136
|
+
entries = readdirSync(dir);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (isFileMissing(err)) return;
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
const full = join(dir, entry);
|
|
143
|
+
let stat;
|
|
144
|
+
try {
|
|
145
|
+
stat = lstatSync(full);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if (isFileMissing(err)) continue;
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
if (stat.isDirectory()) visit(full);
|
|
151
|
+
else if (entry.endsWith(".ts") && fileStartsWithBanner(full)) adopted.push(full);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
visit(routesDir);
|
|
155
|
+
return adopted;
|
|
188
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Generate the TanStack route(s) that mount the MCP server.
|
|
159
|
+
*
|
|
160
|
+
* Place this in your `vite.config.ts` plugins array. Emits during
|
|
161
|
+
* `configResolved` (so dev-server boot and the TanStack file-router both
|
|
162
|
+
* see the routes on the first request) and re-emits when the MCP entry
|
|
163
|
+
* file appears or disappears during dev.
|
|
164
|
+
*
|
|
165
|
+
* The `.lovable/mcp/manifest.json` snapshot is produced separately by the
|
|
166
|
+
* `lovable-mcp-extract-manifest` CLI (run by a host platform at build/commit
|
|
167
|
+
* time) — not by this plugin.
|
|
168
|
+
*/
|
|
189
169
|
function mcpPlugin(options = {}) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
get mcpEntry() {
|
|
284
|
-
return mcpEntry;
|
|
285
|
-
},
|
|
286
|
-
get urlPath() {
|
|
287
|
-
return urlPath;
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
configResolved(config) {
|
|
291
|
-
projectRoot = config.root;
|
|
292
|
-
mcpEntry = resolve(projectRoot, mcpEntryOption);
|
|
293
|
-
({
|
|
294
|
-
routesDir,
|
|
295
|
-
mcpRouteFile,
|
|
296
|
-
metadataRouteFile,
|
|
297
|
-
listToolsRouteFile,
|
|
298
|
-
invokeToolRouteFile,
|
|
299
|
-
listToolsLiteral,
|
|
300
|
-
invokeToolLiteral
|
|
301
|
-
} = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath));
|
|
302
|
-
regenerate();
|
|
303
|
-
},
|
|
304
|
-
configureServer(server) {
|
|
305
|
-
const onChange = (file) => {
|
|
306
|
-
if (normalizePath(file) === normalizePath(mcpEntry)) {
|
|
307
|
-
regenerate();
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
server.watcher.on("add", onChange);
|
|
311
|
-
server.watcher.on("change", onChange);
|
|
312
|
-
server.watcher.on("unlink", onChange);
|
|
313
|
-
server.watcher.once("close", () => {
|
|
314
|
-
server.watcher.off("add", onChange);
|
|
315
|
-
server.watcher.off("change", onChange);
|
|
316
|
-
server.watcher.off("unlink", onChange);
|
|
317
|
-
});
|
|
318
|
-
},
|
|
319
|
-
buildStart() {
|
|
320
|
-
regenerate();
|
|
321
|
-
}
|
|
322
|
-
};
|
|
170
|
+
const mcpEntryOption = options.mcpEntry ?? "src/lib/mcp/index.ts";
|
|
171
|
+
const routesDirOption = options.routesDir ?? "src/routes";
|
|
172
|
+
const rawUrlPath = options.path ?? "/mcp";
|
|
173
|
+
assertUrlPathShape(rawUrlPath);
|
|
174
|
+
const routeFileName = options.routeFileName ?? deriveRouteFileName(rawUrlPath);
|
|
175
|
+
const urlPath = canonicalUrlPath(rawUrlPath);
|
|
176
|
+
const emitRestRoutes = options.restRoutes !== false;
|
|
177
|
+
const emitProtectedResourceMetadataRoute = options.protectedResourceMetadataRoute !== false;
|
|
178
|
+
if (options.metadataPath !== void 0) {
|
|
179
|
+
if (!emitProtectedResourceMetadataRoute) throw new Error(`@lovable.dev/mcp-js: metadataPath sets where the protected-resource metadata route is served, so it can't be combined with protectedResourceMetadataRoute: false.`);
|
|
180
|
+
assertMetadataPathShape(options.metadataPath);
|
|
181
|
+
}
|
|
182
|
+
const metadataUrlPath = canonicalUrlPath(options.metadataPath ?? "/.well-known/oauth-protected-resource");
|
|
183
|
+
const injectedMetadataPath = emitProtectedResourceMetadataRoute ? metadataUrlPath : void 0;
|
|
184
|
+
const trustForwardedHost = options.trustForwardedHost !== false;
|
|
185
|
+
let projectRoot = process.cwd();
|
|
186
|
+
let mcpEntry = resolve(projectRoot, mcpEntryOption);
|
|
187
|
+
let { routesDir, mcpRouteFile, metadataRouteFile, listToolsRouteFile, invokeToolRouteFile, listToolsLiteral, invokeToolLiteral } = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath);
|
|
188
|
+
const regenerate = () => {
|
|
189
|
+
let mcpEntryExists = true;
|
|
190
|
+
try {
|
|
191
|
+
lstatSync(mcpEntry);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
if (!isFileMissing(err)) throw err;
|
|
194
|
+
mcpEntryExists = false;
|
|
195
|
+
}
|
|
196
|
+
const expected = /* @__PURE__ */ new Set();
|
|
197
|
+
if (mcpEntryExists) {
|
|
198
|
+
const routes = [{
|
|
199
|
+
file: mcpRouteFile,
|
|
200
|
+
routeLiteral: urlPath,
|
|
201
|
+
handlerFactory: "createTanStackMcpHandler"
|
|
202
|
+
}];
|
|
203
|
+
if (emitProtectedResourceMetadataRoute) routes.push({
|
|
204
|
+
file: metadataRouteFile,
|
|
205
|
+
routeLiteral: metadataUrlPath,
|
|
206
|
+
handlerFactory: "createTanStackOAuthProtectedResourceMetadataHandler"
|
|
207
|
+
});
|
|
208
|
+
if (emitRestRoutes) routes.push({
|
|
209
|
+
file: listToolsRouteFile,
|
|
210
|
+
routeLiteral: listToolsLiteral,
|
|
211
|
+
handlerFactory: "createTanStackListToolsHandler",
|
|
212
|
+
spaFallbackNote: true
|
|
213
|
+
}, {
|
|
214
|
+
file: invokeToolRouteFile,
|
|
215
|
+
routeLiteral: invokeToolLiteral,
|
|
216
|
+
handlerFactory: "createTanStackInvokeToolHandler",
|
|
217
|
+
spaFallbackNote: true
|
|
218
|
+
});
|
|
219
|
+
for (const route of routes) {
|
|
220
|
+
expected.add(route.file);
|
|
221
|
+
writeIfChanged(route.file, buildRouteSource(route, urlPath, injectedMetadataPath, trustForwardedHost, mcpEntry, projectRoot));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
for (const file of findAdoptedFiles(routesDir)) if (!expected.has(file)) try {
|
|
225
|
+
unlinkSync(file);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
if (!isFileMissing(err)) throw err;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
return {
|
|
231
|
+
name: "@lovable.dev/mcp-js",
|
|
232
|
+
api: {
|
|
233
|
+
get mcpEntry() {
|
|
234
|
+
return mcpEntry;
|
|
235
|
+
},
|
|
236
|
+
get urlPath() {
|
|
237
|
+
return urlPath;
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
configResolved(config) {
|
|
241
|
+
projectRoot = config.root;
|
|
242
|
+
mcpEntry = resolve(projectRoot, mcpEntryOption);
|
|
243
|
+
({routesDir, mcpRouteFile, metadataRouteFile, listToolsRouteFile, invokeToolRouteFile, listToolsLiteral, invokeToolLiteral} = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath));
|
|
244
|
+
regenerate();
|
|
245
|
+
},
|
|
246
|
+
configureServer(server) {
|
|
247
|
+
const onChange = (file) => {
|
|
248
|
+
if (normalizePath(file) === normalizePath(mcpEntry)) regenerate();
|
|
249
|
+
};
|
|
250
|
+
server.watcher.on("add", onChange);
|
|
251
|
+
server.watcher.on("change", onChange);
|
|
252
|
+
server.watcher.on("unlink", onChange);
|
|
253
|
+
server.watcher.once("close", () => {
|
|
254
|
+
server.watcher.off("add", onChange);
|
|
255
|
+
server.watcher.off("change", onChange);
|
|
256
|
+
server.watcher.off("unlink", onChange);
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
buildStart() {
|
|
260
|
+
regenerate();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
323
263
|
}
|
|
324
|
-
|
|
325
|
-
export {
|
|
326
|
-
assertMetadataPathShape,
|
|
327
|
-
assertUrlPathShape,
|
|
328
|
-
vite_default as default,
|
|
329
|
-
deriveRouteFileName,
|
|
330
|
-
mcpPlugin
|
|
331
|
-
};
|
|
264
|
+
//#endregion
|
|
265
|
+
export { assertMetadataPathShape, assertUrlPathShape, mcpPlugin as default, mcpPlugin, deriveRouteFileName };
|