@lovable.dev/mcp-js 0.9.1 → 0.9.4
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.internal.md +11 -6
- package/dist/{chunk-QJ7XEKT3.js → chunk-Y3ZFPEQH.js} +1 -1
- package/dist/{stacks/tanstack/cli → cli}/extract-manifest.cjs +15 -13
- package/dist/{stacks/tanstack/cli → cli}/extract-manifest.js +15 -13
- package/dist/stacks/tanstack/vite.cjs +1 -1
- package/dist/stacks/tanstack/vite.d.cts +13 -4
- package/dist/stacks/tanstack/vite.d.ts +13 -4
- package/dist/stacks/tanstack/vite.js +1 -1
- package/package.json +4 -4
- /package/dist/{stacks/tanstack/cli → cli}/extract-manifest.d.cts +0 -0
- /package/dist/{stacks/tanstack/cli → cli}/extract-manifest.d.ts +0 -0
package/README.internal.md
CHANGED
|
@@ -7,6 +7,7 @@ Architecture rationale, source layout, and dev scripts for contributors. The use
|
|
|
7
7
|
- **`protocols/`** is the wire layer — one folder per externally observable HTTP surface. `protocols/mcp` is the public MCP-over-HTTP surface; `protocols/oauth-metadata` serves the RFC 9728 protected-resource metadata required to bootstrap protected MCP clients; `protocols/rest` is internal RPC for the upstream MCP proxy. Every backend wires both MCP and REST, plus the metadata endpoint when OAuth is configured.
|
|
8
8
|
- **`auth/`** (unpublished) is the cross-cutting OAuth middleware — bearer verification, issuer/JWKS discovery, the `auth.oauth.*` config namespace. It depends only on `core/`; both `protocols/mcp` and `protocols/rest` depend on it for the shared bearer gate, so it isn't a wire-format peer of `mcp`/`rest` and doesn't live under `protocols/`.
|
|
9
9
|
- **`stacks/`** is the framework integration: TanStack today; future entries (Supabase Edge Functions, classic Vite, …) live next to it under the same parent, forwarding to `protocols/{mcp,oauth-metadata,rest}` directly — protocol logic stays shared, only ctx unwrapping differs.
|
|
10
|
+
- **`manifest/`** is the stack-agnostic snapshot writer. `lovable-mcp-extract-manifest` is one CLI for every stack: it asks Vite for the resolved config, finds any plugin whose name matches `@lovable.dev/mcp-js*`, and reads `mcpEntry` + `urlPath` off its `api`. TanStack publishes its configured route path (`/mcp`); Supabase publishes `/functions/v1/<functionName>`. The projection (`extract.ts`) is pure and shares `buildMcpListing` with the live `GET /.mcp/list-tools` route so the committed manifest and the served catalog can't drift.
|
|
10
11
|
|
|
11
12
|
## Design decisions
|
|
12
13
|
|
|
@@ -111,6 +112,7 @@ src/
|
|
|
111
112
|
url.ts # URL parsing/validation
|
|
112
113
|
validation.ts # config assertions
|
|
113
114
|
promise.ts # cachedPromise (resolved-value cache)
|
|
115
|
+
fs-errors.ts # ENOENT-only catch guard (shared by manifest/io + stacks)
|
|
114
116
|
auth/ # cross-cutting OAuth middleware (unpublished); depends only on core/
|
|
115
117
|
config.ts # auth namespace: auth.oauth.issuer
|
|
116
118
|
authorize.ts # request authorizer, bearer parsing, challenges
|
|
@@ -130,14 +132,16 @@ src/
|
|
|
130
132
|
list-tools.ts # createListToolsHandler (Web-Standard, GET)
|
|
131
133
|
invoke-tool.ts # createInvokeToolHandler (Web-Standard, POST)
|
|
132
134
|
index.ts # barrel
|
|
135
|
+
manifest/ # stack-agnostic manifest extraction (TanStack + Supabase both feed it)
|
|
136
|
+
extract.ts # project a defineMcp result to the manifest (types + manifestFromDefinition)
|
|
137
|
+
io.ts # runExtract/syncManifest: read any @lovable.dev/mcp-js* plugin's api + write .lovable/mcp/manifest.json
|
|
138
|
+
ssr-loader.ts # produceViaSsr: load the entry via a throwaway Vite SSR server
|
|
139
|
+
cli/
|
|
140
|
+
extract-manifest.ts # lovable-mcp-extract-manifest bin (runs runExtract on cwd)
|
|
133
141
|
stacks/
|
|
134
142
|
tanstack/
|
|
135
143
|
handlers.ts # TanStack route-ctx adapters
|
|
136
|
-
vite.ts # mcpPlugin (route emission + stale-file cleanup;
|
|
137
|
-
ssr-loader.ts # produceViaSsr: load the entry via a throwaway Vite SSR server
|
|
138
|
-
extract-manifest.ts # project a defineMcp result to the manifest (types + manifestFromDefinition)
|
|
139
|
-
manifest-io.ts # runExtract/syncManifest: write/remove .lovable/mcp/manifest.json
|
|
140
|
-
cli/extract-manifest.ts # lovable-mcp-extract-manifest bin (runs runExtract on cwd)
|
|
144
|
+
vite.ts # mcpPlugin (route emission + stale-file cleanup; exposes McpPluginApi for the extractor)
|
|
141
145
|
index.ts # barrel
|
|
142
146
|
tests/
|
|
143
147
|
core/{define,promise,url}.test.ts
|
|
@@ -146,8 +150,9 @@ tests/
|
|
|
146
150
|
mcp/protocol.test.ts
|
|
147
151
|
rest/{list-tools,invoke-tool}.test.ts
|
|
148
152
|
parity.test.ts # REST↔MCP equivalence contract
|
|
153
|
+
manifest/{extract,io,ssr-loader,run-extract}.test.ts # stack-agnostic extractor (run-extract covers TanStack + Supabase)
|
|
149
154
|
stacks/
|
|
150
|
-
tanstack/{handlers,vite
|
|
155
|
+
tanstack/{handlers,vite}.test.ts
|
|
151
156
|
integration/ # boots an example app, hits live HTTP
|
|
152
157
|
global-setup.ts # spawns example/tanstack on :8080
|
|
153
158
|
tools.test.ts # non-OAuth example
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
// src/
|
|
4
|
+
// src/manifest/io.ts
|
|
5
5
|
var import_node_crypto = require("crypto");
|
|
6
6
|
var import_node_fs = require("fs");
|
|
7
7
|
var import_node_path = require("path");
|
|
8
8
|
var import_vite2 = require("vite");
|
|
9
9
|
|
|
10
|
+
// src/core/fs-errors.ts
|
|
11
|
+
function isFileMissing(err) {
|
|
12
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
// package.json
|
|
11
|
-
var version = "0.9.
|
|
16
|
+
var version = "0.9.4";
|
|
12
17
|
|
|
13
18
|
// src/protocols/rest/list-tools.ts
|
|
14
19
|
var import_zod_compat = require("@modelcontextprotocol/sdk/server/zod-compat.js");
|
|
@@ -57,7 +62,7 @@ function buildMcpListing(mcp) {
|
|
|
57
62
|
};
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
// src/
|
|
65
|
+
// src/manifest/extract.ts
|
|
61
66
|
var MANIFEST_VERSION = 1;
|
|
62
67
|
var MANIFEST_RELATIVE_PATH = ".lovable/mcp/manifest.json";
|
|
63
68
|
function manifestAuth(auth) {
|
|
@@ -116,12 +121,7 @@ function manifestFromDefinition(definition, urlPath, source = "the MCP entry") {
|
|
|
116
121
|
};
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
// src/
|
|
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
|
|
124
|
+
// src/manifest/ssr-loader.ts
|
|
125
125
|
var import_vite = require("vite");
|
|
126
126
|
async function produceViaSsr(projectRoot2, alias, entryAbs, urlPath) {
|
|
127
127
|
const server = await (0, import_vite.createServer)({
|
|
@@ -140,8 +140,8 @@ async function produceViaSsr(projectRoot2, alias, entryAbs, urlPath) {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
// src/
|
|
144
|
-
var
|
|
143
|
+
// src/manifest/io.ts
|
|
144
|
+
var MCP_PLUGIN_NAME_PREFIX = "@lovable.dev/mcp-js";
|
|
145
145
|
var DEFAULT_MCP_ENTRY = "src/lib/mcp/index.ts";
|
|
146
146
|
var DEFAULT_MCP_PATH = "/mcp";
|
|
147
147
|
function writeManifestIfChanged(file, manifest) {
|
|
@@ -195,7 +195,9 @@ async function syncManifest(manifestFile, entryAbs, urlPath, produce) {
|
|
|
195
195
|
async function runExtract(projectRoot2) {
|
|
196
196
|
const resolved = await (0, import_vite2.resolveConfig)({ root: projectRoot2, logLevel: "silent" }, "serve");
|
|
197
197
|
const alias = resolved.resolve?.alias ?? [];
|
|
198
|
-
const pluginApi = resolved.plugins.find(
|
|
198
|
+
const pluginApi = resolved.plugins.find(
|
|
199
|
+
(p) => typeof p.name === "string" && p.name.startsWith(MCP_PLUGIN_NAME_PREFIX)
|
|
200
|
+
)?.api;
|
|
199
201
|
const entryAbs = pluginApi?.mcpEntry ?? (0, import_node_path.resolve)(projectRoot2, DEFAULT_MCP_ENTRY);
|
|
200
202
|
const urlPath = pluginApi?.urlPath ?? DEFAULT_MCP_PATH;
|
|
201
203
|
await syncManifest(
|
|
@@ -206,7 +208,7 @@ async function runExtract(projectRoot2) {
|
|
|
206
208
|
);
|
|
207
209
|
}
|
|
208
210
|
|
|
209
|
-
// src/
|
|
211
|
+
// src/cli/extract-manifest.ts
|
|
210
212
|
var projectRoot = process.argv[2] ?? process.cwd();
|
|
211
213
|
runExtract(projectRoot).catch((err) => {
|
|
212
214
|
console.error(err instanceof Error ? err.message : String(err));
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildMcpListing
|
|
4
|
-
} from "
|
|
5
|
-
import "
|
|
6
|
-
import "
|
|
4
|
+
} from "../chunk-2RGXCDBU.js";
|
|
5
|
+
import "../chunk-YQBOGY7D.js";
|
|
6
|
+
import "../chunk-QC3DXQTH.js";
|
|
7
7
|
import {
|
|
8
8
|
isFileMissing
|
|
9
|
-
} from "
|
|
10
|
-
import "
|
|
9
|
+
} from "../chunk-Y3ZFPEQH.js";
|
|
10
|
+
import "../chunk-6DXGZZA4.js";
|
|
11
11
|
|
|
12
|
-
// src/
|
|
12
|
+
// src/manifest/io.ts
|
|
13
13
|
import { randomUUID } from "crypto";
|
|
14
14
|
import { lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
15
15
|
import { dirname, resolve } from "path";
|
|
16
16
|
import { resolveConfig } from "vite";
|
|
17
17
|
|
|
18
18
|
// package.json
|
|
19
|
-
var version = "0.9.
|
|
19
|
+
var version = "0.9.4";
|
|
20
20
|
|
|
21
|
-
// src/
|
|
21
|
+
// src/manifest/extract.ts
|
|
22
22
|
var MANIFEST_VERSION = 1;
|
|
23
23
|
var MANIFEST_RELATIVE_PATH = ".lovable/mcp/manifest.json";
|
|
24
24
|
function manifestAuth(auth) {
|
|
@@ -77,7 +77,7 @@ function manifestFromDefinition(definition, urlPath, source = "the MCP entry") {
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// src/
|
|
80
|
+
// src/manifest/ssr-loader.ts
|
|
81
81
|
import { createServer } from "vite";
|
|
82
82
|
async function produceViaSsr(projectRoot2, alias, entryAbs, urlPath) {
|
|
83
83
|
const server = await createServer({
|
|
@@ -96,8 +96,8 @@ async function produceViaSsr(projectRoot2, alias, entryAbs, urlPath) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// src/
|
|
100
|
-
var
|
|
99
|
+
// src/manifest/io.ts
|
|
100
|
+
var MCP_PLUGIN_NAME_PREFIX = "@lovable.dev/mcp-js";
|
|
101
101
|
var DEFAULT_MCP_ENTRY = "src/lib/mcp/index.ts";
|
|
102
102
|
var DEFAULT_MCP_PATH = "/mcp";
|
|
103
103
|
function writeManifestIfChanged(file, manifest) {
|
|
@@ -151,7 +151,9 @@ async function syncManifest(manifestFile, entryAbs, urlPath, produce) {
|
|
|
151
151
|
async function runExtract(projectRoot2) {
|
|
152
152
|
const resolved = await resolveConfig({ root: projectRoot2, logLevel: "silent" }, "serve");
|
|
153
153
|
const alias = resolved.resolve?.alias ?? [];
|
|
154
|
-
const pluginApi = resolved.plugins.find(
|
|
154
|
+
const pluginApi = resolved.plugins.find(
|
|
155
|
+
(p) => typeof p.name === "string" && p.name.startsWith(MCP_PLUGIN_NAME_PREFIX)
|
|
156
|
+
)?.api;
|
|
155
157
|
const entryAbs = pluginApi?.mcpEntry ?? resolve(projectRoot2, DEFAULT_MCP_ENTRY);
|
|
156
158
|
const urlPath = pluginApi?.urlPath ?? DEFAULT_MCP_PATH;
|
|
157
159
|
await syncManifest(
|
|
@@ -162,7 +164,7 @@ async function runExtract(projectRoot2) {
|
|
|
162
164
|
);
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
// src/
|
|
167
|
+
// src/cli/extract-manifest.ts
|
|
166
168
|
var projectRoot = process.argv[2] ?? process.cwd();
|
|
167
169
|
runExtract(projectRoot).catch((err) => {
|
|
168
170
|
console.error(err instanceof Error ? err.message : String(err));
|
|
@@ -33,7 +33,7 @@ var import_node_path = require("path");
|
|
|
33
33
|
// src/auth/metadata-path.ts
|
|
34
34
|
var OAUTH_PROTECTED_RESOURCE_METADATA_PATH = "/.well-known/oauth-protected-resource";
|
|
35
35
|
|
|
36
|
-
// src/
|
|
36
|
+
// src/core/fs-errors.ts
|
|
37
37
|
function isFileMissing(err) {
|
|
38
38
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
39
39
|
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Shape every MCP Vite plugin exposes on its `api` so `runExtract` can read
|
|
5
|
+
* back the resolved entry path and the URL the stack mounts the MCP server at.
|
|
6
|
+
* TanStack sets `urlPath` to the configured route path (default `/mcp`);
|
|
7
|
+
* Supabase sets it to `/functions/v1/<functionName>` (the public mount under
|
|
8
|
+
* which the function and its companion routes live).
|
|
9
|
+
*/
|
|
10
|
+
interface McpPluginApi {
|
|
11
|
+
readonly mcpEntry: string;
|
|
12
|
+
readonly urlPath: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
interface McpPluginOptions {
|
|
4
16
|
/**
|
|
5
17
|
* Path to the MCP entry file (default export = `defineMcp(...)` result),
|
|
@@ -64,10 +76,7 @@ interface McpPluginOptions {
|
|
|
64
76
|
*/
|
|
65
77
|
metadataPath?: string;
|
|
66
78
|
}
|
|
67
|
-
|
|
68
|
-
readonly mcpEntry: string;
|
|
69
|
-
readonly urlPath: string;
|
|
70
|
-
}
|
|
79
|
+
|
|
71
80
|
declare function assertUrlPathShape(urlPath: string): string;
|
|
72
81
|
declare function deriveRouteFileName(urlPath: string): string;
|
|
73
82
|
declare function assertMetadataPathShape(metadataPath: string): void;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Shape every MCP Vite plugin exposes on its `api` so `runExtract` can read
|
|
5
|
+
* back the resolved entry path and the URL the stack mounts the MCP server at.
|
|
6
|
+
* TanStack sets `urlPath` to the configured route path (default `/mcp`);
|
|
7
|
+
* Supabase sets it to `/functions/v1/<functionName>` (the public mount under
|
|
8
|
+
* which the function and its companion routes live).
|
|
9
|
+
*/
|
|
10
|
+
interface McpPluginApi {
|
|
11
|
+
readonly mcpEntry: string;
|
|
12
|
+
readonly urlPath: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
interface McpPluginOptions {
|
|
4
16
|
/**
|
|
5
17
|
* Path to the MCP entry file (default export = `defineMcp(...)` result),
|
|
@@ -64,10 +76,7 @@ interface McpPluginOptions {
|
|
|
64
76
|
*/
|
|
65
77
|
metadataPath?: string;
|
|
66
78
|
}
|
|
67
|
-
|
|
68
|
-
readonly mcpEntry: string;
|
|
69
|
-
readonly urlPath: string;
|
|
70
|
-
}
|
|
79
|
+
|
|
71
80
|
declare function assertUrlPathShape(urlPath: string): string;
|
|
72
81
|
declare function deriveRouteFileName(urlPath: string): string;
|
|
73
82
|
declare function assertMetadataPathShape(metadataPath: string): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/mcp-js",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Author MCP servers for Lovable apps. Declare tools with defineTool, register them in defineMcp, and the framework adapter (TanStack today, Supabase Edge Functions next) emits the route(s) at build time.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"bin": {
|
|
54
|
-
"lovable-mcp-extract-manifest": "./dist/
|
|
54
|
+
"lovable-mcp-extract-manifest": "./dist/cli/extract-manifest.cjs"
|
|
55
55
|
},
|
|
56
56
|
"files": [
|
|
57
57
|
"dist"
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
"zod": "^4.1.13"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|
|
85
|
-
"build": "tsup src/index.ts src/protocols/mcp/index.ts src/protocols/oauth-metadata.ts src/protocols/rest/index.ts src/stacks/tanstack/index.ts src/stacks/tanstack/vite.ts src/
|
|
86
|
-
"dev": "tsup src/index.ts src/protocols/mcp/index.ts src/protocols/oauth-metadata.ts src/protocols/rest/index.ts src/stacks/tanstack/index.ts src/stacks/tanstack/vite.ts src/
|
|
85
|
+
"build": "tsup src/index.ts src/protocols/mcp/index.ts src/protocols/oauth-metadata.ts src/protocols/rest/index.ts src/stacks/tanstack/index.ts src/stacks/tanstack/vite.ts src/cli/extract-manifest.ts --format cjs,esm --dts --outDir dist",
|
|
86
|
+
"dev": "tsup src/index.ts src/protocols/mcp/index.ts src/protocols/oauth-metadata.ts src/protocols/rest/index.ts src/stacks/tanstack/index.ts src/stacks/tanstack/vite.ts src/cli/extract-manifest.ts --format cjs,esm --dts --watch",
|
|
87
87
|
"typecheck": "tsgo --noEmit",
|
|
88
88
|
"format": "oxfmt --write src/ tests/",
|
|
89
89
|
"format:check": "oxfmt --check src/ tests/",
|
|
File without changes
|
|
File without changes
|