@lovable.dev/mcp-js 0.15.1 → 0.17.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/dist/{chunk-YTOMGJV5.js → chunk-6TZNGFRM.js} +66 -17
- package/dist/{chunk-UCPYLSNN.js → chunk-EL6YNP2R.js} +16 -5
- package/dist/{chunk-QT5L4CS3.js → chunk-FW6YL5C5.js} +1 -1
- package/dist/{chunk-ILYTJXDR.js → chunk-H2HXKAMQ.js} +2 -2
- package/dist/{chunk-GIHCQT6L.js → chunk-J5BTCP6T.js} +1 -1
- package/dist/{chunk-DCWYK4CA.js → chunk-VQQZAFRA.js} +14 -2
- package/dist/{chunk-CEDD57G2.js → chunk-WYXIX7XF.js} +15 -4
- package/dist/cli/extract-manifest.cjs +4 -3
- package/dist/cli/extract-manifest.js +4 -4
- package/dist/index.cjs +3 -2
- package/dist/index.js +1 -1
- package/dist/protocols/mcp/index.cjs +90 -21
- package/dist/protocols/mcp/index.js +4 -4
- package/dist/protocols/oauth-metadata.cjs +3 -2
- package/dist/protocols/oauth-metadata.js +4 -4
- package/dist/protocols/rest/index.cjs +90 -21
- package/dist/protocols/rest/index.js +5 -5
- package/dist/stacks/supabase/index.cjs +103 -24
- package/dist/stacks/supabase/index.js +7 -7
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +103 -24
- package/dist/stacks/tanstack/index.js +7 -7
- package/package.json +1 -1
|
@@ -71,16 +71,29 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
|
71
71
|
function isLogLevel(value) {
|
|
72
72
|
return typeof value === "string" && value in LEVEL_RANK;
|
|
73
73
|
}
|
|
74
|
+
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
74
75
|
function readEnvLevel() {
|
|
75
76
|
try {
|
|
76
|
-
const raw = typeof process !== "undefined" ? process.env?.[
|
|
77
|
+
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
77
78
|
const normalized = raw?.trim().toLowerCase();
|
|
78
79
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
79
80
|
} catch {
|
|
80
81
|
return void 0;
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
|
-
var currentLevel = readEnvLevel() ?? "
|
|
84
|
+
var currentLevel = readEnvLevel() ?? "debug";
|
|
85
|
+
function setLogLevel(level) {
|
|
86
|
+
currentLevel = level;
|
|
87
|
+
}
|
|
88
|
+
function parseLogLevel(raw) {
|
|
89
|
+
const normalized = raw?.trim().toLowerCase();
|
|
90
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
91
|
+
}
|
|
92
|
+
function applyLogLevelFromEnv(raw) {
|
|
93
|
+
const level = parseLogLevel(raw);
|
|
94
|
+
if (level)
|
|
95
|
+
setLogLevel(level);
|
|
96
|
+
}
|
|
84
97
|
function enabled(level) {
|
|
85
98
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
86
99
|
}
|
|
@@ -140,7 +153,7 @@ function resolveMetricsConfig(config = true) {
|
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
// package.json
|
|
143
|
-
var version = "0.
|
|
156
|
+
var version = "0.17.0";
|
|
144
157
|
|
|
145
158
|
// src/metrics/otlp.ts
|
|
146
159
|
var SCOPE_NAME = "@lovable.dev/mcp-js";
|
|
@@ -205,6 +218,10 @@ function nowUnixNano() {
|
|
|
205
218
|
function nowMs() {
|
|
206
219
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
207
220
|
}
|
|
221
|
+
function reportInvocation(recorder, ev) {
|
|
222
|
+
log.info("tool.invoked", { tool: ev.tool, method: ev.method, outcome: ev.outcome, durationMs: ev.durationMs });
|
|
223
|
+
recorder.record(ev);
|
|
224
|
+
}
|
|
208
225
|
var NOOP_RECORDER = {
|
|
209
226
|
record() {
|
|
210
227
|
},
|
|
@@ -232,51 +249,92 @@ function readRuntimeEnv(name) {
|
|
|
232
249
|
}
|
|
233
250
|
return void 0;
|
|
234
251
|
}
|
|
252
|
+
var cloudflareEnvPromise;
|
|
253
|
+
async function readCloudflareEnv(name) {
|
|
254
|
+
try {
|
|
255
|
+
const moduleSpecifier = "cloudflare:workers";
|
|
256
|
+
cloudflareEnvPromise ??= import(
|
|
257
|
+
/* @vite-ignore */
|
|
258
|
+
moduleSpecifier
|
|
259
|
+
).then((m) => m.env).catch(() => void 0);
|
|
260
|
+
const env = await cloudflareEnvPromise;
|
|
261
|
+
const value = env?.[name];
|
|
262
|
+
return typeof value === "string" && value ? value : void 0;
|
|
263
|
+
} catch {
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
235
267
|
function probeWaitUntil() {
|
|
236
268
|
const fn = globalThis.EdgeRuntime?.waitUntil;
|
|
237
269
|
return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
|
|
238
270
|
}
|
|
239
271
|
function createMetricsRecorder(ctx, deps = {}) {
|
|
240
272
|
const { config, server } = ctx;
|
|
273
|
+
applyLogLevelFromEnv(readRuntimeEnv(LOG_LEVEL_ENV_VAR));
|
|
241
274
|
if (!config.enabled)
|
|
242
275
|
return NOOP_RECORDER;
|
|
243
276
|
const doFetch = deps.fetch ?? globalThis.fetch;
|
|
244
|
-
if (!doFetch)
|
|
245
|
-
|
|
246
|
-
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
247
|
-
const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
|
|
248
|
-
if (usesLovableEndpoint && !apiKey) {
|
|
249
|
-
log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
|
|
277
|
+
if (!doFetch) {
|
|
278
|
+
log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
|
|
250
279
|
return NOOP_RECORDER;
|
|
251
280
|
}
|
|
281
|
+
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
252
282
|
const waitUntil = deps.waitUntil ?? probeWaitUntil();
|
|
253
283
|
const buffer = [];
|
|
254
284
|
let timer;
|
|
285
|
+
let lazyLogLevelApplied = false;
|
|
286
|
+
const applyLazyLogLevel = async () => {
|
|
287
|
+
if (lazyLogLevelApplied)
|
|
288
|
+
return;
|
|
289
|
+
lazyLogLevelApplied = true;
|
|
290
|
+
applyLogLevelFromEnv(await readCloudflareEnv(LOG_LEVEL_ENV_VAR));
|
|
291
|
+
};
|
|
292
|
+
let apiKey;
|
|
293
|
+
const resolveApiKey = async () => {
|
|
294
|
+
if (apiKey)
|
|
295
|
+
return apiKey;
|
|
296
|
+
apiKey = (deps.getApiKey ? deps.getApiKey() : readRuntimeEnv(config.apiKeyEnvVar)) ?? await readCloudflareEnv(config.apiKeyEnvVar);
|
|
297
|
+
return apiKey;
|
|
298
|
+
};
|
|
255
299
|
const schedule = (p) => {
|
|
256
300
|
if (waitUntil) {
|
|
257
301
|
try {
|
|
258
302
|
waitUntil(p);
|
|
259
303
|
return;
|
|
260
|
-
} catch {
|
|
304
|
+
} catch (err) {
|
|
305
|
+
log.warn("metrics.wait_until_failed", describeError(err));
|
|
261
306
|
}
|
|
262
307
|
}
|
|
263
308
|
void p.catch(() => {
|
|
264
309
|
});
|
|
265
310
|
};
|
|
266
|
-
const
|
|
311
|
+
const baseHeaders = {};
|
|
267
312
|
if (!usesLovableEndpoint) {
|
|
268
313
|
for (const [key, value] of Object.entries(config.headers)) {
|
|
269
314
|
if (key.toLowerCase() !== "content-type")
|
|
270
|
-
|
|
315
|
+
baseHeaders[key] = value;
|
|
271
316
|
}
|
|
272
317
|
}
|
|
273
|
-
|
|
274
|
-
if (apiKey)
|
|
275
|
-
headers.authorization = `Bearer ${apiKey}`;
|
|
318
|
+
baseHeaders["content-type"] = "application/json";
|
|
276
319
|
const flush = async () => {
|
|
320
|
+
void applyLazyLogLevel();
|
|
277
321
|
if (buffer.length === 0)
|
|
278
322
|
return;
|
|
279
323
|
const records = buffer.splice(0, buffer.length);
|
|
324
|
+
const dropped = records.length;
|
|
325
|
+
const headers = { ...baseHeaders };
|
|
326
|
+
if (usesLovableEndpoint) {
|
|
327
|
+
const key = await resolveApiKey();
|
|
328
|
+
if (!key) {
|
|
329
|
+
log.warn("metrics.disabled_no_api_key", {
|
|
330
|
+
envVar: config.apiKeyEnvVar,
|
|
331
|
+
endpoint: config.endpoint,
|
|
332
|
+
dropped
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
headers.authorization = `Bearer ${key}`;
|
|
337
|
+
}
|
|
280
338
|
try {
|
|
281
339
|
const res = await doFetch(config.endpoint, {
|
|
282
340
|
method: "POST",
|
|
@@ -284,10 +342,11 @@ function createMetricsRecorder(ctx, deps = {}) {
|
|
|
284
342
|
body: buildLogsPayload(records, server),
|
|
285
343
|
keepalive: true
|
|
286
344
|
});
|
|
287
|
-
if (!res.ok)
|
|
288
|
-
log.
|
|
345
|
+
if (!res.ok) {
|
|
346
|
+
log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
|
|
347
|
+
}
|
|
289
348
|
} catch (err) {
|
|
290
|
-
log.
|
|
349
|
+
log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
|
|
291
350
|
}
|
|
292
351
|
};
|
|
293
352
|
const ensureTimer = () => {
|
|
@@ -873,14 +932,24 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
873
932
|
try {
|
|
874
933
|
result = await tool.handler(args, new ToolContext(auth));
|
|
875
934
|
} catch {
|
|
876
|
-
recorder
|
|
935
|
+
reportInvocation(recorder, {
|
|
936
|
+
tool: tool.name,
|
|
937
|
+
method: "tools/call",
|
|
938
|
+
outcome: "handler_error",
|
|
939
|
+
durationMs: nowMs() - start
|
|
940
|
+
});
|
|
877
941
|
return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
|
|
878
942
|
}
|
|
879
943
|
if (result == null) {
|
|
880
|
-
recorder
|
|
944
|
+
reportInvocation(recorder, {
|
|
945
|
+
tool: tool.name,
|
|
946
|
+
method: "tools/call",
|
|
947
|
+
outcome: "handler_error",
|
|
948
|
+
durationMs: nowMs() - start
|
|
949
|
+
});
|
|
881
950
|
return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
|
|
882
951
|
}
|
|
883
|
-
recorder
|
|
952
|
+
reportInvocation(recorder, {
|
|
884
953
|
tool: tool.name,
|
|
885
954
|
method: "tools/call",
|
|
886
955
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -1113,20 +1182,30 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
|
|
|
1113
1182
|
try {
|
|
1114
1183
|
result = await tool.handler(args, new ToolContext(authResult.auth));
|
|
1115
1184
|
} catch {
|
|
1116
|
-
recorder
|
|
1185
|
+
reportInvocation(recorder, {
|
|
1186
|
+
tool: tool.name,
|
|
1187
|
+
method: "tools/call",
|
|
1188
|
+
outcome: "handler_error",
|
|
1189
|
+
durationMs: nowMs() - start
|
|
1190
|
+
});
|
|
1117
1191
|
return new Response(JSON.stringify({ error: "handler threw", tool: toolName }), {
|
|
1118
1192
|
status: 500,
|
|
1119
1193
|
headers: JSON_HEADERS
|
|
1120
1194
|
});
|
|
1121
1195
|
}
|
|
1122
1196
|
if (result == null) {
|
|
1123
|
-
recorder
|
|
1197
|
+
reportInvocation(recorder, {
|
|
1198
|
+
tool: tool.name,
|
|
1199
|
+
method: "tools/call",
|
|
1200
|
+
outcome: "handler_error",
|
|
1201
|
+
durationMs: nowMs() - start
|
|
1202
|
+
});
|
|
1124
1203
|
return new Response(JSON.stringify({ error: `tool "${toolName}" returned no result` }), {
|
|
1125
1204
|
status: 500,
|
|
1126
1205
|
headers: JSON_HEADERS
|
|
1127
1206
|
});
|
|
1128
1207
|
}
|
|
1129
|
-
recorder
|
|
1208
|
+
reportInvocation(recorder, {
|
|
1130
1209
|
tool: tool.name,
|
|
1131
1210
|
method: "tools/call",
|
|
1132
1211
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
} from "../../chunk-UQK5UO6C.js";
|
|
4
4
|
import {
|
|
5
5
|
createMcpProtocolHandler
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-EL6YNP2R.js";
|
|
7
7
|
import {
|
|
8
8
|
createOAuthProtectedResourceMetadataHandler
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-H2HXKAMQ.js";
|
|
10
10
|
import {
|
|
11
11
|
createInvokeToolHandler
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-WYXIX7XF.js";
|
|
13
13
|
import {
|
|
14
14
|
createListToolsHandler
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-FW6YL5C5.js";
|
|
16
16
|
import "../../chunk-MA5H6PSF.js";
|
|
17
|
-
import "../../chunk-
|
|
18
|
-
import "../../chunk-
|
|
17
|
+
import "../../chunk-6TZNGFRM.js";
|
|
18
|
+
import "../../chunk-VQQZAFRA.js";
|
|
19
19
|
import "../../chunk-6DXGZZA4.js";
|
|
20
|
-
import "../../chunk-
|
|
20
|
+
import "../../chunk-J5BTCP6T.js";
|
|
21
21
|
|
|
22
22
|
// src/stacks/tanstack/handlers.ts
|
|
23
23
|
function forwarded(request, options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/mcp-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"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
5
|
"type": "module",
|
|
6
6
|
"repository": {
|