@lovable.dev/mcp-js 2.0.4 → 2.1.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.md +82 -1
- package/dist/{authorize-oCEahmRv.d.cts → authorize-BC4TM__8.d.cts} +1 -1
- package/dist/{authorize-B5VMSzcN.d.ts → authorize-DEsM-zTN.d.ts} +1 -1
- package/dist/{base-DnCoJOZN.d.ts → base-4G2k3bMl.d.cts} +2 -2
- package/dist/{base-DAkOPRay.d.cts → base-BnlJz6TE.d.ts} +2 -2
- package/dist/cli/extract-manifest.cjs +1 -1
- package/dist/cli/extract-manifest.js +1 -1
- package/dist/{cors-DKVbSs1E.cjs → cors-0ZCwmwyK.cjs} +1 -1
- package/dist/{cors-C0JXMCfE.js → cors-D4BjUpgU.js} +1 -1
- package/dist/errors-CdrGi5aP.cjs +309 -0
- package/dist/errors-DTvfh8lb.js +262 -0
- package/dist/index.cjs +4 -147
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +3 -147
- package/dist/{io-D2PGAhYM.d.cts → io-9jGNgPfl.d.cts} +1 -1
- package/dist/{io-v7czkKcI.js → io-BEoJ_gb5.js} +3 -2
- package/dist/{io-BD2TuqtG.d.ts → io-BLZ8jYW6.d.ts} +1 -1
- package/dist/{io-qjQFeOKp.cjs → io-Cz81GsrM.cjs} +3 -2
- package/dist/{mcp-Bs-rssdi.js → mcp-CuHyVOZK.js} +92 -15
- package/dist/{mcp-CoxglQb6.cjs → mcp-Q_rjpR99.cjs} +92 -15
- package/dist/{package-RbfLOg54.js → package-BAYb-3dX.js} +1 -1
- package/dist/{package-D1rphzeM.cjs → package-DlouusYQ.cjs} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +3 -3
- package/dist/protocols/mcp/index.d.ts +3 -3
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +1 -1
- package/dist/protocols/oauth-metadata.d.cts +3 -3
- package/dist/protocols/oauth-metadata.d.ts +3 -3
- package/dist/protocols/oauth-metadata.js +1 -1
- package/dist/stacks/supabase/index.cjs +2 -2
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +2 -2
- 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 +2 -2
- package/dist/stacks/tanstack/index.d.cts +2 -2
- package/dist/stacks/tanstack/index.d.ts +2 -2
- package/dist/stacks/tanstack/index.js +2 -2
- package/dist/stacks/tanstack/vite.d.cts +1 -1
- package/dist/stacks/tanstack/vite.d.ts +1 -1
- package/dist/{types-C6O4ciic.d.ts → types-S6J_QTXw.d.cts} +60 -20
- package/dist/{types-C6O4ciic.d.cts → types-S6J_QTXw.d.ts} +60 -20
- package/package.json +2 -2
- package/dist/errors-Bwd1L0Rf.js +0 -100
- package/dist/errors-Cr95rSkB.cjs +0 -123
package/dist/index.js
CHANGED
|
@@ -1,149 +1,5 @@
|
|
|
1
|
-
import { a as setLogLevel
|
|
2
|
-
import { t as
|
|
3
|
-
import { i as ToolContext, t as ToolError } from "./errors-Bwd1L0Rf.js";
|
|
4
|
-
//#region src/core/define.ts
|
|
5
|
-
function assertUniqueNames(mcp) {
|
|
6
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7
|
-
for (const tool of mcp.tools) {
|
|
8
|
-
if (seen.has(tool.name)) throw new Error(`@lovable.dev/mcp-js: duplicate tool name "${tool.name}"`);
|
|
9
|
-
seen.add(tool.name);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
function assertNonEmptyString(label, value) {
|
|
13
|
-
if (value.trim() === "") throw new Error(`@lovable.dev/mcp-js: ${label} must not be empty`);
|
|
14
|
-
if (value !== value.trim()) throw new Error(`@lovable.dev/mcp-js: ${label} must not have leading or trailing whitespace`);
|
|
15
|
-
}
|
|
16
|
-
function assertHttpsUrlField(name, value) {
|
|
17
|
-
assertNonEmptyString(`auth.${name}`, value);
|
|
18
|
-
parseSafeUrl(`@lovable.dev/mcp-js: auth.${name}`, value);
|
|
19
|
-
}
|
|
20
|
-
function assertScope(scope) {
|
|
21
|
-
if (scope.trim() === "" || /\s/.test(scope)) throw new Error(`@lovable.dev/mcp-js: OAuth scopes must be non-empty space-free tokens`);
|
|
22
|
-
}
|
|
23
|
-
function assertJwtAlgorithm(algorithm) {
|
|
24
|
-
if (algorithm.trim() === "" || /\s/.test(algorithm)) throw new Error(`@lovable.dev/mcp-js: auth.algorithms must contain non-empty space-free tokens`);
|
|
25
|
-
if (/^HS\d+$/i.test(algorithm) || algorithm.toLowerCase() === "none") throw new Error(`@lovable.dev/mcp-js: auth.algorithms cannot include "${algorithm}"; this verifier is JWKS-only`);
|
|
26
|
-
}
|
|
27
|
-
const MAX_CLOCK_TOLERANCE_SECONDS = 300;
|
|
28
|
-
function assertClockToleranceSeconds(value) {
|
|
29
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > MAX_CLOCK_TOLERANCE_SECONDS) throw new Error(`@lovable.dev/mcp-js: auth.clockToleranceSeconds must be a non-negative number of seconds no greater than ${MAX_CLOCK_TOLERANCE_SECONDS}`);
|
|
30
|
-
}
|
|
31
|
-
function assertOAuthConfig(auth) {
|
|
32
|
-
if (auth.type !== "oauth") {
|
|
33
|
-
const authType = String(auth.type);
|
|
34
|
-
throw new Error(`@lovable.dev/mcp-js: unsupported auth type "${authType}"`);
|
|
35
|
-
}
|
|
36
|
-
if (auth.issuer === void 0) throw new Error(`@lovable.dev/mcp-js: auth.issuer is required`);
|
|
37
|
-
assertHttpsUrlField("issuer", auth.issuer);
|
|
38
|
-
if (auth.jwksUri !== void 0) assertHttpsUrlField("jwksUri", auth.jwksUri);
|
|
39
|
-
if (auth.resource !== void 0) assertHttpsUrlField("resource", auth.resource);
|
|
40
|
-
if (auth.resourceName !== void 0) assertNonEmptyString("auth.resourceName", auth.resourceName);
|
|
41
|
-
if (auth.resourceDocumentation !== void 0) assertHttpsUrlField("resourceDocumentation", auth.resourceDocumentation);
|
|
42
|
-
if (auth.protectedResourceMetadataUrl !== void 0) assertHttpsUrlField("protectedResourceMetadataUrl", auth.protectedResourceMetadataUrl);
|
|
43
|
-
if (auth.acceptedAudiences !== void 0) {
|
|
44
|
-
if (!Array.isArray(auth.acceptedAudiences)) throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences must be an array`);
|
|
45
|
-
if (auth.acceptedAudiences.length === 0) throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences must not be empty`);
|
|
46
|
-
for (const audience of auth.acceptedAudiences) {
|
|
47
|
-
if (typeof audience !== "string") throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences must contain strings`);
|
|
48
|
-
assertNonEmptyString("auth.acceptedAudiences", audience);
|
|
49
|
-
if (trimTrailingSlash(audience) === "") throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences entries must not be only slashes`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (auth.requireOAuthClientClaim !== void 0 && typeof auth.requireOAuthClientClaim !== "boolean") throw new Error(`@lovable.dev/mcp-js: auth.requireOAuthClientClaim must be a boolean`);
|
|
53
|
-
if (auth.acceptResourceClaim !== void 0 && typeof auth.acceptResourceClaim !== "boolean") throw new Error(`@lovable.dev/mcp-js: auth.acceptResourceClaim must be a boolean`);
|
|
54
|
-
if (auth.requiredScopes !== void 0) {
|
|
55
|
-
if (!Array.isArray(auth.requiredScopes)) throw new Error(`@lovable.dev/mcp-js: auth.requiredScopes must be an array`);
|
|
56
|
-
for (const scope of auth.requiredScopes) assertScope(scope);
|
|
57
|
-
}
|
|
58
|
-
if (auth.algorithms !== void 0) {
|
|
59
|
-
if (!Array.isArray(auth.algorithms)) throw new Error(`@lovable.dev/mcp-js: auth.algorithms must be an array`);
|
|
60
|
-
if (auth.algorithms.length === 0) throw new Error(`@lovable.dev/mcp-js: auth.algorithms must not be empty`);
|
|
61
|
-
for (const algorithm of auth.algorithms) assertJwtAlgorithm(algorithm);
|
|
62
|
-
}
|
|
63
|
-
if (auth.accessTokenTyp !== void 0) {
|
|
64
|
-
if (!Array.isArray(auth.accessTokenTyp)) throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must be an array`);
|
|
65
|
-
if (auth.accessTokenTyp.length === 0) throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must not be empty`);
|
|
66
|
-
for (const typ of auth.accessTokenTyp) {
|
|
67
|
-
if (typeof typ !== "string") throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must contain strings`);
|
|
68
|
-
assertNonEmptyString("auth.accessTokenTyp", typ);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (auth.clockToleranceSeconds !== void 0) assertClockToleranceSeconds(auth.clockToleranceSeconds);
|
|
72
|
-
if (auth.resource === void 0 && auth.acceptedAudiences === void 0) throw new Error(`@lovable.dev/mcp-js: auth.resource or auth.acceptedAudiences is required`);
|
|
73
|
-
}
|
|
74
|
-
function freezeAuth(auth) {
|
|
75
|
-
if (!auth) return;
|
|
76
|
-
if (auth.acceptedAudiences) Object.freeze(auth.acceptedAudiences);
|
|
77
|
-
if (auth.requiredScopes) Object.freeze(auth.requiredScopes);
|
|
78
|
-
if (auth.algorithms) Object.freeze(auth.algorithms);
|
|
79
|
-
if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
|
|
80
|
-
Object.freeze(auth);
|
|
81
|
-
}
|
|
82
|
-
function assertAllowedOrigins(allowedOrigins) {
|
|
83
|
-
if (allowedOrigins === void 0 || allowedOrigins === "any") return;
|
|
84
|
-
if (!Array.isArray(allowedOrigins)) throw new Error(`@lovable.dev/mcp-js: allowedOrigins must be "any" or an array of exact origins`);
|
|
85
|
-
for (const origin of allowedOrigins) {
|
|
86
|
-
if (typeof origin !== "string") throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain strings`);
|
|
87
|
-
let parsed;
|
|
88
|
-
try {
|
|
89
|
-
parsed = new URL(origin);
|
|
90
|
-
} catch {
|
|
91
|
-
throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain valid exact origins`);
|
|
92
|
-
}
|
|
93
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.origin !== origin) throw new Error(`@lovable.dev/mcp-js: allowedOrigins entry "${origin}" must be an exact http(s) origin without a path, query, fragment, or trailing slash`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function assertToolSchemas(mcp) {
|
|
97
|
-
for (const tool of mcp.tools) for (const [name, definition, strategy] of [[
|
|
98
|
-
"inputSchema",
|
|
99
|
-
tool.inputSchema,
|
|
100
|
-
"input"
|
|
101
|
-
], [
|
|
102
|
-
"outputSchema",
|
|
103
|
-
tool.outputSchema,
|
|
104
|
-
"output"
|
|
105
|
-
]]) {
|
|
106
|
-
if (!definition) continue;
|
|
107
|
-
try {
|
|
108
|
-
jsonSchemaFromDefinition(definition, strategy);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
111
|
-
throw new Error(`@lovable.dev/mcp-js: tool "${tool.name}" ${name} cannot be represented as JSON Schema${detail}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
117
|
-
* `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
|
|
118
|
-
*
|
|
119
|
-
* `inputSchema` is a raw zod shape (e.g. `{ text: z.string() }`), not a
|
|
120
|
-
* full `z.object(...)` — the MCP SDK builds the object wrapper internally
|
|
121
|
-
* and the raw shape is what the JSON-RPC `tools/list` payload exposes.
|
|
122
|
-
*/
|
|
123
|
-
function defineTool(def) {
|
|
124
|
-
return def;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Declare the MCP server. `export default` the result from your MCP
|
|
128
|
-
* entrypoint (default `lib/mcp/index.ts`); the Vite plugin reads it to
|
|
129
|
-
* emit the framework-specific route(s) at build time.
|
|
130
|
-
*/
|
|
131
|
-
function defineMcp(def) {
|
|
132
|
-
assertNonEmptyString("name", def.name);
|
|
133
|
-
assertNonEmptyString("title", def.title);
|
|
134
|
-
assertNonEmptyString("version", def.version);
|
|
135
|
-
assertUniqueNames(def);
|
|
136
|
-
if (def.auth) assertOAuthConfig(def.auth);
|
|
137
|
-
assertAllowedOrigins(def.allowedOrigins);
|
|
138
|
-
assertToolSchemas(def);
|
|
139
|
-
resolveMetricsConfig(def.metrics);
|
|
140
|
-
freezeAuth(def.auth);
|
|
141
|
-
if (Array.isArray(def.allowedOrigins)) Object.freeze(def.allowedOrigins);
|
|
142
|
-
Object.freeze(def.tools);
|
|
143
|
-
for (const tool of def.tools) Object.freeze(tool);
|
|
144
|
-
return def;
|
|
145
|
-
}
|
|
146
|
-
//#endregion
|
|
1
|
+
import { a as setLogLevel } from "./logger-Ctv5xUSD.js";
|
|
2
|
+
import { a as ToolContext, i as AuthContext, o as defineMcp, s as defineTool, t as ToolError } from "./errors-DTvfh8lb.js";
|
|
147
3
|
//#region src/auth/config.ts
|
|
148
4
|
function frozenArray(value) {
|
|
149
5
|
return value === void 0 ? void 0 : Object.freeze([...value]);
|
|
@@ -178,4 +34,4 @@ function issuer(options) {
|
|
|
178
34
|
const oauth = Object.freeze({ issuer });
|
|
179
35
|
const auth = Object.freeze({ oauth });
|
|
180
36
|
//#endregion
|
|
181
|
-
export { ToolContext, ToolError, auth, defineMcp, defineTool, setLogLevel };
|
|
37
|
+
export { AuthContext, ToolContext, ToolError, auth, defineMcp, defineTool, setLogLevel };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as jsonSchemaFromDefinition } from "./schema-CW1jKvio.js";
|
|
2
|
-
import { t as version } from "./package-
|
|
2
|
+
import { t as version } from "./package-BAYb-3dX.js";
|
|
3
3
|
import { t as isFileMissing } from "./fs-errors-PA2t1TIp.js";
|
|
4
4
|
import { lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
@@ -23,7 +23,8 @@ function buildMcpListing(mcp) {
|
|
|
23
23
|
description: tool.description,
|
|
24
24
|
annotations: tool.annotations,
|
|
25
25
|
inputSchema: schemaToJsonSchema(tool.inputSchema, "input"),
|
|
26
|
-
outputSchema: schemaToJsonSchema(tool.outputSchema, "output")
|
|
26
|
+
outputSchema: schemaToJsonSchema(tool.outputSchema, "output"),
|
|
27
|
+
...tool.enabled ? { conditional: true } : {}
|
|
27
28
|
}))
|
|
28
29
|
};
|
|
29
30
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_schema = require("./schema-D3vClxb7.cjs");
|
|
2
|
-
const require_package = require("./package-
|
|
2
|
+
const require_package = require("./package-DlouusYQ.cjs");
|
|
3
3
|
const require_fs_errors = require("./fs-errors-CWNzOV75.cjs");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
let node_path = require("node:path");
|
|
@@ -23,7 +23,8 @@ function buildMcpListing(mcp) {
|
|
|
23
23
|
description: tool.description,
|
|
24
24
|
annotations: tool.annotations,
|
|
25
25
|
inputSchema: schemaToJsonSchema(tool.inputSchema, "input"),
|
|
26
|
-
outputSchema: schemaToJsonSchema(tool.outputSchema, "output")
|
|
26
|
+
outputSchema: schemaToJsonSchema(tool.outputSchema, "output"),
|
|
27
|
+
...tool.enabled ? { conditional: true } : {}
|
|
27
28
|
}))
|
|
28
29
|
};
|
|
29
30
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as log, r as describeError } from "./logger-Ctv5xUSD.js";
|
|
2
2
|
import { n as schemaFromDefinition, t as jsonSchemaFromDefinition } from "./schema-CW1jKvio.js";
|
|
3
|
-
import {
|
|
4
|
-
import { d as nowMs, i as normalizeHttpMethod, l as emitBeforeResponse, n as createRequestAuthorizer, p as finalizeWireResponse, t as evaluateCors, u as createNoopRecorder } from "./cors-
|
|
3
|
+
import { a as ToolContext, c as toolRequiresAppContext, i as AuthContext, n as errorSummary, r as resolveToolErrorMessage } from "./errors-DTvfh8lb.js";
|
|
4
|
+
import { d as nowMs, i as normalizeHttpMethod, l as emitBeforeResponse, n as createRequestAuthorizer, p as finalizeWireResponse, t as evaluateCors, u as createNoopRecorder } from "./cors-D4BjUpgU.js";
|
|
5
5
|
import { getParseErrorMessage, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
6
6
|
import { CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, McpServer, createMcpHandler } from "@modelcontextprotocol/server";
|
|
7
7
|
//#region src/core/content.ts
|
|
@@ -117,6 +117,52 @@ function createGatedHandler(gate) {
|
|
|
117
117
|
//#endregion
|
|
118
118
|
//#region src/protocols/mcp/protocol.ts
|
|
119
119
|
const MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
120
|
+
const HOUR_MS = 3600 * 1e3;
|
|
121
|
+
const NO_OPERATIONS = {
|
|
122
|
+
listTools: false,
|
|
123
|
+
calledTools: /* @__PURE__ */ new Set()
|
|
124
|
+
};
|
|
125
|
+
function collectRequestOperations(value, calledTools) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
let listTools = false;
|
|
128
|
+
for (const item of value) listTools = collectRequestOperations(item, calledTools) || listTools;
|
|
129
|
+
return listTools;
|
|
130
|
+
}
|
|
131
|
+
if (!isPlainObject(value)) return false;
|
|
132
|
+
if (value.method === "tools/list") return true;
|
|
133
|
+
if (value.method === "tools/call" && isPlainObject(value.params) && typeof value.params.name === "string") calledTools.add(value.params.name);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
/** Marks a failure in app-owned `createAppContext` or `enabled` code so telemetry
|
|
137
|
+
* can attribute it to the author rather than to the transport. */
|
|
138
|
+
var AppContextError = class extends Error {
|
|
139
|
+
constructor(cause) {
|
|
140
|
+
super("app context evaluation failed", { cause });
|
|
141
|
+
this.name = "AppContextError";
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
async function withAppContextError(run) {
|
|
145
|
+
try {
|
|
146
|
+
return await run();
|
|
147
|
+
} catch (cause) {
|
|
148
|
+
throw new AppContextError(cause);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function classifyRequest(request) {
|
|
152
|
+
try {
|
|
153
|
+
const parsedBody = await request.clone().json();
|
|
154
|
+
const calledTools = /* @__PURE__ */ new Set();
|
|
155
|
+
return {
|
|
156
|
+
operations: {
|
|
157
|
+
listTools: collectRequestOperations(parsedBody, calledTools),
|
|
158
|
+
calledTools
|
|
159
|
+
},
|
|
160
|
+
parsedBody
|
|
161
|
+
};
|
|
162
|
+
} catch {
|
|
163
|
+
return { operations: NO_OPERATIONS };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
120
166
|
function statelessMethodNotAllowed(allow) {
|
|
121
167
|
return Response.json({
|
|
122
168
|
jsonrpc: "2.0",
|
|
@@ -191,12 +237,12 @@ function toolRequestContext(sdkCtx, protocol) {
|
|
|
191
237
|
sendProgress
|
|
192
238
|
};
|
|
193
239
|
}
|
|
194
|
-
async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx) {
|
|
240
|
+
async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx, appContext) {
|
|
195
241
|
const endUserId = auth?.principal.sub;
|
|
196
242
|
const start = nowMs();
|
|
197
243
|
let result;
|
|
198
244
|
try {
|
|
199
|
-
result = await tool.handler(args, new ToolContext(auth, toolRequestContext(sdkCtx, protocol)));
|
|
245
|
+
result = await tool.handler(args, new ToolContext(auth, toolRequestContext(sdkCtx, protocol), appContext));
|
|
200
246
|
} catch (err) {
|
|
201
247
|
const callerMessage = resolveToolErrorMessage(err);
|
|
202
248
|
if (callerMessage === void 0) {
|
|
@@ -254,10 +300,10 @@ async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx) {
|
|
|
254
300
|
});
|
|
255
301
|
return result;
|
|
256
302
|
}
|
|
257
|
-
function adaptToolCallback(tool, auth, recorder, protocol) {
|
|
303
|
+
function adaptToolCallback(tool, auth, recorder, protocol, appContext) {
|
|
258
304
|
return async (first, second) => {
|
|
259
305
|
const sdkCtx = tool.inputSchema ? second : first;
|
|
260
|
-
const result = await invokeTool(tool, auth, recorder, tool.inputSchema ? first ?? {} : {}, protocol, sdkCtx);
|
|
306
|
+
const result = await invokeTool(tool, auth, recorder, tool.inputSchema ? first ?? {} : {}, protocol, sdkCtx, appContext);
|
|
261
307
|
return {
|
|
262
308
|
content: result.content ?? [],
|
|
263
309
|
structuredContent: result.structuredContent,
|
|
@@ -265,23 +311,52 @@ function adaptToolCallback(tool, auth, recorder, protocol) {
|
|
|
265
311
|
};
|
|
266
312
|
};
|
|
267
313
|
}
|
|
268
|
-
function buildServer(mcp, auth, recorder, era) {
|
|
314
|
+
async function buildServer(mcp, auth, recorder, era, signal, operations) {
|
|
315
|
+
const calledTools = operations ? mcp.tools.filter((tool) => operations.calledTools.has(tool.name)) : mcp.tools;
|
|
316
|
+
const selectedTools = !operations || operations.listTools ? mcp.tools : calledTools;
|
|
317
|
+
const hasConditionalTools = mcp.tools.some((tool) => tool.enabled !== void 0);
|
|
318
|
+
const needsAppContext = operations?.listTools === true && hasConditionalTools || calledTools.some((tool) => tool.enabled !== void 0 || toolRequiresAppContext(tool));
|
|
319
|
+
const { createAppContext } = mcp;
|
|
320
|
+
const appContext = needsAppContext && createAppContext ? await withAppContextError(() => createAppContext({
|
|
321
|
+
auth: new AuthContext(auth),
|
|
322
|
+
signal
|
|
323
|
+
})) : void 0;
|
|
324
|
+
const visibilityContext = Object.freeze({ appContext });
|
|
325
|
+
const cacheHint = hasConditionalTools ? {
|
|
326
|
+
cacheScope: "private",
|
|
327
|
+
ttlMs: 12 * HOUR_MS
|
|
328
|
+
} : {
|
|
329
|
+
cacheScope: "public",
|
|
330
|
+
ttlMs: 24 * HOUR_MS
|
|
331
|
+
};
|
|
269
332
|
const server = new McpServer({
|
|
270
333
|
name: mcp.name,
|
|
271
334
|
version: mcp.version,
|
|
272
335
|
title: mcp.title
|
|
273
336
|
}, {
|
|
274
337
|
instructions: mcp.instructions,
|
|
275
|
-
capabilities: { tools: { listChanged: false } }
|
|
338
|
+
capabilities: { tools: { listChanged: false } },
|
|
339
|
+
cacheHints: {
|
|
340
|
+
"tools/list": cacheHint,
|
|
341
|
+
"server/discover": cacheHint
|
|
342
|
+
}
|
|
276
343
|
});
|
|
277
|
-
for (const tool of
|
|
344
|
+
for (const tool of selectedTools) {
|
|
345
|
+
if (tool.enabled) {
|
|
346
|
+
const { enabled } = tool;
|
|
347
|
+
if (!await withAppContextError(() => {
|
|
348
|
+
const value = enabled(visibilityContext);
|
|
349
|
+
if (typeof value !== "boolean") throw new TypeError(`@lovable.dev/mcp-js: tool "${tool.name}" enabled must return a boolean synchronously`);
|
|
350
|
+
return value;
|
|
351
|
+
})) continue;
|
|
352
|
+
}
|
|
278
353
|
const config = {
|
|
279
354
|
title: tool.title,
|
|
280
355
|
description: tool.description,
|
|
281
356
|
annotations: tool.annotations,
|
|
282
357
|
...tool.outputSchema ? { outputSchema: standardSchemaFromDefinition(tool.outputSchema) } : {}
|
|
283
358
|
};
|
|
284
|
-
const callback = adaptToolCallback(tool, auth, recorder, era === "legacy" ? "legacy" : MODERN_PROTOCOL_VERSION);
|
|
359
|
+
const callback = adaptToolCallback(tool, auth, recorder, era === "legacy" ? "legacy" : MODERN_PROTOCOL_VERSION, appContext);
|
|
285
360
|
if (tool.inputSchema) server.registerTool(tool.name, {
|
|
286
361
|
...config,
|
|
287
362
|
inputSchema: standardSchemaFromDefinition(tool.inputSchema)
|
|
@@ -302,26 +377,28 @@ function createMcpProtocolHandler(mcp, options = {}) {
|
|
|
302
377
|
const handle = async (request, authResult, recorder) => {
|
|
303
378
|
let protocolError;
|
|
304
379
|
const reportTransportError = async (error) => {
|
|
380
|
+
const appContextFailure = error instanceof AppContextError;
|
|
305
381
|
await recorder.emit({
|
|
306
382
|
tool: null,
|
|
307
383
|
method: "transport",
|
|
308
|
-
outcome: "transport_error",
|
|
384
|
+
outcome: appContextFailure ? "app_context_error" : "transport_error",
|
|
309
385
|
durationMs: 0,
|
|
310
386
|
endUserId: authResult.auth?.principal.sub
|
|
311
387
|
});
|
|
312
|
-
log.error("mcp.transport_error", {
|
|
313
|
-
...error === void 0 ? {} : describeError(error),
|
|
388
|
+
log.error(appContextFailure ? "mcp.app_context_error" : "mcp.transport_error", {
|
|
389
|
+
...error === void 0 ? {} : describeError(appContextFailure ? error.cause : error),
|
|
314
390
|
outcome: "500 internal error"
|
|
315
391
|
});
|
|
316
392
|
};
|
|
317
393
|
try {
|
|
318
|
-
const
|
|
394
|
+
const classified = mcp.createAppContext ? await classifyRequest(request) : void 0;
|
|
395
|
+
const response = await createMcpHandler(({ era, requestInfo }) => buildServer(mcp, authResult.auth, recorder, era, requestInfo?.signal ?? request.signal, classified?.operations), {
|
|
319
396
|
legacy: "stateless",
|
|
320
397
|
maxSubscriptions: 0,
|
|
321
398
|
onerror: (error) => {
|
|
322
399
|
protocolError = error;
|
|
323
400
|
}
|
|
324
|
-
}).fetch(request);
|
|
401
|
+
}).fetch(request, classified?.parsedBody === void 0 ? void 0 : { parsedBody: classified.parsedBody });
|
|
325
402
|
if (response.status >= 500) await reportTransportError(protocolError);
|
|
326
403
|
return response;
|
|
327
404
|
} catch (err) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_logger = require("./logger-CN1q-KNn.cjs");
|
|
2
2
|
const require_schema = require("./schema-D3vClxb7.cjs");
|
|
3
|
-
const require_errors = require("./errors-
|
|
4
|
-
const require_cors = require("./cors-
|
|
3
|
+
const require_errors = require("./errors-CdrGi5aP.cjs");
|
|
4
|
+
const require_cors = require("./cors-0ZCwmwyK.cjs");
|
|
5
5
|
let _modelcontextprotocol_sdk_server_zod_compat_js = require("@modelcontextprotocol/sdk/server/zod-compat.js");
|
|
6
6
|
let _modelcontextprotocol_server = require("@modelcontextprotocol/server");
|
|
7
7
|
//#region src/core/content.ts
|
|
@@ -117,6 +117,52 @@ function createGatedHandler(gate) {
|
|
|
117
117
|
//#endregion
|
|
118
118
|
//#region src/protocols/mcp/protocol.ts
|
|
119
119
|
const MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
120
|
+
const HOUR_MS = 3600 * 1e3;
|
|
121
|
+
const NO_OPERATIONS = {
|
|
122
|
+
listTools: false,
|
|
123
|
+
calledTools: /* @__PURE__ */ new Set()
|
|
124
|
+
};
|
|
125
|
+
function collectRequestOperations(value, calledTools) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
let listTools = false;
|
|
128
|
+
for (const item of value) listTools = collectRequestOperations(item, calledTools) || listTools;
|
|
129
|
+
return listTools;
|
|
130
|
+
}
|
|
131
|
+
if (!isPlainObject(value)) return false;
|
|
132
|
+
if (value.method === "tools/list") return true;
|
|
133
|
+
if (value.method === "tools/call" && isPlainObject(value.params) && typeof value.params.name === "string") calledTools.add(value.params.name);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
/** Marks a failure in app-owned `createAppContext` or `enabled` code so telemetry
|
|
137
|
+
* can attribute it to the author rather than to the transport. */
|
|
138
|
+
var AppContextError = class extends Error {
|
|
139
|
+
constructor(cause) {
|
|
140
|
+
super("app context evaluation failed", { cause });
|
|
141
|
+
this.name = "AppContextError";
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
async function withAppContextError(run) {
|
|
145
|
+
try {
|
|
146
|
+
return await run();
|
|
147
|
+
} catch (cause) {
|
|
148
|
+
throw new AppContextError(cause);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function classifyRequest(request) {
|
|
152
|
+
try {
|
|
153
|
+
const parsedBody = await request.clone().json();
|
|
154
|
+
const calledTools = /* @__PURE__ */ new Set();
|
|
155
|
+
return {
|
|
156
|
+
operations: {
|
|
157
|
+
listTools: collectRequestOperations(parsedBody, calledTools),
|
|
158
|
+
calledTools
|
|
159
|
+
},
|
|
160
|
+
parsedBody
|
|
161
|
+
};
|
|
162
|
+
} catch {
|
|
163
|
+
return { operations: NO_OPERATIONS };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
120
166
|
function statelessMethodNotAllowed(allow) {
|
|
121
167
|
return Response.json({
|
|
122
168
|
jsonrpc: "2.0",
|
|
@@ -191,12 +237,12 @@ function toolRequestContext(sdkCtx, protocol) {
|
|
|
191
237
|
sendProgress
|
|
192
238
|
};
|
|
193
239
|
}
|
|
194
|
-
async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx) {
|
|
240
|
+
async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx, appContext) {
|
|
195
241
|
const endUserId = auth?.principal.sub;
|
|
196
242
|
const start = require_cors.nowMs();
|
|
197
243
|
let result;
|
|
198
244
|
try {
|
|
199
|
-
result = await tool.handler(args, new require_errors.ToolContext(auth, toolRequestContext(sdkCtx, protocol)));
|
|
245
|
+
result = await tool.handler(args, new require_errors.ToolContext(auth, toolRequestContext(sdkCtx, protocol), appContext));
|
|
200
246
|
} catch (err) {
|
|
201
247
|
const callerMessage = require_errors.resolveToolErrorMessage(err);
|
|
202
248
|
if (callerMessage === void 0) {
|
|
@@ -254,10 +300,10 @@ async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx) {
|
|
|
254
300
|
});
|
|
255
301
|
return result;
|
|
256
302
|
}
|
|
257
|
-
function adaptToolCallback(tool, auth, recorder, protocol) {
|
|
303
|
+
function adaptToolCallback(tool, auth, recorder, protocol, appContext) {
|
|
258
304
|
return async (first, second) => {
|
|
259
305
|
const sdkCtx = tool.inputSchema ? second : first;
|
|
260
|
-
const result = await invokeTool(tool, auth, recorder, tool.inputSchema ? first ?? {} : {}, protocol, sdkCtx);
|
|
306
|
+
const result = await invokeTool(tool, auth, recorder, tool.inputSchema ? first ?? {} : {}, protocol, sdkCtx, appContext);
|
|
261
307
|
return {
|
|
262
308
|
content: result.content ?? [],
|
|
263
309
|
structuredContent: result.structuredContent,
|
|
@@ -265,23 +311,52 @@ function adaptToolCallback(tool, auth, recorder, protocol) {
|
|
|
265
311
|
};
|
|
266
312
|
};
|
|
267
313
|
}
|
|
268
|
-
function buildServer(mcp, auth, recorder, era) {
|
|
314
|
+
async function buildServer(mcp, auth, recorder, era, signal, operations) {
|
|
315
|
+
const calledTools = operations ? mcp.tools.filter((tool) => operations.calledTools.has(tool.name)) : mcp.tools;
|
|
316
|
+
const selectedTools = !operations || operations.listTools ? mcp.tools : calledTools;
|
|
317
|
+
const hasConditionalTools = mcp.tools.some((tool) => tool.enabled !== void 0);
|
|
318
|
+
const needsAppContext = operations?.listTools === true && hasConditionalTools || calledTools.some((tool) => tool.enabled !== void 0 || require_errors.toolRequiresAppContext(tool));
|
|
319
|
+
const { createAppContext } = mcp;
|
|
320
|
+
const appContext = needsAppContext && createAppContext ? await withAppContextError(() => createAppContext({
|
|
321
|
+
auth: new require_errors.AuthContext(auth),
|
|
322
|
+
signal
|
|
323
|
+
})) : void 0;
|
|
324
|
+
const visibilityContext = Object.freeze({ appContext });
|
|
325
|
+
const cacheHint = hasConditionalTools ? {
|
|
326
|
+
cacheScope: "private",
|
|
327
|
+
ttlMs: 12 * HOUR_MS
|
|
328
|
+
} : {
|
|
329
|
+
cacheScope: "public",
|
|
330
|
+
ttlMs: 24 * HOUR_MS
|
|
331
|
+
};
|
|
269
332
|
const server = new _modelcontextprotocol_server.McpServer({
|
|
270
333
|
name: mcp.name,
|
|
271
334
|
version: mcp.version,
|
|
272
335
|
title: mcp.title
|
|
273
336
|
}, {
|
|
274
337
|
instructions: mcp.instructions,
|
|
275
|
-
capabilities: { tools: { listChanged: false } }
|
|
338
|
+
capabilities: { tools: { listChanged: false } },
|
|
339
|
+
cacheHints: {
|
|
340
|
+
"tools/list": cacheHint,
|
|
341
|
+
"server/discover": cacheHint
|
|
342
|
+
}
|
|
276
343
|
});
|
|
277
|
-
for (const tool of
|
|
344
|
+
for (const tool of selectedTools) {
|
|
345
|
+
if (tool.enabled) {
|
|
346
|
+
const { enabled } = tool;
|
|
347
|
+
if (!await withAppContextError(() => {
|
|
348
|
+
const value = enabled(visibilityContext);
|
|
349
|
+
if (typeof value !== "boolean") throw new TypeError(`@lovable.dev/mcp-js: tool "${tool.name}" enabled must return a boolean synchronously`);
|
|
350
|
+
return value;
|
|
351
|
+
})) continue;
|
|
352
|
+
}
|
|
278
353
|
const config = {
|
|
279
354
|
title: tool.title,
|
|
280
355
|
description: tool.description,
|
|
281
356
|
annotations: tool.annotations,
|
|
282
357
|
...tool.outputSchema ? { outputSchema: standardSchemaFromDefinition(tool.outputSchema) } : {}
|
|
283
358
|
};
|
|
284
|
-
const callback = adaptToolCallback(tool, auth, recorder, era === "legacy" ? "legacy" : MODERN_PROTOCOL_VERSION);
|
|
359
|
+
const callback = adaptToolCallback(tool, auth, recorder, era === "legacy" ? "legacy" : MODERN_PROTOCOL_VERSION, appContext);
|
|
285
360
|
if (tool.inputSchema) server.registerTool(tool.name, {
|
|
286
361
|
...config,
|
|
287
362
|
inputSchema: standardSchemaFromDefinition(tool.inputSchema)
|
|
@@ -302,26 +377,28 @@ function createMcpProtocolHandler(mcp, options = {}) {
|
|
|
302
377
|
const handle = async (request, authResult, recorder) => {
|
|
303
378
|
let protocolError;
|
|
304
379
|
const reportTransportError = async (error) => {
|
|
380
|
+
const appContextFailure = error instanceof AppContextError;
|
|
305
381
|
await recorder.emit({
|
|
306
382
|
tool: null,
|
|
307
383
|
method: "transport",
|
|
308
|
-
outcome: "transport_error",
|
|
384
|
+
outcome: appContextFailure ? "app_context_error" : "transport_error",
|
|
309
385
|
durationMs: 0,
|
|
310
386
|
endUserId: authResult.auth?.principal.sub
|
|
311
387
|
});
|
|
312
|
-
require_logger.log.error("mcp.transport_error", {
|
|
313
|
-
...error === void 0 ? {} : require_logger.describeError(error),
|
|
388
|
+
require_logger.log.error(appContextFailure ? "mcp.app_context_error" : "mcp.transport_error", {
|
|
389
|
+
...error === void 0 ? {} : require_logger.describeError(appContextFailure ? error.cause : error),
|
|
314
390
|
outcome: "500 internal error"
|
|
315
391
|
});
|
|
316
392
|
};
|
|
317
393
|
try {
|
|
318
|
-
const
|
|
394
|
+
const classified = mcp.createAppContext ? await classifyRequest(request) : void 0;
|
|
395
|
+
const response = await (0, _modelcontextprotocol_server.createMcpHandler)(({ era, requestInfo }) => buildServer(mcp, authResult.auth, recorder, era, requestInfo?.signal ?? request.signal, classified?.operations), {
|
|
319
396
|
legacy: "stateless",
|
|
320
397
|
maxSubscriptions: 0,
|
|
321
398
|
onerror: (error) => {
|
|
322
399
|
protocolError = error;
|
|
323
400
|
}
|
|
324
|
-
}).fetch(request);
|
|
401
|
+
}).fetch(request, classified?.parsedBody === void 0 ? void 0 : { parsedBody: classified.parsedBody });
|
|
325
402
|
if (response.status >= 500) await reportTransportError(protocolError);
|
|
326
403
|
return response;
|
|
327
404
|
} catch (err) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as MetricsRecorder } from "../../base-
|
|
3
|
-
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../../authorize-
|
|
1
|
+
import { p as McpDefinition } from "../../types-S6J_QTXw.cjs";
|
|
2
|
+
import { t as MetricsRecorder } from "../../base-4G2k3bMl.cjs";
|
|
3
|
+
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../../authorize-BC4TM__8.cjs";
|
|
4
4
|
//#region src/protocols/mcp/protocol.d.ts
|
|
5
5
|
type McpProtocolHandler = (request: Request, recorder?: MetricsRecorder, scheduleBackground?: ScheduleBackground) => Promise<Response>;
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as MetricsRecorder } from "../../base-
|
|
3
|
-
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../../authorize-
|
|
1
|
+
import { p as McpDefinition } from "../../types-S6J_QTXw.js";
|
|
2
|
+
import { t as MetricsRecorder } from "../../base-BnlJz6TE.js";
|
|
3
|
+
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../../authorize-DEsM-zTN.js";
|
|
4
4
|
//#region src/protocols/mcp/protocol.d.ts
|
|
5
5
|
type McpProtocolHandler = (request: Request, recorder?: MetricsRecorder, scheduleBackground?: ScheduleBackground) => Promise<Response>;
|
|
6
6
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createMcpProtocolHandler } from "../../mcp-
|
|
1
|
+
import { t as createMcpProtocolHandler } from "../../mcp-CuHyVOZK.js";
|
|
2
2
|
export { createMcpProtocolHandler };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_logger = require("../logger-CN1q-KNn.cjs");
|
|
3
|
-
const require_cors = require("../cors-
|
|
3
|
+
const require_cors = require("../cors-0ZCwmwyK.cjs");
|
|
4
4
|
//#region src/protocols/oauth-metadata.ts
|
|
5
5
|
function notFound() {
|
|
6
6
|
return new Response(JSON.stringify({ error: "not found" }), {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as MetricsRecorder } from "../base-
|
|
3
|
-
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../authorize-
|
|
1
|
+
import { p as McpDefinition } from "../types-S6J_QTXw.cjs";
|
|
2
|
+
import { t as MetricsRecorder } from "../base-4G2k3bMl.cjs";
|
|
3
|
+
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../authorize-BC4TM__8.cjs";
|
|
4
4
|
//#region src/protocols/oauth-metadata.d.ts
|
|
5
5
|
type OAuthProtectedResourceMetadataHandler = (request: Request, recorder?: MetricsRecorder, scheduleBackground?: ScheduleBackground) => Promise<Response>;
|
|
6
6
|
declare function createOAuthProtectedResourceMetadataHandler(mcp: McpDefinition, options?: McpRuntimeOptions): OAuthProtectedResourceMetadataHandler;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as MetricsRecorder } from "../base-
|
|
3
|
-
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../authorize-
|
|
1
|
+
import { p as McpDefinition } from "../types-S6J_QTXw.js";
|
|
2
|
+
import { t as MetricsRecorder } from "../base-BnlJz6TE.js";
|
|
3
|
+
import { n as ScheduleBackground, t as McpRuntimeOptions } from "../authorize-DEsM-zTN.js";
|
|
4
4
|
//#region src/protocols/oauth-metadata.d.ts
|
|
5
5
|
type OAuthProtectedResourceMetadataHandler = (request: Request, recorder?: MetricsRecorder, scheduleBackground?: ScheduleBackground) => Promise<Response>;
|
|
6
6
|
declare function createOAuthProtectedResourceMetadataHandler(mcp: McpDefinition, options?: McpRuntimeOptions): OAuthProtectedResourceMetadataHandler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as log, r as describeError } from "../logger-Ctv5xUSD.js";
|
|
2
|
-
import { a as oauthConfigurationErrorResponse, f as JSON_HEADERS, i as normalizeHttpMethod, l as emitBeforeResponse, m as methodNotAllowed, p as finalizeWireResponse, r as getOAuthRuntime, s as resolveProtectedResource, t as evaluateCors, u as createNoopRecorder } from "../cors-
|
|
2
|
+
import { a as oauthConfigurationErrorResponse, f as JSON_HEADERS, i as normalizeHttpMethod, l as emitBeforeResponse, m as methodNotAllowed, p as finalizeWireResponse, r as getOAuthRuntime, s as resolveProtectedResource, t as evaluateCors, u as createNoopRecorder } from "../cors-D4BjUpgU.js";
|
|
3
3
|
//#region src/protocols/oauth-metadata.ts
|
|
4
4
|
function notFound() {
|
|
5
5
|
return new Response(JSON.stringify({ error: "not found" }), {
|