@lovable.dev/mcp-js 0.14.0 → 0.15.1

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/README.md +28 -0
  2. package/dist/{chunk-YWLMMDET.js → chunk-CEDD57G2.js} +14 -3
  3. package/dist/{chunk-QC3DXQTH.js → chunk-DCWYK4CA.js} +34 -0
  4. package/dist/{chunk-NOFPRSSI.js → chunk-GIHCQT6L.js} +1 -1
  5. package/dist/{chunk-QDKOF4UF.js → chunk-ILYTJXDR.js} +2 -2
  6. package/dist/{chunk-VNRQPA4K.js → chunk-QT5L4CS3.js} +4 -3
  7. package/dist/{chunk-WLNT2FX7.js → chunk-UCPYLSNN.js} +18 -6
  8. package/dist/{chunk-G4XA7IJM.js → chunk-YTOMGJV5.js} +189 -2
  9. package/dist/cli/extract-manifest.cjs +1 -1
  10. package/dist/cli/extract-manifest.js +6 -6
  11. package/dist/index.cjs +33 -0
  12. package/dist/index.d.cts +2 -2
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +3 -1
  15. package/dist/protocols/mcp/index.cjs +230 -5
  16. package/dist/protocols/mcp/index.d.cts +3 -2
  17. package/dist/protocols/mcp/index.d.ts +3 -2
  18. package/dist/protocols/mcp/index.js +4 -3
  19. package/dist/protocols/oauth-metadata.d.cts +1 -1
  20. package/dist/protocols/oauth-metadata.d.ts +1 -1
  21. package/dist/protocols/oauth-metadata.js +4 -3
  22. package/dist/protocols/rest/index.cjs +229 -5
  23. package/dist/protocols/rest/index.d.cts +4 -3
  24. package/dist/protocols/rest/index.d.ts +4 -3
  25. package/dist/protocols/rest/index.js +5 -4
  26. package/dist/recorder-qlzUKFxG.d.ts +21 -0
  27. package/dist/stacks/supabase/index.cjs +263 -28
  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 +13 -10
  31. package/dist/stacks/supabase/vite.cjs +28 -4
  32. package/dist/stacks/supabase/vite.js +30 -6
  33. package/dist/stacks/tanstack/index.cjs +243 -9
  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 +7 -6
  37. package/dist/{types-BanzncFh.d.ts → types-COY42xux.d.ts} +30 -1
  38. package/package.json +1 -1
@@ -1,6 +1,3 @@
1
- import {
2
- version
3
- } from "../../chunk-NOFPRSSI.js";
4
1
  import {
5
2
  isFileMissing
6
3
  } from "../../chunk-Y3ZFPEQH.js";
@@ -8,9 +5,13 @@ import {
8
5
  FUNCTIONS_MOUNT_PREFIX,
9
6
  assertFunctionName
10
7
  } from "../../chunk-XQWJN6DC.js";
8
+ import {
9
+ version
10
+ } from "../../chunk-GIHCQT6L.js";
11
11
 
12
12
  // src/stacks/supabase/vite.ts
13
13
  import { resolve as resolve2, sep as sep2 } from "path";
14
+ import { loadEnv } from "vite";
14
15
 
15
16
  // src/stacks/supabase/emit.ts
16
17
  import { build } from "esbuild";
@@ -95,7 +96,16 @@ function externalizeBareAsNpm(versions) {
95
96
  }
96
97
  };
97
98
  }
