@lovable.dev/mcp-js 0.17.0 → 0.20.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 (37) hide show
  1. package/dist/{recorder-qlzUKFxG.d.ts → base-C9rhAHZ0.d.ts} +3 -4
  2. package/dist/{chunk-FW6YL5C5.js → chunk-7JJEPFSO.js} +8 -8
  3. package/dist/{chunk-H2HXKAMQ.js → chunk-DUNDNXK5.js} +2 -2
  4. package/dist/{chunk-6TZNGFRM.js → chunk-EPEEEI3C.js} +121 -136
  5. package/dist/{chunk-J5BTCP6T.js → chunk-FTTVLHNN.js} +1 -1
  6. package/dist/{chunk-VQQZAFRA.js → chunk-H37EB22A.js} +2 -10
  7. package/dist/{chunk-WYXIX7XF.js → chunk-WKIXRW46.js} +11 -12
  8. package/dist/{chunk-EL6YNP2R.js → chunk-WW7NRRCJ.js} +13 -14
  9. package/dist/cli/extract-manifest.cjs +1 -1
  10. package/dist/cli/extract-manifest.js +4 -4
  11. package/dist/index.cjs +1 -7
  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 +16 -272
  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.d.cts +1 -1
  20. package/dist/protocols/oauth-metadata.d.ts +1 -1
  21. package/dist/protocols/oauth-metadata.js +4 -4
  22. package/dist/protocols/rest/index.cjs +21 -277
  23. package/dist/protocols/rest/index.d.cts +6 -6
  24. package/dist/protocols/rest/index.d.ts +6 -6
  25. package/dist/protocols/rest/index.js +5 -5
  26. package/dist/stacks/supabase/index.cjs +178 -201
  27. package/dist/stacks/supabase/index.d.cts +1 -1
  28. package/dist/stacks/supabase/index.d.ts +1 -1
  29. package/dist/stacks/supabase/index.js +14 -14
  30. package/dist/stacks/supabase/vite.cjs +1 -1
  31. package/dist/stacks/supabase/vite.js +1 -1
  32. package/dist/stacks/tanstack/index.cjs +187 -209
  33. package/dist/stacks/tanstack/index.d.cts +1 -1
  34. package/dist/stacks/tanstack/index.d.ts +1 -1
  35. package/dist/stacks/tanstack/index.js +13 -10
  36. package/dist/{types-COY42xux.d.ts → types-CPkhCRxc.d.ts} +3 -3
  37. package/package.json +1 -1
@@ -7,7 +7,7 @@ import {
7
7
  } from "../../chunk-XQWJN6DC.js";
8
8
  import {
9
9
  version
10
- } from "../../chunk-J5BTCP6T.js";
10
+ } from "../../chunk-FTTVLHNN.js";
11
11
 
12
12
  // src/stacks/supabase/vite.ts
13
13
  import { resolve as resolve2, sep as sep2 } from "path";
@@ -50,19 +50,29 @@ function firstForwardedValue(request, header) {
50
50
  return value ? value : void 0;
51
51
  }
52
52
 
53
- // src/protocols/mcp/protocol.ts
54
- var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
55
- var import_webStandardStreamableHttp = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
56
-
57
- // src/core/http.ts
58
- var JSON_HEADERS = { "Content-Type": "application/json" };
59
- function headResponse(response) {
60
- return new Response(null, { status: response.status, statusText: response.statusText, headers: response.headers });
53
+ // src/metrics/config.ts
54
+ var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
55
+ var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
56
+ function assertMetricsEndpoint(endpoint) {
57
+ let url;
58
+ try {
59
+ url = new URL(endpoint);
60
+ } catch {
61
+ throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be an absolute URL, got ${JSON.stringify(endpoint)}`);
62
+ }
63
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
64
+ throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be http(s), got ${JSON.stringify(endpoint)}`);
65
+ }
61
66
  }
