@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.
- package/dist/{recorder-qlzUKFxG.d.ts → base-C9rhAHZ0.d.ts} +3 -4
- package/dist/{chunk-FW6YL5C5.js → chunk-7JJEPFSO.js} +8 -8
- package/dist/{chunk-H2HXKAMQ.js → chunk-DUNDNXK5.js} +2 -2
- package/dist/{chunk-6TZNGFRM.js → chunk-EPEEEI3C.js} +121 -136
- package/dist/{chunk-J5BTCP6T.js → chunk-FTTVLHNN.js} +1 -1
- package/dist/{chunk-VQQZAFRA.js → chunk-H37EB22A.js} +2 -10
- package/dist/{chunk-WYXIX7XF.js → chunk-WKIXRW46.js} +11 -12
- package/dist/{chunk-EL6YNP2R.js → chunk-WW7NRRCJ.js} +13 -14
- package/dist/cli/extract-manifest.cjs +1 -1
- package/dist/cli/extract-manifest.js +4 -4
- package/dist/index.cjs +1 -7
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/protocols/mcp/index.cjs +16 -272
- package/dist/protocols/mcp/index.d.cts +6 -5
- package/dist/protocols/mcp/index.d.ts +6 -5
- package/dist/protocols/mcp/index.js +4 -4
- package/dist/protocols/oauth-metadata.d.cts +1 -1
- package/dist/protocols/oauth-metadata.d.ts +1 -1
- package/dist/protocols/oauth-metadata.js +4 -4
- package/dist/protocols/rest/index.cjs +21 -277
- package/dist/protocols/rest/index.d.cts +6 -6
- package/dist/protocols/rest/index.d.ts +6 -6
- package/dist/protocols/rest/index.js +5 -5
- package/dist/stacks/supabase/index.cjs +178 -201
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +14 -14
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +187 -209
- package/dist/stacks/tanstack/index.d.cts +1 -1
- package/dist/stacks/tanstack/index.d.ts +1 -1
- package/dist/stacks/tanstack/index.js +13 -10
- package/dist/{types-COY42xux.d.ts → types-CPkhCRxc.d.ts} +3 -3
- package/package.json +1 -1
|
@@ -11,11 +11,10 @@ interface InvocationRecord {
|
|
|
11
11
|
reqBytes?: number;
|
|
12
12
|
resBytes?: number;
|
|
13
13
|
}
|
|
14
|
+
/** A per-request telemetry recorder. `emit` logs the invocation and sends it as a
|
|
15
|
+
* single OTLP POST, awaited so it lands before the request ends. */
|
|
14
16
|
interface MetricsRecorder {
|
|
15
|
-
|
|
16
|
-
record(ev: InvocationRecord): void;
|
|
17
|
-
/** Best-effort POST of the current buffer. Resolves even on network failure. */
|
|
18
|
-
flush(): Promise<void>;
|
|
17
|
+
emit(ev: InvocationRecord): Promise<void>;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export type { MetricsRecorder as M };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertRestResourceBinding,
|
|
3
3
|
corsPreflightResponse,
|
|
4
|
-
|
|
4
|
+
createNoopRecorder,
|
|
5
5
|
createRequestAuthorizer,
|
|
6
6
|
headResponse,
|
|
7
7
|
methodNotAllowed,
|
|
8
8
|
withCors
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-EPEEEI3C.js";
|
|
10
10
|
|
|
11
11
|
// src/protocols/rest/list-tools.ts
|
|
12
12
|
import { objectFromShape } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
@@ -33,11 +33,11 @@ function buildMcpListing(mcp) {
|
|
|
33
33
|
}))
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
function createListToolsHandler(mcp, options = {}
|
|
36
|
+
function createListToolsHandler(mcp, options = {}) {
|
|
37
37
|
assertRestResourceBinding(mcp, options);
|
|
38
|
-
const authorizer = createRequestAuthorizer(mcp, options
|
|
39
|
-
const handle = async (request) => {
|
|
40
|
-
const authResult = await authorizer.authorize(request);
|
|
38
|
+
const authorizer = createRequestAuthorizer(mcp, options);
|
|
39
|
+
const handle = async (request, recorder) => {
|
|
40
|
+
const authResult = await authorizer.authorize(request, recorder);
|
|
41
41
|
if (!authResult.ok)
|
|
42
42
|
return authResult.response;
|
|
43
43
|
if (request.method !== "GET" && request.method !== "HEAD")
|
|
@@ -45,10 +45,10 @@ function createListToolsHandler(mcp, options = {}, recorder = createRecorderForR
|
|
|
45
45
|
const response = Response.json(buildMcpListing(mcp));
|
|
46
46
|
return request.method === "HEAD" ? headResponse(response) : response;
|
|
47
47
|
};
|
|
48
|
-
return async (request) => {
|
|
48
|
+
return async (request, recorder = createNoopRecorder()) => {
|
|
49
49
|
if (request.method === "OPTIONS")
|
|
50
50
|
return corsPreflightResponse("GET, HEAD, OPTIONS");
|
|
51
|
-
return withCors(await handle(request));
|
|
51
|
+
return withCors(await handle(request, recorder));
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
oauthConfigurationErrorResponse,
|
|
8
8
|
resolveProtectedResource,
|
|
9
9
|
withCors
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EPEEEI3C.js";
|
|
11
11
|
import {
|
|
12
12
|
describeError,
|
|
13
13
|
log
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-H37EB22A.js";
|
|
15
15
|
|
|
16
16
|
// src/protocols/oauth-metadata.ts
|
|
17
17
|
function notFound() {
|
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
parseSafeUrl,
|
|
8
8
|
resolveMetricsConfig,
|
|
9
9
|
trimTrailingSlash
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-H37EB22A.js";
|
|
11
11
|
import {
|
|
12
12
|
OAUTH_PROTECTED_RESOURCE_METADATA_PATH
|
|
13
13
|
} from "./chunk-6DXGZZA4.js";
|
|
14
14
|
import {
|
|
15
15
|
version
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-FTTVLHNN.js";
|
|
17
17
|
|
|
18
18
|
// src/core/http.ts
|
|
19
19
|
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
@@ -74,6 +74,7 @@ function toLogRecord(rec) {
|
|
|
74
74
|
attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
|
|
75
75
|
if (rec.resBytes !== void 0)
|
|
76
76
|
attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
|
|
77
|
+
attributes.push(strAttr("mcp.stack", rec.stack));
|
|
77
78
|
return {
|
|
78
79
|
timeUnixNano: rec.timeUnixNano,
|
|
79
80
|
observedTimeUnixNano: rec.timeUnixNano,
|
|
@@ -110,164 +111,148 @@ function nowUnixNano() {
|
|
|
110
111
|
return `${Date.now()}000000`;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
// src/metrics/
|
|
114
|
+
// src/metrics/impl/base.ts
|
|
114
115
|
function nowMs() {
|
|
115
116
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
116
117
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
recorder.record(ev);
|
|
120
|
-
}
|
|
121
|
-
var NOOP_RECORDER = {
|
|
122
|
-
record() {
|
|
123
|
-
},
|
|
124
|
-
async flush() {
|
|
125
|
-
}
|
|
126
|
-
};
|
|
118
|
+
var NOOP_RECORDER = { async emit() {
|
|
119
|
+
} };
|
|
127
120
|
function createNoopRecorder() {
|
|
128
121
|
return NOOP_RECORDER;
|
|
129
122
|
}
|
|
130
|
-
function
|
|
131
|
-
try {
|
|
132
|
-
const denoEnv = globalThis.Deno?.env;
|
|
133
|
-
const value = denoEnv?.get?.(name);
|
|
134
|
-
if (value)
|
|
135
|
-
return value;
|
|
136
|
-
} catch {
|
|
137
|
-
}
|
|
123
|
+
function readProcessEnv(name) {
|
|
138
124
|
try {
|
|
139
|
-
|
|
140
|
-
const value = process.env?.[name];
|
|
141
|
-
if (value)
|
|
142
|
-
return value;
|
|
143
|
-
}
|
|
144
|
-
} catch {
|
|
145
|
-
}
|
|
146
|
-
return void 0;
|
|
147
|
-
}
|
|
148
|
-
var cloudflareEnvPromise;
|
|
149
|
-
async function readCloudflareEnv(name) {
|
|
150
|
-
try {
|
|
151
|
-
const moduleSpecifier = "cloudflare:workers";
|
|
152
|
-
cloudflareEnvPromise ??= import(
|
|
153
|
-
/* @vite-ignore */
|
|
154
|
-
moduleSpecifier
|
|
155
|
-
).then((m) => m.env).catch(() => void 0);
|
|
156
|
-
const env = await cloudflareEnvPromise;
|
|
157
|
-
const value = env?.[name];
|
|
158
|
-
return typeof value === "string" && value ? value : void 0;
|
|
125
|
+
return typeof process !== "undefined" ? process.env?.[name] || void 0 : void 0;
|
|
159
126
|
} catch {
|
|
160
127
|
return void 0;
|
|
161
128
|
}
|
|
162
129
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return NOOP_RECORDER;
|
|
176
|
-
}
|
|
177
|
-
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
178
|
-
const waitUntil = deps.waitUntil ?? probeWaitUntil();
|
|
179
|
-
const buffer = [];
|
|
180
|
-
let timer;
|
|
181
|
-
let lazyLogLevelApplied = false;
|
|
182
|
-
const applyLazyLogLevel = async () => {
|
|
183
|
-
if (lazyLogLevelApplied)
|
|
184
|
-
return;
|
|
185
|
-
lazyLogLevelApplied = true;
|
|
186
|
-
applyLogLevelFromEnv(await readCloudflareEnv(LOG_LEVEL_ENV_VAR));
|
|
187
|
-
};
|
|
188
|
-
let apiKey;
|
|
189
|
-
const resolveApiKey = async () => {
|
|
190
|
-
if (apiKey)
|
|
191
|
-
return apiKey;
|
|
192
|
-
apiKey = (deps.getApiKey ? deps.getApiKey() : readRuntimeEnv(config.apiKeyEnvVar)) ?? await readCloudflareEnv(config.apiKeyEnvVar);
|
|
193
|
-
return apiKey;
|
|
194
|
-
};
|
|
195
|
-
const schedule = (p) => {
|
|
196
|
-
if (waitUntil) {
|
|
197
|
-
try {
|
|
198
|
-
waitUntil(p);
|
|
199
|
-
return;
|
|
200
|
-
} catch (err) {
|
|
201
|
-
log.warn("metrics.wait_until_failed", describeError(err));
|
|
130
|
+
var BaseMetricRecorder = class {
|
|
131
|
+
constructor(config, server, deps = {}) {
|
|
132
|
+
this.config = config;
|
|
133
|
+
this.server = server;
|
|
134
|
+
this.deps = deps;
|
|
135
|
+
this.doFetch = deps.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
|
|
136
|
+
this.usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
137
|
+
const headers = {};
|
|
138
|
+
if (!this.usesLovableEndpoint) {
|
|
139
|
+
for (const [key, value] of Object.entries(config.headers)) {
|
|
140
|
+
if (key.toLowerCase() !== "content-type")
|
|
141
|
+
headers[key] = value;
|
|
202
142
|
}
|
|
203
143
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
};
|
|
207
|
-
const baseHeaders = {};
|
|
208
|
-
if (!usesLovableEndpoint) {
|
|
209
|
-
for (const [key, value] of Object.entries(config.headers)) {
|
|
210
|
-
if (key.toLowerCase() !== "content-type")
|
|
211
|
-
baseHeaders[key] = value;
|
|
212
|
-
}
|
|
144
|
+
headers["content-type"] = "application/json";
|
|
145
|
+
this.baseHeaders = headers;
|
|
213
146
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
147
|
+
doFetch;
|
|
148
|
+
usesLovableEndpoint;
|
|
149
|
+
baseHeaders;
|
|
150
|
+
apiKey;
|
|
151
|
+
lazyLogLevelApplied = false;
|
|
152
|
+
async emit(ev) {
|
|
153
|
+
await this.applyLazyLogLevel();
|
|
154
|
+
log.info("tool.invoked", {
|
|
155
|
+
tool: ev.tool,
|
|
156
|
+
method: ev.method,
|
|
157
|
+
outcome: ev.outcome,
|
|
158
|
+
durationMs: ev.durationMs,
|
|
159
|
+
stack: this.stack
|
|
160
|
+
});
|
|
161
|
+
if (!this.config.enabled)
|
|
162
|
+
return;
|
|
163
|
+
if (!this.doFetch) {
|
|
164
|
+
log.warn("metrics.disabled_no_fetch", { endpoint: this.config.endpoint });
|
|
218
165
|
return;
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const key = await resolveApiKey();
|
|
166
|
+
}
|
|
167
|
+
const headers = { ...this.baseHeaders };
|
|
168
|
+
if (this.usesLovableEndpoint) {
|
|
169
|
+
const key = await this.resolveApiKey();
|
|
224
170
|
if (!key) {
|
|
225
|
-
log.warn("metrics.disabled_no_api_key", {
|
|
226
|
-
envVar: config.apiKeyEnvVar,
|
|
227
|
-
endpoint: config.endpoint,
|
|
228
|
-
dropped
|
|
229
|
-
});
|
|
171
|
+
log.warn("metrics.disabled_no_api_key", { envVar: this.config.apiKeyEnvVar, endpoint: this.config.endpoint });
|
|
230
172
|
return;
|
|
231
173
|
}
|
|
232
174
|
headers.authorization = `Bearer ${key}`;
|
|
233
175
|
}
|
|
176
|
+
const body = buildLogsPayload([{ ...ev, timeUnixNano: nowUnixNano(), stack: this.stack }], this.server);
|
|
234
177
|
try {
|
|
235
|
-
const res = await doFetch(config.endpoint, {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
body: buildLogsPayload(records, server),
|
|
239
|
-
keepalive: true
|
|
240
|
-
});
|
|
241
|
-
if (!res.ok) {
|
|
242
|
-
log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
|
|
243
|
-
}
|
|
178
|
+
const res = await this.doFetch(this.config.endpoint, { method: "POST", headers, body, keepalive: true });
|
|
179
|
+
if (!res.ok)
|
|
180
|
+
log.warn("metrics.rejected", { status: res.status, endpoint: this.config.endpoint });
|
|
244
181
|
} catch (err) {
|
|
245
|
-
log.warn("metrics.
|
|
182
|
+
log.warn("metrics.failed", { ...describeError(err), endpoint: this.config.endpoint });
|
|
246
183
|
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
184
|
+
}
|
|
185
|
+
// Cache only a successful resolution so a miss is retried on the next emit (the
|
|
186
|
+
// Cloudflare binding may not be readable on the very first attempt in an isolate).
|
|
187
|
+
async resolveApiKey() {
|
|
188
|
+
if (this.apiKey)
|
|
189
|
+
return this.apiKey;
|
|
190
|
+
this.apiKey = this.deps.getApiKey ? this.deps.getApiKey() : await this.readEnv(this.config.apiKeyEnvVar);
|
|
191
|
+
return this.apiKey;
|
|
192
|
+
}
|
|
193
|
+
async applyLazyLogLevel() {
|
|
194
|
+
if (this.lazyLogLevelApplied)
|
|
250
195
|
return;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
196
|
+
this.lazyLogLevelApplied = true;
|
|
197
|
+
applyLogLevelFromEnv(await this.readEnv(LOG_LEVEL_ENV_VAR));
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// src/metrics/impl/cloudflare.ts
|
|
202
|
+
var cloudflareEnvPromise;
|
|
203
|
+
async function readCloudflareEnv(name) {
|
|
204
|
+
try {
|
|
205
|
+
const moduleSpecifier = "cloudflare:workers";
|
|
206
|
+
cloudflareEnvPromise ??= import(
|
|
207
|
+
/* @vite-ignore */
|
|
208
|
+
moduleSpecifier
|
|
209
|
+
).then((m) => m.env).catch((err) => {
|
|
210
|
+
log.debug("metrics.cloudflare_env_import_failed", describeError(err));
|
|
211
|
+
return void 0;
|
|
212
|
+
});
|
|
213
|
+
const env = await cloudflareEnvPromise;
|
|
214
|
+
const raw = env?.[name];
|
|
215
|
+
const value = typeof raw === "string" && raw ? raw : void 0;
|
|
216
|
+
log.debug("metrics.read_cloudflare_env", { name, hasEnvBinding: !!env, found: value !== void 0 });
|
|
217
|
+
return value;
|
|
218
|
+
} catch (err) {
|
|
219
|
+
log.debug("metrics.read_cloudflare_env_error", { name, ...describeError(err) });
|
|
220
|
+
return void 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
var TanStackMetricRecorder = class extends BaseMetricRecorder {
|
|
224
|
+
stack = "tanstack";
|
|
225
|
+
async readEnv(name) {
|
|
226
|
+
return readProcessEnv(name) ?? await readCloudflareEnv(name);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// src/metrics/impl/supabase.ts
|
|
231
|
+
function readDenoEnv(name) {
|
|
232
|
+
try {
|
|
233
|
+
const denoEnv = globalThis.Deno?.env;
|
|
234
|
+
const value = denoEnv?.get?.(name) || void 0;
|
|
235
|
+
log.debug("metrics.read_deno_env", { name, hasDenoEnv: !!denoEnv, found: !!value });
|
|
236
|
+
return value;
|
|
237
|
+
} catch (err) {
|
|
238
|
+
log.debug("metrics.read_deno_env_error", { name, ...describeError(err) });
|
|
239
|
+
return void 0;
|
|
240
|
+
}
|
|
265
241
|
}
|
|
266
|
-
|
|
242
|
+
var SupabaseMetricRecorder = class extends BaseMetricRecorder {
|
|
243
|
+
stack = "supabase";
|
|
244
|
+
async readEnv(name) {
|
|
245
|
+
return readDenoEnv(name) ?? readProcessEnv(name);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// src/metrics/recorder.ts
|
|
250
|
+
function createRecorderForRuntime(mcp, opts) {
|
|
267
251
|
const config = resolveMetricsConfig(mcp.metrics);
|
|
252
|
+
const server = { name: mcp.name, version: mcp.version };
|
|
268
253
|
if (!config.enabled)
|
|
269
254
|
return createNoopRecorder();
|
|
270
|
-
return
|
|
255
|
+
return opts.stack === "tanstack" ? new TanStackMetricRecorder(config, server) : new SupabaseMetricRecorder(config, server);
|
|
271
256
|
}
|
|
272
257
|
|
|
273
258
|
// src/core/promise.ts
|
|
@@ -635,10 +620,10 @@ function assertRequiredScopes(auth, context) {
|
|
|
635
620
|
throw new OAuthTokenError(403, "insufficient_scope", "Additional OAuth scope is required");
|
|
636
621
|
}
|
|
637
622
|
}
|
|
638
|
-
function createRequestAuthorizer(mcp, options = {}
|
|
623
|
+
function createRequestAuthorizer(mcp, options = {}) {
|
|
639
624
|
const runtime = getOAuthRuntime(mcp, options);
|
|
640
625
|
return {
|
|
641
|
-
async authorize(request) {
|
|
626
|
+
async authorize(request, recorder) {
|
|
642
627
|
if (runtime.kind === "unconfigured")
|
|
643
628
|
return { ok: true };
|
|
644
629
|
const startedAt = nowMs();
|
|
@@ -654,7 +639,7 @@ function createRequestAuthorizer(mcp, options = {}, recorder) {
|
|
|
654
639
|
} catch (err) {
|
|
655
640
|
if (err instanceof OAuthConfigurationError) {
|
|
656
641
|
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
657
|
-
recorder?.
|
|
642
|
+
await recorder?.emit({
|
|
658
643
|
tool: null,
|
|
659
644
|
method: "authorize",
|
|
660
645
|
outcome: "auth_config_error",
|
|
@@ -718,7 +703,7 @@ export {
|
|
|
718
703
|
headResponse,
|
|
719
704
|
methodNotAllowed,
|
|
720
705
|
nowMs,
|
|
721
|
-
|
|
706
|
+
createNoopRecorder,
|
|
722
707
|
createRecorderForRuntime,
|
|
723
708
|
resolveProtectedResource,
|
|
724
709
|
assertResourcePathShape,
|
|
@@ -22,9 +22,7 @@ function parseLogLevel(raw) {
|
|
|
22
22
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
23
23
|
}
|
|
24
24
|
function applyLogLevelFromEnv(raw) {
|
|
25
|
-
|
|
26
|
-
if (level)
|
|
27
|
-
setLogLevel(level);
|
|
25
|
+
setLogLevel(parseLogLevel(raw) ?? "debug");
|
|
28
26
|
}
|
|
29
27
|
function enabled(level) {
|
|
30
28
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
@@ -55,9 +53,6 @@ function describeError(err) {
|
|
|
55
53
|
// src/metrics/config.ts
|
|
56
54
|
var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
|
|
57
55
|
var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
|
|
58
|
-
var METRICS_SAMPLE_RATE = 1;
|
|
59
|
-
var METRICS_FLUSH_INTERVAL_MS = 5e3;
|
|
60
|
-
var METRICS_MAX_BATCH_SIZE = 50;
|
|
61
56
|
function assertMetricsEndpoint(endpoint) {
|
|
62
57
|
let url;
|
|
63
58
|
try {
|
|
@@ -77,10 +72,7 @@ function resolveMetricsConfig(config = true) {
|
|
|
77
72
|
enabled: options.enabled ?? true,
|
|
78
73
|
endpoint,
|
|
79
74
|
headers: options.headers ?? {},
|
|
80
|
-
apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
|
|
81
|
-
sampleRate: METRICS_SAMPLE_RATE,
|
|
82
|
-
flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
|
|
83
|
-
maxBatchSize: METRICS_MAX_BATCH_SIZE
|
|
75
|
+
apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
|
|
84
76
|
});
|
|
85
77
|
}
|
|
86
78
|
|
|
@@ -5,13 +5,12 @@ import {
|
|
|
5
5
|
JSON_HEADERS,
|
|
6
6
|
assertRestResourceBinding,
|
|
7
7
|
corsPreflightResponse,
|
|
8
|
-
|
|
8
|
+
createNoopRecorder,
|
|
9
9
|
createRequestAuthorizer,
|
|
10
10
|
methodNotAllowed,
|
|
11
11
|
nowMs,
|
|
12
|
-
reportInvocation,
|
|
13
12
|
withCors
|
|
14
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-EPEEEI3C.js";
|
|
15
14
|
|
|
16
15
|
// src/protocols/rest/invoke-tool.ts
|
|
17
16
|
import { getParseErrorMessage, objectFromShape, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
@@ -27,11 +26,11 @@ function isEmptyArgs(value) {
|
|
|
27
26
|
return false;
|
|
28
27
|
return Object.keys(value).length === 0;
|
|
29
28
|
}
|
|
30
|
-
function createInvokeToolHandler(mcp, options = {}
|
|
29
|
+
function createInvokeToolHandler(mcp, options = {}) {
|
|
31
30
|
assertRestResourceBinding(mcp, options);
|
|
32
|
-
const authorizer = createRequestAuthorizer(mcp, options
|
|
33
|
-
const handle = async (request, toolName) => {
|
|
34
|
-
const authResult = await authorizer.authorize(request);
|
|
31
|
+
const authorizer = createRequestAuthorizer(mcp, options);
|
|
32
|
+
const handle = async (request, toolName, recorder) => {
|
|
33
|
+
const authResult = await authorizer.authorize(request, recorder);
|
|
35
34
|
if (!authResult.ok)
|
|
36
35
|
return authResult.response;
|
|
37
36
|
if (request.method !== "POST")
|
|
@@ -87,7 +86,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
|
|
|
87
86
|
try {
|
|
88
87
|
result = await tool.handler(args, new ToolContext(authResult.auth));
|
|
89
88
|
} catch {
|
|
90
|
-
|
|
89
|
+
await recorder.emit({
|
|
91
90
|
tool: tool.name,
|
|
92
91
|
method: "tools/call",
|
|
93
92
|
outcome: "handler_error",
|
|
@@ -99,7 +98,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
|
|
|
99
98
|
});
|
|
100
99
|
}
|
|
101
100
|
if (result == null) {
|
|
102
|
-
|
|
101
|
+
await recorder.emit({
|
|
103
102
|
tool: tool.name,
|
|
104
103
|
method: "tools/call",
|
|
105
104
|
outcome: "handler_error",
|
|
@@ -110,7 +109,7 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
|
|
|
110
109
|
headers: JSON_HEADERS
|
|
111
110
|
});
|
|
112
111
|
}
|
|
113
|
-
|
|
112
|
+
await recorder.emit({
|
|
114
113
|
tool: tool.name,
|
|
115
114
|
method: "tools/call",
|
|
116
115
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -122,10 +121,10 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
|
|
|
122
121
|
isError: result.isError
|
|
123
122
|
});
|
|
124
123
|
};
|
|
125
|
-
return async (request, toolName) => {
|
|
124
|
+
return async (request, toolName, recorder = createNoopRecorder()) => {
|
|
126
125
|
if (request.method === "OPTIONS")
|
|
127
126
|
return corsPreflightResponse("POST, OPTIONS");
|
|
128
|
-
return withCors(await handle(request, toolName));
|
|
127
|
+
return withCors(await handle(request, toolName, recorder));
|
|
129
128
|
};
|
|
130
129
|
}
|
|
131
130
|
|
|
@@ -3,16 +3,15 @@ import {
|
|
|
3
3
|
} from "./chunk-MA5H6PSF.js";
|
|
4
4
|
import {
|
|
5
5
|
corsPreflightResponse,
|
|
6
|
-
|
|
6
|
+
createNoopRecorder,
|
|
7
7
|
createRequestAuthorizer,
|
|
8
8
|
nowMs,
|
|
9
|
-
reportInvocation,
|
|
10
9
|
withCors
|
|
11
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EPEEEI3C.js";
|
|
12
11
|
import {
|
|
13
12
|
describeError,
|
|
14
13
|
log
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-H37EB22A.js";
|
|
16
15
|
|
|
17
16
|
// src/protocols/mcp/protocol.ts
|
|
18
17
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -25,7 +24,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
25
24
|
try {
|
|
26
25
|
result = await tool.handler(args, new ToolContext(auth));
|
|
27
26
|
} catch {
|
|
28
|
-
|
|
27
|
+
await recorder.emit({
|
|
29
28
|
tool: tool.name,
|
|
30
29
|
method: "tools/call",
|
|
31
30
|
outcome: "handler_error",
|
|
@@ -34,7 +33,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
34
33
|
return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
|
|
35
34
|
}
|
|
36
35
|
if (result == null) {
|
|
37
|
-
|
|
36
|
+
await recorder.emit({
|
|
38
37
|
tool: tool.name,
|
|
39
38
|
method: "tools/call",
|
|
40
39
|
outcome: "handler_error",
|
|
@@ -42,7 +41,7 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
42
41
|
});
|
|
43
42
|
return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
|
|
44
43
|
}
|
|
45
|
-
|
|
44
|
+
await recorder.emit({
|
|
46
45
|
tool: tool.name,
|
|
47
46
|
method: "tools/call",
|
|
48
47
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -51,10 +50,10 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
51
50
|
return { content: result.content ?? [], structuredContent: result.structuredContent, isError: result.isError };
|
|
52
51
|
};
|
|
53
52
|
}
|
|
54
|
-
function createMcpProtocolHandler(mcp, options = {}
|
|
55
|
-
const authorizer = createRequestAuthorizer(mcp, options
|
|
56
|
-
const handle = async (request) => {
|
|
57
|
-
const authResult = await authorizer.authorize(request);
|
|
53
|
+
function createMcpProtocolHandler(mcp, options = {}) {
|
|
54
|
+
const authorizer = createRequestAuthorizer(mcp, options);
|
|
55
|
+
const handle = async (request, recorder) => {
|
|
56
|
+
const authResult = await authorizer.authorize(request, recorder);
|
|
58
57
|
if (!authResult.ok)
|
|
59
58
|
return authResult.response;
|
|
60
59
|
try {
|
|
@@ -81,7 +80,7 @@ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderFo
|
|
|
81
80
|
await server.connect(transport);
|
|
82
81
|
return await transport.handleRequest(request);
|
|
83
82
|
} catch (err) {
|
|
84
|
-
recorder.
|
|
83
|
+
await recorder.emit({ tool: null, method: "transport", outcome: "transport_error", durationMs: 0 });
|
|
85
84
|
log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
|
|
86
85
|
return Response.json(
|
|
87
86
|
{ jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
|
|
@@ -89,10 +88,10 @@ function createMcpProtocolHandler(mcp, options = {}, recorder = createRecorderFo
|
|
|
89
88
|
);
|
|
90
89
|
}
|
|
91
90
|
};
|
|
92
|
-
return async (request) => {
|
|
91
|
+
return async (request, recorder = createNoopRecorder()) => {
|
|
93
92
|
if (request.method === "OPTIONS")
|
|
94
93
|
return corsPreflightResponse("GET, POST, DELETE, OPTIONS");
|
|
95
|
-
return withCors(await handle(request));
|
|
94
|
+
return withCors(await handle(request, recorder));
|
|
96
95
|
};
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildMcpListing
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-7JJEPFSO.js";
|
|
5
|
+
import "../chunk-EPEEEI3C.js";
|
|
6
|
+
import "../chunk-H37EB22A.js";
|
|
7
7
|
import "../chunk-6DXGZZA4.js";
|
|
8
8
|
import {
|
|
9
9
|
isFileMissing
|
|
10
10
|
} from "../chunk-Y3ZFPEQH.js";
|
|
11
11
|
import {
|
|
12
12
|
version
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-FTTVLHNN.js";
|
|
14
14
|
|
|
15
15
|
// src/manifest/io.ts
|
|
16
16
|
import { randomUUID } from "crypto";
|
package/dist/index.cjs
CHANGED
|
@@ -31,9 +31,6 @@ module.exports = __toCommonJS(src_exports);
|
|
|
31
31
|
// src/metrics/config.ts
|
|
32
32
|
var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
|
|
33
33
|
var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
|
|
34
|
-
var METRICS_SAMPLE_RATE = 1;
|
|
35
|
-
var METRICS_FLUSH_INTERVAL_MS = 5e3;
|
|
36
|
-
var METRICS_MAX_BATCH_SIZE = 50;
|
|
37
34
|
function assertMetricsEndpoint(endpoint) {
|
|
38
35
|
let url;
|
|
39
36
|
try {
|
|
@@ -53,10 +50,7 @@ function resolveMetricsConfig(config = true) {
|
|
|
53
50
|
enabled: options.enabled ?? true,
|
|
54
51
|
endpoint,
|
|
55
52
|
headers: options.headers ?? {},
|
|
56
|
-
apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
|
|
57
|
-
sampleRate: METRICS_SAMPLE_RATE,
|
|
58
|
-
flushIntervalMs: METRICS_FLUSH_INTERVAL_MS,
|
|
59
|
-
maxBatchSize: METRICS_MAX_BATCH_SIZE
|
|
53
|
+
apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
|
|
60
54
|
});
|
|
61
55
|
}
|
|
62
56
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { e as McpDefinitionInput, d as McpDefinition, Z as ZodRawShape, l as ToolDefinition, M as McpAuthConfig } from './types-
|
|
2
|
-
export { A as AudioContent, C as ContentAnnotations, a as ContentBlock, E as EmbeddedBlobResource, b as EmbeddedResource, c as EmbeddedTextResource, I as ImageContent, J as JwtClaims, f as MetricsConfig, g as MetricsOptions, R as ResourceLink, h as ResourceLinkIcon, T as TextContent, i as ToolAnnotations, j as ToolContent, k as ToolContext, m as ToolHandlerResult, n as ZodSchema } from './types-
|
|
1
|
+
import { e as McpDefinitionInput, d as McpDefinition, Z as ZodRawShape, l as ToolDefinition, M as McpAuthConfig } from './types-CPkhCRxc.js';
|
|
2
|
+
export { A as AudioContent, C as ContentAnnotations, a as ContentBlock, E as EmbeddedBlobResource, b as EmbeddedResource, c as EmbeddedTextResource, I as ImageContent, J as JwtClaims, f as MetricsConfig, g as MetricsOptions, R as ResourceLink, h as ResourceLinkIcon, T as TextContent, i as ToolAnnotations, j as ToolContent, k as ToolContext, m as ToolHandlerResult, n as ZodSchema } from './types-CPkhCRxc.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|