@lovable.dev/mcp-js 0.13.0 → 0.14.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/README.md +3 -1
- package/dist/{chunk-SIITHGUD.js → chunk-NOFPRSSI.js} +1 -1
- package/dist/cli/extract-manifest.cjs +1 -1
- package/dist/cli/extract-manifest.js +1 -1
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/vite.cjs +36 -22
- package/dist/stacks/tanstack/vite.d.cts +9 -7
- package/dist/stacks/tanstack/vite.d.ts +9 -7
- package/dist/stacks/tanstack/vite.js +36 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,9 @@ That's the whole authoring surface. No imperative server construction, no route
|
|
|
49
49
|
| `GET /.mcp/list-tools` | `src/routes/[.mcp]/list-tools.ts` | Tool catalog with JSON Schemas |
|
|
50
50
|
| `POST /.mcp/invoke-tool/<tool>` | `src/routes/[.mcp]/invoke-tool/$tool.ts` | REST dispatcher; one handler call per tool |
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
The table shows the default `path: "/mcp"`. The REST companions co-locate under the parent of the configured `path`, so `mcpPlugin({ path: "/api/public/mcp" })` emits them at `/api/public/.mcp/list-tools` and `/api/public/.mcp/invoke-tool/<tool>` (file: `src/routes/api/public/[.mcp]/...`). Pass `restRoutes: false` to drop them.
|
|
53
|
+
|
|
54
|
+
MCP (`POST <path>`, `/mcp` by default) is the public wire format clients speak directly. REST (the `.mcp/*` companions, co-located under the `path` parent) is internal RPC — only an upstream MCP proxy calls it; never a browser or a hand-rolled client. All runtime routes import the same `defineMcp` result, so they stay in sync on which tools exist and which auth policy protects them. If `defineMcp({ auth: ... })` is omitted, the handlers stay unauthenticated; if OAuth auth is configured, MCP and REST both require the proxy/client to pass `Authorization: Bearer <token>`. CORS and rate limiting still belong at the app host or edge.
|
|
53
55
|
|
|
54
56
|
The OAuth metadata route is emitted by default and returns `404` until OAuth auth is configured. Disable it with `mcpPlugin({ protectedResourceMetadataRoute: false })` only if the app owns `/.well-known/oauth-protected-resource` itself.
|
|
55
57
|
|
|
@@ -56,20 +56,28 @@ function wellKnownRouteFile(routesDir, urlPath) {
|
|
|
56
56
|
});
|
|
57
57
|
return (0, import_node_path.resolve)(routesDir, ...fileSegments);
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function resolveRestRoutes(routesDir, mcpUrlPath) {
|
|
60
|
+
const parentSegments = mcpUrlPath.replace(/^\/+/, "").replace(/\/+$/, "").split("/").slice(0, -1);
|
|
61
|
+
const restDir = (0, import_node_path.resolve)(routesDir, ...parentSegments, "[.mcp]");
|
|
62
|
+
const restUrlBase = `/${[...parentSegments, ".mcp"].join("/")}`;
|
|
63
|
+
return {
|
|
64
|
+
listToolsRouteFile: (0, import_node_path.resolve)(restDir, "list-tools.ts"),
|
|
65
|
+
invokeToolRouteFile: (0, import_node_path.resolve)(restDir, "invoke-tool", "$tool.ts"),
|
|
66
|
+
listToolsLiteral: `${restUrlBase}/list-tools`,
|
|
67
|
+
invokeToolLiteral: `${restUrlBase}/invoke-tool/$tool`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function resolveAllRoutes(projectRoot, routesDirOption, routeFileName, mcpUrlPath, metadataUrlPath) {
|
|
60
71
|
const routesDir = (0, import_node_path.resolve)(projectRoot, routesDirOption);
|
|
61
72
|
assertContains(projectRoot, routesDir, `routesDir "${routesDirOption}"`);
|
|
62
73
|
const mcpRouteFile = (0, import_node_path.resolve)(routesDir, routeFileName);
|
|
63
74
|
assertContains(routesDir, mcpRouteFile, `routeFileName "${routeFileName}"`);
|
|
64
75
|
const metadataRouteFile = wellKnownRouteFile(routesDir, metadataUrlPath);
|
|
65
76
|
assertContains(routesDir, metadataRouteFile, `metadataPath "${metadataUrlPath}"`);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
listToolsRouteFile: (0, import_node_path.resolve)(routesDir, "[.mcp]", "list-tools.ts"),
|
|
71
|
-
invokeToolRouteFile: (0, import_node_path.resolve)(routesDir, "[.mcp]", "invoke-tool", "$tool.ts")
|
|
72
|
-
};
|
|
77
|
+
const rest = resolveRestRoutes(routesDir, mcpUrlPath);
|
|
78
|
+
assertContains(routesDir, rest.listToolsRouteFile, `REST routes for path "${mcpUrlPath}"`);
|
|
79
|
+
assertContains(routesDir, rest.invokeToolRouteFile, `REST routes for path "${mcpUrlPath}"`);
|
|
80
|
+
return { routesDir, mcpRouteFile, metadataRouteFile, ...rest };
|
|
73
81
|
}
|
|
74
82
|
function assertUrlPathShape(urlPath) {
|
|
75
83
|
if (!urlPath.startsWith("/")) {
|
|
@@ -231,12 +239,15 @@ function mcpPlugin(options = {}) {
|
|
|
231
239
|
const trustForwardedHost = options.trustForwardedHost !== false;
|
|
232
240
|
let projectRoot = process.cwd();
|
|
233
241
|
let mcpEntry = (0, import_node_path.resolve)(projectRoot, mcpEntryOption);
|
|
234
|
-
let {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
242
|
+
let {
|
|
243
|
+
routesDir,
|
|
244
|
+
mcpRouteFile,
|
|
245
|
+
metadataRouteFile,
|
|
246
|
+
listToolsRouteFile,
|
|
247
|
+
invokeToolRouteFile,
|
|
248
|
+
listToolsLiteral,
|
|
249
|
+
invokeToolLiteral
|
|
250
|
+
} = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath);
|
|
240
251
|
const regenerate = () => {
|
|
241
252
|
let mcpEntryExists = true;
|
|
242
253
|
try {
|
|
@@ -262,13 +273,13 @@ function mcpPlugin(options = {}) {
|
|
|
262
273
|
routes.push(
|
|
263
274
|
{
|
|
264
275
|
file: listToolsRouteFile,
|
|
265
|
-
routeLiteral:
|
|
276
|
+
routeLiteral: listToolsLiteral,
|
|
266
277
|
handlerFactory: "createTanStackListToolsHandler",
|
|
267
278
|
spaFallbackNote: true
|
|
268
279
|
},
|
|
269
280
|
{
|
|
270
281
|
file: invokeToolRouteFile,
|
|
271
|
-
routeLiteral:
|
|
282
|
+
routeLiteral: invokeToolLiteral,
|
|
272
283
|
handlerFactory: "createTanStackInvokeToolHandler",
|
|
273
284
|
spaFallbackNote: true
|
|
274
285
|
}
|
|
@@ -310,12 +321,15 @@ function mcpPlugin(options = {}) {
|
|
|
310
321
|
configResolved(config) {
|
|
311
322
|
projectRoot = config.root;
|
|
312
323
|
mcpEntry = (0, import_node_path.resolve)(projectRoot, mcpEntryOption);
|
|
313
|
-
({
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
324
|
+
({
|
|
325
|
+
routesDir,
|
|
326
|
+
mcpRouteFile,
|
|
327
|
+
metadataRouteFile,
|
|
328
|
+
listToolsRouteFile,
|
|
329
|
+
invokeToolRouteFile,
|
|
330
|
+
listToolsLiteral,
|
|
331
|
+
invokeToolLiteral
|
|
332
|
+
} = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath));
|
|
319
333
|
regenerate();
|
|
320
334
|
},
|
|
321
335
|
configureServer(server) {
|
|
@@ -16,13 +16,15 @@ interface McpPluginOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
routesDir?: string;
|
|
18
18
|
/**
|
|
19
|
-
* Public URL path for the MCP-protocol route
|
|
19
|
+
* Public URL path for the MCP-protocol route.
|
|
20
20
|
*
|
|
21
|
-
* The REST companion routes
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* The REST companion routes co-locate with this path under a `.mcp`
|
|
22
|
+
* sub-segment of the same parent prefix: `/mcp` keeps them at
|
|
23
|
+
* `/.mcp/list-tools` and `/.mcp/invoke-tool/$tool`, while
|
|
24
|
+
* `/api/public/mcp` moves them to `/api/public/.mcp/*`. The `.mcp`
|
|
25
|
+
* namespace keeps them from colliding with user-defined URLs without
|
|
26
|
+
* anchoring them at the root. Set `restRoutes: false` to drop the
|
|
27
|
+
* companions entirely.
|
|
26
28
|
*
|
|
27
29
|
* Must start with `/` and use only `/mcp`-style segments; anything more
|
|
28
30
|
* exotic doesn't map cleanly to a TanStack file name.
|
|
@@ -37,7 +39,7 @@ interface McpPluginOptions {
|
|
|
37
39
|
routeFileName?: string;
|
|
38
40
|
/**
|
|
39
41
|
* Set to `false` to skip emitting the REST companion routes at
|
|
40
|
-
*
|
|
42
|
+
* `<path-parent>/.mcp/list-tools` and `<path-parent>/.mcp/<tool>`. Set
|
|
41
43
|
* `protectedResourceMetadataRoute: false` as well if you want the MCP
|
|
42
44
|
* protocol route to be the only emitted surface.
|
|
43
45
|
* @default true
|
|
@@ -16,13 +16,15 @@ interface McpPluginOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
routesDir?: string;
|
|
18
18
|
/**
|
|
19
|
-
* Public URL path for the MCP-protocol route
|
|
19
|
+
* Public URL path for the MCP-protocol route.
|
|
20
20
|
*
|
|
21
|
-
* The REST companion routes
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* The REST companion routes co-locate with this path under a `.mcp`
|
|
22
|
+
* sub-segment of the same parent prefix: `/mcp` keeps them at
|
|
23
|
+
* `/.mcp/list-tools` and `/.mcp/invoke-tool/$tool`, while
|
|
24
|
+
* `/api/public/mcp` moves them to `/api/public/.mcp/*`. The `.mcp`
|
|
25
|
+
* namespace keeps them from colliding with user-defined URLs without
|
|
26
|
+
* anchoring them at the root. Set `restRoutes: false` to drop the
|
|
27
|
+
* companions entirely.
|
|
26
28
|
*
|
|
27
29
|
* Must start with `/` and use only `/mcp`-style segments; anything more
|
|
28
30
|
* exotic doesn't map cleanly to a TanStack file name.
|
|
@@ -37,7 +39,7 @@ interface McpPluginOptions {
|
|
|
37
39
|
routeFileName?: string;
|
|
38
40
|
/**
|
|
39
41
|
* Set to `false` to skip emitting the REST companion routes at
|
|
40
|
-
*
|
|
42
|
+
* `<path-parent>/.mcp/list-tools` and `<path-parent>/.mcp/<tool>`. Set
|
|
41
43
|
* `protectedResourceMetadataRoute: false` as well if you want the MCP
|
|
42
44
|
* protocol route to be the only emitted surface.
|
|
43
45
|
* @default true
|
|
@@ -25,20 +25,28 @@ function wellKnownRouteFile(routesDir, urlPath) {
|
|
|
25
25
|
});
|
|
26
26
|
return resolve(routesDir, ...fileSegments);
|
|
27
27
|
}
|
|
28
|
-
function
|
|
28
|
+
function resolveRestRoutes(routesDir, mcpUrlPath) {
|
|
29
|
+
const parentSegments = mcpUrlPath.replace(/^\/+/, "").replace(/\/+$/, "").split("/").slice(0, -1);
|
|
30
|
+
const restDir = resolve(routesDir, ...parentSegments, "[.mcp]");
|
|
31
|
+
const restUrlBase = `/${[...parentSegments, ".mcp"].join("/")}`;
|
|
32
|
+
return {
|
|
33
|
+
listToolsRouteFile: resolve(restDir, "list-tools.ts"),
|
|
34
|
+
invokeToolRouteFile: resolve(restDir, "invoke-tool", "$tool.ts"),
|
|
35
|
+
listToolsLiteral: `${restUrlBase}/list-tools`,
|
|
36
|
+
invokeToolLiteral: `${restUrlBase}/invoke-tool/$tool`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function resolveAllRoutes(projectRoot, routesDirOption, routeFileName, mcpUrlPath, metadataUrlPath) {
|
|
29
40
|
const routesDir = resolve(projectRoot, routesDirOption);
|
|
30
41
|
assertContains(projectRoot, routesDir, `routesDir "${routesDirOption}"`);
|
|
31
42
|
const mcpRouteFile = resolve(routesDir, routeFileName);
|
|
32
43
|
assertContains(routesDir, mcpRouteFile, `routeFileName "${routeFileName}"`);
|
|
33
44
|
const metadataRouteFile = wellKnownRouteFile(routesDir, metadataUrlPath);
|
|
34
45
|
assertContains(routesDir, metadataRouteFile, `metadataPath "${metadataUrlPath}"`);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
listToolsRouteFile: resolve(routesDir, "[.mcp]", "list-tools.ts"),
|
|
40
|
-
invokeToolRouteFile: resolve(routesDir, "[.mcp]", "invoke-tool", "$tool.ts")
|
|
41
|
-
};
|
|
46
|
+
const rest = resolveRestRoutes(routesDir, mcpUrlPath);
|
|
47
|
+
assertContains(routesDir, rest.listToolsRouteFile, `REST routes for path "${mcpUrlPath}"`);
|
|
48
|
+
assertContains(routesDir, rest.invokeToolRouteFile, `REST routes for path "${mcpUrlPath}"`);
|
|
49
|
+
return { routesDir, mcpRouteFile, metadataRouteFile, ...rest };
|
|
42
50
|
}
|
|
43
51
|
function assertUrlPathShape(urlPath) {
|
|
44
52
|
if (!urlPath.startsWith("/")) {
|
|
@@ -200,12 +208,15 @@ function mcpPlugin(options = {}) {
|
|
|
200
208
|
const trustForwardedHost = options.trustForwardedHost !== false;
|
|
201
209
|
let projectRoot = process.cwd();
|
|
202
210
|
let mcpEntry = resolve(projectRoot, mcpEntryOption);
|
|
203
|
-
let {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
let {
|
|
212
|
+
routesDir,
|
|
213
|
+
mcpRouteFile,
|
|
214
|
+
metadataRouteFile,
|
|
215
|
+
listToolsRouteFile,
|
|
216
|
+
invokeToolRouteFile,
|
|
217
|
+
listToolsLiteral,
|
|
218
|
+
invokeToolLiteral
|
|
219
|
+
} = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath);
|
|
209
220
|
const regenerate = () => {
|
|
210
221
|
let mcpEntryExists = true;
|
|
211
222
|
try {
|
|
@@ -231,13 +242,13 @@ function mcpPlugin(options = {}) {
|
|
|
231
242
|
routes.push(
|
|
232
243
|
{
|
|
233
244
|
file: listToolsRouteFile,
|
|
234
|
-
routeLiteral:
|
|
245
|
+
routeLiteral: listToolsLiteral,
|
|
235
246
|
handlerFactory: "createTanStackListToolsHandler",
|
|
236
247
|
spaFallbackNote: true
|
|
237
248
|
},
|
|
238
249
|
{
|
|
239
250
|
file: invokeToolRouteFile,
|
|
240
|
-
routeLiteral:
|
|
251
|
+
routeLiteral: invokeToolLiteral,
|
|
241
252
|
handlerFactory: "createTanStackInvokeToolHandler",
|
|
242
253
|
spaFallbackNote: true
|
|
243
254
|
}
|
|
@@ -279,12 +290,15 @@ function mcpPlugin(options = {}) {
|
|
|
279
290
|
configResolved(config) {
|
|
280
291
|
projectRoot = config.root;
|
|
281
292
|
mcpEntry = resolve(projectRoot, mcpEntryOption);
|
|
282
|
-
({
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
293
|
+
({
|
|
294
|
+
routesDir,
|
|
295
|
+
mcpRouteFile,
|
|
296
|
+
metadataRouteFile,
|
|
297
|
+
listToolsRouteFile,
|
|
298
|
+
invokeToolRouteFile,
|
|
299
|
+
listToolsLiteral,
|
|
300
|
+
invokeToolLiteral
|
|
301
|
+
} = resolveAllRoutes(projectRoot, routesDirOption, routeFileName, urlPath, metadataUrlPath));
|
|
288
302
|
regenerate();
|
|
289
303
|
},
|
|
290
304
|
configureServer(server) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/mcp-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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": {
|