@lovable.dev/mcp-js 0.29.0-rc.4 → 0.30.0-rc.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 +3 -3
- package/README.md +25 -2
- package/dist/{authorize-DIYS7zBP.d.cts → authorize-9hTTG-qJ.d.cts} +1 -1
- package/dist/{authorize-CqYsQYfa.d.ts → authorize-DNUlPT7k.d.ts} +1 -1
- package/dist/base-7cq6HCPO.d.ts +36 -0
- package/dist/base-CHNFFrMG.d.cts +36 -0
- package/dist/cli/extract-manifest.cjs +7 -12
- package/dist/cli/extract-manifest.js +7 -12
- package/dist/{authorize-b3VqTC_7.js → cors-5P69YRDK.js} +79 -28
- package/dist/{authorize-CKMlIxm9.cjs → cors-DIQIXIHY.cjs} +90 -33
- package/dist/{errors-DSe96Ffd.js → errors-Bwd1L0Rf.js} +23 -8
- package/dist/{errors-CN6Da8iz.cjs → errors-Cr95rSkB.cjs} +23 -8
- package/dist/forwarded-DG83ubwT.js +26 -0
- package/dist/forwarded-zNAIofM2.cjs +31 -0
- package/dist/index.cjs +38 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +38 -1
- package/dist/{io-Dx0nPc1-.d.cts → io-CDV3mud0.d.cts} +1 -1
- package/dist/{io-DP3icuxa.d.ts → io-D_5cuaEL.d.ts} +1 -1
- package/dist/mcp-Cb5SavFT.js +348 -0
- package/dist/mcp-Dq6dXUKT.cjs +353 -0
- package/dist/{package-Xk8p8Q_E.cjs → package-QViIguII.cjs} +1 -1
- package/dist/{package-DMs4EVwG.js → package-ZFJI6B7S.js} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +3 -33
- package/dist/protocols/mcp/index.d.ts +3 -33
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +27 -10
- package/dist/protocols/oauth-metadata.d.cts +4 -3
- package/dist/protocols/oauth-metadata.d.ts +4 -3
- package/dist/protocols/oauth-metadata.js +21 -4
- package/dist/schema-CW1jKvio.js +65 -0
- package/dist/schema-D3vClxb7.cjs +76 -0
- package/dist/stacks/supabase/index.cjs +6 -6
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +4 -4
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.d.cts +1 -1
- package/dist/stacks/supabase/vite.d.ts +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +5 -5
- package/dist/stacks/tanstack/index.d.cts +2 -2
- package/dist/stacks/tanstack/index.d.ts +2 -2
- package/dist/stacks/tanstack/index.js +4 -4
- package/dist/stacks/tanstack/vite.cjs +8 -4
- package/dist/stacks/tanstack/vite.d.cts +4 -1
- package/dist/stacks/tanstack/vite.d.ts +4 -1
- package/dist/stacks/tanstack/vite.js +8 -4
- package/dist/{types-C0Wgm9zp.d.cts → types-3CZPz364.d.cts} +65 -29
- package/dist/{types-C0Wgm9zp.d.ts → types-3CZPz364.d.ts} +65 -29
- package/package.json +5 -1
- package/dist/forwarded-Bhwcj22u.cjs +0 -48
- package/dist/forwarded-BlLF3dfc.js +0 -43
- package/dist/mcp-BLs6_g8i.js +0 -174
- package/dist/mcp-DtHM-41c.cjs +0 -179
|
@@ -85,16 +85,49 @@ interface McpAuthConfig {
|
|
|
85
85
|
}
|
|
86
86
|
//#endregion
|
|
87
87
|
//#region src/auth/context.d.ts
|
|
88
|
-
/**
|
|
89
|
-
*
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
*
|
|
93
|
-
|
|
94
|
-
*/
|
|
88
|
+
/** Protocol era of an in-flight request: self-describing `2026-07-28` or the
|
|
89
|
+
* initialization-era `legacy` fallback. */
|
|
90
|
+
type McpProtocolEra = "2026-07-28" | "legacy";
|
|
91
|
+
/** What the client declared about itself on the in-flight request. Unverified
|
|
92
|
+
* client input — a hint, never an authorization signal (use the auth accessors). */
|
|
93
|
+
interface McpClientInfo {
|
|
94
|
+
/** Protocol era of the in-flight request. */
|
|
95
|
+
protocol: McpProtocolEra;
|
|
96
|
+
/** Client name/version from the request envelope (`2026-07-28` era only). */
|
|
97
|
+
info?: {
|
|
98
|
+
name: string;
|
|
99
|
+
version: string;
|
|
100
|
+
title?: string;
|
|
101
|
+
};
|
|
102
|
+
/** Client capabilities from the request envelope (`2026-07-28` era only). */
|
|
103
|
+
capabilities?: Record<string, unknown>;
|
|
104
|
+
/** Extension declarations (`capabilities.extensions`), keyed by extension id. */
|
|
105
|
+
extensions?: Record<string, Record<string, unknown>>;
|
|
106
|
+
}
|
|
107
|
+
/** A progress notification payload. `progress` should increase monotonically. */
|
|
108
|
+
interface ProgressUpdate {
|
|
109
|
+
progress: number;
|
|
110
|
+
total?: number;
|
|
111
|
+
message?: string;
|
|
112
|
+
}
|
|
113
|
+
/** Per-request runtime surface threaded into `ToolContext` by the protocol layer. */
|
|
114
|
+
interface ToolRequestContext {
|
|
115
|
+
signal?: AbortSignal;
|
|
116
|
+
client?: McpClientInfo;
|
|
117
|
+
sendProgress?: (update: ProgressUpdate) => Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
/** The per-request context passed to every tool handler as its second argument. The
|
|
120
|
+
* auth context lives in a private field so the accessors are the only way out; `getToken()` is the sole bearer escape hatch. */
|
|
95
121
|
declare class ToolContext {
|
|
96
122
|
#private;
|
|
97
|
-
constructor(auth: McpAuthContext | undefined);
|
|
123
|
+
constructor(auth: McpAuthContext | undefined, request?: ToolRequestContext);
|
|
124
|
+
/** Aborts when the client cancels the request or disconnects. */
|
|
125
|
+
get signal(): AbortSignal;
|
|
126
|
+
/** What the client declared about itself on this request. Unverified input. */
|
|
127
|
+
get client(): McpClientInfo;
|
|
128
|
+
/** Send a progress notification for this call. A no-op when the client
|
|
129
|
+
* didn't request progress (no `progressToken` on the request). */
|
|
130
|
+
progress(update: ProgressUpdate): Promise<void>;
|
|
98
131
|
/** Whether the in-flight tool call carries a verified auth context. */
|
|
99
132
|
isAuthenticated(): boolean;
|
|
100
133
|
/** The verified bearer token, or `undefined` when unauthenticated. Pass it to downstream APIs; never return or log it. */
|
|
@@ -148,11 +181,10 @@ type MetricsConfig = boolean | MetricsOptions;
|
|
|
148
181
|
* `inputSchema`, use `ZodRawShape`.
|
|
149
182
|
*/
|
|
150
183
|
type ZodSchema = ZodType;
|
|
151
|
-
/**
|
|
152
|
-
*
|
|
153
|
-
* `defineTool`'s `inputSchema`/`outputSchema` expects.
|
|
154
|
-
*/
|
|
184
|
+
/** A raw zod shape (`{ text: z.string() }`) — the format `defineTool`'s
|
|
185
|
+
* `inputSchema` expects and the object shorthand accepted by `outputSchema`. */
|
|
155
186
|
type ZodRawShape$1 = ZodRawShape;
|
|
187
|
+
type ZodOutputSchema = ZodSchema | ZodRawShape$1;
|
|
156
188
|
/**
|
|
157
189
|
* Infer the parsed-output type of a zod raw shape — given
|
|
158
190
|
* `{ text: ZodString, n: ZodNumber }`, produces `{ text: string, n: number }`.
|
|
@@ -217,8 +249,8 @@ type EmbeddedResource = EmbeddedTextResource | EmbeddedBlobResource;
|
|
|
217
249
|
interface ResourceLinkIcon {
|
|
218
250
|
src: string;
|
|
219
251
|
mimeType?: string;
|
|
220
|
-
/**
|
|
221
|
-
sizes?: string;
|
|
252
|
+
/** Array per MCP 2026-07-28; legacy space-separated strings remain accepted. */
|
|
253
|
+
sizes?: string | string[];
|
|
222
254
|
theme?: "light" | "dark";
|
|
223
255
|
}
|
|
224
256
|
interface ResourceLink extends ContentBlockBase {
|
|
@@ -241,6 +273,15 @@ type ContentBlock = TextContent | ImageContent | AudioContent | EmbeddedResource
|
|
|
241
273
|
* the tool returns to the caller.
|
|
242
274
|
*/
|
|
243
275
|
type ToolContent = ContentBlock[];
|
|
276
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
277
|
+
[key: string]: JsonValue;
|
|
278
|
+
};
|
|
279
|
+
/** Handler-input relaxation of `JsonValue`: object properties may be `undefined`
|
|
280
|
+
* (they serialize away, matching `JSON.stringify` semantics). */
|
|
281
|
+
type JsonValueInput = null | boolean | number | string | JsonValueInput[] | {
|
|
282
|
+
[key: string]: JsonValueInput | undefined;
|
|
283
|
+
};
|
|
284
|
+
type McpAllowedOrigins = "any" | readonly string[];
|
|
244
285
|
/**
|
|
245
286
|
* Per-tool client hints (not enforcement). `annotations.title` is
|
|
246
287
|
* intentionally omitted — use `ToolDefinition.title`, which the MCP
|
|
@@ -258,10 +299,10 @@ interface ToolAnnotations {
|
|
|
258
299
|
}
|
|
259
300
|
interface ToolHandlerResult {
|
|
260
301
|
content?: ToolContent;
|
|
261
|
-
structuredContent?:
|
|
302
|
+
structuredContent?: JsonValueInput;
|
|
262
303
|
isError?: boolean;
|
|
263
304
|
}
|
|
264
|
-
interface ToolDefinition<TInput extends ZodRawShape$1 | undefined = ZodRawShape$1 | undefined, TOutput extends
|
|
305
|
+
interface ToolDefinition<TInput extends ZodRawShape$1 | undefined = ZodRawShape$1 | undefined, TOutput extends ZodOutputSchema | undefined = ZodOutputSchema | undefined> {
|
|
265
306
|
readonly name: string;
|
|
266
307
|
/** Human-readable display name shown to the user in MCP clients. Required. */
|
|
267
308
|
readonly title: string;
|
|
@@ -272,32 +313,27 @@ interface ToolDefinition<TInput extends ZodRawShape$1 | undefined = ZodRawShape$
|
|
|
272
313
|
readonly annotations?: ToolAnnotations;
|
|
273
314
|
readonly handler: TInput extends ZodRawShape$1 ? (args: ShapeOutput<TInput>, ctx: ToolContext) => ToolHandlerResult | Promise<ToolHandlerResult> : (args: Record<string, never> | undefined, ctx: ToolContext) => ToolHandlerResult | Promise<ToolHandlerResult>;
|
|
274
315
|
}
|
|
275
|
-
/**
|
|
276
|
-
*
|
|
277
|
-
* happens at the `defineTool` call site; see README §6 for the rationale.
|
|
278
|
-
*/
|
|
316
|
+
/** Type-erased element of `McpDefinition.tools`; per-tool generic typing
|
|
317
|
+
* happens at the `defineTool` call site. */
|
|
279
318
|
interface AnyToolDefinition {
|
|
280
319
|
readonly name: string;
|
|
281
320
|
readonly title: string;
|
|
282
321
|
readonly description: string;
|
|
283
322
|
readonly inputSchema?: ZodRawShape$1;
|
|
284
|
-
readonly outputSchema?:
|
|
323
|
+
readonly outputSchema?: ZodOutputSchema;
|
|
285
324
|
readonly annotations?: ToolAnnotations;
|
|
286
325
|
readonly handler: (args: any, ctx: ToolContext) => ToolHandlerResult | Promise<ToolHandlerResult>;
|
|
287
326
|
}
|
|
288
|
-
/**
|
|
289
|
-
*
|
|
290
|
-
* shape to `defineMcp(...)` — its return type is the branded `McpDefinition`,
|
|
291
|
-
* which is the type every internal factory (`createInvokeToolHandler`,
|
|
292
|
-
* `createMcpProtocolHandler`, …) consumes. The brand forces consumers
|
|
293
|
-
* through `defineMcp`'s `assertUniqueNames` + freeze.
|
|
294
|
-
*/
|
|
327
|
+
/** Input shape for `defineMcp(...)`, whose branded `McpDefinition` return type is what
|
|
328
|
+
* `createMcpProtocolHandler` consumes — the brand forces validation + freeze. */
|
|
295
329
|
interface McpDefinitionInput {
|
|
296
330
|
readonly name: string;
|
|
297
331
|
/** Human-readable display name shown to MCP clients. Required. */
|
|
298
332
|
readonly title: string;
|
|
299
333
|
readonly version: string;
|
|
300
334
|
readonly auth?: McpAuthConfig;
|
|
335
|
+
/** Cross-origin browser access. Omit to allow only same-origin and non-browser requests. */
|
|
336
|
+
readonly allowedOrigins?: McpAllowedOrigins;
|
|
301
337
|
/**
|
|
302
338
|
* Server-level prose the LLM reads as context for all tools on this server.
|
|
303
339
|
* Required for the same reason `ToolDefinition.description` is — optional
|
|
@@ -318,4 +354,4 @@ type McpDefinition = McpDefinitionInput & {
|
|
|
318
354
|
readonly [McpDefinitionBrand]: never;
|
|
319
355
|
};
|
|
320
356
|
//#endregion
|
|
321
|
-
export { McpAuthConfig as C,
|
|
357
|
+
export { McpAuthConfig as A, MetricsConfig as C, ProgressUpdate as D, McpProtocolEra as E, ToolContext as O, ZodSchema as S, McpClientInfo as T, ToolContent as _, EmbeddedResource as a, ZodOutputSchema as b, JsonValue as c, McpDefinition as d, McpDefinitionInput as f, ToolAnnotations as g, TextContent as h, EmbeddedBlobResource as i, JwtClaims as k, JsonValueInput as l, ResourceLinkIcon as m, ContentAnnotations as n, EmbeddedTextResource as o, ResourceLink as p, ContentBlock as r, ImageContent as s, AudioContent as t, McpAllowedOrigins as u, ToolDefinition as v, MetricsOptions as w, ZodRawShape$1 as x, ToolHandlerResult as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/mcp-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0-rc.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=20"
|
|
6
|
+
},
|
|
4
7
|
"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
8
|
"type": "module",
|
|
6
9
|
"repository": {
|
|
@@ -106,6 +109,7 @@
|
|
|
106
109
|
"license": "MIT",
|
|
107
110
|
"dependencies": {
|
|
108
111
|
"@modelcontextprotocol/sdk": "1.28.0",
|
|
112
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
109
113
|
"esbuild": "^0.27.0",
|
|
110
114
|
"jose": "^6.2.2"
|
|
111
115
|
},
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
//#region src/core/forwarded.ts
|
|
2
|
-
/**
|
|
3
|
-
* Rewrites a request's URL to the public origin a reverse proxy forwards via
|
|
4
|
-
* `X-Forwarded-*`, so SDK-derived OAuth `resource`/metadata/`WWW-Authenticate`
|
|
5
|
-
* URLs name the host the client actually connected to — not the internal
|
|
6
|
-
* backend origin the proxy rewrites `Host` to. Without this, an app on a custom
|
|
7
|
-
* domain advertises its internal `*.lovable.dev`/`*.supabase.co` host and every
|
|
8
|
-
* MCP call 401s on an audience the client can't satisfy.
|
|
9
|
-
*
|
|
10
|
-
* Each header is trusted independently, because a forwarded value is only as
|
|
11
|
-
* trustworthy as the proxy that sets it:
|
|
12
|
-
* - `X-Forwarded-Host` can redirect the advertised resource to an attacker
|
|
13
|
-
* origin (the threat `auth/resource.ts` guards against), so it is honored only
|
|
14
|
-
* when `trustForwardedHost` is set — enabled on emit paths that sit behind a
|
|
15
|
-
* proxy which *overwrites* the header (Lovable's web-proxy does), never by a
|
|
16
|
-
* bare SDK consumer.
|
|
17
|
-
* - `X-Forwarded-Proto` only selects http/https, so it is gated separately:
|
|
18
|
-
* Supabase terminates TLS upstream and forwards over http with a
|
|
19
|
-
* gateway-stamped proto, so that stack always trusts it; the TanStack proxy
|
|
20
|
-
* does not sanitize proto, so its Vite plugin never emits the flag.
|
|
21
|
-
*/
|
|
22
|
-
function applyForwardedOrigin(request, options) {
|
|
23
|
-
const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
|
|
24
|
-
const host = options.trustForwardedHost ? firstForwardedValue(request, "x-forwarded-host") : void 0;
|
|
25
|
-
if (proto === void 0 && host === void 0) return request;
|
|
26
|
-
const url = new URL(request.url);
|
|
27
|
-
let changed = false;
|
|
28
|
-
if (proto !== void 0 && `${proto}:` !== url.protocol) {
|
|
29
|
-
url.protocol = `${proto}:`;
|
|
30
|
-
changed = true;
|
|
31
|
-
}
|
|
32
|
-
if (host !== void 0 && host !== url.host) {
|
|
33
|
-
url.host = host;
|
|
34
|
-
changed = true;
|
|
35
|
-
}
|
|
36
|
-
return changed ? new Request(url.href, request) : request;
|
|
37
|
-
}
|
|
38
|
-
function firstForwardedValue(request, header) {
|
|
39
|
-
const value = request.headers.get(header)?.split(",")[0]?.trim();
|
|
40
|
-
return value ? value : void 0;
|
|
41
|
-
}
|
|
42
|
-
//#endregion
|
|
43
|
-
Object.defineProperty(exports, "applyForwardedOrigin", {
|
|
44
|
-
enumerable: true,
|
|
45
|
-
get: function() {
|
|
46
|
-
return applyForwardedOrigin;
|
|
47
|
-
}
|
|
48
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
//#region src/core/forwarded.ts
|
|
2
|
-
/**
|
|
3
|
-
* Rewrites a request's URL to the public origin a reverse proxy forwards via
|
|
4
|
-
* `X-Forwarded-*`, so SDK-derived OAuth `resource`/metadata/`WWW-Authenticate`
|
|
5
|
-
* URLs name the host the client actually connected to — not the internal
|
|
6
|
-
* backend origin the proxy rewrites `Host` to. Without this, an app on a custom
|
|
7
|
-
* domain advertises its internal `*.lovable.dev`/`*.supabase.co` host and every
|
|
8
|
-
* MCP call 401s on an audience the client can't satisfy.
|
|
9
|
-
*
|
|
10
|
-
* Each header is trusted independently, because a forwarded value is only as
|
|
11
|
-
* trustworthy as the proxy that sets it:
|
|
12
|
-
* - `X-Forwarded-Host` can redirect the advertised resource to an attacker
|
|
13
|
-
* origin (the threat `auth/resource.ts` guards against), so it is honored only
|
|
14
|
-
* when `trustForwardedHost` is set — enabled on emit paths that sit behind a
|
|
15
|
-
* proxy which *overwrites* the header (Lovable's web-proxy does), never by a
|
|
16
|
-
* bare SDK consumer.
|
|
17
|
-
* - `X-Forwarded-Proto` only selects http/https, so it is gated separately:
|
|
18
|
-
* Supabase terminates TLS upstream and forwards over http with a
|
|
19
|
-
* gateway-stamped proto, so that stack always trusts it; the TanStack proxy
|
|
20
|
-
* does not sanitize proto, so its Vite plugin never emits the flag.
|
|
21
|
-
*/
|
|
22
|
-
function applyForwardedOrigin(request, options) {
|
|
23
|
-
const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
|
|
24
|
-
const host = options.trustForwardedHost ? firstForwardedValue(request, "x-forwarded-host") : void 0;
|
|
25
|
-
if (proto === void 0 && host === void 0) return request;
|
|
26
|
-
const url = new URL(request.url);
|
|
27
|
-
let changed = false;
|
|
28
|
-
if (proto !== void 0 && `${proto}:` !== url.protocol) {
|
|
29
|
-
url.protocol = `${proto}:`;
|
|
30
|
-
changed = true;
|
|
31
|
-
}
|
|
32
|
-
if (host !== void 0 && host !== url.host) {
|
|
33
|
-
url.host = host;
|
|
34
|
-
changed = true;
|
|
35
|
-
}
|
|
36
|
-
return changed ? new Request(url.href, request) : request;
|
|
37
|
-
}
|
|
38
|
-
function firstForwardedValue(request, header) {
|
|
39
|
-
const value = request.headers.get(header)?.split(",")[0]?.trim();
|
|
40
|
-
return value ? value : void 0;
|
|
41
|
-
}
|
|
42
|
-
//#endregion
|
|
43
|
-
export { applyForwardedOrigin as t };
|
package/dist/mcp-BLs6_g8i.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { i as log, r as describeError } from "./logger-Ctv5xUSD.js";
|
|
2
|
-
import { i as ToolContext, n as errorSummary, r as resolveToolErrorMessage } from "./errors-DSe96Ffd.js";
|
|
3
|
-
import { c as nowMs, f as corsPreflightResponse, s as createNoopRecorder, t as createRequestAuthorizer, u as finalizeWireResponse } from "./authorize-b3VqTC_7.js";
|
|
4
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
6
|
-
//#region src/core/content.ts
|
|
7
|
-
/** Join the text of a result's `type: "text"` blocks into one string, dropping
|
|
8
|
-
* binary/resource blocks. Used to surface a `tool_error` message in the local
|
|
9
|
-
* log; never sent over the telemetry wire. Returns undefined when there is no
|
|
10
|
-
* text to show. */
|
|
11
|
-
function extractTextContent(content) {
|
|
12
|
-
if (!content) return void 0;
|
|
13
|
-
const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
|
|
14
|
-
return text.length > 0 ? text : void 0;
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/protocols/gate.ts
|
|
18
|
-
function createGatedHandler(gate) {
|
|
19
|
-
const allow = [...gate.methods, "OPTIONS"].join(", ");
|
|
20
|
-
const respond = async (request, recorder) => {
|
|
21
|
-
const authResult = await gate.authorizer.authorize(request, recorder);
|
|
22
|
-
if (!authResult.ok) return authResult.response;
|
|
23
|
-
if (!gate.methods.includes(request.method)) return gate.methodNotAllowed(allow);
|
|
24
|
-
return gate.handle(request, authResult, recorder);
|
|
25
|
-
};
|
|
26
|
-
return async (request, recorder = createNoopRecorder()) => {
|
|
27
|
-
if (request.method === "OPTIONS") return corsPreflightResponse(allow);
|
|
28
|
-
const final = finalizeWireResponse(request, await respond(request, recorder));
|
|
29
|
-
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
30
|
-
return final;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/protocols/mcp/protocol.ts
|
|
35
|
-
function statelessMethodNotAllowed(allow) {
|
|
36
|
-
return Response.json({
|
|
37
|
-
jsonrpc: "2.0",
|
|
38
|
-
id: null,
|
|
39
|
-
error: {
|
|
40
|
-
code: -32e3,
|
|
41
|
-
message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
|
|
42
|
-
}
|
|
43
|
-
}, {
|
|
44
|
-
status: 405,
|
|
45
|
-
headers: { Allow: allow }
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Adapt a Lovable tool to the SDK's tool callback. The boundary cast bridges
|
|
50
|
-
* our SDK-decoupled content-block types to the SDK's; they match structurally.
|
|
51
|
-
*/
|
|
52
|
-
function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
53
|
-
const endUserId = auth?.principal.sub;
|
|
54
|
-
return (async (first) => {
|
|
55
|
-
const args = tool.inputSchema ? first ?? {} : {};
|
|
56
|
-
const start = nowMs();
|
|
57
|
-
let result;
|
|
58
|
-
try {
|
|
59
|
-
result = await tool.handler(args, new ToolContext(auth));
|
|
60
|
-
} catch (err) {
|
|
61
|
-
const callerMessage = resolveToolErrorMessage(err);
|
|
62
|
-
if (callerMessage === void 0) {
|
|
63
|
-
await recorder.emit({
|
|
64
|
-
tool: tool.name,
|
|
65
|
-
method: "tools/call",
|
|
66
|
-
outcome: "handler_error",
|
|
67
|
-
durationMs: nowMs() - start,
|
|
68
|
-
errorText: errorSummary(err),
|
|
69
|
-
endUserId
|
|
70
|
-
});
|
|
71
|
-
return {
|
|
72
|
-
content: [{
|
|
73
|
-
type: "text",
|
|
74
|
-
text: "tool execution failed"
|
|
75
|
-
}],
|
|
76
|
-
isError: true
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
result = {
|
|
80
|
-
content: [{
|
|
81
|
-
type: "text",
|
|
82
|
-
text: callerMessage
|
|
83
|
-
}],
|
|
84
|
-
isError: true
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (result == null) {
|
|
88
|
-
await recorder.emit({
|
|
89
|
-
tool: tool.name,
|
|
90
|
-
method: "tools/call",
|
|
91
|
-
outcome: "handler_error",
|
|
92
|
-
durationMs: nowMs() - start,
|
|
93
|
-
endUserId
|
|
94
|
-
});
|
|
95
|
-
return {
|
|
96
|
-
content: [{
|
|
97
|
-
type: "text",
|
|
98
|
-
text: `tool "${tool.name}" returned no result`
|
|
99
|
-
}],
|
|
100
|
-
isError: true
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
await recorder.emit({
|
|
104
|
-
tool: tool.name,
|
|
105
|
-
method: "tools/call",
|
|
106
|
-
outcome: result.isError ? "tool_error" : "ok",
|
|
107
|
-
durationMs: nowMs() - start,
|
|
108
|
-
errorText: result.isError ? extractTextContent(result.content) : void 0,
|
|
109
|
-
endUserId
|
|
110
|
-
});
|
|
111
|
-
return {
|
|
112
|
-
content: result.content ?? [],
|
|
113
|
-
structuredContent: result.structuredContent,
|
|
114
|
-
isError: result.isError
|
|
115
|
-
};
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Build a Web-Standard request handler that serves the MCP protocol over
|
|
120
|
-
* streamable HTTP. A fresh `McpServer` + transport is constructed per
|
|
121
|
-
* request — the transport is stateless, so this is cheap and there is no
|
|
122
|
-
* cross-request state to leak. The per-request `recorder` is supplied by the
|
|
123
|
-
* caller (the stack handler) so telemetry reads the runtime env in-flight.
|
|
124
|
-
*/
|
|
125
|
-
function createMcpProtocolHandler(mcp, options = {}) {
|
|
126
|
-
const authorizer = createRequestAuthorizer(mcp, options);
|
|
127
|
-
const handle = async (request, authResult, recorder) => {
|
|
128
|
-
try {
|
|
129
|
-
const server = new McpServer({
|
|
130
|
-
name: mcp.name,
|
|
131
|
-
version: mcp.version,
|
|
132
|
-
title: mcp.title
|
|
133
|
-
}, { instructions: mcp.instructions });
|
|
134
|
-
for (const tool of mcp.tools) server.registerTool(tool.name, {
|
|
135
|
-
title: tool.title,
|
|
136
|
-
description: tool.description,
|
|
137
|
-
inputSchema: tool.inputSchema,
|
|
138
|
-
outputSchema: tool.outputSchema,
|
|
139
|
-
annotations: tool.annotations
|
|
140
|
-
}, adaptToolToSdkCallback(tool, authResult.auth, recorder));
|
|
141
|
-
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
142
|
-
await server.connect(transport);
|
|
143
|
-
return await transport.handleRequest(request);
|
|
144
|
-
} catch (err) {
|
|
145
|
-
await recorder.emit({
|
|
146
|
-
tool: null,
|
|
147
|
-
method: "transport",
|
|
148
|
-
outcome: "transport_error",
|
|
149
|
-
durationMs: 0,
|
|
150
|
-
endUserId: authResult.auth?.principal.sub
|
|
151
|
-
});
|
|
152
|
-
log.error("mcp.transport_error", {
|
|
153
|
-
...describeError(err),
|
|
154
|
-
outcome: "500 internal error"
|
|
155
|
-
});
|
|
156
|
-
return Response.json({
|
|
157
|
-
jsonrpc: "2.0",
|
|
158
|
-
id: null,
|
|
159
|
-
error: {
|
|
160
|
-
code: -32603,
|
|
161
|
-
message: "internal error"
|
|
162
|
-
}
|
|
163
|
-
}, { status: 500 });
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
return createGatedHandler({
|
|
167
|
-
authorizer,
|
|
168
|
-
methods: ["POST"],
|
|
169
|
-
methodNotAllowed: statelessMethodNotAllowed,
|
|
170
|
-
handle
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
//#endregion
|
|
174
|
-
export { createMcpProtocolHandler as t };
|
package/dist/mcp-DtHM-41c.cjs
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
const require_logger = require("./logger-CN1q-KNn.cjs");
|
|
2
|
-
const require_errors = require("./errors-CN6Da8iz.cjs");
|
|
3
|
-
const require_authorize = require("./authorize-CKMlIxm9.cjs");
|
|
4
|
-
let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
|
-
let _modelcontextprotocol_sdk_server_webStandardStreamableHttp_js = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
|
|
6
|
-
//#region src/core/content.ts
|
|
7
|
-
/** Join the text of a result's `type: "text"` blocks into one string, dropping
|
|
8
|
-
* binary/resource blocks. Used to surface a `tool_error` message in the local
|
|
9
|
-
* log; never sent over the telemetry wire. Returns undefined when there is no
|
|
10
|
-
* text to show. */
|
|
11
|
-
function extractTextContent(content) {
|
|
12
|
-
if (!content) return void 0;
|
|
13
|
-
const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
|
|
14
|
-
return text.length > 0 ? text : void 0;
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/protocols/gate.ts
|
|
18
|
-
function createGatedHandler(gate) {
|
|
19
|
-
const allow = [...gate.methods, "OPTIONS"].join(", ");
|
|
20
|
-
const respond = async (request, recorder) => {
|
|
21
|
-
const authResult = await gate.authorizer.authorize(request, recorder);
|
|
22
|
-
if (!authResult.ok) return authResult.response;
|
|
23
|
-
if (!gate.methods.includes(request.method)) return gate.methodNotAllowed(allow);
|
|
24
|
-
return gate.handle(request, authResult, recorder);
|
|
25
|
-
};
|
|
26
|
-
return async (request, recorder = require_authorize.createNoopRecorder()) => {
|
|
27
|
-
if (request.method === "OPTIONS") return require_authorize.corsPreflightResponse(allow);
|
|
28
|
-
const final = require_authorize.finalizeWireResponse(request, await respond(request, recorder));
|
|
29
|
-
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
30
|
-
return final;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/protocols/mcp/protocol.ts
|
|
35
|
-
function statelessMethodNotAllowed(allow) {
|
|
36
|
-
return Response.json({
|
|
37
|
-
jsonrpc: "2.0",
|
|
38
|
-
id: null,
|
|
39
|
-
error: {
|
|
40
|
-
code: -32e3,
|
|
41
|
-
message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
|
|
42
|
-
}
|
|
43
|
-
}, {
|
|
44
|
-
status: 405,
|
|
45
|
-
headers: { Allow: allow }
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Adapt a Lovable tool to the SDK's tool callback. The boundary cast bridges
|
|
50
|
-
* our SDK-decoupled content-block types to the SDK's; they match structurally.
|
|
51
|
-
*/
|
|
52
|
-
function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
53
|
-
const endUserId = auth?.principal.sub;
|
|
54
|
-
return (async (first) => {
|
|
55
|
-
const args = tool.inputSchema ? first ?? {} : {};
|
|
56
|
-
const start = require_authorize.nowMs();
|
|
57
|
-
let result;
|
|
58
|
-
try {
|
|
59
|
-
result = await tool.handler(args, new require_errors.ToolContext(auth));
|
|
60
|
-
} catch (err) {
|
|
61
|
-
const callerMessage = require_errors.resolveToolErrorMessage(err);
|
|
62
|
-
if (callerMessage === void 0) {
|
|
63
|
-
await recorder.emit({
|
|
64
|
-
tool: tool.name,
|
|
65
|
-
method: "tools/call",
|
|
66
|
-
outcome: "handler_error",
|
|
67
|
-
durationMs: require_authorize.nowMs() - start,
|
|
68
|
-
errorText: require_errors.errorSummary(err),
|
|
69
|
-
endUserId
|
|
70
|
-
});
|
|
71
|
-
return {
|
|
72
|
-
content: [{
|
|
73
|
-
type: "text",
|
|
74
|
-
text: "tool execution failed"
|
|
75
|
-
}],
|
|
76
|
-
isError: true
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
result = {
|
|
80
|
-
content: [{
|
|
81
|
-
type: "text",
|
|
82
|
-
text: callerMessage
|
|
83
|
-
}],
|
|
84
|
-
isError: true
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (result == null) {
|
|
88
|
-
await recorder.emit({
|
|
89
|
-
tool: tool.name,
|
|
90
|
-
method: "tools/call",
|
|
91
|
-
outcome: "handler_error",
|
|
92
|
-
durationMs: require_authorize.nowMs() - start,
|
|
93
|
-
endUserId
|
|
94
|
-
});
|
|
95
|
-
return {
|
|
96
|
-
content: [{
|
|
97
|
-
type: "text",
|
|
98
|
-
text: `tool "${tool.name}" returned no result`
|
|
99
|
-
}],
|
|
100
|
-
isError: true
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
await recorder.emit({
|
|
104
|
-
tool: tool.name,
|
|
105
|
-
method: "tools/call",
|
|
106
|
-
outcome: result.isError ? "tool_error" : "ok",
|
|
107
|
-
durationMs: require_authorize.nowMs() - start,
|
|
108
|
-
errorText: result.isError ? extractTextContent(result.content) : void 0,
|
|
109
|
-
endUserId
|
|
110
|
-
});
|
|
111
|
-
return {
|
|
112
|
-
content: result.content ?? [],
|
|
113
|
-
structuredContent: result.structuredContent,
|
|
114
|
-
isError: result.isError
|
|
115
|
-
};
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Build a Web-Standard request handler that serves the MCP protocol over
|
|
120
|
-
* streamable HTTP. A fresh `McpServer` + transport is constructed per
|
|
121
|
-
* request — the transport is stateless, so this is cheap and there is no
|
|
122
|
-
* cross-request state to leak. The per-request `recorder` is supplied by the
|
|
123
|
-
* caller (the stack handler) so telemetry reads the runtime env in-flight.
|
|
124
|
-
*/
|
|
125
|
-
function createMcpProtocolHandler(mcp, options = {}) {
|
|
126
|
-
const authorizer = require_authorize.createRequestAuthorizer(mcp, options);
|
|
127
|
-
const handle = async (request, authResult, recorder) => {
|
|
128
|
-
try {
|
|
129
|
-
const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
|
|
130
|
-
name: mcp.name,
|
|
131
|
-
version: mcp.version,
|
|
132
|
-
title: mcp.title
|
|
133
|
-
}, { instructions: mcp.instructions });
|
|
134
|
-
for (const tool of mcp.tools) server.registerTool(tool.name, {
|
|
135
|
-
title: tool.title,
|
|
136
|
-
description: tool.description,
|
|
137
|
-
inputSchema: tool.inputSchema,
|
|
138
|
-
outputSchema: tool.outputSchema,
|
|
139
|
-
annotations: tool.annotations
|
|
140
|
-
}, adaptToolToSdkCallback(tool, authResult.auth, recorder));
|
|
141
|
-
const transport = new _modelcontextprotocol_sdk_server_webStandardStreamableHttp_js.WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
142
|
-
await server.connect(transport);
|
|
143
|
-
return await transport.handleRequest(request);
|
|
144
|
-
} catch (err) {
|
|
145
|
-
await recorder.emit({
|
|
146
|
-
tool: null,
|
|
147
|
-
method: "transport",
|
|
148
|
-
outcome: "transport_error",
|
|
149
|
-
durationMs: 0,
|
|
150
|
-
endUserId: authResult.auth?.principal.sub
|
|
151
|
-
});
|
|
152
|
-
require_logger.log.error("mcp.transport_error", {
|
|
153
|
-
...require_logger.describeError(err),
|
|
154
|
-
outcome: "500 internal error"
|
|
155
|
-
});
|
|
156
|
-
return Response.json({
|
|
157
|
-
jsonrpc: "2.0",
|
|
158
|
-
id: null,
|
|
159
|
-
error: {
|
|
160
|
-
code: -32603,
|
|
161
|
-
message: "internal error"
|
|
162
|
-
}
|
|
163
|
-
}, { status: 500 });
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
return createGatedHandler({
|
|
167
|
-
authorizer,
|
|
168
|
-
methods: ["POST"],
|
|
169
|
-
methodNotAllowed: statelessMethodNotAllowed,
|
|
170
|
-
handle
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
//#endregion
|
|
174
|
-
Object.defineProperty(exports, "createMcpProtocolHandler", {
|
|
175
|
-
enumerable: true,
|
|
176
|
-
get: function() {
|
|
177
|
-
return createMcpProtocolHandler;
|
|
178
|
-
}
|
|
179
|
-
});
|