@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.
@@ -1,17 +1,19 @@
1
1
  import {
2
2
  DEFAULT_METRICS_ENDPOINT,
3
+ LOG_LEVEL_ENV_VAR,
4
+ applyLogLevelFromEnv,
3
5
  describeError,
4
6
  log,
5
7
  parseSafeUrl,
6
8
  resolveMetricsConfig,
7
9
  trimTrailingSlash
8
- } from "./chunk-DCWYK4CA.js";
10
+ } from "./chunk-VQQZAFRA.js";
9
11
  import {
10
12
  OAUTH_PROTECTED_RESOURCE_METADATA_PATH
11
13
  } from "./chunk-6DXGZZA4.js";
12
14
  import {
13
15
  version
14
- } from "./chunk-GIHCQT6L.js";
16
+ } from "./chunk-J5BTCP6T.js";
15
17
 
16
18
  // src/core/http.ts
17
19
  var JSON_HEADERS = { "Content-Type": "application/json" };
@@ -112,6 +114,10 @@ function nowUnixNano() {
112
114
  function nowMs() {
113
115
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
114
116
  }
117
+ function reportInvocation(recorder, ev) {
118
+ log.info("tool.invoked", { tool: ev.tool, method: ev.method, outcome: ev.outcome, durationMs: ev.durationMs });
119
+ recorder.record(ev);
120
+ }
115
121
  var NOOP_RECORDER = {
116
122
  record() {
117
123
  },
@@ -139,51 +145,92 @@ function readRuntimeEnv(name) {
139
145
  }
140
146
  return void 0;
141
147
  }
148
+ var cloudflareEnvPromise;
149
+ async function readCloudflareEnv(name) {
150
+ try {
151
+ const moduleSpecifier = "cloudflare:workers";
152
+ cloudflareEnvPromise ??= import(
153
+ /* @vite-ignore */
154
+ moduleSpecifier
155
+ ).then((m) => m.env).catch(() => void 0);
156
+ const env = await cloudflareEnvPromise;
157
+ const value = env?.[name];
158
+ return typeof value === "string" && value ? value : void 0;
159
+ } catch {
160
+ return void 0;
161
+ }
162
+ }
142
163
  function probeWaitUntil() {
143
164
  const fn = globalThis.EdgeRuntime?.waitUntil;
144
165
  return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
145
166
  }
146
167
  function createMetricsRecorder(ctx, deps = {}) {
147
168
  const { config, server } = ctx;
169
+ applyLogLevelFromEnv(readRuntimeEnv(LOG_LEVEL_ENV_VAR));
148
170
  if (!config.enabled)
149
171
  return NOOP_RECORDER;
150
172
  const doFetch = deps.fetch ?? globalThis.fetch;
151
- if (!doFetch)
152
- return NOOP_RECORDER;
153
- const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
154
- const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
155
- if (usesLovableEndpoint && !apiKey) {
156
- log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
173
+ if (!doFetch) {
174
+ log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
157
175
  return NOOP_RECORDER;
158
176
  }
177
+ const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
159
178
  const waitUntil = deps.waitUntil ?? probeWaitUntil();
160
179
  const buffer = [];
161
180
  let timer;
181
+ let lazyLogLevelApplied = false;
182
+ const applyLazyLogLevel = async () => {
183
+ if (lazyLogLevelApplied)
184
+ return;
185
+ lazyLogLevelApplied = true;
186
+ applyLogLevelFromEnv(await readCloudflareEnv(LOG_LEVEL_ENV_VAR));
187
+ };
188
+ let apiKey;
189
+ const resolveApiKey = async () => {
190
+ if (apiKey)
191
+ return apiKey;
192
+ apiKey = (deps.getApiKey ? deps.getApiKey() : readRuntimeEnv(config.apiKeyEnvVar)) ?? await readCloudflareEnv(config.apiKeyEnvVar);
193
+ return apiKey;
194
+ };
162
195
  const schedule = (p) => {
163
196
  if (waitUntil) {
164
197
  try {
165
198
  waitUntil(p);
166
199
  return;
167
- } catch {
200
+ } catch (err) {
201
+ log.warn("metrics.wait_until_failed", describeError(err));
168
202
  }
169
203
  }
170
204
  void p.catch(() => {
171
205
  });
172
206
  };
173
- const headers = {};
207
+ const baseHeaders = {};
174
208
  if (!usesLovableEndpoint) {
175
209
  for (const [key, value] of Object.entries(config.headers)) {
176
210
  if (key.toLowerCase() !== "content-type")
177
- headers[key] = value;
211
+ baseHeaders[key] = value;
178
212
  }
179
213
  }
180
- headers["content-type"] = "application/json";
181
- if (apiKey)
182
- headers.authorization = `Bearer ${apiKey}`;
214
+ baseHeaders["content-type"] = "application/json";
183
215
  const flush = async () => {
216
+ void applyLazyLogLevel();
184
217
  if (buffer.length === 0)
185
218
  return;
186
219
  const records = buffer.splice(0, buffer.length);
220
+ const dropped = records.length;
221
+ const headers = { ...baseHeaders };
222
+ if (usesLovableEndpoint) {
223
+ const key = await resolveApiKey();
224
+ if (!key) {
225
+ log.warn("metrics.disabled_no_api_key", {
226
+ envVar: config.apiKeyEnvVar,
227
+ endpoint: config.endpoint,
228
+ dropped
229
+ });
230
+ return;
231
+ }
232
+ headers.authorization = `Bearer ${key}`;
233
+ }
187
234
  try {
188
235
  const res = await doFetch(config.endpoint, {
189
236
  method: "POST",
@@ -191,10 +238,11 @@ function createMetricsRecorder(ctx, deps = {}) {
191
238
  body: buildLogsPayload(records, server),
192
239
  keepalive: true
193
240
  });
194
- if (!res.ok)
195
- log.debug("metrics.flush_rejected", { status: res.status });
241
+ if (!res.ok) {
242
+ log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
243
+ }
196
244
  } catch (err) {
197
- log.debug("metrics.flush_failed", describeError(err));
245
+ log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
198
246
  }
199
247
  };
200
248
  const ensureTimer = () => {
@@ -670,6 +718,7 @@ export {
670
718
  headResponse,
671
719
  methodNotAllowed,
672
720
  nowMs,
721
+ reportInvocation,
673
722
  createRecorderForRuntime,
674
723
  resolveProtectedResource,
675
724
  assertResourcePathShape,
@@ -6,12 +6,13 @@ import {
6
6
  createRecorderForRuntime,
7
7
  createRequestAuthorizer,
8
8
  nowMs,
9
+ reportInvocation,
9
10
  withCors
10
- } from "./chunk-YTOMGJV5.js";
11
+ } from "./chunk-6TZNGFRM.js";
11
12
  import {
12
13
  describeError,
13
14
  log
14
- } from "./chunk-DCWYK4CA.js";
15
+ } from "./chunk-VQQZAFRA.js";
15
16
 
16
17
  // src/protocols/mcp/protocol.ts
17
18
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -24,14 +25,24 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
24
25
  try {
25
26
  result = await tool.handler(args, new ToolContext(auth));
26
27
  } catch {
27
- recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
28
+ reportInvocation(recorder, {
29
+ tool: tool.name,
30
+ method: "tools/call",
31
+ outcome: "handler_error",
32
+ durationMs: nowMs() - start
33
+ });
28
34
  return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
29
35
  }
30
36
  if (result == null) {
31
- recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
37
+ reportInvocation(recorder, {
38
+ tool: tool.name,
39
+ method: "tools/call",
40
+ outcome: "handler_error",
41
+ durationMs: nowMs() - start
42
+ });
32
43
  return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
33
44
  }
34
- recorder.record({
45
+ reportInvocation(recorder, {
35
46
  tool: tool.name,
36
47
  method: "tools/call",
37
48
  outcome: result.isError ? "tool_error" : "ok",
@@ -6,7 +6,7 @@ import {
6
6
  headResponse,
7
7
  methodNotAllowed,
8
8
  withCors
9
- } from "./chunk-YTOMGJV5.js";
9
+ } from "./chunk-6TZNGFRM.js";
10
10
 
11
11
  // src/protocols/rest/list-tools.ts
12
12
  import { objectFromShape } from "@modelcontextprotocol/sdk/server/zod-compat.js";
@@ -7,11 +7,11 @@ import {
7
7
  oauthConfigurationErrorResponse,
8
8
  resolveProtectedResource,
9
9
  withCors
10
- } from "./chunk-YTOMGJV5.js";
10
+ } from "./chunk-6TZNGFRM.js";
11
11
  import {
12
12
  describeError,
13
13
  log
14
- } from "./chunk-DCWYK4CA.js";
14
+ } from "./chunk-VQQZAFRA.js";
15
15
 
16
16
  // src/protocols/oauth-metadata.ts
17
17
  function notFound() {
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.15.1";
2
+ var version = "0.17.0";
3
3
 
4
4
  export {
5
5
  version
@@ -3,19 +3,29 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
3
3
  function isLogLevel(value) {
4
4
  return typeof value === "string" && value in LEVEL_RANK;
5
5
  }
6
+ var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
6
7
  function readEnvLevel() {
7
8
  try {
8
- const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
9
+ const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
9
10
  const normalized = raw?.trim().toLowerCase();
10
11
  return isLogLevel(normalized) ? normalized : void 0;
11
12
  } catch {
12
13
  return void 0;
13
14
  }
14
15
  }
15
- var currentLevel = readEnvLevel() ?? "silent";
16
+ var currentLevel = readEnvLevel() ?? "debug";
16
17
  function setLogLevel(level) {
17
18
  currentLevel = level;
18
19
  }
20
+ function parseLogLevel(raw) {
21
+ const normalized = raw?.trim().toLowerCase();
22
+ return isLogLevel(normalized) ? normalized : void 0;
23
+ }
24
+ function applyLogLevelFromEnv(raw) {
25
+ const level = parseLogLevel(raw);
26
+ if (level)
27
+ setLogLevel(level);
28
+ }
19
29
  function enabled(level) {
20
30
  return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
21
31
  }
@@ -115,7 +125,9 @@ export {
115
125
  resolveMetricsConfig,
116
126
  trimTrailingSlash,
117
127
  parseSafeUrl,
128
+ LOG_LEVEL_ENV_VAR,
118
129
  setLogLevel,
130
+ applyLogLevelFromEnv,
119
131
  log,
120
132
  describeError
121
133
  };
@@ -9,8 +9,9 @@ import {
9
9
  createRequestAuthorizer,
10
10
  methodNotAllowed,
11
11
  nowMs,
12
+ reportInvocation,
12
13
  withCors
13
- } from "./chunk-YTOMGJV5.js";
14
+ } from "./chunk-6TZNGFRM.js";
14
15
 
15
16
  // src/protocols/rest/invoke-tool.ts
16
17
  import { getParseErrorMessage, objectFromShape, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
@@ -86,20 +87,30 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
86
87
  try {
87
88
  result = await tool.handler(args, new ToolContext(authResult.auth));
88
89
  } catch {
89
- recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
90
+ reportInvocation(recorder, {
91
+ tool: tool.name,
92
+ method: "tools/call",
93
+ outcome: "handler_error",
94
+ durationMs: nowMs() - start
95
+ });
90
96
  return new Response(JSON.stringify({ error: "handler threw", tool: toolName }), {
91
97
  status: 500,
92
98
  headers: JSON_HEADERS
93
99
  });
94
100
  }
95
101
  if (result == null) {
96
- recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
102
+ reportInvocation(recorder, {
103
+ tool: tool.name,
104
+ method: "tools/call",
105
+ outcome: "handler_error",
106
+ durationMs: nowMs() - start
107
+ });
97
108
  return new Response(JSON.stringify({ error: `tool "${toolName}" returned no result` }), {
98
109
  status: 500,
99
110
  headers: JSON_HEADERS
100
111
  });
101
112
  }
102
- recorder.record({
113
+ reportInvocation(recorder, {
103
114
  tool: tool.name,
104
115
  method: "tools/call",
105
116
  outcome: result.isError ? "tool_error" : "ok",
@@ -13,7 +13,7 @@ function isFileMissing(err) {
13
13
  }
14
14
 
15
15
  // package.json
16
- var version = "0.15.1";
16
+ var version = "0.17.0";
17
17
 
18
18
  // src/protocols/rest/list-tools.ts
19
19
  var import_zod_compat = require("@modelcontextprotocol/sdk/server/zod-compat.js");
@@ -24,16 +24,17 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
24
24
  function isLogLevel(value) {
25
25
  return typeof value === "string" && value in LEVEL_RANK;
26
26
  }
27
+ var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
27
28
  function readEnvLevel() {
28
29
  try {
29
- const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
30
+ const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
30
31
  const normalized = raw?.trim().toLowerCase();
31
32
  return isLogLevel(normalized) ? normalized : void 0;
32
33
  } catch {
33
34
  return void 0;
34
35
  }
35
36
  }
36
- var currentLevel = readEnvLevel() ?? "silent";
37
+ var currentLevel = readEnvLevel() ?? "debug";
37
38
 
38
39
  // src/auth/verifier.ts
39
40
  var import_jose = require("jose");
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildMcpListing
4
- } from "../chunk-QT5L4CS3.js";
5
- import "../chunk-YTOMGJV5.js";
6
- import "../chunk-DCWYK4CA.js";
4
+ } from "../chunk-FW6YL5C5.js";
5
+ import "../chunk-6TZNGFRM.js";
6
+ import "../chunk-VQQZAFRA.js";
7
7
  import "../chunk-6DXGZZA4.js";
8
8
  import {
9
9
  isFileMissing
10
10
  } from "../chunk-Y3ZFPEQH.js";
11
11
  import {
12
12
  version
13
- } from "../chunk-GIHCQT6L.js";
13
+ } from "../chunk-J5BTCP6T.js";
14
14
 
15
15
  // src/manifest/io.ts
16
16
  import { randomUUID } from "crypto";
package/dist/index.cjs CHANGED
@@ -319,16 +319,17 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
319
319
  function isLogLevel(value) {
320
320
  return typeof value === "string" && value in LEVEL_RANK;
321
321
  }
322
+ var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
322
323
  function readEnvLevel() {
323
324
  try {
324
- const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
325
+ const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
325
326
  const normalized = raw?.trim().toLowerCase();
326
327
  return isLogLevel(normalized) ? normalized : void 0;
327
328
  } catch {
328
329
  return void 0;
329
330
  }
330
331
  }
331
- var currentLevel = readEnvLevel() ?? "silent";
332
+ var currentLevel = readEnvLevel() ?? "debug";
332
333
  function setLogLevel(level) {
333
334
  currentLevel = level;
334
335
  }
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  parseSafeUrl,
6
6
  resolveMetricsConfig,
7
7
  setLogLevel
8
- } from "./chunk-DCWYK4CA.js";
8
+ } from "./chunk-VQQZAFRA.js";
9
9
 
10
10
  // src/core/define.ts
11
11
  function assertUniqueNames(mcp) {
@@ -36,16 +36,29 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
36
36
  function isLogLevel(value) {
37
37
  return typeof value === "string" && value in LEVEL_RANK;
38
38
  }
39
+ var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
39
40
  function readEnvLevel() {
40
41
  try {
41
- const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
42
+ const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
42
43
  const normalized = raw?.trim().toLowerCase();
43
44
  return isLogLevel(normalized) ? normalized : void 0;
44
45
  } catch {
45
46
  return void 0;
46
47
  }
47
48
  }
48
- var currentLevel = readEnvLevel() ?? "silent";
49
+ var currentLevel = readEnvLevel() ?? "debug";
50
+ function setLogLevel(level) {
51
+ currentLevel = level;
52
+ }
53
+ function parseLogLevel(raw) {
54
+ const normalized = raw?.trim().toLowerCase();
55
+ return isLogLevel(normalized) ? normalized : void 0;
56
+ }
57
+ function applyLogLevelFromEnv(raw) {
58
+ const level = parseLogLevel(raw);
59
+ if (level)
60
+ setLogLevel(level);
61
+ }
49
62
  function enabled(level) {
50
63
  return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
51
64
  }
@@ -105,7 +118,7 @@ function resolveMetricsConfig(config = true) {
105
118
  }
106
119
 
107
120
  // package.json
108
- var version = "0.15.1";
121
+ var version = "0.17.0";
109
122
 
110
123
  // src/metrics/otlp.ts
111
124
  var SCOPE_NAME = "@lovable.dev/mcp-js";
@@ -170,6 +183,10 @@ function nowUnixNano() {
170
183
  function nowMs() {
171
184
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
172
185
  }
186
+ function reportInvocation(recorder, ev) {
187
+ log.info("tool.invoked", { tool: ev.tool, method: ev.method, outcome: ev.outcome, durationMs: ev.durationMs });
188
+ recorder.record(ev);
189
+ }
173
190
  var NOOP_RECORDER = {
174
191
  record() {
175
192
  },
@@ -197,51 +214,92 @@ function readRuntimeEnv(name) {
197
214
  }
198
215
  return void 0;
199
216
  }
217
+ var cloudflareEnvPromise;
218
+ async function readCloudflareEnv(name) {
219
+ try {
220
+ const moduleSpecifier = "cloudflare:workers";
221
+ cloudflareEnvPromise ??= import(
222
+ /* @vite-ignore */
223
+ moduleSpecifier
224
+ ).then((m) => m.env).catch(() => void 0);
225
+ const env = await cloudflareEnvPromise;
226
+ const value = env?.[name];
227
+ return typeof value === "string" && value ? value : void 0;
228
+ } catch {
229
+ return void 0;
230
+ }
231
+ }
200
232
  function probeWaitUntil() {
201
233
  const fn = globalThis.EdgeRuntime?.waitUntil;
202
234
  return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
203
235
  }
204
236
  function createMetricsRecorder(ctx, deps = {}) {
205
237
  const { config, server } = ctx;
238
+ applyLogLevelFromEnv(readRuntimeEnv(LOG_LEVEL_ENV_VAR));
206
239
  if (!config.enabled)
207
240
  return NOOP_RECORDER;
208
241
  const doFetch = deps.fetch ?? globalThis.fetch;
209
- if (!doFetch)
210
- return NOOP_RECORDER;
211
- const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
212
- const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
213
- if (usesLovableEndpoint && !apiKey) {
214
- log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
242
+ if (!doFetch) {
243
+ log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
215
244
  return NOOP_RECORDER;
216
245
  }
246
+ const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
217
247
  const waitUntil = deps.waitUntil ?? probeWaitUntil();
218
248
  const buffer = [];
219
249
  let timer;
250
+ let lazyLogLevelApplied = false;
251
+ const applyLazyLogLevel = async () => {
252
+ if (lazyLogLevelApplied)
253
+ return;
254
+ lazyLogLevelApplied = true;
255
+ applyLogLevelFromEnv(await readCloudflareEnv(LOG_LEVEL_ENV_VAR));
256
+ };
257
+ let apiKey;
258
+ const resolveApiKey = async () => {
259
+ if (apiKey)
260
+ return apiKey;
261
+ apiKey = (deps.getApiKey ? deps.getApiKey() : readRuntimeEnv(config.apiKeyEnvVar)) ?? await readCloudflareEnv(config.apiKeyEnvVar);
262
+ return apiKey;
263
+ };
220
264
  const schedule = (p) => {
221
265
  if (waitUntil) {
222
266
  try {
223
267
  waitUntil(p);
224
268
  return;
225
- } catch {
269
+ } catch (err) {
270
+ log.warn("metrics.wait_until_failed", describeError(err));
226
271
  }
227
272
  }
228
273
  void p.catch(() => {
229
274
  });
230
275
  };
231
- const headers = {};
276
+ const baseHeaders = {};
232
277
  if (!usesLovableEndpoint) {
233
278
  for (const [key, value] of Object.entries(config.headers)) {
234
279
  if (key.toLowerCase() !== "content-type")
235
- headers[key] = value;
280
+ baseHeaders[key] = value;
236
281
  }
237
282
  }
238
- headers["content-type"] = "application/json";
239
- if (apiKey)
240
- headers.authorization = `Bearer ${apiKey}`;
283
+ baseHeaders["content-type"] = "application/json";
241
284
  const flush = async () => {
285
+ void applyLazyLogLevel();
242
286
  if (buffer.length === 0)
243
287
  return;
244
288
  const records = buffer.splice(0, buffer.length);
289
+ const dropped = records.length;
290
+ const headers = { ...baseHeaders };
291
+ if (usesLovableEndpoint) {
292
+ const key = await resolveApiKey();
293
+ if (!key) {
294
+ log.warn("metrics.disabled_no_api_key", {
295
+ envVar: config.apiKeyEnvVar,
296
+ endpoint: config.endpoint,
297
+ dropped
298
+ });
299
+ return;
300
+ }
301
+ headers.authorization = `Bearer ${key}`;
302
+ }
245
303
  try {
246
304
  const res = await doFetch(config.endpoint, {
247
305
  method: "POST",
@@ -249,10 +307,11 @@ function createMetricsRecorder(ctx, deps = {}) {
249
307
  body: buildLogsPayload(records, server),
250
308
  keepalive: true
251
309
  });
252
- if (!res.ok)
253
- log.debug("metrics.flush_rejected", { status: res.status });
310
+ if (!res.ok) {
311
+ log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
312
+ }
254
313
  } catch (err) {
255
- log.debug("metrics.flush_failed", describeError(err));
314
+ log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
256
315
  }
257
316
  };
258
317
  const ensureTimer = () => {
@@ -830,14 +889,24 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
830
889
  try {
831
890
  result = await tool.handler(args, new ToolContext(auth));
832
891
  } catch {
833
- recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
892
+ reportInvocation(recorder, {
893
+ tool: tool.name,
894
+ method: "tools/call",
895
+ outcome: "handler_error",
896
+ durationMs: nowMs() - start
897
+ });
834
898
  return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
835
899
  }
836
900
  if (result == null) {
837
- recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
901
+ reportInvocation(recorder, {
902
+ tool: tool.name,
903
+ method: "tools/call",
904
+ outcome: "handler_error",
905
+ durationMs: nowMs() - start
906
+ });
838
907
  return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
839
908
  }
840
- recorder.record({
909
+ reportInvocation(recorder, {
841
910
  tool: tool.name,
842
911
  method: "tools/call",
843
912
  outcome: result.isError ? "tool_error" : "ok",
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createMcpProtocolHandler
3
- } from "../../chunk-UCPYLSNN.js";
3
+ } from "../../chunk-EL6YNP2R.js";
4
4
  import "../../chunk-MA5H6PSF.js";
5
- import "../../chunk-YTOMGJV5.js";
6
- import "../../chunk-DCWYK4CA.js";
5
+ import "../../chunk-6TZNGFRM.js";
6
+ import "../../chunk-VQQZAFRA.js";
7
7
  import "../../chunk-6DXGZZA4.js";
8
- import "../../chunk-GIHCQT6L.js";
8
+ import "../../chunk-J5BTCP6T.js";
9
9
  export {
10
10
  createMcpProtocolHandler
11
11
  };
@@ -41,16 +41,17 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
41
41
  function isLogLevel(value) {
42
42
  return typeof value === "string" && value in LEVEL_RANK;
43
43
  }
44
+ var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
44
45
  function readEnvLevel() {
45
46
  try {
46
- const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
47
+ const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
47
48
  const normalized = raw?.trim().toLowerCase();
48
49
  return isLogLevel(normalized) ? normalized : void 0;
49
50
  } catch {
50
51
  return void 0;
51
52
  }
52
53
  }
53
- var currentLevel = readEnvLevel() ?? "silent";
54
+ var currentLevel = readEnvLevel() ?? "debug";
54
55
  function enabled(level) {
55
56
  return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
56
57
  }
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createOAuthProtectedResourceMetadataHandler
3
- } from "../chunk-ILYTJXDR.js";
4
- import "../chunk-YTOMGJV5.js";
5
- import "../chunk-DCWYK4CA.js";
3
+ } from "../chunk-H2HXKAMQ.js";
4
+ import "../chunk-6TZNGFRM.js";
5
+ import "../chunk-VQQZAFRA.js";
6
6
  import "../chunk-6DXGZZA4.js";
7
- import "../chunk-GIHCQT6L.js";
7
+ import "../chunk-J5BTCP6T.js";
8
8
  export {
9
9
  createOAuthProtectedResourceMetadataHandler
10
10
  };