@lovable.dev/mcp-js 0.9.4 → 0.10.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.internal.md +15 -8
- package/README.md +50 -1
- package/dist/chunk-4XJ2R3DD.js +6 -0
- package/dist/{chunk-OXSSMKQE.js → chunk-DF3DD3KD.js} +1 -1
- package/dist/{chunk-YQBOGY7D.js → chunk-LAFJDJRY.js} +1 -0
- package/dist/{chunk-2RGXCDBU.js → chunk-PJXKJNSZ.js} +1 -1
- package/dist/{chunk-6N7B7NZY.js → chunk-SZLSAGDF.js} +1 -1
- package/dist/{chunk-4JO3BZQG.js → chunk-X7V2CL7C.js} +1 -1
- package/dist/chunk-XQWJN6DC.js +14 -0
- package/dist/cli/extract-manifest.cjs +1 -1
- package/dist/cli/extract-manifest.js +6 -6
- package/dist/io-D4srzRKc.d.ts +13 -0
- package/dist/protocols/mcp/index.js +2 -2
- package/dist/protocols/oauth-metadata.js +2 -2
- package/dist/protocols/rest/index.js +4 -4
- package/dist/stacks/supabase/index.cjs +939 -0
- package/dist/stacks/supabase/index.d.cts +39 -0
- package/dist/stacks/supabase/index.d.ts +39 -0
- package/dist/stacks/supabase/index.js +84 -0
- package/dist/stacks/supabase/vite.cjs +263 -0
- package/dist/stacks/supabase/vite.d.cts +47 -0
- package/dist/stacks/supabase/vite.d.ts +47 -0
- package/dist/stacks/supabase/vite.js +229 -0
- package/dist/stacks/tanstack/index.js +6 -6
- package/dist/stacks/tanstack/vite.d.cts +2 -13
- package/dist/stacks/tanstack/vite.d.ts +2 -13
- package/dist/stacks/tanstack/vite.js +3 -3
- package/package.json +26 -11
package/README.internal.md
CHANGED
|
@@ -6,7 +6,7 @@ Architecture rationale, source layout, and dev scripts for contributors. The use
|
|
|
6
6
|
|
|
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
|
-
- **`stacks/`** is the framework integration:
|
|
9
|
+
- **`stacks/`** is the framework integration. Two are published: `stacks/tanstack/` (file-router routes emitted by a Vite plugin) and `stacks/supabase/` (a single Deno-served function with URL-suffix dispatch, emitted by the Supabase Vite plugin). Future entries (classic Vite, Cloudflare Workers, …) live alongside them. Each stack forwards to `protocols/{mcp,oauth-metadata,rest}` directly — protocol logic stays shared, only ctx unwrapping and route emission differ.
|
|
10
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.
|
|
11
11
|
|
|
12
12
|
## Design decisions
|
|
@@ -143,6 +143,11 @@ src/
|
|
|
143
143
|
handlers.ts # TanStack route-ctx adapters
|
|
144
144
|
vite.ts # mcpPlugin (route emission + stale-file cleanup; exposes McpPluginApi for the extractor)
|
|
145
145
|
index.ts # barrel
|
|
146
|
+
supabase/
|
|
147
|
+
handler.ts # createSupabaseHandler (URL-suffix dispatcher to protocols/*)
|
|
148
|
+
emit.ts # syncSupabaseFunction (emits supabase/functions/<name>/{index.ts,deno.json})
|
|
149
|
+
vite.ts # mcpPlugin (Vite-lifecycle trigger; exposes McpPluginApi with urlPath=/functions/v1/<functionName>)
|
|
150
|
+
index.ts # barrel
|
|
146
151
|
tests/
|
|
147
152
|
core/{define,promise,url}.test.ts
|
|
148
153
|
auth/{claims,config,context,oauth}.test.ts # OAuth config + bearer verification
|
|
@@ -153,6 +158,7 @@ tests/
|
|
|
153
158
|
manifest/{extract,io,ssr-loader,run-extract}.test.ts # stack-agnostic extractor (run-extract covers TanStack + Supabase)
|
|
154
159
|
stacks/
|
|
155
160
|
tanstack/{handlers,vite}.test.ts
|
|
161
|
+
supabase/{handler,emit,vite}.test.ts
|
|
156
162
|
integration/ # boots an example app, hits live HTTP
|
|
157
163
|
global-setup.ts # spawns example/tanstack on :8080
|
|
158
164
|
tools.test.ts # non-OAuth example
|
|
@@ -169,19 +175,20 @@ pnpm lint:fix # format + oxlint --fix
|
|
|
169
175
|
pnpm typecheck # tsgo --noEmit
|
|
170
176
|
pnpm test # vitest run --project unit (fast, hermetic)
|
|
171
177
|
pnpm test:watch # vitest --project unit
|
|
172
|
-
pnpm test:integration
|
|
173
|
-
pnpm test:integration:oauth # build + install OAuth example + vitest
|
|
178
|
+
pnpm test:integration:tanstack # build + install tanstack example + vitest --project integration
|
|
179
|
+
pnpm test:integration:tanstack:oauth # build + install OAuth example + vitest --project integration-oauth
|
|
174
180
|
pnpm build # tsup → dist/{cjs,esm}/{index,protocols/*,stacks/*}
|
|
175
181
|
pnpm pack:local # build + npm pack into /tmp/lovable.dev-mcp-js-<version>.tgz
|
|
176
|
-
pnpm example:install
|
|
177
|
-
pnpm example:oauth:install
|
|
178
|
-
pnpm example:oauth:build
|
|
179
|
-
pnpm example:oauth:smoke
|
|
182
|
+
pnpm example:tanstack:install # install example/tanstack deps (called by test:integration:tanstack)
|
|
183
|
+
pnpm example:tanstack:oauth:install # install example/tanstack-supabase-oauth deps
|
|
184
|
+
pnpm example:tanstack:oauth:build # production-build the OAuth example
|
|
185
|
+
pnpm example:tanstack:oauth:smoke # smoke a running OAuth example, optionally with MCP_ACCESS_TOKEN
|
|
186
|
+
pnpm example:supabase:install # install example/supabase deps
|
|
180
187
|
```
|
|
181
188
|
|
|
182
189
|
`lint` checks both formatting (oxfmt) and lint rules (oxlint) in one pass — the format check fails fast before the lint pass so you see the smaller diff first. `prepublishOnly` chains `clean → typecheck → test → build`; CI enforces `lint` separately.
|
|
183
190
|
|
|
184
|
-
**Integration tests** live under `tests/integration/` and run against a live HTTP server. `pnpm test:integration` builds the SDK, installs `example/tanstack`, boots its dev server on port 8080 via a Vitest `globalSetup`, runs the suite, and tears the server down. If the server is already running at `MCP_BASE_URL` (default `http://localhost:8080`), the setup reuses it — useful when iterating on the suite. See `CHANGELOG.md` for version history.
|
|
191
|
+
**Integration tests** live under `tests/integration/` and run against a live HTTP server. `pnpm test:integration:tanstack` builds the SDK, installs `example/tanstack`, boots its dev server on port 8080 via a Vitest `globalSetup`, runs the suite, and tears the server down. If the server is already running at `MCP_BASE_URL` (default `http://localhost:8080`), the setup reuses it — useful when iterating on the suite. See `CHANGELOG.md` for version history.
|
|
185
192
|
|
|
186
193
|
**OAuth integration test** (`tests/integration/oauth.test.ts`) is hermetic: `pnpm test:integration:oauth` builds + installs `example/tanstack-supabase-oauth`, then a self-contained `beforeAll` starts a mock RS256 issuer (JWKS + Supabase userinfo/PostgREST stubs), boots the example pointed at it on port 8081, signs a local test JWT, and asserts metadata, bearer challenges, and authorized REST + MCP calls. It runs in its own `integration-oauth` Vitest project (no shared `globalSetup`).
|
|
187
194
|
|
package/README.md
CHANGED
|
@@ -215,6 +215,53 @@ LOVABLE_MCP_LOG_LEVEL=debug
|
|
|
215
215
|
|
|
216
216
|
A `500` on an OAuth-protected route is always one of two causes, logged at `error`: an `OAuthConfigurationError` from issuer-metadata discovery or a JWKS *fetch* failure (`oauth.discovery.config_error` / `oauth.jwks.fetch_failed` → `auth.config_error`), or a transport-level fault in the MCP handler (`mcp.transport_error`). Auth outcomes are logged at `info`: a granted request as `oauth.verify.ok`, and a `401`/`403` as `auth.token_rejected` (with the `jose` reason) or `auth.no_bearer_token` — so a request rejected for insufficient scope shows both `oauth.verify.ok` and the `auth.token_rejected` that follows it.
|
|
217
217
|
|
|
218
|
+
## Supabase Edge Functions
|
|
219
|
+
|
|
220
|
+
Ship the same `defineMcp` server as a single Supabase Edge Function. Authoring is identical to the TanStack flow; only the build-time step differs — a different Vite plugin emits a single Deno function entry (plus a `deno.json` import map) instead of a tree of file-router routes.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
// src/lib/mcp/index.ts — same as the TanStack flow
|
|
224
|
+
import { auth, defineMcp } from "@lovable.dev/mcp-js";
|
|
225
|
+
import echoTool from "./tools/echo";
|
|
226
|
+
|
|
227
|
+
export default defineMcp({
|
|
228
|
+
name: "my-app-mcp",
|
|
229
|
+
title: "My App MCP",
|
|
230
|
+
version: "0.1.0",
|
|
231
|
+
instructions: "Tools for interacting with My App.",
|
|
232
|
+
auth: auth.oauth.issuer({
|
|
233
|
+
issuer: `${process.env.SUPABASE_URL!.replace(/\/+$/, "")}/auth/v1`,
|
|
234
|
+
acceptedAudiences: "authenticated",
|
|
235
|
+
}),
|
|
236
|
+
tools: [echoTool],
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
// vite.config.ts — Lovable Classic projects already run Vite for the
|
|
242
|
+
// frontend, so the plugin is the natural authoring surface (the emitted
|
|
243
|
+
// output is a Deno function, not a Vite-bundled file; Vite is just the
|
|
244
|
+
// trigger). The export is named `mcpPlugin` in every stack — the import
|
|
245
|
+
// path is what discriminates the TanStack and Supabase variants.
|
|
246
|
+
import { defineConfig } from "vite";
|
|
247
|
+
import { mcpPlugin } from "@lovable.dev/mcp-js/stacks/supabase/vite";
|
|
248
|
+
|
|
249
|
+
export default defineConfig({ plugins: [mcpPlugin()] });
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The plugin emits `supabase/functions/mcp/index.ts` and `supabase/functions/mcp/deno.json` on `configResolved`, re-emits on `buildStart`, and watches `src/lib/mcp/index.ts` for changes during `vite dev`. The emitted function calls `Deno.serve(createSupabaseHandler(mcp, { functionName: "mcp" }))`. The handler is one Web-Standard `Request → Response` that dispatches by URL suffix to the same protocol handlers the TanStack stack uses:
|
|
253
|
+
|
|
254
|
+
| URL suffix | Target |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| (root) | MCP streamable-HTTP protocol |
|
|
257
|
+
| `/.mcp/list-tools` | REST tool catalog |
|
|
258
|
+
| `/.mcp/invoke-tool/<tool>` | REST tool dispatcher |
|
|
259
|
+
| `/.well-known/oauth-protected-resource` | RFC 9728 metadata |
|
|
260
|
+
|
|
261
|
+
In production the function is mounted at `https://<project-ref>.supabase.co/functions/v1/mcp` and the dispatcher binds the OAuth `resource` to that URL via `functionName`. Locally, run `supabase functions serve mcp --no-verify-jwt` so Supabase's edge-runtime doesn't 401 before the SDK's OAuth verifier sees the bearer.
|
|
262
|
+
|
|
263
|
+
Ownership: every emitted file carries a `// AUTO-GENERATED by @lovable.dev/mcp-js` banner. The plugin rewrites banner-tagged files in place and cleans up orphans; banner-less files (yours) are never touched. Delete the banner line to take ownership of a file. Deleting the `defineMcp` entry and re-running removes the SDK-owned function files.
|
|
264
|
+
|
|
218
265
|
## Subpath exports
|
|
219
266
|
|
|
220
267
|
| Subpath | Contents |
|
|
@@ -225,5 +272,7 @@ A `500` on an OAuth-protected route is always one of two causes, logged at `erro
|
|
|
225
272
|
| `@lovable.dev/mcp-js/protocols/rest` | `createListToolsHandler`, `createInvokeToolHandler` — Web-Standard |
|
|
226
273
|
| `@lovable.dev/mcp-js/stacks/tanstack` | TanStack-route-ctx adapters (`createTanStack*Handler`) |
|
|
227
274
|
| `@lovable.dev/mcp-js/stacks/tanstack/vite` | The Vite plugin |
|
|
275
|
+
| `@lovable.dev/mcp-js/stacks/supabase` | `createSupabaseHandler` — Web-Standard Supabase Edge Function dispatcher |
|
|
276
|
+
| `@lovable.dev/mcp-js/stacks/supabase/vite` | `mcpPlugin` — the Vite plugin (Supabase variant; same name as TanStack's, picked by import path) |
|
|
228
277
|
|
|
229
|
-
End users only need the root import (`@lovable.dev/mcp-js`). Generated route files import from `@lovable.dev/mcp-js/stacks/tanstack
|
|
278
|
+
End users only need the root import (`@lovable.dev/mcp-js`). Generated route files import from `@lovable.dev/mcp-js/stacks/tanstack` (TanStack) or `@lovable.dev/mcp-js/stacks/supabase` (Supabase). The other subpaths are escape hatches for hand-wiring custom stacks against the protocol layer directly.
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
createRequestAuthorizer,
|
|
9
9
|
methodNotAllowed,
|
|
10
10
|
withCors
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LAFJDJRY.js";
|
|
12
12
|
|
|
13
13
|
// src/protocols/rest/invoke-tool.ts
|
|
14
14
|
import { getParseErrorMessage, objectFromShape, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/stacks/supabase/paths.ts
|
|
2
|
+
var FUNCTIONS_MOUNT_PREFIX = "/functions/v1/";
|
|
3
|
+
function assertFunctionName(value, label = "functionName") {
|
|
4
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
|
|
5
|
+
throw new Error(
|
|
6
|
+
`@lovable.dev/mcp-js: ${label} must be a single path segment matching [A-Za-z0-9_-], got ${JSON.stringify(value)}`
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
FUNCTIONS_MOUNT_PREFIX,
|
|
13
|
+
assertFunctionName
|
|
14
|
+
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildMcpListing
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-PJXKJNSZ.js";
|
|
5
|
+
import "../chunk-LAFJDJRY.js";
|
|
6
6
|
import "../chunk-QC3DXQTH.js";
|
|
7
|
+
import "../chunk-6DXGZZA4.js";
|
|
8
|
+
import {
|
|
9
|
+
version
|
|
10
|
+
} from "../chunk-4XJ2R3DD.js";
|
|
7
11
|
import {
|
|
8
12
|
isFileMissing
|
|
9
13
|
} from "../chunk-Y3ZFPEQH.js";
|
|
10
|
-
import "../chunk-6DXGZZA4.js";
|
|
11
14
|
|
|
12
15
|
// src/manifest/io.ts
|
|
13
16
|
import { randomUUID } from "crypto";
|
|
@@ -15,9 +18,6 @@ import { lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSy
|
|
|
15
18
|
import { dirname, resolve } from "path";
|
|
16
19
|
import { resolveConfig } from "vite";
|
|
17
20
|
|
|
18
|
-
// package.json
|
|
19
|
-
var version = "0.9.4";
|
|
20
|
-
|
|
21
21
|
// src/manifest/extract.ts
|
|
22
22
|
var MANIFEST_VERSION = 1;
|
|
23
23
|
var MANIFEST_RELATIVE_PATH = ".lovable/mcp/manifest.json";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape every MCP Vite plugin exposes on its `api` so `runExtract` can read
|
|
3
|
+
* back the resolved entry path and the URL the stack mounts the MCP server at.
|
|
4
|
+
* TanStack sets `urlPath` to the configured route path (default `/mcp`);
|
|
5
|
+
* Supabase sets it to `/functions/v1/<functionName>` (the public mount under
|
|
6
|
+
* which the function and its companion routes live).
|
|
7
|
+
*/
|
|
8
|
+
interface McpPluginApi {
|
|
9
|
+
readonly mcpEntry: string;
|
|
10
|
+
readonly urlPath: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type { McpPluginApi as M };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMcpProtocolHandler
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-DF3DD3KD.js";
|
|
4
4
|
import "../../chunk-MA5H6PSF.js";
|
|
5
|
-
import "../../chunk-
|
|
5
|
+
import "../../chunk-LAFJDJRY.js";
|
|
6
6
|
import "../../chunk-QC3DXQTH.js";
|
|
7
7
|
import "../../chunk-6DXGZZA4.js";
|
|
8
8
|
export {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createInvokeToolHandler
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-MA5H6PSF.js";
|
|
3
|
+
} from "../../chunk-SZLSAGDF.js";
|
|
5
4
|
import {
|
|
6
5
|
createListToolsHandler
|
|
7
|
-
} from "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
6
|
+
} from "../../chunk-PJXKJNSZ.js";
|
|
7
|
+
import "../../chunk-MA5H6PSF.js";
|
|
8
|
+
import "../../chunk-LAFJDJRY.js";
|
|
9
9
|
import "../../chunk-QC3DXQTH.js";
|
|
10
10
|
import "../../chunk-6DXGZZA4.js";
|
|
11
11
|
export {
|