62
- function methodNotAllowed(allow) {
63
- return new Response(JSON.stringify({ error: "method not allowed" }), {
64
- status: 405,
65
- headers: { ...JSON_HEADERS, Allow: allow }
67
+ function resolveMetricsConfig(config = true) {
68
+ const options = typeof config === "boolean" ? { enabled: config } : config;
69
+ const endpoint = options.endpoint ?? DEFAULT_METRICS_ENDPOINT;
70
+ assertMetricsEndpoint(endpoint);
71
+ return Object.freeze({
72
+ enabled: options.enabled ?? true,
73
+ endpoint,
74
+ headers: options.headers ?? {},
75
+ apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
66
76
  });
67
77
  }
68
78
 
@@ -90,9 +100,7 @@ function parseLogLevel(raw) {
90
100
  return isLogLevel(normalized) ? normalized : void 0;
91
101
  }
92
102
  function applyLogLevelFromEnv(raw) {
93
- const level = parseLogLevel(raw);
94
- if (level)
95
- setLogLevel(level);
103
+ setLogLevel(parseLogLevel(raw) ?? "debug");
96
104
  }
97
105
  function enabled(level) {
98
106
  return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
@@ -120,40 +128,8 @@ function describeError(err) {
120
128
  return { value: String(err) };
121
129
  }
122
130
 
123
- // src/metrics/config.ts
124
- var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
125
- var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
126
- var METRICS_SAMPLE_RATE = 1;
127
- var METRICS_FLUSH_INTERVAL_MS = 5e3;
128
- var METRICS_MAX_BATCH_SIZE = 50;
129
- function assertMetricsEndpoint(endpoint) {
130
- let url;
131
- try {
132
- url = new URL(endpoint);
133
- } catch {
134
- throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be an absolute URL, got ${JSON.stringify(endpoint)}`);
135
- }
136
- if (url.protocol !== "https:" && url.protocol !== "http:") {
137
- throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be http(s), got ${JSON.stringify(endpoint)}`);
138
- }
139
- }
140
- function resolveMetricsConfig(config = true) {
141
- const options = typeof config === "boolean" ? { enabled: config } : config;
142
- const endpoint = options.endpoint ?? DEFAULT_METRICS_ENDPOINT;
143
- assertMetricsEndpoint(endpoint);
144
- return Object.freeze({
145
- enabled: options.enabled ?? true,
146
- endpoint,
147
- headers: options.headers ?? {},
148
- apiKeyEnvVar: METRICS_API_KEY_ENV_VAR,
149
- sampleRate: METRICS_SAMPLE_RATE,
150
- flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
151
- maxBatchSize: METRICS_MAX_BATCH_SIZE
152
- });
153
- }
154
-
155
131
  // package.json
156
- var version = "0.17.0";
132
+ var version = "0.20.0";
157
133
 
158
134
  // src/metrics/otlp.ts
159
135
  var SCOPE_NAME = "@lovable.dev/mcp-js";
@@ -178,6 +154,7 @@ function toLogRecord(rec) {
178
154
  attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
179
155
  if (rec.resBytes !== void 0)
180
156
  attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
157
+ attributes.push(strAttr("mcp.stack", rec.stack));
181
158
  return {
182
159
  timeUnixNano: rec.timeUnixNano,
183
160
  observedTimeUnixNano: rec.timeUnixNano,
@@ -214,164 +191,164 @@ function nowUnixNano() {
214
191
  return `${Date.now()}000000`;
215
192
  }
216
193
 
217
- // src/metrics/recorder.ts
194
+ // src/metrics/impl/base.ts
218
195
  function nowMs() {
219
196
  return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
220
197
  }
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
- }
225
- var NOOP_RECORDER = {
226
- record() {
227
- },
228
- async flush() {
229
- }
230
- };
198
+ var NOOP_RECORDER = { async emit() {
199
+ } };
231
200
  function createNoopRecorder() {
232
201
  return NOOP_RECORDER;
233
202
  }
234
- function readRuntimeEnv(name) {
235
- try {
236
- const denoEnv = globalThis.Deno?.env;
237
- const value = denoEnv?.get?.(name);
238
- if (value)
239
- return value;
240
- } catch {
241
- }
203
+ function readProcessEnv(name) {
242
204
  try {
243
- if (typeof process !== "undefined") {
244
- const value = process.env?.[name];
245
- if (value)
246
- return value;
247
- }
248
- } catch {
249
- }
250
- return void 0;
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;
205
+ return typeof process !== "undefined" ? process.env?.[name] || void 0 : void 0;
263
206
  } catch {
264
207
  return void 0;
265
208
  }
266
209
  }
267
- function probeWaitUntil() {
268
- const fn = globalThis.EdgeRuntime?.waitUntil;
269
- return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
270
- }
271
- function createMetricsRecorder(ctx, deps = {}) {
272
- const { config, server } = ctx;
273
- applyLogLevelFromEnv(readRuntimeEnv(LOG_LEVEL_ENV_VAR));
274
- if (!config.enabled)
275
- return NOOP_RECORDER;
276
- const doFetch = deps.fetch ?? globalThis.fetch;
277
- if (!doFetch) {
278
- log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
279
- return NOOP_RECORDER;
280
- }
281
- const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
282
- const waitUntil = deps.waitUntil ?? probeWaitUntil();
283
- const buffer = [];
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
- };
299
- const schedule = (p) => {
300
- if (waitUntil) {
301
- try {
302
- waitUntil(p);
303
- return;
304
- } catch (err) {
305
- log.warn("metrics.wait_until_failed", describeError(err));
210
+ var BaseMetricRecorder = class {
211
+ constructor(config, server, deps = {}) {
212
+ this.config = config;
213
+ this.server = server;
214
+ this.deps = deps;
215
+ this.doFetch = deps.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
216
+ this.usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
217
+ const headers = {};
218
+ if (!this.usesLovableEndpoint) {
219
+ for (const [key, value] of Object.entries(config.headers)) {
220
+ if (key.toLowerCase() !== "content-type")
221
+ headers[key] = value;
306
222
  }
307
223
  }
308
- void p.catch(() => {
309
- });
310
- };
311
- const baseHeaders = {};
312
- if (!usesLovableEndpoint) {
313
- for (const [key, value] of Object.entries(config.headers)) {
314
- if (key.toLowerCase() !== "content-type")
315
- baseHeaders[key] = value;
316
- }
224
+ headers["content-type"] = "application/json";
225
+ this.baseHeaders = headers;
317
226
  }
318
- baseHeaders["content-type"] = "application/json";
319
- const flush = async () => {
320
- void applyLazyLogLevel();
321
- if (buffer.length === 0)
227
+ doFetch;
228
+ usesLovableEndpoint;
229
+ baseHeaders;
230
+ apiKey;
231
+ lazyLogLevelApplied = false;
232
+ async emit(ev) {
233
+ await this.applyLazyLogLevel();
234
+ log.info("tool.invoked", {
235
+ tool: ev.tool,
236
+ method: ev.method,
237
+ outcome: ev.outcome,
238
+ durationMs: ev.durationMs,
239
+ stack: this.stack
240
+ });
241
+ if (!this.config.enabled)
242
+ return;
243
+ if (!this.doFetch) {
244
+ log.warn("metrics.disabled_no_fetch", { endpoint: this.config.endpoint });
322
245
  return;
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();
246
+ }
247
+ const headers = { ...this.baseHeaders };
248
+ if (this.usesLovableEndpoint) {
249
+ const key = await this.resolveApiKey();
328
250
  if (!key) {
329
- log.warn("metrics.disabled_no_api_key", {
330
- envVar: config.apiKeyEnvVar,
331
- endpoint: config.endpoint,
332
- dropped
333
- });
251
+ log.warn("metrics.disabled_no_api_key", { envVar: this.config.apiKeyEnvVar, endpoint: this.config.endpoint });
334
252
  return;
335
253
  }
336
254
  headers.authorization = `Bearer ${key}`;
337
255
  }
256
+ const body = buildLogsPayload([{ ...ev, timeUnixNano: nowUnixNano(), stack: this.stack }], this.server);
338
257
  try {
339
- const res = await doFetch(config.endpoint, {
340
- method: "POST",
341
- headers,
342
- body: buildLogsPayload(records, server),
343
- keepalive: true
344
- });
345
- if (!res.ok) {
346
- log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
347
- }
258
+ const res = await this.doFetch(this.config.endpoint, { method: "POST", headers, body, keepalive: true });
259
+ if (!res.ok)
260
+ log.warn("metrics.rejected", { status: res.status, endpoint: this.config.endpoint });
348
261
  } catch (err) {
349
- log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
262
+ log.warn("metrics.failed", { ...describeError(err), endpoint: this.config.endpoint });
350
263
  }
351
- };
352
- const ensureTimer = () => {
353
- if (timer !== void 0)
264
+ }
265
+ // Cache only a successful resolution so a miss is retried on the next emit (the
266
+ // Cloudflare binding may not be readable on the very first attempt in an isolate).
267
+ async resolveApiKey() {
268
+ if (this.apiKey)
269
+ return this.apiKey;
270
+ this.apiKey = this.deps.getApiKey ? this.deps.getApiKey() : await this.readEnv(this.config.apiKeyEnvVar);
271
+ return this.apiKey;
272
+ }
273
+ async applyLazyLogLevel() {
274
+ if (this.lazyLogLevelApplied)
354
275
  return;
355
- timer = setInterval(() => schedule(flush()), config.flushIntervalMs);
356
- timer.unref?.();
357
- };
358
- return {
359
- record(ev) {
360
- if (config.sampleRate < 1 && Math.random() >= config.sampleRate)
361
- return;
362
- buffer.push({ ...ev, timeUnixNano: nowUnixNano() });
363
- ensureTimer();
364
- if (buffer.length >= config.maxBatchSize)
365
- schedule(flush());
366
- },
367
- flush
368
- };
276
+ this.lazyLogLevelApplied = true;
277
+ applyLogLevelFromEnv(await this.readEnv(LOG_LEVEL_ENV_VAR));
278
+ }
279
+ };
280
+
281
+ // src/metrics/impl/cloudflare.ts
282
+ var cloudflareEnvPromise;
283
+ async function readCloudflareEnv(name) {
284
+ try {
285
+ const moduleSpecifier = "cloudflare:workers";
286
+ cloudflareEnvPromise ??= import(
287
+ /* @vite-ignore */
288
+ moduleSpecifier
289
+ ).then((m) => m.env).catch((err) => {
290
+ log.debug("metrics.cloudflare_env_import_failed", describeError(err));
291
+ return void 0;
292
+ });
293
+ const env = await cloudflareEnvPromise;
294
+ const raw = env?.[name];
295
+ const value = typeof raw === "string" && raw ? raw : void 0;
296
+ log.debug("metrics.read_cloudflare_env", { name, hasEnvBinding: !!env, found: value !== void 0 });
297
+ return value;
298
+ } catch (err) {
299
+ log.debug("metrics.read_cloudflare_env_error", { name, ...describeError(err) });
300
+ return void 0;
301
+ }
369
302
  }
370
- function createRecorderForRuntime(mcp) {
303
+ var TanStackMetricRecorder = class extends BaseMetricRecorder {
304
+ stack = "tanstack";
305
+ async readEnv(name) {
306
+ return readProcessEnv(name) ?? await readCloudflareEnv(name);
307
+ }
308
+ };
309
+
310
+ // src/metrics/impl/supabase.ts
311
+ function readDenoEnv(name) {
312
+ try {
313
+ const denoEnv = globalThis.Deno?.env;
314
+ const value = denoEnv?.get?.(name) || void 0;
315
+ log.debug("metrics.read_deno_env", { name, hasDenoEnv: !!denoEnv, found: !!value });
316
+ return value;
317
+ } catch (err) {
318
+ log.debug("metrics.read_deno_env_error", { name, ...describeError(err) });
319
+ return void 0;
320
+ }
321
+ }
322
+ var SupabaseMetricRecorder = class extends BaseMetricRecorder {
323
+ stack = "supabase";
324
+ async readEnv(name) {
325
+ return readDenoEnv(name) ?? readProcessEnv(name);
326
+ }
327
+ };
328
+
329
+ // src/metrics/recorder.ts
330
+ function createRecorderForRuntime(mcp, opts) {
371
331
  const config = resolveMetricsConfig(mcp.metrics);
332
+ const server = { name: mcp.name, version: mcp.version };
372
333
  if (!config.enabled)
373
334
  return createNoopRecorder();
374
- return createMetricsRecorder({ config, server: { name: mcp.name, version: mcp.version } });
335
+ return opts.stack === "tanstack" ? new TanStackMetricRecorder(config, server) : new SupabaseMetricRecorder(config, server);
336
+ }
337
+
338
+ // src/protocols/mcp/protocol.ts
339
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
340
+ var import_webStandardStreamableHttp = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
341
+
342
+ // src/core/http.ts
343
+ var JSON_HEADERS = { "Content-Type": "application/json" };
344
+ function headResponse(response) {
345
+ return new Response(null, { status: response.status, statusText: response.statusText, headers: response.headers });
346
+ }
347
+ function methodNotAllowed(allow) {
348
+ return new Response(JSON.stringify({ error: "method not allowed" }), {
349
+ status: 405,
350
+ headers: { ...JSON_HEADERS, Allow: allow }
351
+ });
375
352
  }
376
353
 
377
354
  // src/core/promise.ts
@@ -802,10 +779,10 @@ function assertRequiredScopes(auth, context) {
802
779
  throw new OAuthTokenError(403, "insufficient_scope", "Additional OAuth scope is required");
803
780
  }
804
781
  }
805
- function createRequestAuthorizer(mcp, options = {}, recorder) {
782
+ function createRequestAuthorizer(mcp, options = {}) {
806
783
  const runtime = getOAuthRuntime(mcp, options);
807
784
  return {
808
- async authorize(request) {
785
+ async authorize(request, recorder) {
809
786
  if (runtime.kind === "unconfigured")
810
787
  return { ok: true };
811
788
  const startedAt = nowMs();
@@ -821,7 +798,7 @@ function createRequestAuthorizer(mcp, options = {}, recorder) {
821
798
  } catch (err) {
822
799
  if (err instanceof OAuthConfigurationError) {
823
800
  log.error("auth.config_error", { ...describeError(err), outcome: "500" });
824
- recorder?.record({
801
+ await recorder?.emit({
825
802
  tool: null,
826
803
  method: "authorize",
827
804
  outcome: "auth_config_error",
@@ -932,7 +909,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
932
909
  try {
933
910
  result = await tool.handler(args, new ToolContext(auth));
934
911
  } catch {
935
- reportInvocation(recorder, {
912
+ await recorder.emit({
936
913
  tool: tool.name,
937
914
  method: "tools/call",
938
915
  outcome: "handler_error",
@@ -941,7 +918,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
941
918
  return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
942
919
  }
943
920
  if (result == null) {
944
- reportInvocation(recorder, {
921
+ await recorder.emit({
945
922
  tool: tool.name,
946
923
  method: "tools/call",
947
924
  outcome: "handler_error",
@@ -949,7 +926,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
949
926
  });
950
927
  return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
951
928
  }
952
- reportInvocation(recorder, {
929
+ await recorder.emit({
953
930
  tool: tool.name,
954
931
  method: "tools/call",
955
932
  outcome: result.isError ? "tool_error" : "ok",
@@ -958,10 +935,10 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
958
935
  return { content: result.content ?? [], structuredContent: result.structuredContent, isError: result.isError };
959
936
  };
960
937
  }
961
- function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
962
- const authorizer = createRequestAuthorizer(mcp, options, recorder);
963
- const handle = async (request) => {
964
- const authResult = await authorizer.authorize(request);
938
+ function createMcpProtocolHandler(mcp, options = {}) {
939
+ const authorizer = createRequestAuthorizer(mcp, options);
940
+ const handle = async (request, recorder) => {
941
+ const authResult = await authorizer.authorize(request, recorder);
965
942
  if (!authResult.ok)
966
943
  return authResult.response;
967
944
  try {
@@ -988,7 +965,7 @@ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderFo
988
965
  await server.connect(transport);
989
966
  return await transport.handleRequest(request);
990
967
  } catch (err) {
991
- recorder.record({ tool: null, method: "transport", outcome: "transport_error", durationMs: 0 });
968
+ await recorder.emit({ tool: null, method: "transport", outcome: "transport_error", durationMs: 0 });
992
969
  log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
993
970
  return Response.json(
994
971
  { jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
@@ -996,10 +973,10 @@ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderFo
996
973
  );
997
974
  }
998
975
  };
999
- return async (request) => {
976
+ return async (request, recorder = createNoopRecorder()) => {
1000
977
  if (request.method === "OPTIONS")
1001
978
  return corsPreflightResponse("GET, POST, DELETE, OPTIONS");
1002
- return withCors(await handle(request));
979
+ return withCors(await handle(request, recorder));
1003
980
  };
1004
981
  }
1005
982
 
@@ -1089,11 +1066,11 @@ function buildMcpListing(mcp) {
1089
1066
  }))
1090
1067
  };
1091
1068
  }
1092
- function createListToolsHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
1069
+ function createListToolsHandler(mcp, options = {}) {
1093
1070
  assertRestResourceBinding(mcp, options);
1094
- const authorizer = createRequestAuthorizer(mcp, options, recorder);
1095
- const handle = async (request) => {
1096
- const authResult = await authorizer.authorize(request);
1071
+ const authorizer = createRequestAuthorizer(mcp, options);
1072
+ const handle = async (request, recorder) => {
1073
+ const authResult = await authorizer.authorize(request, recorder);
1097
1074
  if (!authResult.ok)
1098
1075
  return authResult.response;
1099
1076
  if (request.method !== "GET" && request.method !== "HEAD")
@@ -1101,10 +1078,10 @@ function createListToolsHandler(mcp, options = {}, recorder = createRecorderForR
1101
1078
  const response = Response.json(buildMcpListing(mcp));
1102
1079
  return request.method === "HEAD" ? headResponse(response) : response;
1103
1080
  };
1104
- return async (request) => {
1081
+ return async (request, recorder = createNoopRecorder()) => {
1105
1082
  if (request.method === "OPTIONS")
1106
1083
  return corsPreflightResponse("GET, HEAD, OPTIONS");
1107
- return withCors(await handle(request));
1084
+ return withCors(await handle(request, recorder));
1108
1085
  };
1109
1086
  }
1110
1087
 
@@ -1122,11 +1099,11 @@ function isEmptyArgs(value) {
1122
1099
  return false;
1123
1100
  return Object.keys(value).length === 0;
1124
1101
  }
1125
- function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
1102
+ function createInvokeToolHandler(mcp, options = {}) {
1126
1103
  assertRestResourceBinding(mcp, options);
1127
- const authorizer = createRequestAuthorizer(mcp, options, recorder);
1128
- const handle = async (request, toolName) => {
1129
- const authResult = await authorizer.authorize(request);
1104
+ const authorizer = createRequestAuthorizer(mcp, options);
1105
+ const handle = async (request, toolName, recorder) => {
1106
+ const authResult = await authorizer.authorize(request, recorder);
1130
1107
  if (!authResult.ok)
1131
1108
  return authResult.response;
1132
1109
  if (request.method !== "POST")
@@ -1182,7 +1159,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
1182
1159
  try {
1183
1160
  result = await tool.handler(args, new ToolContext(authResult.auth));
1184
1161
  } catch {
1185
- reportInvocation(recorder, {
1162
+ await recorder.emit({
1186
1163
  tool: tool.name,
1187
1164
  method: "tools/call",
1188
1165
  outcome: "handler_error",
@@ -1194,7 +1171,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
1194
1171
  });
1195
1172
  }
1196
1173
  if (result == null) {
1197
- reportInvocation(recorder, {
1174
+ await recorder.emit({
1198
1175
  tool: tool.name,
1199
1176
  method: "tools/call",
1200
1177
  outcome: "handler_error",
@@ -1205,7 +1182,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
1205
1182
  headers: JSON_HEADERS
1206
1183
  });
1207
1184
  }
1208
- reportInvocation(recorder, {
1185
+ await recorder.emit({
1209
1186
  tool: tool.name,
1210
1187
  method: "tools/call",
1211
1188
  outcome: result.isError ? "tool_error" : "ok",
@@ -1217,28 +1194,29 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
1217
1194
  isError: result.isError
1218
1195
  });
1219
1196
  };
1220
- return async (request, toolName) => {
1197
+ return async (request, toolName, recorder = createNoopRecorder()) => {
1221
1198
  if (request.method === "OPTIONS")
1222
1199
  return corsPreflightResponse("POST, OPTIONS");
1223
- return withCors(await handle(request, toolName));
1200
+ return withCors(await handle(request, toolName, recorder));
1224
1201
  };
1225
1202
  }
1226
1203
 
1227
1204
  // src/stacks/tanstack/handlers.ts
1205
+ var STACK = "tanstack";
1228
1206
  function forwarded(request, options) {
1229
1207
  return applyForwardedOrigin(request, { trustForwardedHost: options.trustForwardedHost });
1230
1208
  }
1231
1209
  function createTanStackMcpHandler(mcp, options = {}) {
1232
1210
  const handler = createMcpProtocolHandler(mcp, options);
1233
- return ({ request }) => handler(forwarded(request, options));
1211
+ return ({ request }) => handler(forwarded(request, options), createRecorderForRuntime(mcp, { stack: STACK }));
1234
1212
  }
1235
1213
  function createTanStackListToolsHandler(mcp, options = {}) {
1236
1214
  const handler = createListToolsHandler(mcp, options);
1237
- return ({ request }) => handler(forwarded(request, options));
1215
+ return ({ request }) => handler(forwarded(request, options), createRecorderForRuntime(mcp, { stack: STACK }));
1238
1216
  }
1239
1217
  function createTanStackInvokeToolHandler(mcp, options = {}) {
1240
1218
  const handler = createInvokeToolHandler(mcp, options);
1241
- return ({ request, params }) => handler(forwarded(request, options), params.tool);
1219
+ return ({ request, params }) => handler(forwarded(request, options), params.tool, createRecorderForRuntime(mcp, { stack: STACK }));
1242
1220
  }
1243
1221
  function createTanStackOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
1244
1222
  const handler = createOAuthProtectedResourceMetadataHandler(mcp, options);
@@ -1,5 +1,5 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
- import { d as McpDefinition } from '../../types-COY42xux.js';
2
+ import { d as McpDefinition } from '../../types-CPkhCRxc.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-COY42xux.js';
2
+ import { d as McpDefinition } from '../../types-CPkhCRxc.js';
3
3
  import 'zod';
4
4
 
5
5
  interface TanStackRouteCtx {
@@ -3,37 +3,40 @@ import {
3
3
  } from "../../chunk-UQK5UO6C.js";
4
4
  import {
5
5
  createMcpProtocolHandler
6
- } from "../../chunk-EL6YNP2R.js";
6
+ } from "../../chunk-WW7NRRCJ.js";
7
7
  import {
8
8
  createOAuthProtectedResourceMetadataHandler
9
- } from "../../chunk-H2HXKAMQ.js";
9
+ } from "../../chunk-DUNDNXK5.js";
10
10
  import {
11
11
  createInvokeToolHandler
12
- } from "../../chunk-WYXIX7XF.js";
12
+ } from "../../chunk-WKIXRW46.js";
13
13
  import {
14
14
  createListToolsHandler
15
- } from "../../chunk-FW6YL5C5.js";
15
+ } from "../../chunk-7JJEPFSO.js";
16
16
  import "../../chunk-MA5H6PSF.js";
17
- import "../../chunk-6TZNGFRM.js";
18
- import "../../chunk-VQQZAFRA.js";
17
+ import {
18
+ createRecorderForRuntime
19
+ } from "../../chunk-EPEEEI3C.js";
20
+ import "../../chunk-H37EB22A.js";
19
21
  import "../../chunk-6DXGZZA4.js";
20
- import "../../chunk-J5BTCP6T.js";
22
+ import "../../chunk-FTTVLHNN.js";
21
23
 
22
24
  // src/stacks/tanstack/handlers.ts
25
+ var STACK = "tanstack";
23
26
  function forwarded(request, options) {
24
27
  return applyForwardedOrigin(request, { trustForwardedHost: options.trustForwardedHost });
25
28
  }
26
29
  function createTanStackMcpHandler(mcp, options = {}) {
27
30
  const handler = createMcpProtocolHandler(mcp, options);
28
- return ({ request }) => handler(forwarded(request, options));
31
+ return ({ request }) => handler(forwarded(request, options), createRecorderForRuntime(mcp, { stack: STACK }));
29
32
  }
30
33
  function createTanStackListToolsHandler(mcp, options = {}) {
31
34
  const handler = createListToolsHandler(mcp, options);
32
- return ({ request }) => handler(forwarded(request, options));
35
+ return ({ request }) => handler(forwarded(request, options), createRecorderForRuntime(mcp, { stack: STACK }));
33
36
  }
34
37
  function createTanStackInvokeToolHandler(mcp, options = {}) {
35
38
  const handler = createInvokeToolHandler(mcp, options);
36
- return ({ request, params }) => handler(forwarded(request, options), params.tool);
39
+ return ({ request, params }) => handler(forwarded(request, options), params.tool, createRecorderForRuntime(mcp, { stack: STACK }));
37
40
  }
38
41
  function createTanStackOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
39
42
  const handler = createOAuthProtectedResourceMetadataHandler(mcp, options);