@lovable.dev/mcp-js 0.15.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.
@@ -82,6 +82,214 @@ function describeError(err) {
82
82
  return { value: String(err) };
83
83
  }
84
84
 
85
+ // src/metrics/config.ts
86
+ var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
87
+ var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
88
+ var METRICS_SAMPLE_RATE = 1;
89
+ var METRICS_FLUSH_INTERVAL_MS = 5e3;
90
+ var METRICS_MAX_BATCH_SIZE = 50;
91
+ function assertMetricsEndpoint(endpoint) {
92
+ let url;
93
+ try {
94
+ url = new URL(endpoint);
95
+ } catch {
96
+ throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be an absolute URL, got ${JSON.stringify(endpoint)}`);
97
+ }
98
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
99
+ throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be http(s), got ${JSON.stringify(endpoint)}`);
100
+ }
101
+ }
102
+ function resolveMetricsConfig(config = true) {
103
+ const options = typeof config === "boolean" ? { enabled: config } : config;
104
+ const endpoint = options.endpoint ?? DEFAULT_METRICS_ENDPOINT;
105
+ assertMetricsEndpoint(endpoint);
106
+ return Object.freeze({
107
+ enabled: options.enabled ?? true,
108
+ endpoint,
109
+ headers: options.headers ?? {},
110
+ apiKeyEnvVar: METRICS_API_KEY_ENV_VAR,
111
+ sampleRate: METRICS_SAMPLE_RATE,
112
+ flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
113
+ maxBatchSize: METRICS_MAX_BATCH_SIZE
114
+ });
115
+ }
116
+
117
+ // package.json
118
+ var version = "0.15.1";
119
+
120
+ // src/metrics/otlp.ts
121
+ var SCOPE_NAME = "@lovable.dev/mcp-js";
122
+ var EVENT_NAME = "mcp.tool.invocation";
123
+ var SEVERITY_INFO = 9;
124
+ function strAttr(key, value) {
125
+ return { key, value: { stringValue: value } };
126
+ }
127
+ function intAttr(key, value) {
128
+ return { key, value: { intValue: String(Math.round(value)) } };
129
+ }
130
+ function toLogRecord(rec) {
131
+ const attributes = [
132
+ strAttr("event.name", EVENT_NAME),
133
+ strAttr("mcp.method", rec.method),
134
+ strAttr("mcp.outcome", rec.outcome),
135
+ intAttr("mcp.duration_ms", rec.durationMs)
136
+ ];
137
+ if (rec.tool !== null)
138
+ attributes.push(strAttr("mcp.tool", rec.tool));
139
+ if (rec.reqBytes !== void 0)
140
+ attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
141
+ if (rec.resBytes !== void 0)
142
+ attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
143
+ return {
144
+ timeUnixNano: rec.timeUnixNano,
145
+ observedTimeUnixNano: rec.timeUnixNano,
146
+ severityNumber: SEVERITY_INFO,
147
+ severityText: "INFO",
148
+ body: { stringValue: EVENT_NAME },
149
+ attributes
150
+ };
151
+ }
152
+ function buildLogsPayload(records, server) {
153
+ return JSON.stringify({
154
+ resourceLogs: [
155
+ {
156
+ resource: {
157
+ attributes: [
158
+ strAttr("service.name", server.name),
159
+ strAttr("service.version", server.version),
160
+ strAttr("telemetry.sdk.name", SCOPE_NAME),
161
+ strAttr("telemetry.sdk.version", version),
162
+ strAttr("telemetry.sdk.language", "webjs")
163
+ ]
164
+ },
165
+ scopeLogs: [
166
+ {
167
+ scope: { name: SCOPE_NAME, version },
168
+ logRecords: records.map(toLogRecord)
169
+ }
170
+ ]
171
+ }
172
+ ]
173
+ });
174
+ }
175
+ function nowUnixNano() {
176
+ return `${Date.now()}000000`;
177
+ }
178
+
179
+ // src/metrics/recorder.ts
180
+ function nowMs() {
181
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
182
+ }
183
+ var NOOP_RECORDER = {
184
+ record() {
185
+ },
186
+ async flush() {
187
+ }
188
+ };
189
+ function createNoopRecorder() {
190
+ return NOOP_RECORDER;
191
+ }
192
+ function readRuntimeEnv(name) {
193
+ try {
194
+ const denoEnv = globalThis.Deno?.env;
195
+ const value = denoEnv?.get?.(name);
196
+ if (value)
197
+ return value;
198
+ } catch {
199
+ }
200
+ try {
201
+ if (typeof process !== "undefined") {
202
+ const value = process.env?.[name];
203
+ if (value)
204
+ return value;
205
+ }
206
+ } catch {
207
+ }
208
+ return void 0;
209
+ }
210
+ function probeWaitUntil() {
211
+ const fn = globalThis.EdgeRuntime?.waitUntil;
212
+ return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
213
+ }
214
+ function createMetricsRecorder(ctx, deps = {}) {
215
+ const { config, server } = ctx;
216
+ if (!config.enabled)
217
+ return NOOP_RECORDER;
218
+ const doFetch = deps.fetch ?? globalThis.fetch;
219
+ if (!doFetch)
220
+ return NOOP_RECORDER;
221
+ const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
222
+ const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
223
+ if (usesLovableEndpoint && !apiKey) {
224
+ log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
225
+ return NOOP_RECORDER;
226
+ }
227
+ const waitUntil = deps.waitUntil ?? probeWaitUntil();
228
+ const buffer = [];
229
+ let timer;
230
+ const schedule = (p) => {
231
+ if (waitUntil) {
232
+ try {
233
+ waitUntil(p);
234
+ return;
235
+ } catch {
236
+ }
237
+ }
238
+ void p.catch(() => {
239
+ });
240
+ };
241
+ const headers = {};
242
+ if (!usesLovableEndpoint) {
243
+ for (const [key, value] of Object.entries(config.headers)) {
244
+ if (key.toLowerCase() !== "content-type")
245
+ headers[key] = value;
246
+ }
247
+ }
248
+ headers["content-type"] = "application/json";
249
+ if (apiKey)
250
+ headers.authorization = `Bearer ${apiKey}`;
251
+ const flush = async () => {
252
+ if (buffer.length === 0)
253
+ return;
254
+ const records = buffer.splice(0, buffer.length);
255
+ try {
256
+ const res = await doFetch(config.endpoint, {
257
+ method: "POST",
258
+ headers,
259
+ body: buildLogsPayload(records, server),
260
+ keepalive: true
261
+ });
262
+ if (!res.ok)
263
+ log.debug("metrics.flush_rejected", { status: res.status });
264
+ } catch (err) {
265
+ log.debug("metrics.flush_failed", describeError(err));
266
+ }
267
+ };
268
+ const ensureTimer = () => {
269
+ if (timer !== void 0)
270
+ return;
271
+ timer = setInterval(() => schedule(flush()), config.flushIntervalMs);
272
+ timer.unref?.();
273
+ };
274
+ return {
275
+ record(ev) {
276
+ if (config.sampleRate < 1 && Math.random() >= config.sampleRate)
277
+ return;
278
+ buffer.push({ ...ev, timeUnixNano: nowUnixNano() });
279
+ ensureTimer();
280
+ if (buffer.length >= config.maxBatchSize)
281
+ schedule(flush());
282
+ },
283
+ flush
284
+ };
285
+ }
286
+ function createRecorderForRuntime(mcp) {
287
+ const config = resolveMetricsConfig(mcp.metrics);
288
+ if (!config.enabled)
289
+ return createNoopRecorder();
290
+ return createMetricsRecorder({ config, server: { name: mcp.name, version: mcp.version } });
291
+ }
292
+
85
293
  // src/core/promise.ts
