@lovable.dev/mcp-js 0.5.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/LICENSE +21 -0
- package/README.md +386 -0
- package/dist/authorize-DpTEIFvs.d.ts +9 -0
- package/dist/chunk-6DXGZZA4.js +6 -0
- package/dist/chunk-DDF63QWG.js +80 -0
- package/dist/chunk-GLG5RZGE.js +66 -0
- package/dist/chunk-MA5H6PSF.js +46 -0
- package/dist/chunk-QA3FWDUV.js +40 -0
- package/dist/chunk-VD6CS7Y6.js +144 -0
- package/dist/chunk-XEDRJFAR.js +371 -0
- package/dist/index.cjs +271 -0
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +172 -0
- package/dist/protocols/mcp/index.cjs +505 -0
- package/dist/protocols/mcp/index.d.cts +14 -0
- package/dist/protocols/mcp/index.d.ts +14 -0
- package/dist/protocols/mcp/index.js +10 -0
- package/dist/protocols/oauth-metadata.cjs +390 -0
- package/dist/protocols/oauth-metadata.d.cts +8 -0
- package/dist/protocols/oauth-metadata.d.ts +8 -0
- package/dist/protocols/oauth-metadata.js +9 -0
- package/dist/protocols/rest/index.cjs +599 -0
- package/dist/protocols/rest/index.d.cts +11 -0
- package/dist/protocols/rest/index.d.ts +11 -0
- package/dist/protocols/rest/index.js +12 -0
- package/dist/stacks/tanstack/index.cjs +743 -0
- package/dist/stacks/tanstack/index.d.cts +38 -0
- package/dist/stacks/tanstack/index.d.ts +38 -0
- package/dist/stacks/tanstack/index.js +38 -0
- package/dist/stacks/tanstack/vite.cjs +291 -0
- package/dist/stacks/tanstack/vite.d.cts +64 -0
- package/dist/stacks/tanstack/vite.d.ts +64 -0
- package/dist/stacks/tanstack/vite.js +263 -0
- package/dist/types-zMlr1mOK.d.ts +270 -0
- package/package.json +101 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { M as McpRuntimeOptions } from '../../authorize-DpTEIFvs.js';
|
|
2
|
+
import { d as McpDefinition } from '../../types-zMlr1mOK.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
interface TanStackRouteCtx {
|
|
6
|
+
request: Request;
|
|
7
|
+
}
|
|
8
|
+
interface TanStackParamRouteCtx<TParam extends string> {
|
|
9
|
+
request: Request;
|
|
10
|
+
params: Record<TParam, string>;
|
|
11
|
+
}
|
|
12
|
+
type TanStackMcpHandler = (ctx: TanStackRouteCtx) => Promise<Response>;
|
|
13
|
+
type TanStackListToolsHandler = (ctx: TanStackRouteCtx) => Promise<Response>;
|
|
14
|
+
type TanStackInvokeToolHandler = (ctx: TanStackParamRouteCtx<"tool">) => Promise<Response>;
|
|
15
|
+
type TanStackOAuthProtectedResourceMetadataHandler = (ctx: TanStackRouteCtx) => Promise<Response>;
|
|
16
|
+
/**
|
|
17
|
+
* Build a TanStack Start handler for the MCP protocol endpoint
|
|
18
|
+
* (`POST /mcp`). Assignable directly to a route's `ANY` handler.
|
|
19
|
+
*/
|
|
20
|
+
declare function createTanStackMcpHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackMcpHandler;
|
|
21
|
+
/**
|
|
22
|
+
* Build a TanStack Start handler for `GET /.mcp/list-tools`. Returns the
|
|
23
|
+
* tool catalog (name, description, annotations, JSON Schemas) as JSON.
|
|
24
|
+
*/
|
|
25
|
+
declare function createTanStackListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackListToolsHandler;
|
|
26
|
+
/**
|
|
27
|
+
* Build a TanStack Start handler for `POST /.mcp/invoke-tool/$tool`.
|
|
28
|
+
* Reads the tool name from `params.tool`, validates the JSON body, and
|
|
29
|
+
* dispatches to the matching `defineTool` handler.
|
|
30
|
+
*/
|
|
31
|
+
declare function createTanStackInvokeToolHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackInvokeToolHandler;
|
|
32
|
+
/**
|
|
33
|
+
* Build a TanStack Start handler for `GET /.well-known/oauth-protected-resource`.
|
|
34
|
+
* When the MCP definition has no OAuth auth config the handler returns 404.
|
|
35
|
+
*/
|
|
36
|
+
declare function createTanStackOAuthProtectedResourceMetadataHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackOAuthProtectedResourceMetadataHandler;
|
|
37
|
+
|
|
38
|
+
export { type TanStackInvokeToolHandler, type TanStackListToolsHandler, type TanStackMcpHandler, type TanStackOAuthProtectedResourceMetadataHandler, createTanStackInvokeToolHandler, createTanStackListToolsHandler, createTanStackMcpHandler, createTanStackOAuthProtectedResourceMetadataHandler };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { M as McpRuntimeOptions } from '../../authorize-DpTEIFvs.js';
|
|
2
|
+
import { d as McpDefinition } from '../../types-zMlr1mOK.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
interface TanStackRouteCtx {
|
|
6
|
+
request: Request;
|
|
7
|
+
}
|
|
8
|
+
interface TanStackParamRouteCtx<TParam extends string> {
|
|
9
|
+
request: Request;
|
|
10
|
+
params: Record<TParam, string>;
|
|
11
|
+
}
|
|
12
|
+
type TanStackMcpHandler = (ctx: TanStackRouteCtx) => Promise<Response>;
|
|
13
|
+
type TanStackListToolsHandler = (ctx: TanStackRouteCtx) => Promise<Response>;
|
|
14
|
+
type TanStackInvokeToolHandler = (ctx: TanStackParamRouteCtx<"tool">) => Promise<Response>;
|
|
15
|
+
type TanStackOAuthProtectedResourceMetadataHandler = (ctx: TanStackRouteCtx) => Promise<Response>;
|
|
16
|
+
/**
|
|
17
|
+
* Build a TanStack Start handler for the MCP protocol endpoint
|
|
18
|
+
* (`POST /mcp`). Assignable directly to a route's `ANY` handler.
|
|
19
|
+
*/
|
|
20
|
+
declare function createTanStackMcpHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackMcpHandler;
|
|
21
|
+
/**
|
|
22
|
+
* Build a TanStack Start handler for `GET /.mcp/list-tools`. Returns the
|
|
23
|
+
* tool catalog (name, description, annotations, JSON Schemas) as JSON.
|
|
24
|
+
*/
|
|
25
|
+
declare function createTanStackListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackListToolsHandler;
|
|
26
|
+
/**
|
|
27
|
+
* Build a TanStack Start handler for `POST /.mcp/invoke-tool/$tool`.
|
|
28
|
+
* Reads the tool name from `params.tool`, validates the JSON body, and
|
|
29
|
+
* dispatches to the matching `defineTool` handler.
|
|
30
|
+
*/
|
|
31
|
+
declare function createTanStackInvokeToolHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackInvokeToolHandler;
|
|
32
|
+
/**
|
|
33
|
+
* Build a TanStack Start handler for `GET /.well-known/oauth-protected-resource`.
|
|
34
|
+
* When the MCP definition has no OAuth auth config the handler returns 404.
|
|
35
|
+
*/
|
|
36
|
+
declare function createTanStackOAuthProtectedResourceMetadataHandler(mcp: McpDefinition, options?: McpRuntimeOptions): TanStackOAuthProtectedResourceMetadataHandler;
|
|
37
|
+
|
|
38
|
+
export { type TanStackInvokeToolHandler, type TanStackListToolsHandler, type TanStackMcpHandler, type TanStackOAuthProtectedResourceMetadataHandler, createTanStackInvokeToolHandler, createTanStackListToolsHandler, createTanStackMcpHandler, createTanStackOAuthProtectedResourceMetadataHandler };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMcpProtocolHandler
|
|
3
|
+
} from "../../chunk-GLG5RZGE.js";
|
|
4
|
+
import {
|
|
5
|
+
createOAuthProtectedResourceMetadataHandler
|
|
6
|
+
} from "../../chunk-DDF63QWG.js";
|
|
7
|
+
import {
|
|
8
|
+
createInvokeToolHandler,
|
|
9
|
+
createListToolsHandler
|
|
10
|
+
} from "../../chunk-VD6CS7Y6.js";
|
|
11
|
+
import "../../chunk-MA5H6PSF.js";
|
|
12
|
+
import "../../chunk-XEDRJFAR.js";
|
|
13
|
+
import "../../chunk-QA3FWDUV.js";
|
|
14
|
+
import "../../chunk-6DXGZZA4.js";
|
|
15
|
+
|
|
16
|
+
// src/stacks/tanstack/handlers.ts
|
|
17
|
+
function createTanStackMcpHandler(mcp, options = {}) {
|
|
18
|
+
const handler = createMcpProtocolHandler(mcp, options);
|
|
19
|
+
return ({ request }) => handler(request);
|
|
20
|
+
}
|
|
21
|
+
function createTanStackListToolsHandler(mcp, options = {}) {
|
|
22
|
+
const handler = createListToolsHandler(mcp, options);
|
|
23
|
+
return ({ request }) => handler(request);
|
|
24
|
+
}
|
|
25
|
+
function createTanStackInvokeToolHandler(mcp, options = {}) {
|
|
26
|
+
const handler = createInvokeToolHandler(mcp, options);
|
|
27
|
+
return ({ request, params }) => handler(request, params.tool);
|
|
28
|
+
}
|
|
29
|
+
function createTanStackOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
30
|
+
const handler = createOAuthProtectedResourceMetadataHandler(mcp, options);
|
|
31
|
+
return ({ request }) => handler(request);
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
createTanStackInvokeToolHandler,
|
|
35
|
+
createTanStackListToolsHandler,
|
|
36
|
+
createTanStackMcpHandler,
|
|
37
|
+
createTanStackOAuthProtectedResourceMetadataHandler
|
|
38
|
+
};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/stacks/tanstack/vite.ts
|
|
21
|
+
var vite_exports = {};
|
|
22
|
+
__export(vite_exports, {
|
|
23
|
+
assertUrlPathShape: () => assertUrlPathShape,
|
|
24
|
+
default: () => vite_default,
|
|
25
|
+
deriveRouteFileName: () => deriveRouteFileName,
|
|
26
|
+
mcpPlugin: () => mcpPlugin
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(vite_exports);
|
|
29
|
+
var import_node_fs = require("fs");
|
|
30
|
+
var import_node_path = require("path");
|
|
31
|
+
|
|
32
|
+
// src/auth/metadata-path.ts
|
|
33
|
+
var OAUTH_PROTECTED_RESOURCE_METADATA_PATH = "/.well-known/oauth-protected-resource";
|
|
34
|
+
|
|
35
|
+
// src/stacks/tanstack/vite.ts
|
|
36
|
+
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.";
|
|
37
|
+
function isFileMissing(err) {
|
|
38
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
39
|
+
}
|
|
40
|
+
function normalizePath(p) {
|
|
41
|
+
return p.split(import_node_path.sep).join("/");
|
|
42
|
+
}
|
|
43
|
+
function assertContains(parent, child, label) {
|
|
44
|
+
if (child !== parent && !child.startsWith(parent + import_node_path.sep)) {
|
|
45
|
+
throw new Error(`@lovable.dev/mcp-js: ${label} must resolve under ${parent}, got ${child}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function wellKnownRouteFile(routesDir, urlPath) {
|
|
49
|
+
const segments = urlPath.replace(/^\//, "").split("/");
|
|
50
|
+
const fileSegments = segments.map((segment, index) => {
|
|
51
|
+
const escaped = segment.startsWith(".") ? `[${segment}]` : segment;
|
|
52
|
+
return index === segments.length - 1 ? `${escaped}.ts` : escaped;
|
|
53
|
+
});
|
|
54
|
+
return (0, import_node_path.resolve)(routesDir, ...fileSegments);
|
|
55
|
+
}
|
|
56
|
+
function resolveAllRoutes(projectRoot, routesDirOption, routeFileName) {
|
|
57
|
+
const routesDir = (0, import_node_path.resolve)(projectRoot, routesDirOption);
|
|
58
|
+
assertContains(projectRoot, routesDir, `routesDir "${routesDirOption}"`);
|
|
59
|
+
const mcpRouteFile = (0, import_node_path.resolve)(routesDir, routeFileName);
|
|
60
|
+
assertContains(routesDir, mcpRouteFile, `routeFileName "${routeFileName}"`);
|
|
61
|
+
return {
|
|
62
|
+
routesDir,
|
|
63
|
+
mcpRouteFile,
|
|
64
|
+
metadataRouteFile: wellKnownRouteFile(routesDir, OAUTH_PROTECTED_RESOURCE_METADATA_PATH),
|
|
65
|
+
listToolsRouteFile: (0, import_node_path.resolve)(routesDir, "[.mcp]", "list-tools.ts"),
|
|
66
|
+
invokeToolRouteFile: (0, import_node_path.resolve)(routesDir, "[.mcp]", "invoke-tool", "$tool.ts")
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function assertUrlPathShape(urlPath) {
|
|
70
|
+
if (!urlPath.startsWith("/")) {
|
|
71
|
+
throw new Error(`@lovable.dev/mcp-js: path must start with "/", got "${urlPath}"`);
|
|
72
|
+
}
|
|
73
|
+
if (/\/{2,}/.test(urlPath)) {
|
|
74
|
+
throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" contains empty segments ("//")`);
|
|
75
|
+
}
|
|
76
|
+
const trimmed = urlPath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
77
|
+
if (!trimmed) {
|
|
78
|
+
throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" has no route segment`);
|
|
79
|
+
}
|
|
80
|
+
if (!/^[a-zA-Z0-9_\-/]+$/.test(trimmed)) {
|
|
81
|
+
throw new Error(`@lovable.dev/mcp-js: path "${urlPath}" contains unsupported characters`);
|
|
82
|
+
}
|
|
83
|
+
return trimmed;
|
|
84
|
+
}
|
|
85
|
+
function deriveRouteFileName(urlPath) {
|
|
86
|
+
return `${assertUrlPathShape(urlPath)}.ts`;
|
|
87
|
+
}
|
|
88
|
+
function canonicalUrlPath(urlPath) {
|
|
89
|
+
return `/${urlPath.replace(/^\/+/, "").replace(/\/+$/, "")}`;
|
|
90
|
+
}
|
|
91
|
+
function relativeImportSpecifier(routeFile, target) {
|
|
92
|
+
const rel = normalizePath((0, import_node_path.relative)((0, import_node_path.dirname)(routeFile), target));
|
|
93
|
+
const withoutExt = rel.replace(/\.ts$/, "");
|
|
94
|
+
return withoutExt.startsWith(".") ? withoutExt : `./${withoutExt}`;
|
|
95
|
+
}
|
|
96
|
+
function buildRouteSource(route, resourcePath, mcpEntry, projectRoot) {
|
|
97
|
+
const mcpImport = relativeImportSpecifier(route.file, mcpEntry);
|
|
98
|
+
const routeRel = normalizePath((0, import_node_path.relative)(projectRoot, route.file));
|
|
99
|
+
const note = route.spaFallbackNote ? " // ANY: TanStack returns SPA HTML for methods not in `handlers`; the SDK 405s instead.\n" : "";
|
|
100
|
+
return `${GENERATED_BANNER}
|
|
101
|
+
// route: ${route.routeLiteral}
|
|
102
|
+
// emitted to: ${routeRel}
|
|
103
|
+
|
|
104
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
105
|
+
|
|
106
|
+
import { ${route.handlerFactory} } from "@lovable.dev/mcp-js/stacks/tanstack";
|
|
107
|
+
|
|
108
|
+
import mcp from "${mcpImport}";
|
|
109
|
+
|
|
110
|
+
export const Route = createFileRoute("${route.routeLiteral}")({
|
|
111
|
+
server: {
|
|
112
|
+
handlers: {
|
|
113
|
+
${note} ANY: ${route.handlerFactory}(mcp, { resourcePath: "${resourcePath}" }),
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
function hasGeneratedBanner(content) {
|
|
120
|
+
return content.replace(/\r\n/g, "\n").startsWith(GENERATED_BANNER);
|
|
121
|
+
}
|
|
122
|
+
function writeIfChanged(file, content) {
|
|
123
|
+
let existing;
|
|
124
|
+
try {
|
|
125
|
+
existing = (0, import_node_fs.readFileSync)(file, "utf8");
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (!isFileMissing(err))
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
if (existing !== void 0) {
|
|
131
|
+
if (!hasGeneratedBanner(existing)) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`@lovable.dev/mcp-js: refusing to overwrite user-authored route at ${file}. Delete it or move it first.`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (existing === content)
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(file), { recursive: true });
|
|
140
|
+
(0, import_node_fs.writeFileSync)(file, content, "utf8");
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
function fileStartsWithBanner(file) {
|
|
144
|
+
try {
|
|
145
|
+
return hasGeneratedBanner((0, import_node_fs.readFileSync)(file, "utf8"));
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if (isFileMissing(err))
|
|
148
|
+
return false;
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function findAdoptedFiles(routesDir) {
|
|
153
|
+
const adopted = [];
|
|
154
|
+
const visit = (dir) => {
|
|
155
|
+
let entries;
|
|
156
|
+
try {
|
|
157
|
+
entries = (0, import_node_fs.readdirSync)(dir);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (isFileMissing(err))
|
|
160
|
+
return;
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const full = (0, import_node_path.join)(dir, entry);
|
|
165
|
+
let stat;
|
|
166
|
+
try {
|
|
167
|
+
stat = (0, import_node_fs.lstatSync)(full);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (isFileMissing(err))
|
|
170
|
+
continue;
|
|
171
|
+
throw err;
|
|
172
|
+
}
|
|
173
|
+
if (stat.isDirectory()) {
|
|
174
|
+
visit(full);
|
|
175
|
+
} else if (entry.endsWith(".ts") && fileStartsWithBanner(full)) {
|
|
176
|
+
adopted.push(full);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
visit(routesDir);
|
|
181
|
+
return adopted;
|
|
182
|
+
}
|
|
183
|
+
function mcpPlugin(options = {}) {
|
|
184
|
+
const mcpEntryOption = options.mcpEntry ?? "src/lib/mcp/index.ts";
|
|
185
|
+
const routesDirOption = options.routesDir ?? "src/routes";
|
|
186
|
+
const rawUrlPath = options.path ?? "/mcp";
|
|
187
|
+
assertUrlPathShape(rawUrlPath);
|
|
188
|
+
const routeFileName = options.routeFileName ?? deriveRouteFileName(rawUrlPath);
|
|
189
|
+
const urlPath = canonicalUrlPath(rawUrlPath);
|
|
190
|
+
const emitRestRoutes = options.restRoutes !== false;
|
|
191
|
+
const emitProtectedResourceMetadataRoute = options.protectedResourceMetadataRoute !== false;
|
|
192
|
+
let projectRoot = process.cwd();
|
|
193
|
+
let mcpEntry = (0, import_node_path.resolve)(projectRoot, mcpEntryOption);
|
|
194
|
+
let { routesDir, mcpRouteFile, metadataRouteFile, listToolsRouteFile, invokeToolRouteFile } = resolveAllRoutes(
|
|
195
|
+
projectRoot,
|
|
196
|
+
routesDirOption,
|
|
197
|
+
routeFileName
|
|
198
|
+
);
|
|
199
|
+
const regenerate = () => {
|
|
200
|
+
let mcpEntryExists = true;
|
|
201
|
+
try {
|
|
202
|
+
(0, import_node_fs.lstatSync)(mcpEntry);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
if (!isFileMissing(err))
|
|
205
|
+
throw err;
|
|
206
|
+
mcpEntryExists = false;
|
|
207
|
+
}
|
|
208
|
+
const expected = /* @__PURE__ */ new Set();
|
|
209
|
+
if (mcpEntryExists) {
|
|
210
|
+
const routes = [
|
|
211
|
+
{ file: mcpRouteFile, routeLiteral: urlPath, handlerFactory: "createTanStackMcpHandler" }
|
|
212
|
+
];
|
|
213
|
+
if (emitProtectedResourceMetadataRoute) {
|
|
214
|
+
routes.push({
|
|
215
|
+
file: metadataRouteFile,
|
|
216
|
+
routeLiteral: OAUTH_PROTECTED_RESOURCE_METADATA_PATH,
|
|
217
|
+
handlerFactory: "createTanStackOAuthProtectedResourceMetadataHandler"
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (emitRestRoutes) {
|
|
221
|
+
routes.push(
|
|
222
|
+
{
|
|
223
|
+
file: listToolsRouteFile,
|
|
224
|
+
routeLiteral: "/.mcp/list-tools",
|
|
225
|
+
handlerFactory: "createTanStackListToolsHandler",
|
|
226
|
+
spaFallbackNote: true
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
file: invokeToolRouteFile,
|
|
230
|
+
routeLiteral: "/.mcp/invoke-tool/$tool",
|
|
231
|
+
handlerFactory: "createTanStackInvokeToolHandler",
|
|
232
|
+
spaFallbackNote: true
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
for (const route of routes) {
|
|
237
|
+
expected.add(route.file);
|
|
238
|
+
writeIfChanged(route.file, buildRouteSource(route, urlPath, mcpEntry, projectRoot));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
for (const file of findAdoptedFiles(routesDir)) {
|
|
242
|
+
if (!expected.has(file)) {
|
|
243
|
+
try {
|
|
244
|
+
(0, import_node_fs.unlinkSync)(file);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (!isFileMissing(err))
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
name: "@lovable.dev/mcp-js",
|
|
254
|
+
configResolved(config) {
|
|
255
|
+
projectRoot = config.root;
|
|
256
|
+
mcpEntry = (0, import_node_path.resolve)(projectRoot, mcpEntryOption);
|
|
257
|
+
({ routesDir, mcpRouteFile, metadataRouteFile, listToolsRouteFile, invokeToolRouteFile } = resolveAllRoutes(
|
|
258
|
+
projectRoot,
|
|
259
|
+
routesDirOption,
|
|
260
|
+
routeFileName
|
|
261
|
+
));
|
|
262
|
+
regenerate();
|
|
263
|
+
},
|
|
264
|
+
configureServer(server) {
|
|
265
|
+
const watchedEntry = mcpEntry;
|
|
266
|
+
const onEntryChange = (file) => {
|
|
267
|
+
if (normalizePath(file) === normalizePath(watchedEntry)) {
|
|
268
|
+
regenerate();
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
server.watcher.on("add", onEntryChange);
|
|
272
|
+
server.watcher.on("change", onEntryChange);
|
|
273
|
+
server.watcher.on("unlink", onEntryChange);
|
|
274
|
+
server.watcher.once("close", () => {
|
|
275
|
+
server.watcher.off("add", onEntryChange);
|
|
276
|
+
server.watcher.off("change", onEntryChange);
|
|
277
|
+
server.watcher.off("unlink", onEntryChange);
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
buildStart() {
|
|
281
|
+
regenerate();
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
var vite_default = mcpPlugin;
|
|
286
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
287
|
+
0 && (module.exports = {
|
|
288
|
+
assertUrlPathShape,
|
|
289
|
+
deriveRouteFileName,
|
|
290
|
+
mcpPlugin
|
|
291
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface McpPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Path to the MCP entry file (default export = `defineMcp(...)` result),
|
|
6
|
+
* relative to the project root.
|
|
7
|
+
* @default "src/lib/mcp/index.ts"
|
|
8
|
+
*/
|
|
9
|
+
mcpEntry?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Where TanStack route files live, relative to the project root.
|
|
12
|
+
* The plugin emits `<routesDir>/<routeFileName>` plus the REST companion
|
|
13
|
+
* routes here.
|
|
14
|
+
* @default "src/routes"
|
|
15
|
+
*/
|
|
16
|
+
routesDir?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Public URL path for the MCP-protocol route only.
|
|
19
|
+
*
|
|
20
|
+
* The REST companion routes are fixed at `/.mcp/list-tools` and
|
|
21
|
+
* `/.mcp/invoke-tool/$tool` and are not affected by this option — the
|
|
22
|
+
* `[.mcp]` namespace exists to keep them from colliding with
|
|
23
|
+
* user-defined URLs, so making them follow `path` would defeat the
|
|
24
|
+
* purpose. Set `restRoutes: false` to drop the companions entirely.
|
|
25
|
+
*
|
|
26
|
+
* Must start with `/` and use only `/mcp`-style segments; anything more
|
|
27
|
+
* exotic doesn't map cleanly to a TanStack file name.
|
|
28
|
+
* @default "/mcp"
|
|
29
|
+
*/
|
|
30
|
+
path?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Filename to emit for the main MCP route, relative to `routesDir`.
|
|
33
|
+
* Defaults to a name derived from `path` (e.g. `/mcp` → `mcp.ts`,
|
|
34
|
+
* `/api/mcp` → `api/mcp.ts`).
|
|
35
|
+
*/
|
|
36
|
+
routeFileName?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Set to `false` to skip emitting the REST companion routes at
|
|
39
|
+
* `/.mcp/list-tools` and `/.mcp/<tool>`. Set
|
|
40
|
+
* `protectedResourceMetadataRoute: false` as well if you want the MCP
|
|
41
|
+
* protocol route to be the only emitted surface.
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
restRoutes?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Set to `false` to skip emitting the OAuth protected-resource metadata
|
|
47
|
+
* route at `/.well-known/oauth-protected-resource`.
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
protectedResourceMetadataRoute?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare function assertUrlPathShape(urlPath: string): string;
|
|
53
|
+
declare function deriveRouteFileName(urlPath: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Generate the TanStack route(s) that mount the MCP server.
|
|
56
|
+
*
|
|
57
|
+
* Place this in your `vite.config.ts` plugins array. Emits during
|
|
58
|
+
* `configResolved` (so dev-server boot and the TanStack file-router both
|
|
59
|
+
* see the routes on the first request) and re-emits when the MCP entry
|
|
60
|
+
* file changes during dev.
|
|
61
|
+
*/
|
|
62
|
+
declare function mcpPlugin(options?: McpPluginOptions): Plugin;
|
|
63
|
+
|
|
64
|
+
export { type McpPluginOptions, assertUrlPathShape, mcpPlugin as default, deriveRouteFileName, mcpPlugin };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface McpPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Path to the MCP entry file (default export = `defineMcp(...)` result),
|
|
6
|
+
* relative to the project root.
|
|
7
|
+
* @default "src/lib/mcp/index.ts"
|
|
8
|
+
*/
|
|
9
|
+
mcpEntry?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Where TanStack route files live, relative to the project root.
|
|
12
|
+
* The plugin emits `<routesDir>/<routeFileName>` plus the REST companion
|
|
13
|
+
* routes here.
|
|
14
|
+
* @default "src/routes"
|
|
15
|
+
*/
|
|
16
|
+
routesDir?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Public URL path for the MCP-protocol route only.
|
|
19
|
+
*
|
|
20
|
+
* The REST companion routes are fixed at `/.mcp/list-tools` and
|
|
21
|
+
* `/.mcp/invoke-tool/$tool` and are not affected by this option — the
|
|
22
|
+
* `[.mcp]` namespace exists to keep them from colliding with
|
|
23
|
+
* user-defined URLs, so making them follow `path` would defeat the
|
|
24
|
+
* purpose. Set `restRoutes: false` to drop the companions entirely.
|
|
25
|
+
*
|
|
26
|
+
* Must start with `/` and use only `/mcp`-style segments; anything more
|
|
27
|
+
* exotic doesn't map cleanly to a TanStack file name.
|
|
28
|
+
* @default "/mcp"
|
|
29
|
+
*/
|
|
30
|
+
path?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Filename to emit for the main MCP route, relative to `routesDir`.
|
|
33
|
+
* Defaults to a name derived from `path` (e.g. `/mcp` → `mcp.ts`,
|
|
34
|
+
* `/api/mcp` → `api/mcp.ts`).
|
|
35
|
+
*/
|
|
36
|
+
routeFileName?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Set to `false` to skip emitting the REST companion routes at
|
|
39
|
+
* `/.mcp/list-tools` and `/.mcp/<tool>`. Set
|
|
40
|
+
* `protectedResourceMetadataRoute: false` as well if you want the MCP
|
|
41
|
+
* protocol route to be the only emitted surface.
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
restRoutes?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Set to `false` to skip emitting the OAuth protected-resource metadata
|
|
47
|
+
* route at `/.well-known/oauth-protected-resource`.
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
protectedResourceMetadataRoute?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare function assertUrlPathShape(urlPath: string): string;
|
|
53
|
+
declare function deriveRouteFileName(urlPath: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Generate the TanStack route(s) that mount the MCP server.
|
|
56
|
+
*
|
|
57
|
+
* Place this in your `vite.config.ts` plugins array. Emits during
|
|
58
|
+
* `configResolved` (so dev-server boot and the TanStack file-router both
|
|
59
|
+
* see the routes on the first request) and re-emits when the MCP entry
|
|
60
|
+
* file changes during dev.
|
|
61
|
+
*/
|
|
62
|
+
declare function mcpPlugin(options?: McpPluginOptions): Plugin;
|
|
63
|
+
|
|
64
|
+
export { type McpPluginOptions, assertUrlPathShape, mcpPlugin as default, deriveRouteFileName, mcpPlugin };
|