@lovable.dev/mcp-js 0.16.0 → 0.19.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.
Files changed (38) hide show
  1. package/dist/{recorder-qlzUKFxG.d.ts → base-C9rhAHZ0.d.ts} +3 -4
  2. package/dist/{chunk-W2EZWPJ5.js → chunk-57ZH2WW5.js} +11 -12
  3. package/dist/{chunk-H6BKTNJY.js → chunk-5FX6IIQ6.js} +129 -105
  4. package/dist/{chunk-SKSKC747.js → chunk-CDZ7MBXC.js} +1 -1
  5. package/dist/{chunk-EZQVUNXB.js → chunk-CLXKPZZE.js} +13 -14
  6. package/dist/{chunk-DCWYK4CA.js → chunk-H37EB22A.js} +13 -9
  7. package/dist/{chunk-I5CAQUQI.js → chunk-OMWXY6WY.js} +8 -8
  8. package/dist/{chunk-T24Z753F.js → chunk-UEOBGHXF.js} +2 -2
  9. package/dist/cli/extract-manifest.cjs +4 -3
  10. package/dist/cli/extract-manifest.js +4 -4
  11. package/dist/index.cjs +4 -9
  12. package/dist/index.d.cts +2 -2
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +1 -1
  15. package/dist/protocols/mcp/index.cjs +19 -225
  16. package/dist/protocols/mcp/index.d.cts +6 -5
  17. package/dist/protocols/mcp/index.d.ts +6 -5
  18. package/dist/protocols/mcp/index.js +4 -4
  19. package/dist/protocols/oauth-metadata.cjs +3 -2
  20. package/dist/protocols/oauth-metadata.d.cts +1 -1
  21. package/dist/protocols/oauth-metadata.d.ts +1 -1
  22. package/dist/protocols/oauth-metadata.js +4 -4
  23. package/dist/protocols/rest/index.cjs +24 -230
  24. package/dist/protocols/rest/index.d.cts +6 -6
  25. package/dist/protocols/rest/index.d.ts +6 -6
  26. package/dist/protocols/rest/index.js +5 -5
  27. package/dist/stacks/supabase/index.cjs +196 -169
  28. package/dist/stacks/supabase/index.d.cts +1 -1
  29. package/dist/stacks/supabase/index.d.ts +1 -1
  30. package/dist/stacks/supabase/index.js +14 -14
  31. package/dist/stacks/supabase/vite.cjs +1 -1
  32. package/dist/stacks/supabase/vite.js +1 -1
  33. package/dist/stacks/tanstack/index.cjs +205 -177
  34. package/dist/stacks/tanstack/index.d.cts +1 -1
  35. package/dist/stacks/tanstack/index.d.ts +1 -1
  36. package/dist/stacks/tanstack/index.js +13 -10
  37. package/dist/{types-COY42xux.d.ts → types-CPkhCRxc.d.ts} +3 -3
  38. package/package.json +1 -1
@@ -11,11 +11,10 @@ interface InvocationRecord {
11
11
  reqBytes?: number;
12
12
  resBytes?: number;
13
13
  }
14
+ /** A per-request telemetry recorder. `emit` logs the invocation and sends it as a
15
+ * single OTLP POST, awaited so it lands before the request ends. */
14
16
  interface MetricsRecorder {
15
- /** Buffer one invocation. Synchronous, sampled, and never throws. */
16
- record(ev: InvocationRecord): void;
17
- /** Best-effort POST of the current buffer. Resolves even on network failure. */
18
- flush(): Promise<void>;
17
+ emit(ev: InvocationRecord): Promise<void>;
19
18
  }
20
19
 
21
20
  export type { MetricsRecorder as M };
@@ -5,13 +5,12 @@ import {
5
5
  JSON_HEADERS,
6
6
  assertRestResourceBinding,
7
7
  corsPreflightResponse,
8
- createRecorderForRuntime,
8
+ createNoopRecorder,
9
9
  createRequestAuthorizer,
10
10
  methodNotAllowed,
11
11
  nowMs,
12
- reportInvocation,
13
12
  withCors
14
- } from "./chunk-H6BKTNJY.js";
13
+ } from "./chunk-5FX6IIQ6.js";
15
14
 
16
15
  // src/protocols/rest/invoke-tool.ts