86
294
  function cachedPromise(load, label) {
87
295
  let settled = false;
@@ -510,12 +718,13 @@ function assertRequiredScopes(auth, context) {
510
718
  throw new OAuthTokenError(403, "insufficient_scope", "Additional OAuth scope is required");
511
719
  }
512
720
  }
513
- function createRequestAuthorizer(mcp, options = {}) {
721
+ function createRequestAuthorizer(mcp, options = {}, recorder) {
514
722
  const runtime = getOAuthRuntime(mcp, options);
515
723
  return {
516
724
  async authorize(request) {
517
725
  if (runtime.kind === "unconfigured")
518
726
  return { ok: true };
727
+ const startedAt = nowMs();
519
728
  const token = parseBearerToken(request);
520
729
  if (!token) {
521
730
  log.info("auth.no_bearer_token", { outcome: "401" });
@@ -528,6 +737,12 @@ function createRequestAuthorizer(mcp, options = {}) {
528
737
  } catch (err) {
529
738
  if (err instanceof OAuthConfigurationError) {
530
739
  log.error("auth.config_error", { ...describeError(err), outcome: "500" });
740
+ recorder?.record({
741
+ tool: null,
742
+ method: "authorize",
743
+ outcome: "auth_config_error",
744
+ durationMs: nowMs() - startedAt
745
+ });
531
746
  return { ok: false, response: oauthConfigurationErrorResponse() };
532
747
  }
533
748
  if (err instanceof OAuthTokenError) {
@@ -604,9 +819,9 @@ function buildMcpListing(mcp) {
604
819
  }))
605
820
  };
606
821
  }
607
- function createListToolsHandler(mcp, options = {}) {
822
+ function createListToolsHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
608
823
  assertRestResourceBinding(mcp, options);
609
- const authorizer = createRequestAuthorizer(mcp, options);
824
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
610
825
  const handle = async (request) => {
611
826
  const authResult = await authorizer.authorize(request);
612
827
  if (!authResult.ok)
@@ -669,214 +884,6 @@ var ToolContext = class {
669
884
  }
670
885
  };
671
886
 
672
- // src/metrics/config.ts
673
- var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
674
- var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
675
- var METRICS_SAMPLE_RATE = 1;
676
- var METRICS_FLUSH_INTERVAL_MS = 5e3;
677
- var METRICS_MAX_BATCH_SIZE = 50;
678
- function assertMetricsEndpoint(endpoint) {
679
- let url;
680
- try {
681
- url = new URL(endpoint);
682
- } catch {
683
- throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be an absolute URL, got ${JSON.stringify(endpoint)}`);
684
- }
685
- if (url.protocol !== "https:" && url.protocol !== "http:") {
686
- throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be http(s), got ${JSON.stringify(endpoint)}`);
687
- }
688
- }
689
- function resolveMetricsConfig(config = true) {
690
- const options = typeof config === "boolean" ? { enabled: config } : config;
691
- const endpoint = options.endpoint ?? DEFAULT_METRICS_ENDPOINT;
692
- assertMetricsEndpoint(endpoint);
693
- return Object.freeze({
694
- enabled: options.enabled ?? true,
695
- endpoint,
696
- headers: options.headers ?? {},
697
- apiKeyEnvVar: METRICS_API_KEY_ENV_VAR,
698
- sampleRate: METRICS_SAMPLE_RATE,
699
- flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
700
- maxBatchSize: METRICS_MAX_BATCH_SIZE
701
- });
702
- }
703
-
704
- // package.json
705
- var version = "0.15.0";
706
-
707
- // src/metrics/otlp.ts
708
- var SCOPE_NAME = "@lovable.dev/mcp-js";
709
- var EVENT_NAME = "mcp.tool.invocation";
710
- var SEVERITY_INFO = 9;
711
- function strAttr(key, value) {
712
- return { key, value: { stringValue: value } };
713
- }
714
- function intAttr(key, value) {
715
- return { key, value: { intValue: String(Math.round(value)) } };
716
- }
717
- function toLogRecord(rec) {
718
- const attributes = [
719
- strAttr("event.name", EVENT_NAME),
720
- strAttr("mcp.method", rec.method),
721
- strAttr("mcp.outcome", rec.outcome),
722
- intAttr("mcp.duration_ms", rec.durationMs)
723
- ];
724
- if (rec.tool !== null)
725
- attributes.push(strAttr("mcp.tool", rec.tool));
726
- if (rec.reqBytes !== void 0)
727
- attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
728
- if (rec.resBytes !== void 0)
729
- attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
730
- return {
731
- timeUnixNano: rec.timeUnixNano,
732
- observedTimeUnixNano: rec.timeUnixNano,
733
- severityNumber: SEVERITY_INFO,
734
- severityText: "INFO",
735
- body: { stringValue: EVENT_NAME },
736
- attributes
737
- };
738
- }
739
- function buildLogsPayload(records, server) {
740
- return JSON.stringify({
741
- resourceLogs: [
742
- {
743
- resource: {
744
- attributes: [
745
- strAttr("service.name", server.name),
746
- strAttr("service.version", server.version),
747
- strAttr("telemetry.sdk.name", SCOPE_NAME),
748
- strAttr("telemetry.sdk.version", version),
749
- strAttr("telemetry.sdk.language", "webjs")
750
- ]
751
- },
752
- scopeLogs: [
753
- {
754
- scope: { name: SCOPE_NAME, version },
755
- logRecords: records.map(toLogRecord)
756
- }
757
- ]
758
- }
759
- ]
760
- });
761
- }
762
- function nowUnixNano() {
763
- return `${Date.now()}000000`;
764
- }
765
-
766
- // src/metrics/recorder.ts
767
- function nowMs() {
768
- return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
769
- }
770
- var NOOP_RECORDER = {
771
- record() {
772
- },
773
- async flush() {
774
- }
775
- };
776
- function createNoopRecorder() {
777
- return NOOP_RECORDER;
778
- }
779
- function readRuntimeEnv(name) {
780
- try {
781
- const denoEnv = globalThis.Deno?.env;
782
- const value = denoEnv?.get?.(name);
783
- if (value)
784
- return value;
785
- } catch {
786
- }
787
- try {
788
- if (typeof process !== "undefined") {
789
- const value = process.env?.[name];
790
- if (value)
791
- return value;
792
- }
793
- } catch {
794
- }
795
- return void 0;
796
- }
797
- function probeWaitUntil() {
798
- const fn = globalThis.EdgeRuntime?.waitUntil;
799
- return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
800
- }
801
- function createMetricsRecorder(ctx, deps = {}) {
802
- const { config, server } = ctx;
803
- if (!config.enabled)
804
- return NOOP_RECORDER;
805
- const doFetch = deps.fetch ?? globalThis.fetch;
806
- if (!doFetch)
807
- return NOOP_RECORDER;
808
- const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
809
- const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
810
- if (usesLovableEndpoint && !apiKey) {
811
- log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
812
- return NOOP_RECORDER;
813
- }
814
- const waitUntil = deps.waitUntil ?? probeWaitUntil();
815
- const buffer = [];
816
- let timer;
817
- const schedule = (p) => {
818
- if (waitUntil) {
819
- try {
820
- waitUntil(p);
821
- return;
822
- } catch {
823
- }
824
- }
825
- void p.catch(() => {
826
- });
827
- };
828
- const headers = {};
829
- if (!usesLovableEndpoint) {
830
- for (const [key, value] of Object.entries(config.headers)) {
831
- if (key.toLowerCase() !== "content-type")
832
- headers[key] = value;
833
- }
834
- }
835
- headers["content-type"] = "application/json";
836
- if (apiKey)
837
- headers.authorization = `Bearer ${apiKey}`;
838
- const flush = async () => {
839
- if (buffer.length === 0)
840
- return;
841
- const records = buffer.splice(0, buffer.length);
842
- try {
843
- const res = await doFetch(config.endpoint, {
844
- method: "POST",
845
- headers,
846
- body: buildLogsPayload(records, server),
847
- keepalive: true
848
- });
849
- if (!res.ok)
850
- log.debug("metrics.flush_rejected", { status: res.status });
851
- } catch (err) {
852
- log.debug("metrics.flush_failed", describeError(err));
853
- }
854
- };
855
- const ensureTimer = () => {
856
- if (timer !== void 0)
857
- return;
858
- timer = setInterval(() => schedule(flush()), config.flushIntervalMs);
859
- timer.unref?.();
860
- };
861
- return {
862
- record(ev) {
863
- if (config.sampleRate < 1 && Math.random() >= config.sampleRate)
864
- return;
865
- buffer.push({ ...ev, timeUnixNano: nowUnixNano() });
866
- ensureTimer();
867
- if (buffer.length >= config.maxBatchSize)
868
- schedule(flush());
869
- },
870
- flush
871
- };
872
- }
873
- function createRecorderForRuntime(mcp) {
874
- const config = resolveMetricsConfig(mcp.metrics);
875
- if (!config.enabled)
876
- return createNoopRecorder();
877
- return createMetricsRecorder({ config, server: { name: mcp.name, version: mcp.version } });
878
- }
879
-
880
887
  // src/protocols/rest/invoke-tool.ts
881
888
  var MAX_REFLECTED_TOOL_NAME = 256;
882
889
  function safeReflectName(name) {
@@ -892,7 +899,7 @@ function isEmptyArgs(value) {
892
899
  }
893
900
  function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
894
901
  assertRestResourceBinding(mcp, options);
895
- const authorizer = createRequestAuthorizer(mcp, options);
902
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
896
903
  const handle = async (request, toolName) => {
897
904
  const authResult = await authorizer.authorize(request);
898
905
  if (!authResult.ok)
@@ -1,10 +1,10 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
2
  import { d as McpDefinition } from '../../types-COY42xux.js';
3
- import { M as MetricsRecorder } from '../../recorder-B-eJU4Qu.js';
3
+ import { M as MetricsRecorder } from '../../recorder-qlzUKFxG.js';
4
4
  import 'zod';
5
5
 
6
6
  type RestListToolsHandler = (request: Request) => Promise<Response>;
7
- declare function createListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions): RestListToolsHandler;
7
+ declare function createListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions, recorder?: MetricsRecorder): RestListToolsHandler;
8
8
 
9
9
  type RestInvokeToolHandler = (request: Request, toolName: string) => Promise<Response>;
10
10
  declare function createInvokeToolHandler(mcp: McpDefinition, options?: McpRuntimeOptions, recorder?: MetricsRecorder): RestInvokeToolHandler;
@@ -1,10 +1,10 @@
1
1
  import { M as McpRuntimeOptions } from '../../authorize-BMeXd_Sh.js';
2
2
  import { d as McpDefinition } from '../../types-COY42xux.js';
3
- import { M as MetricsRecorder } from '../../recorder-B-eJU4Qu.js';
3
+ import { M as MetricsRecorder } from '../../recorder-qlzUKFxG.js';
4
4
  import 'zod';
5
5
 
6
6
  type RestListToolsHandler = (request: Request) => Promise<Response>;
7
- declare function createListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions): RestListToolsHandler;
7
+ declare function createListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions, recorder?: MetricsRecorder): RestListToolsHandler;
8
8
 
9
9
  type RestInvokeToolHandler = (request: Request, toolName: string) => Promise<Response>;
10
10
  declare function createInvokeToolHandler(mcp: McpDefinition, options?: McpRuntimeOptions, recorder?: MetricsRecorder): RestInvokeToolHandler;
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  createInvokeToolHandler
3
- } from "../../chunk-T2RED2TG.js";
3
+ } from "../../chunk-CEDD57G2.js";
4
4
  import {
5
5
  createListToolsHandler
6
- } from "../../chunk-VNRQPA4K.js";
7
- import "../../chunk-XQSTN54Y.js";
8
- import "../../chunk-P3F324UC.js";
9
- import "../../chunk-G4XA7IJM.js";
10
- import "../../chunk-QC3DXQTH.js";
6
+ } from "../../chunk-QT5L4CS3.js";
7
+ import "../../chunk-MA5H6PSF.js";
8
+ import "../../chunk-YTOMGJV5.js";
9
+ import "../../chunk-DCWYK4CA.js";
11
10
  import "../../chunk-6DXGZZA4.js";
