@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.
- package/dist/{chunk-T2RED2TG.js → chunk-CEDD57G2.js} +5 -7
- package/dist/{chunk-QC3DXQTH.js → chunk-DCWYK4CA.js} +34 -0
- package/dist/{chunk-LWHVXXKQ.js → chunk-GIHCQT6L.js} +1 -1
- package/dist/{chunk-QDKOF4UF.js → chunk-ILYTJXDR.js} +2 -2
- package/dist/{chunk-P3F324UC.js → chunk-MA5H6PSF.js} +0 -34
- package/dist/{chunk-VNRQPA4K.js → chunk-QT5L4CS3.js} +4 -3
- package/dist/{chunk-RHDEW56Y.js → chunk-UCPYLSNN.js} +6 -8
- package/dist/{chunk-G4XA7IJM.js → chunk-YTOMGJV5.js} +189 -2
- package/dist/cli/extract-manifest.cjs +1 -1
- package/dist/cli/extract-manifest.js +4 -4
- package/dist/index.js +4 -4
- package/dist/protocols/mcp/index.cjs +217 -210
- package/dist/protocols/mcp/index.d.cts +1 -1
- package/dist/protocols/mcp/index.d.ts +1 -1
- package/dist/protocols/mcp/index.js +5 -6
- package/dist/protocols/oauth-metadata.js +4 -3
- package/dist/protocols/rest/index.cjs +219 -212
- package/dist/protocols/rest/index.d.cts +2 -2
- package/dist/protocols/rest/index.d.ts +2 -2
- package/dist/protocols/rest/index.js +6 -7
- package/dist/{recorder-B-eJU4Qu.d.ts → recorder-qlzUKFxG.d.ts} +3 -3
- package/dist/stacks/supabase/index.cjs +14 -7
- package/dist/stacks/supabase/index.js +10 -12
- package/dist/stacks/supabase/vite.cjs +28 -4
- package/dist/stacks/supabase/vite.js +28 -4
- package/dist/stacks/tanstack/index.cjs +220 -213
- package/dist/stacks/tanstack/index.js +8 -9
- package/package.json +1 -1
- package/dist/chunk-XQSTN54Y.js +0 -189
|
@@ -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-
|
|
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-
|
|
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-
|
|
3
|
+
} from "../../chunk-CEDD57G2.js";
|
|
4
4
|
import {
|
|
5
5
|
createListToolsHandler
|
|
6
|
-
} from "../../chunk-
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
9
|
-
import "../../chunk-
|
|
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-
|
|
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
|
|
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.
|
|
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-
|
|
6
|
+
} from "../../chunk-UCPYLSNN.js";
|
|
7
7
|
import {
|
|
8
8
|
createOAuthProtectedResourceMetadataHandler
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-ILYTJXDR.js";
|
|
10
10
|
import {
|
|
11
11
|
createInvokeToolHandler
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-CEDD57G2.js";
|
|
13
13
|
import {
|
|
14
14
|
createListToolsHandler
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-QT5L4CS3.js";
|
|
16
|
+
import "../../chunk-MA5H6PSF.js";
|
|
16
17
|
import {
|
|
18
|
+
assertResourcePathShape,
|
|
17
19
|
createRecorderForRuntime
|
|
18
|
-
} from "../../chunk-
|
|
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-
|
|
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-
|
|
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) => {
|