17
16
  import { getParseErrorMessage, objectFromShape, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
@@ -27,11 +26,11 @@ function isEmptyArgs(value) {
27
26
  return false;
28
27
  return Object.keys(value).length === 0;
29
28
  }
30
- function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
29
+ function createInvokeToolHandler(mcp, options = {}) {
31
30
  assertRestResourceBinding(mcp, options);
32
- const authorizer = createRequestAuthorizer(mcp, options, recorder);
33
- const handle = async (request, toolName) => {
34
- const authResult = await authorizer.authorize(request);
31
+ const authorizer = createRequestAuthorizer(mcp, options);
32
+ const handle = async (request, toolName, recorder) => {
33
+ const authResult = await authorizer.authorize(request, recorder);
35
34
  if (!authResult.ok)
36
35
  return authResult.response;
37
36
  if (request.method !== "POST")
@@ -87,7 +86,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
87
86
  try {
88
87
  result = await tool.handler(args, new ToolContext(authResult.auth));
89
88
  } catch {
90
- reportInvocation(recorder, {
89
+ await recorder.emit({
91
90
  tool: tool.name,
92
91
  method: "tools/call",
93
92
  outcome: "handler_error",
@@ -99,7 +98,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
99
98
  });
100
99
  }
101
100
  if (result == null) {
102
- reportInvocation(recorder, {
101
+ await recorder.emit({
103
102
  tool: tool.name,
104
103
  method: "tools/call",
105
104
  outcome: "handler_error",
@@ -110,7 +109,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
110
109
  headers: JSON_HEADERS
111
110
  });
112
111
  }
113
- reportInvocation(recorder, {
112
+ await recorder.emit({
114
113
  tool: tool.name,
115
114
  method: "tools/call",
116
115
  outcome: result.isError ? "tool_error" : "ok",
@@ -122,10 +121,10 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
122
121
  isError: result.isError
123
122
  });
124
123
  };
125
- return async (request, toolName) => {
124
+ return async (request, toolName, recorder = createNoopRecorder()) => {
126
125
  if (request.method === "OPTIONS")
127
126
  return corsPreflightResponse("POST, OPTIONS");
128
- return withCors(await handle(request, toolName));
127
+ return withCors(await handle(request, toolName, recorder));
129
128
  };
130
129
  }
131
130
 
@@ -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-H37EB22A.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-SKSKC747.js";
16
+ } from "./chunk-CDZ7MBXC.js";
15
17
 
16
18
  // src/core/http.ts
17
19
  var JSON_HEADERS = { "Content-Type": "application/json" };
@@ -72,6 +74,7 @@ function toLogRecord(rec) {
72
74
  attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
73
75
  if (rec.resBytes !== void 0)
74
76
  attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
77
+ attributes.push(strAttr("mcp.stack", rec.stack));
75
78
  return {
76
79
  timeUnixNano: rec.timeUnixNano,
77
80
  observedTimeUnixNano: rec.timeUnixNano,
@@ -108,127 +111,148 @@ function nowUnixNano() {
108
111
  return `${Date.now()}000000`;
109
112
  }
110
113
 
111
- // src/metrics/recorder.ts
114
+ // src/metrics/impl/base.ts
112
115
  function nowMs() {
113
116
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
114
117
  }
115
- function reportInvocation(recorder, ev) {
116
- log.info("tool.invoked", { tool: ev.tool, method: ev.method, outcome: ev.outcome, durationMs: ev.durationMs });
117
- recorder.record(ev);
118
- }
119
- var NOOP_RECORDER = {
120
- record() {
121
- },
122
- async flush() {
123
- }
124
- };
118
+ var NOOP_RECORDER = { async emit() {
119
+ } };
125
120
  function createNoopRecorder() {
126
121
  return NOOP_RECORDER;
127
122
  }
128
- function readRuntimeEnv(name) {
123
+ function readProcessEnv(name) {
129
124
  try {
130
- const denoEnv = globalThis.Deno?.env;
131
- const value = denoEnv?.get?.(name);
132
- if (value)
133
- return value;
134
- } catch {
135
- }
136
- try {
137
- if (typeof process !== "undefined") {
138
- const value = process.env?.[name];
139
- if (value)
140
- return value;
141
- }
125
+ return typeof process !== "undefined" ? process.env?.[name] || void 0 : void 0;
142
126
  } catch {
127
+ return void 0;
143
128
  }
144
- return void 0;
145
- }
146
- function probeWaitUntil() {
147
- const fn = globalThis.EdgeRuntime?.waitUntil;
148
- return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
149
129
  }
150
- function createMetricsRecorder(ctx, deps = {}) {
151
- const { config, server } = ctx;
152
- if (!config.enabled)
153
- return NOOP_RECORDER;
154
- const doFetch = deps.fetch ?? globalThis.fetch;
155
- if (!doFetch) {
156
- log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
157
- return NOOP_RECORDER;
158
- }
159
- const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
160
- const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
161
- if (usesLovableEndpoint && !apiKey) {
162
- log.warn("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar, endpoint: config.endpoint });
163
- return NOOP_RECORDER;
164
- }
165
- const waitUntil = deps.waitUntil ?? probeWaitUntil();
166
- const buffer = [];
167
- let timer;
168
- const schedule = (p) => {
169
- if (waitUntil) {
170
- try {
171
- waitUntil(p);
172
- return;
173
- } catch (err) {
174
- log.warn("metrics.wait_until_failed", describeError(err));
130
+ var BaseMetricRecorder = class {
131
+ constructor(config, server, deps = {}) {
132
+ this.config = config;
133
+ this.server = server;
134
+ this.deps = deps;
135
+ this.doFetch = deps.fetch ?? globalThis.fetch;
136
+ this.usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
137
+ const headers = {};
138
+ if (!this.usesLovableEndpoint) {
139
+ for (const [key, value] of Object.entries(config.headers)) {
140
+ if (key.toLowerCase() !== "content-type")
141
+ headers[key] = value;
175
142
  }
176
143
  }
177
- void p.catch(() => {
178
- });
179
- };
180
- const headers = {};
181
- if (!usesLovableEndpoint) {
182
- for (const [key, value] of Object.entries(config.headers)) {
183
- if (key.toLowerCase() !== "content-type")
184
- headers[key] = value;
185
- }
144
+ headers["content-type"] = "application/json";
145
+ this.baseHeaders = headers;
186
146
  }
187
- headers["content-type"] = "application/json";
188
- if (apiKey)
189
- headers.authorization = `Bearer ${apiKey}`;
190
- const flush = async () => {
191
- if (buffer.length === 0)
147
+ doFetch;
148
+ usesLovableEndpoint;
149
+ baseHeaders;
150
+ apiKey;
151
+ lazyLogLevelApplied = false;
152
+ async emit(ev) {
153
+ await this.applyLazyLogLevel();
154
+ log.info("tool.invoked", {
155
+ tool: ev.tool,
156
+ method: ev.method,
157
+ outcome: ev.outcome,
158
+ durationMs: ev.durationMs,
159
+ stack: this.stack
160
+ });
161
+ if (!this.config.enabled)
192
162
  return;
193
- const records = buffer.splice(0, buffer.length);
194
- const dropped = records.length;
195
- try {
196
- const res = await doFetch(config.endpoint, {
197
- method: "POST",
198
- headers,
199
- body: buildLogsPayload(records, server),
200
- keepalive: true
201
- });
202
- if (!res.ok) {
203
- log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
163
+ if (!this.doFetch) {
164
+ log.warn("metrics.disabled_no_fetch", { endpoint: this.config.endpoint });
165
+ return;
166
+ }
167
+ const headers = { ...this.baseHeaders };
168
+ if (this.usesLovableEndpoint) {
169
+ const key = await this.resolveApiKey();
170
+ if (!key) {
171
+ log.warn("metrics.disabled_no_api_key", { envVar: this.config.apiKeyEnvVar, endpoint: this.config.endpoint });
172
+ return;
204
173
  }
174
+ headers.authorization = `Bearer ${key}`;
175
+ }
176
+ const body = buildLogsPayload([{ ...ev, timeUnixNano: nowUnixNano(), stack: this.stack }], this.server);
177
+ try {
178
+ const res = await this.doFetch(this.config.endpoint, { method: "POST", headers, body, keepalive: true });
179
+ if (!res.ok)
180
+ log.warn("metrics.rejected", { status: res.status, endpoint: this.config.endpoint });
205
181
  } catch (err) {
206
- log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
182
+ log.warn("metrics.failed", { ...describeError(err), endpoint: this.config.endpoint });
207
183
  }
208
- };
209
- const ensureTimer = () => {
210
- if (timer !== void 0)
184
+ }
185
+ // Cache only a successful resolution so a miss is retried on the next emit (the
186
+ // Cloudflare binding may not be readable on the very first attempt in an isolate).
187
+ async resolveApiKey() {
188
+ if (this.apiKey)
189
+ return this.apiKey;
190
+ this.apiKey = this.deps.getApiKey ? this.deps.getApiKey() : await this.readEnv(this.config.apiKeyEnvVar);
191
+ return this.apiKey;
192
+ }
193
+ async applyLazyLogLevel() {
194
+ if (this.lazyLogLevelApplied)
211
195
  return;
212
- timer = setInterval(() => schedule(flush()), config.flushIntervalMs);
213
- timer.unref?.();
214
- };
215
- return {
216
- record(ev) {
217
- if (config.sampleRate < 1 && Math.random() >= config.sampleRate)
218
- return;
219
- buffer.push({ ...ev, timeUnixNano: nowUnixNano() });
220
- ensureTimer();
221
- if (buffer.length >= config.maxBatchSize)
222
- schedule(flush());
223
- },
224
- flush
225
- };
196
+ this.lazyLogLevelApplied = true;
197
+ applyLogLevelFromEnv(await this.readEnv(LOG_LEVEL_ENV_VAR));
198
+ }
199
+ };
200
+
201
+ // src/metrics/impl/cloudflare.ts
202
+ var cloudflareEnvPromise;
203
+ async function readCloudflareEnv(name) {
204
+ try {
205
+ const moduleSpecifier = "cloudflare:workers";
206
+ cloudflareEnvPromise ??= import(
207
+ /* @vite-ignore */
208
+ moduleSpecifier
209
+ ).then((m) => m.env).catch((err) => {
210
+ log.debug("metrics.cloudflare_env_import_failed", describeError(err));
211
+ return void 0;
212
+ });
213
+ const env = await cloudflareEnvPromise;
214
+ const raw = env?.[name];
215
+ const value = typeof raw === "string" && raw ? raw : void 0;
216
+ log.debug("metrics.read_cloudflare_env", { name, hasEnvBinding: !!env, found: value !== void 0 });
217
+ return value;
218
+ } catch (err) {
219
+ log.debug("metrics.read_cloudflare_env_error", { name, ...describeError(err) });
220
+ return void 0;
221
+ }
222
+ }
223
+ var TanStackMetricRecorder = class extends BaseMetricRecorder {
224
+ stack = "tanstack";
225
+ async readEnv(name) {
226
+ return readProcessEnv(name) ?? await readCloudflareEnv(name);
227
+ }
228
+ };
229
+
230
+ // src/metrics/impl/supabase.ts
231
+ function readDenoEnv(name) {
232
+ try {
233
+ const denoEnv = globalThis.Deno?.env;
234
+ const value = denoEnv?.get?.(name) || void 0;
235
+ log.debug("metrics.read_deno_env", { name, hasDenoEnv: !!denoEnv, found: !!value });
236
+ return value;
237
+ } catch (err) {
238
+ log.debug("metrics.read_deno_env_error", { name, ...describeError(err) });
239
+ return void 0;
240
+ }
226
241
  }
227
- function createRecorderForRuntime(mcp) {
242
+ var SupabaseMetricRecorder = class extends BaseMetricRecorder {
243
+ stack = "supabase";
244
+ async readEnv(name) {
245
+ return readDenoEnv(name) ?? readProcessEnv(name);
246
+ }
247
+ };
248
+
249
+ // src/metrics/recorder.ts
250
+ function createRecorderForRuntime(mcp, opts) {
228
251
  const config = resolveMetricsConfig(mcp.metrics);
252
+ const server = { name: mcp.name, version: mcp.version };
229
253
  if (!config.enabled)
230
254
  return createNoopRecorder();
231
- return createMetricsRecorder({ config, server: { name: mcp.name, version: mcp.version } });
255
+ return opts.stack === "tanstack" ? new TanStackMetricRecorder(config, server) : new SupabaseMetricRecorder(config, server);
232
256
  }
233
257
 
234
258
  // src/core/promise.ts
@@ -596,10 +620,10 @@ function assertRequiredScopes(auth, context) {
596
620
  throw new OAuthTokenError(403, "insufficient_scope", "Additional OAuth scope is required");
597
621
  }
598
622
  }
599
- function createRequestAuthorizer(mcp, options = {}, recorder) {
623
+ function createRequestAuthorizer(mcp, options = {}) {
600
624
  const runtime = getOAuthRuntime(mcp, options);
601
625
  return {
602
- async authorize(request) {
626
+ async authorize(request, recorder) {
603
627
  if (runtime.kind === "unconfigured")
604
628
  return { ok: true };
605
629
  const startedAt = nowMs();
@@ -615,7 +639,7 @@ function createRequestAuthorizer(mcp, options = {}, recorder) {
615
639
  } catch (err) {
616
640
  if (err instanceof OAuthConfigurationError) {
617
641
  log.error("auth.config_error", { ...describeError(err), outcome: "500" });
618
- recorder?.record({
642
+ await recorder?.emit({
619
643
  tool: null,
620
644
  method: "authorize",
621
645
  outcome: "auth_config_error",
@@ -679,7 +703,7 @@ export {
679
703
  headResponse,
680
704
  methodNotAllowed,
681
705
  nowMs,
682
- reportInvocation,
706
+ createNoopRecorder,
683
707
  createRecorderForRuntime,
684
708
  resolveProtectedResource,
685
709
  assertResourcePathShape,
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.16.0";
2
+ var version = "0.19.0";
3
3
 
4
4
  export {
5
5
  version
@@ -3,16 +3,15 @@ import {
3
3
  } from "./chunk-MA5H6PSF.js";
4
4
  import {
5
5
  corsPreflightResponse,
6
- createRecorderForRuntime,
6
+ createNoopRecorder,
7
7
  createRequestAuthorizer,
8
8
  nowMs,
9
- reportInvocation,
10
9
  withCors
11
- } from "./chunk-H6BKTNJY.js";
10
+ } from "./chunk-5FX6IIQ6.js";
12
11
  import {
13
12
  describeError,
14
13
  log
15
- } from "./chunk-DCWYK4CA.js";
14
+ } from "./chunk-H37EB22A.js";
16
15
 
17
16
  // src/protocols/mcp/protocol.ts
18
17
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -25,7 +24,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
25
24
  try {
26
25
  result = await tool.handler(args, new ToolContext(auth));
27
26
  } catch {
28
- reportInvocation(recorder, {
27
+ await recorder.emit({
29
28
  tool: tool.name,
30
29
  method: "tools/call",
31
30
  outcome: "handler_error",
@@ -34,7 +33,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
34
33
  return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
35
34
  }
36
35
  if (result == null) {
37
- reportInvocation(recorder, {
36
+ await recorder.emit({
38
37
  tool: tool.name,
39
38
  method: "tools/call",
40
39
  outcome: "handler_error",
@@ -42,7 +41,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
42
41
  });
43
42
  return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
44
43
  }
45
- reportInvocation(recorder, {
44
+ await recorder.emit({
46
45
  tool: tool.name,
47
46
  method: "tools/call",
48
47
  outcome: result.isError ? "tool_error" : "ok",
@@ -51,10 +50,10 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
51
50
  return { content: result.content ?? [], structuredContent: result.structuredContent, isError: result.isError };
52
51
  };
53
52
  }
54
- function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
55
- const authorizer = createRequestAuthorizer(mcp, options, recorder);
56
- const handle = async (request) => {
57
- const authResult = await authorizer.authorize(request);
53
+ function createMcpProtocolHandler(mcp, options = {}) {
54
+ const authorizer = createRequestAuthorizer(mcp, options);
55
+ const handle = async (request, recorder) => {
56
+ const authResult = await authorizer.authorize(request, recorder);
58
57
  if (!authResult.ok)
59
58
  return authResult.response;
60
59
  try {
@@ -81,7 +80,7 @@ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderFo
81
80
  await server.connect(transport);
82
81
  return await transport.handleRequest(request);
83
82
  } catch (err) {
84
- recorder.record({ tool: null, method: "transport", outcome: "transport_error", durationMs: 0 });
83
+ await recorder.emit({ tool: null, method: "transport", outcome: "transport_error", durationMs: 0 });
85
84
  log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
86
85
  return Response.json(
87
86
  { jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
@@ -89,10 +88,10 @@ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderFo
89
88
  );
90
89
  }
91
90
  };
92
- return async (request) => {
91
+ return async (request, recorder = createNoopRecorder()) => {
93
92
  if (request.method === "OPTIONS")
94
93
  return corsPreflightResponse("GET, POST, DELETE, OPTIONS");
95
- return withCors(await handle(request));
94
+ return withCors(await handle(request, recorder));
96
95
  };
97
96
  }
98
97
 
@@ -3,19 +3,27 @@ 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
+ setLogLevel(parseLogLevel(raw) ?? "debug");
26
+ }
19
27
  function enabled(level) {
20
28
  return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
21
29
  }
@@ -45,9 +53,6 @@ function describeError(err) {
45
53
  // src/metrics/config.ts
46
54
  var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
47
55
  var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
48
- var METRICS_SAMPLE_RATE = 1;
49
- var METRICS_FLUSH_INTERVAL_MS = 5e3;
50
- var METRICS_MAX_BATCH_SIZE = 50;
51
56
  function assertMetricsEndpoint(endpoint) {
52
57
  let url;
53
58
  try {
@@ -67,10 +72,7 @@ function resolveMetricsConfig(config = true) {
67
72
  enabled: options.enabled ?? true,
68
73
  endpoint,
69
74
  headers: options.headers ?? {},
70
- apiKeyEnvVar: METRICS_API_KEY_ENV_VAR,
71
- sampleRate: METRICS_SAMPLE_RATE,
72
- flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
73
- maxBatchSize: METRICS_MAX_BATCH_SIZE
75
+ apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
74
76
  });
75
77
  }
76
78
 
@@ -115,7 +117,9 @@ export {
115
117
  resolveMetricsConfig,
116
118
  trimTrailingSlash,
117
119
  parseSafeUrl,
120
+ LOG_LEVEL_ENV_VAR,
118
121
  setLogLevel,
122
+ applyLogLevelFromEnv,
119
123
  log,
120
124
  describeError
121
125
  };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  assertRestResourceBinding,
3
3
  corsPreflightResponse,
4
- createRecorderForRuntime,
4
+ createNoopRecorder,
5
5
  createRequestAuthorizer,
6
6
  headResponse,
7
7
  methodNotAllowed,
8
8
  withCors
9
- } from "./chunk-H6BKTNJY.js";
9
+ } from "./chunk-5FX6IIQ6.js";
10
10
 
11
11
  // src/protocols/rest/list-tools.ts
12
12
  import { objectFromShape } from "@modelcontextprotocol/sdk/server/zod-compat.js";
@@ -33,11 +33,11 @@ function buildMcpListing(mcp) {
33
33
  }))
34
34
  };
35
35
  }
36
- function createListToolsHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
36
+ function createListToolsHandler(mcp, options = {}) {
37
37
  assertRestResourceBinding(mcp, options);
38
- const authorizer = createRequestAuthorizer(mcp, options, recorder);
39
- const handle = async (request) => {
40
- const authResult = await authorizer.authorize(request);
38
+ const authorizer = createRequestAuthorizer(mcp, options);
39
+ const handle = async (request, recorder) => {
40
+ const authResult = await authorizer.authorize(request, recorder);
41
41
  if (!authResult.ok)
42
42
  return authResult.response;
43
43
  if (request.method !== "GET" && request.method !== "HEAD")
@@ -45,10 +45,10 @@ function createListToolsHandler(mcp, options = {}, recorder = createRecorderForR
45
45
  const response = Response.json(buildMcpListing(mcp));
46
46
  return request.method === "HEAD" ? headResponse(response) : response;
47
47
  };
48
- return async (request) => {
48
+ return async (request, recorder = createNoopRecorder()) => {
49
49
  if (request.method === "OPTIONS")
50
50
  return corsPreflightResponse("GET, HEAD, OPTIONS");
51
- return withCors(await handle(request));
51
+ return withCors(await handle(request, recorder));
52
52
  };
53
53
  }
54
54
 
@@ -7,11 +7,11 @@ import {
7
7
  oauthConfigurationErrorResponse,
8
8
  resolveProtectedResource,
9
9
  withCors
10
- } from "./chunk-H6BKTNJY.js";
10
+ } from "./chunk-5FX6IIQ6.js";
11
11
  import {
12
12
  describeError,
13
13
  log
14
- } from "./chunk-DCWYK4CA.js";
14
+ } from "./chunk-H37EB22A.js";
15
15
 
16
16
  // src/protocols/oauth-metadata.ts
17
17
  function notFound() {
@@ -13,7 +13,7 @@ function isFileMissing(err) {
13
13
  }
14
14
 
15
15
  // package.json
16
- var version = "0.16.0";
16
+ var version = "0.19.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-I5CAQUQI.js";
5
- import "../chunk-H6BKTNJY.js";
6
- import "../chunk-DCWYK4CA.js";
4
+ } from "../chunk-OMWXY6WY.js";
5
+ import "../chunk-5FX6IIQ6.js";
6
+ import "../chunk-H37EB22A.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-SKSKC747.js";
13
+ } from "../chunk-CDZ7MBXC.js";
14
14
 
15
15
  // src/manifest/io.ts
16
16
  import { randomUUID } from "crypto";