12
- import "../../chunk-LWHVXXKQ.js";
11
+ import "../../chunk-GIHCQT6L.js";
13
12
  export {
14
13
  createInvokeToolHandler,
15
14
  createListToolsHandler
@@ -1,10 +1,10 @@
1
- type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error";
1
+ type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_config_error";
2
2
  /** One recorded MCP call. Carries no arguments or response payloads — only the
3
3
  * tool name, the JSON-RPC method, the result class, timing, and byte sizes. */
4
4
  interface InvocationRecord {
5
- /** Tool name for `tools/call`; `null` for list/initialize and transport faults. */
5
+ /** Tool name for `tools/call`; `null` for list/initialize, transport faults, and auth-config failures. */
6
6
  tool: string | null;
7
- /** JSON-RPC method, e.g. `tools/call`. */
7
+ /** JSON-RPC method, e.g. `tools/call`; `authorize` for auth-config failures. */
8
8
  method: string;
9
9
  outcome: InvocationOutcome;
10
10
  durationMs: number;
@@ -169,7 +169,7 @@ function resolveMetricsConfig(config = true) {
169
169
  }
170
170
 
171
171
  // package.json
172
- var version = "0.15.0";
172
+ var version = "0.15.1";
173
173
 
174
174
  // src/metrics/otlp.ts
175
175
  var SCOPE_NAME = "@lovable.dev/mcp-js";
@@ -740,12 +740,13 @@ function assertRequiredScopes(auth, context) {
740
740
  throw new OAuthTokenError(403, "insufficient_scope", "Additional OAuth scope is required");
741
741
  }
742
742
  }
743
- function createRequestAuthorizer(mcp, options = {}) {
743
+ function createRequestAuthorizer(mcp, options = {}, recorder) {
744
744
  const runtime = getOAuthRuntime(mcp, options);
745
745
  return {
746
746
  async authorize(request) {
747
747
  if (runtime.kind === "unconfigured")
748
748
  return { ok: true };
749
+ const startedAt = nowMs();
749
750
  const token = parseBearerToken(request);
750
751
  if (!token) {
751
752
  log.info("auth.no_bearer_token", { outcome: "401" });
@@ -758,6 +759,12 @@ function createRequestAuthorizer(mcp, options = {}) {
758
759
  } catch (err) {
759
760
  if (err instanceof OAuthConfigurationError) {
760
761
  log.error("auth.config_error", { ...describeError(err), outcome: "500" });
762
+ recorder?.record({
763
+ tool: null,
764
+ method: "authorize",
765
+ outcome: "auth_config_error",
766
+ durationMs: nowMs() - startedAt
767
+ });
761
768
  return { ok: false, response: oauthConfigurationErrorResponse() };
762
769
  }
763
770
  if (err instanceof OAuthTokenError) {
@@ -880,7 +887,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
880
887
  };
881
888
  }
882
889
  function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
883
- const authorizer = createRequestAuthorizer(mcp, options);
890
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
884
891
  const handle = async (request) => {
885
892
  const authResult = await authorizer.authorize(request);
886
893
  if (!authResult.ok)
@@ -1010,9 +1017,9 @@ function buildMcpListing(mcp) {
1010
1017
  }))
1011
1018
  };
1012
1019
  }
1013
- function createListToolsHandler(mcp, options = {}) {
1020
+ function createListToolsHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
1014
1021
  assertRestResourceBinding(mcp, options);
1015
- const authorizer = createRequestAuthorizer(mcp, options);
1022
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
1016
1023
  const handle = async (request) => {
1017
1024
  const authResult = await authorizer.authorize(request);
1018
1025
  if (!authResult.ok)
@@ -1045,7 +1052,7 @@ function isEmptyArgs(value) {
1045
1052
  }
1046
1053
  function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderForRuntime(mcp)) {
1047
1054
  assertRestResourceBinding(mcp, options);
1048
- const authorizer = createRequestAuthorizer(mcp, options);
1055
+ const authorizer = createRequestAuthorizer(mcp, options, recorder);
1049
1056
  const handle = async (request, toolName) => {
1050
1057
  const authResult = await authorizer.authorize(request);
1051
1058
  if (!authResult.ok)
@@ -1183,7 +1190,7 @@ function createSupabaseHandler(mcp, options = {}) {
1183
1190
  const runtimeOptions = resourcePath === void 0 ? {} : { resourcePath, ...metadataPath ? { metadataPath } : {} };
1184
1191
  const recorder = createRecorderForRuntime(mcp);
1185
1192
  const mcpHandler = createMcpProtocolHandler(mcp, runtimeOptions, recorder);
1186
- const listToolsHandler = createListToolsHandler(mcp, runtimeOptions);
1193
+ const listToolsHandler = createListToolsHandler(mcp, runtimeOptions, recorder);
1187
1194
  const invokeToolHandler = createInvokeToolHandler(mcp, runtimeOptions, recorder);
1188
1195
  const metadataHandler = createOAuthProtectedResourceMetadataHandler(mcp, runtimeOptions);
1189
1196
  return async (request) => {
@@ -3,26 +3,24 @@ import {
3
3
  } from "../../chunk-UQK5UO6C.js";
4
4
  import {
5
5
  createMcpProtocolHandler
6
- } from "../../chunk-RHDEW56Y.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-T2RED2TG.js";
12
+ } from "../../chunk-CEDD57G2.js";
13
13
  import {
14
14
  createListToolsHandler
15
- } from "../../chunk-VNRQPA4K.js";
15
+ } from "../../chunk-QT5L4CS3.js";
16
+ import "../../chunk-MA5H6PSF.js";
16
17
  import {
18
+ assertResourcePathShape,
17
19
  createRecorderForRuntime
18
- } from "../../chunk-XQSTN54Y.js";
19
- import "../../chunk-P3F324UC.js";
20
- import {
21
- assertResourcePathShape
22
- } from "../../chunk-G4XA7IJM.js";
20
+ } from "../../chunk-YTOMGJV5.js";
23
21
  import {
24
22
  trimTrailingSlash
25
- } from "../../chunk-QC3DXQTH.js";
23
+ } from "../../chunk-DCWYK4CA.js";
26
24
  import {
27
25
  OAUTH_PROTECTED_RESOURCE_METADATA_PATH
28
26
  } from "../../chunk-6DXGZZA4.js";
@@ -30,7 +28,7 @@ import {
30
28
  FUNCTIONS_MOUNT_PREFIX,
31
29
  assertFunctionName
32
30
  } from "../../chunk-XQWJN6DC.js";
33
- import "../../chunk-LWHVXXKQ.js";
31
+ import "../../chunk-GIHCQT6L.js";
34
32
 
35
33
  // src/stacks/supabase/handler.ts
36
34
  function deriveResourcePath(options) {
@@ -70,7 +68,7 @@ function createSupabaseHandler(mcp, options = {}) {
70
68
  const runtimeOptions = resourcePath === void 0 ? {} : { resourcePath, ...metadataPath ? { metadataPath } : {} };
71
69
  const recorder = createRecorderForRuntime(mcp);
72
70
  const mcpHandler = createMcpProtocolHandler(mcp, runtimeOptions, recorder);
73
- const listToolsHandler = createListToolsHandler(mcp, runtimeOptions);
71
+ const listToolsHandler = createListToolsHandler(mcp, runtimeOptions, recorder);
74
72
  const invokeToolHandler = createInvokeToolHandler(mcp, runtimeOptions, recorder);
75
73
  const metadataHandler = createOAuthProtectedResourceMetadataHandler(mcp, runtimeOptions);
76
74
  return async (request) => {