98
- async function bundleFunctionEntry(projectRoot, mcpEntryAbs, functionName) {
99
+ function importMetaEnvDefines(inlineEnv) {
100
+ const env = inlineEnv ?? {};
101
+ const defines = { "import.meta.env": JSON.stringify(env) };
102
+ for (const [key, value] of Object.entries(env)) {
103
+ if (/^[A-Za-z_$][\w$]*$/.test(key))
104
+ defines[`import.meta.env.${key}`] = JSON.stringify(value);
105
+ }
106
+ return defines;
107
+ }
108
+ async function bundleFunctionEntry(projectRoot, mcpEntryAbs, functionName, inlineEnv) {
99
109
  const versions = readProjectDependencyVersions(projectRoot);
100
110
  const wrapper = `import mcp from ${JSON.stringify(mcpEntryAbs)};
101
111
  import { createSupabaseHandler } from "@lovable.dev/mcp-js/stacks/supabase";
@@ -111,6 +121,7 @@ Deno.serve(createSupabaseHandler(mcp, { functionName: ${JSON.stringify(functionN
111
121
  target: "esnext",
112
122
  absWorkingDir: projectRoot,
113
123
  logLevel: "silent",
124
+ define: importMetaEnvDefines(inlineEnv),
114
125
  plugins: [externalizeBareAsNpm(versions)]
115
126
  });
116
127
  const out = result.outputFiles[0]?.text ?? "";
@@ -149,7 +160,7 @@ async function syncSupabaseFunction(options) {
149
160
  cleanupOrphans(paths.functionDir, /* @__PURE__ */ new Set(), removed);
150
161
  return { written, removed, inputs: [] };
151
162
  }
152
- const { code, inputs } = await bundleFunctionEntry(projectRoot, paths.mcpEntryAbs, functionName);
163
+ const { code, inputs } = await bundleFunctionEntry(projectRoot, paths.mcpEntryAbs, functionName, options.inlineEnv);
153
164
  if (writeIfChanged(paths.functionEntry, code, hasGeneratedBanner)) {
154
165
  written.push(paths.functionEntry);
155
166
  }
@@ -193,6 +204,16 @@ function cleanupOrphans(functionDir, expected, removed) {
193
204
  function normalizePath(p) {
194
205
  return p.split(sep2).join("/");
195
206
  }
207
+ function productionImportMetaEnv(config) {
208
+ return {
209
+ ...loadEnv("production", config.envDir, config.envPrefix),
210
+ MODE: "production",
211
+ BASE_URL: config.base ?? "/",
212
+ DEV: false,
213
+ PROD: true,
214
+ SSR: false
215
+ };
216
+ }
196
217
  function mcpPlugin(options = {}) {
197
218
  const mcpEntryOption = options.mcpEntry ?? DEFAULT_MCP_ENTRY;
198
219
  const functionsDirOption = options.functionsDir ?? DEFAULT_FUNCTIONS_DIR;
@@ -200,13 +221,15 @@ function mcpPlugin(options = {}) {
200
221
  const urlPath = `${FUNCTIONS_MOUNT_PREFIX}${functionName}`;
201
222
  let projectRoot = process.cwd();
202
223
  let mcpEntryAbs = resolve2(projectRoot, mcpEntryOption);
224
+ let inlineEnv = {};
203
225
  let watchedInputs = /* @__PURE__ */ new Set();
204
226
  const regenerate = async () => {
205
227
  const { inputs } = await syncSupabaseFunction({
206
228
  projectRoot,
207
229
  mcpEntry: mcpEntryOption,
208
230
  functionsDir: functionsDirOption,
209
- functionName
231
+ functionName,
232
+ inlineEnv
210
233
  });
211
234
  watchedInputs = new Set(inputs.map(normalizePath));
212
235
  };
@@ -224,6 +247,7 @@ function mcpPlugin(options = {}) {
224
247
  async configResolved(config) {
225
248
  projectRoot = config.root;
226
249
  mcpEntryAbs = resolve2(projectRoot, mcpEntryOption);
250
+ inlineEnv = productionImportMetaEnv(config);
227
251
  await regenerate();
228
252
  },
229
253
  configureServer(server) {
@@ -107,6 +107,214 @@ function describeError(err) {
107
107
  return { value: String(err) };
108
108
  }
109
109
 
110
+ // src/metrics/config.ts
111
+ var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
112
+ var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
113
+ var METRICS_SAMPLE_RATE = 1;
114
+ var METRICS_FLUSH_INTERVAL_MS = 5e3;
115
+ var METRICS_MAX_BATCH_SIZE = 50;
116
+ function assertMetricsEndpoint(endpoint) {
117
+ let url;
118
+ try {
119
+ url = new URL(endpoint);
120
+ } catch {
121
+ throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be an absolute URL, got ${JSON.stringify(endpoint)}`);
122
+ }
123
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
124
+ throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be http(s), got ${JSON.stringify(endpoint)}`);
125
+ }
126
+ }
127
+ function resolveMetricsConfig(config = true) {
128
+ const options = typeof config === "boolean" ? { enabled: config } : config;
129
+ const endpoint = options.endpoint ?? DEFAULT_METRICS_ENDPOINT;
130
+ assertMetricsEndpoint(endpoint);
131
+ return Object.freeze({
132
+ enabled: options.enabled ?? true,
133
+ endpoint,
134
+ headers: options.headers ?? {},
135
+ apiKeyEnvVar: METRICS_API_KEY_ENV_VAR,
136
+ sampleRate: METRICS_SAMPLE_RATE,
137
+ flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
138
+ maxBatchSize: METRICS_MAX_BATCH_SIZE
139
+ });
140
+ }
141
+
142
+ // package.json
143
+ var version = "0.15.1";
144
+
145
+ // src/metrics/otlp.ts
146
+ var SCOPE_NAME = "@lovable.dev/mcp-js";
147
+ var EVENT_NAME = "mcp.tool.invocation";
148
+ var SEVERITY_INFO = 9;
149
+ function strAttr(key, value) {
150
+ return { key, value: { stringValue: value } };
151
+ }
152
+ function intAttr(key, value) {
153
+ return { key, value: { intValue: String(Math.round(value)) } };
154
+ }
155
+ function toLogRecord(rec) {
156
+ const attributes = [
157
+ strAttr("event.name", EVENT_NAME),
158
+ strAttr("mcp.method", rec.method),
159
+ strAttr("mcp.outcome", rec.outcome),
160
+ intAttr("mcp.duration_ms", rec.durationMs)
161
+ ];
162
+ if (rec.tool !== null)
163
+ attributes.push(strAttr("mcp.tool", rec.tool));
164
+ if (rec.reqBytes !== void 0)
165
+ attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
166
+ if (rec.resBytes !== void 0)
167
+ attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
168
+ return {
169
+ timeUnixNano: rec.timeUnixNano,
170
+ observedTimeUnixNano: rec.timeUnixNano,
171
+ severityNumber: SEVERITY_INFO,
172
+ severityText: "INFO",
173
+ body: { stringValue: EVENT_NAME },
174
+ attributes
175
+ };
176
+ }
177
+ function buildLogsPayload(records, server) {
178
+ return JSON.stringify({
179
+ resourceLogs: [
180
+ {
181
+ resource: {
182
+ attributes: [
183
+ strAttr("service.name", server.name),
184
+ strAttr("service.version", server.version),
185
+ strAttr("telemetry.sdk.name", SCOPE_NAME),
186
+ strAttr("telemetry.sdk.version", version),
187
+ strAttr("telemetry.sdk.language", "webjs")
188
+ ]
189
+ },
190
+ scopeLogs: [
191
+ {
192
+ scope: { name: SCOPE_NAME, version },
193
+ logRecords: records.map(toLogRecord)
194
+ }
195
+ ]
196
+ }
197
+ ]
198
+ });
199
+ }
200
+ function nowUnixNano() {
201
+ return `${Date.now()}000000`;
202
+ }
203
+
204
+ // src/metrics/recorder.ts
205
+ function nowMs() {
206
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
207
+ }
208
+ var NOOP_RECORDER = {
209
+ record() {
210
+ },
211
+ async flush() {
212
+ }
213
+ };
214
+ function createNoopRecorder() {
215
+ return NOOP_RECORDER;
216
+ }
217
+ function readRuntimeEnv(name) {
218
+ try {
219
+ const denoEnv = globalThis.Deno?.env;
220
+ const value = denoEnv?.get?.(name);
221
+ if (value)
222
+ return value;
223
+ } catch {
224
+ }
225
+ try {
226
+ if (typeof process !== "undefined") {
227
+ const value = process.env?.[name];
228
+ if (value)
229
+ return value;
230
+ }
231
+ } catch {
232
+ }
233
+ return void 0;
234
+ }
235
+ function probeWaitUntil() {
236
+ const fn = globalThis.EdgeRuntime?.waitUntil;
237
+ return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
238
+ }
239
+ function createMetricsRecorder(ctx, deps = {}) {
240
+ const { config, server } = ctx;
241
+ if (!config.enabled)
242
+ return NOOP_RECORDER;
243
+ 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 });
250
+ return NOOP_RECORDER;
251
+ }
252
+ const waitUntil = deps.waitUntil ?? probeWaitUntil();
253
+ const buffer = [];
254
+ let timer;
255
+ const schedule = (p) => {
256
+ if (waitUntil) {
257
+ try {
258
+ waitUntil(p);
259
+ return;
260
+ } catch {
261
+ }
262
+ }
263
+ void p.catch(() => {
264
+ });
265
+ };
266
+ const headers = {};
267
+ if (!usesLovableEndpoint) {
268
+ for (const [key, value] of Object.entries(config.headers)) {
269
+ if (key.toLowerCase() !== "content-type")
270
+ headers[key] = value;
271
+ }
272
+ }
273
+ headers["content-type"] = "application/json";
274
+ if (apiKey)
275
+ headers.authorization = `Bearer ${apiKey}`;
276
+ const flush = async () => {
277
+ if (buffer.length === 0)
278
+ return;
279
+ const records = buffer.splice(0, buffer.length);
280
+ try {
281
+ const res = await doFetch(config.endpoint, {
282
+ method: "POST",
283
+ headers,
284
+ body: buildLogsPayload(records, server),
285
+ keepalive: true
286
+ });
287
+ if (!res.ok)
288
+ log.debug("metrics.flush_rejected", { status: res.status });
289
+ } catch (err) {
290
+ log.debug("metrics.flush_failed", describeError(err));
291
+ }
292
+ };
293
+ const ensureTimer = () => {
294
+ if (timer !== void 0)
295
+ return;
296
+ timer = setInterval(() => schedule(flush()), config.flushIntervalMs);
297
+ timer.unref?.();
298
+ };
299
+ return {
300
+ record(ev) {
301
+ if (config.sampleRate < 1 && Math.random() >= config.sampleRate)
302
+ return;
303
+ buffer.push({ ...ev, timeUnixNano: nowUnixNano() });
304
+ ensureTimer();
305
+ if (buffer.length >= config.maxBatchSize)
306
+ schedule(flush());
307
+ },
308
+ flush
309
+ };
310
+ }
311
+ function createRecorderForRuntime(mcp) {
312
+ const config = resolveMetricsConfig(mcp.metrics);
313
+ if (!config.enabled)
314
+ return createNoopRecorder();
315
+ return createMetricsRecorder({ config, server: { name: mcp.name, version: mcp.version } });
316
+ }
317
+
110
318
  // src/core/promise.ts
111
319
  function cachedPromise(load, label) {
112
320
  let settled = false;
@@ -535,12 +743,13 @@ function assertRequiredScopes(auth, context) {
535
743
  throw new OAuthTokenError(403, "insufficient_scope", "Additional OAuth scope is required");
536
744
  }
537
745
  }
538
- function createRequestAuthorizer(mcp, options = {}) {
746
+ function createRequestAuthorizer(mcp, options = {}, recorder) {
539
747
  const runtime = getOAuthRuntime(mcp, options);
540
748
  return {
541
749
  async authorize(request) {
542
750
  if (runtime.kind === "unconfigured")
543
751
  return { ok: true };
752
+ const startedAt = nowMs();
544
753
  const token = parseBearerToken(request);
545
754
  if (!token) {
546
755
  log.info("auth.no_bearer_token", { outcome: "401" });
@@ -553,6 +762,12 @@ function createRequestAuthorizer(mcp, options = {}) {
553
762
  } catch (err) {
554
763
  if (err instanceof OAuthConfigurationError) {
555
764
  log.error("auth.config_error", { ...describeError(err), outcome: "500" });
765
+ recorder?.record({
766
+ tool: null,
767
+ method: "authorize",
768
+ outcome: "auth_config_error",
769
+ durationMs: nowMs() - startedAt
770
+ });
556
771
  return { ok: false, response: oauthConfigurationErrorResponse() };
557
772
  }
558
773
  if (err instanceof OAuthTokenError) {
@@ -650,23 +865,32 @@ function corsPreflightResponse(allowMethods) {
650
865
  }
651
866
 
652
867
  // src/protocols/mcp/protocol.ts
653
- function adaptToolToSdkCallback(tool, auth) {
868
+ function adaptToolToSdkCallback(tool, auth, recorder) {
654
869
  return async (first) => {
655
870
  const args = tool.inputSchema ? first ?? {} : {};
871
+ const start = nowMs();
656
872
  let result;
657
873
  try {
658
874
  result = await tool.handler(args, new ToolContext(auth));
659
875
  } catch {
876
+ recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
660
877
  return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
661
878
  }
662
879
  if (result == null) {
880
+ recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
663
881
  return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
664
882
  }
883
+ recorder.record({
884
+ tool: tool.name,
885
+ method: "tools/call",
886
+ outcome: result.isError ? "tool_error" : "ok",
887
+ durationMs: nowMs() - start
888
+ });
665
889
  return { content: result.content ?? [], structuredContent: result.structuredContent, isError: result.isError };
666
890
  };
667
891
  }
668
- function createMcpProtocolHandler(mcp, options = {}) {
669
- const authorizer = createRequestAuthorizer(mcp, options);
892
+ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
893
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
670
894
  const handle = async (request) => {
671
895
  const authResult = await authorizer.authorize(request);
672
896
  if (!authResult.ok)
@@ -686,7 +910,7 @@ function createMcpProtocolHandler(mcp, options = {}) {
686
910
  outputSchema: tool.outputSchema,
687
911
  annotations: tool.annotations
688
912
  },
689
- adaptToolToSdkCallback(tool, authResult.auth)
913
+ adaptToolToSdkCallback(tool, authResult.auth, recorder)
690
914
  );
691
915
  }
692
916
  const transport = new import_webStandardStreamableHttp.WebStandardStreamableHTTPServerTransport({
@@ -695,6 +919,7 @@ function createMcpProtocolHandler(mcp, options = {}) {
695
919
  await server.connect(transport);
696
920
  return await transport.handleRequest(request);
697
921
  } catch (err) {
922
+ recorder.record({ tool: null, method: "transport", outcome: "transport_error", durationMs: 0 });
698
923
  log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
699
924
  return Response.json(
700
925
  { jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
@@ -795,9 +1020,9 @@ function buildMcpListing(mcp) {
795
1020
  }))
796
1021
  };
797
1022
  }
798
- function createListToolsHandler(mcp, options = {}) {
1023
+ function createListToolsHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
799
1024
  assertRestResourceBinding(mcp, options);
800
- const authorizer = createRequestAuthorizer(mcp, options);
1025
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
801
1026
  const handle = async (request) => {
802
1027
  const authResult = await authorizer.authorize(request);
803
1028
  if (!authResult.ok)
@@ -828,9 +1053,9 @@ function isEmptyArgs(value) {
828
1053
  return false;
829
1054
  return Object.keys(value).length === 0;
830
1055
  }
831
- function createInvokeToolHandler(mcp, options = {}) {
1056
+ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
832
1057
  assertRestResourceBinding(mcp, options);
833
- const authorizer = createRequestAuthorizer(mcp, options);
1058
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
834
1059
  const handle = async (request, toolName) => {
835
1060
  const authResult = await authorizer.authorize(request);
836
1061
  if (!authResult.ok)
@@ -884,20 +1109,29 @@ function createInvokeToolHandler(mcp, options = {}) {
884
1109
  });
885
1110
  }
886
1111
  let result;
1112
+ const start = nowMs();
887
1113
  try {
888
1114
  result = await tool.handler(args, new ToolContext(authResult.auth));
889
1115
  } catch {
1116
+ recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
890
1117
  return new Response(JSON.stringify({ error: "handler threw", tool: toolName }), {
891
1118
  status: 500,
892
1119
  headers: JSON_HEADERS
893
1120
  });
894
1121
  }
895
1122
  if (result == null) {
1123
+ recorder.record({ tool: tool.name, method: "tools/call", outcome: "handler_error", durationMs: nowMs() - start });
896
1124
  return new Response(JSON.stringify({ error: `tool "${toolName}" returned no result` }), {
897
1125
  status: 500,
898
1126
  headers: JSON_HEADERS
899
1127
  });
900
1128
  }
1129
+ recorder.record({
1130
+ tool: tool.name,
1131
+ method: "tools/call",
1132
+ outcome: result.isError ? "tool_error" : "ok",
1133
+ durationMs: nowMs() - start
1134
+ });
901
1135
  return Response.json({
902
1136
  content: result.content ?? [],
903
1137
  structuredContent: result.structuredContent,
@@ -1,5 +1,5 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../../types-BanzncFh.js';
2
+ import { d as McpDefinition } from '../../types-COY42xux.js';
3
3
  import 'zod';
4
4
 
5
5
  interface TanStackRouteCtx {
@@ -1,5 +1,5 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../../types-BanzncFh.js';
2
+ import { d as McpDefinition } from '../../types-COY42xux.js';
3
3
  import 'zod';
4
4
 
5
5
  interface TanStackRouteCtx {
@@ -3,20 +3,21 @@ import {
3
3
  } from "../../chunk-UQK5UO6C.js";
4
4
  import {
5
5
  createMcpProtocolHandler
6
- } from "../../chunk-WLNT2FX7.js";
6
+ } from "../../chunk-UCPYLSNN.js";
7
7
  import {
8
8
  createOAuthProtectedResourceMetadataHandler
9
- } from "../../chunk-QDKOF4UF.js";
9
+ } from "../../chunk-ILYTJXDR.js";
10
10
  import {
11
11
  createInvokeToolHandler
12
- } from "../../chunk-YWLMMDET.js";
12
+ } from "../../chunk-CEDD57G2.js";
13
13
  import {
14
14
  createListToolsHandler
15
- } from "../../chunk-VNRQPA4K.js";
15
+ } from "../../chunk-QT5L4CS3.js";
16
16
  import "../../chunk-MA5H6PSF.js";
17
- import "../../chunk-G4XA7IJM.js";
18
- import "../../chunk-QC3DXQTH.js";
17
+ import "../../chunk-YTOMGJV5.js";
18
+ import "../../chunk-DCWYK4CA.js";
19
19
  import "../../chunk-6DXGZZA4.js";
20
+ import "../../chunk-GIHCQT6L.js";
20
21
 
21
22
  // src/stacks/tanstack/handlers.ts
22
23
  function forwarded(request, options) {
@@ -105,6 +105,29 @@ declare class ToolContext {
105
105
  getClaims(): JwtClaims | undefined;
106
106
  }
107
107
 
108
+ /**
109
+ * Usage-metrics options. Pass a boolean to the Vite plugins for the common
110
+ * on/off case, or this object to override the ingest endpoint (e.g. to
111
+ * self-host the collector outside Lovable).
112
+ */
113
+ interface MetricsOptions {
114
+ /** Master switch. Default `true`; metrics still self-disable at runtime when
115
+ * the API key env var is absent. */
116
+ enabled?: boolean;
117
+ /** OTLP/HTTP (JSON) logs URL to POST batches to — the default Lovable route or
118
+ * any OTLP collector. Must be an absolute http(s) URL.
119
+ * @default "https://api.lovable.dev/v1/app-mcp-usage" */
120
+ endpoint?: string;
121
+ /** Extra request headers — e.g. the auth token for a private OTLP collector.
122
+ * Sent verbatim with every batch (and baked into the build output, so source
123
+ * your own build-time env for secrets). Ignored for the default Lovable
124
+ * endpoint, which authenticates with `LOVABLE_API_KEY` instead. `content-type`
125
+ * cannot be overridden (OTLP/HTTP JSON requires `application/json`). */
126
+ headers?: Record<string, string>;
127
+ }
128
+ /** Either the bare on/off toggle or the full options object. */
129
+ type MetricsConfig = boolean | MetricsOptions;
130
+
108
131
  /**
109
132
  * Any zod schema. Aliased to zod's `ZodType` (no generics) — non-deprecated
110
133
  * in both v3 and v4. Use this when you need to type an individual schema;
@@ -273,10 +296,16 @@ interface McpDefinitionInput {
273
296
  */
274
297
  readonly instructions: string;
275
298
  readonly tools: readonly AnyToolDefinition[];
299
+ /**
300
+ * Usage-metrics emission (runtime telemetry), on by default. `false` opts out;
301
+ * `{ endpoint, headers }` sends to your own OTLP collector. Self-disables at
302
+ * runtime without `LOVABLE_API_KEY` on the default Lovable endpoint.
303
+ */
304
+ readonly metrics?: MetricsConfig;
276
305
  }
277
306
  declare const McpDefinitionBrand: unique symbol;
278
307
  type McpDefinition = McpDefinitionInput & {
279
308
  readonly [McpDefinitionBrand]: never;
280
309
  };
281
310
 
282
- export { type AudioContent as A, type ContentAnnotations as C, type EmbeddedBlobResource as E, type ImageContent as I, type JwtClaims as J, type McpAuthConfig as M, type ResourceLink as R, type TextContent as T, type ZodRawShape as Z, type ContentBlock as a, type EmbeddedResource as b, type EmbeddedTextResource as c, type McpDefinition as d, type McpDefinitionInput as e, type ResourceLinkIcon as f, type ToolAnnotations as g, type ToolContent as h, ToolContext as i, type ToolDefinition as j, type ToolHandlerResult as k, type ZodSchema as l };
311
+ export { type AudioContent as A, type ContentAnnotations as C, type EmbeddedBlobResource as E, type ImageContent as I, type JwtClaims as J, type McpAuthConfig as M, type ResourceLink as R, type TextContent as T, type ZodRawShape as Z, type ContentBlock as a, type EmbeddedResource as b, type EmbeddedTextResource as c, type McpDefinition as d, type McpDefinitionInput as e, type MetricsConfig as f, type MetricsOptions as g, type ResourceLinkIcon as h, type ToolAnnotations as i, type ToolContent as j, ToolContext as k, type ToolDefinition as l, type ToolHandlerResult as m, type ZodSchema as n };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovable.dev/mcp-js",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
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": {