@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.
@@ -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?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
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() ?? "silent";
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.15.1";
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
- return NOOP_RECORDER;
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 headers = {};
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
- headers[key] = value;
315
+ baseHeaders[key] = value;
271
316
  }
272
317
  }
273
- headers["content-type"] = "application/json";
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.debug("metrics.flush_rejected", { status: res.status });
345
+ if (!res.ok) {
346
+ log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
347
+ }
289
348
  } catch (err) {
290
- log.debug("metrics.flush_failed", describeError(err));
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.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
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.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
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.record({
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.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
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.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
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.record({
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-UCPYLSNN.js";
6
+ } from "../../chunk-EL6YNP2R.js";
7
7
  import {
8
8
  createOAuthProtectedResourceMetadataHandler
9
- } from "../../chunk-ILYTJXDR.js";
9
+ } from "../../chunk-H2HXKAMQ.js";
10
10
  import {
11
11
  createInvokeToolHandler
12
- } from "../../chunk-CEDD57G2.js";
12
+ } from "../../chunk-WYXIX7XF.js";
13
13
  import {
14
14
  createListToolsHandler
15
- } from "../../chunk-QT5L4CS3.js";
15
+ } from "../../chunk-FW6YL5C5.js";
16
16
  import "../../chunk-MA5H6PSF.js";
17
- import "../../chunk-YTOMGJV5.js";
18
- import "../../chunk-DCWYK4CA.js";
17
+ import "../../chunk-6TZNGFRM.js";
18
+ import "../../chunk-VQQZAFRA.js";
19
19
  import "../../chunk-6DXGZZA4.js";
20
- import "../../chunk-GIHCQT6L.js";
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.15.1",
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": {