@lovable.dev/mcp-js 0.15.1 → 0.17.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/{chunk-YTOMGJV5.js → chunk-6TZNGFRM.js} +66 -17
- package/dist/{chunk-UCPYLSNN.js → chunk-EL6YNP2R.js} +16 -5
- package/dist/{chunk-QT5L4CS3.js → chunk-FW6YL5C5.js} +1 -1
- package/dist/{chunk-ILYTJXDR.js → chunk-H2HXKAMQ.js} +2 -2
- package/dist/{chunk-GIHCQT6L.js → chunk-J5BTCP6T.js} +1 -1
- package/dist/{chunk-DCWYK4CA.js → chunk-VQQZAFRA.js} +14 -2
- package/dist/{chunk-CEDD57G2.js → chunk-WYXIX7XF.js} +15 -4
- package/dist/cli/extract-manifest.cjs +4 -3
- package/dist/cli/extract-manifest.js +4 -4
- package/dist/index.cjs +3 -2
- package/dist/index.js +1 -1
- package/dist/protocols/mcp/index.cjs +90 -21
- package/dist/protocols/mcp/index.js +4 -4
- package/dist/protocols/oauth-metadata.cjs +3 -2
- package/dist/protocols/oauth-metadata.js +4 -4
- package/dist/protocols/rest/index.cjs +90 -21
- package/dist/protocols/rest/index.js +5 -5
- package/dist/stacks/supabase/index.cjs +103 -24
- package/dist/stacks/supabase/index.js +7 -7
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +103 -24
- package/dist/stacks/tanstack/index.js +7 -7
- package/package.json +1 -1
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_METRICS_ENDPOINT,
|
|
3
|
+
LOG_LEVEL_ENV_VAR,
|
|
4
|
+
applyLogLevelFromEnv,
|
|
3
5
|
describeError,
|
|
4
6
|
log,
|
|
5
7
|
parseSafeUrl,
|
|
6
8
|
resolveMetricsConfig,
|
|
7
9
|
trimTrailingSlash
|
|
8
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VQQZAFRA.js";
|
|
9
11
|
import {
|
|
10
12
|
OAUTH_PROTECTED_RESOURCE_METADATA_PATH
|
|
11
13
|
} from "./chunk-6DXGZZA4.js";
|
|
12
14
|
import {
|
|
13
15
|
version
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-J5BTCP6T.js";
|
|
15
17
|
|
|
16
18
|
// src/core/http.ts
|
|
17
19
|
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
@@ -112,6 +114,10 @@ function nowUnixNano() {
|
|
|
112
114
|
function nowMs() {
|
|
113
115
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
114
116
|
}
|
|
117
|
+
function reportInvocation(recorder, ev) {
|
|
118
|
+
log.info("tool.invoked", { tool: ev.tool, method: ev.method, outcome: ev.outcome, durationMs: ev.durationMs });
|
|
119
|
+
recorder.record(ev);
|
|
120
|
+
}
|
|
115
121
|
var NOOP_RECORDER = {
|
|
116
122
|
record() {
|
|
117
123
|
},
|
|
@@ -139,51 +145,92 @@ function readRuntimeEnv(name) {
|
|
|
139
145
|
}
|
|
140
146
|
return void 0;
|
|
141
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;
|
|
159
|
+
} catch {
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
142
163
|
function probeWaitUntil() {
|
|
143
164
|
const fn = globalThis.EdgeRuntime?.waitUntil;
|
|
144
165
|
return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
|
|
145
166
|
}
|
|
146
167
|
function createMetricsRecorder(ctx, deps = {}) {
|
|
147
168
|
const { config, server } = ctx;
|
|
169
|
+
applyLogLevelFromEnv(readRuntimeEnv(LOG_LEVEL_ENV_VAR));
|
|
148
170
|
if (!config.enabled)
|
|
149
171
|
return NOOP_RECORDER;
|
|
150
172
|
const doFetch = deps.fetch ?? globalThis.fetch;
|
|
151
|
-
if (!doFetch)
|
|
152
|
-
|
|
153
|
-
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
154
|
-
const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
|
|
155
|
-
if (usesLovableEndpoint && !apiKey) {
|
|
156
|
-
log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
|
|
173
|
+
if (!doFetch) {
|
|
174
|
+
log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
|
|
157
175
|
return NOOP_RECORDER;
|
|
158
176
|
}
|
|
177
|
+
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
159
178
|
const waitUntil = deps.waitUntil ?? probeWaitUntil();
|
|
160
179
|
const buffer = [];
|
|
161
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
|
+
};
|
|
162
195
|
const schedule = (p) => {
|
|
163
196
|
if (waitUntil) {
|
|
164
197
|
try {
|
|
165
198
|
waitUntil(p);
|
|
166
199
|
return;
|
|
167
|
-
} catch {
|
|
200
|
+
} catch (err) {
|
|
201
|
+
log.warn("metrics.wait_until_failed", describeError(err));
|
|
168
202
|
}
|
|
169
203
|
}
|
|
170
204
|
void p.catch(() => {
|
|
171
205
|
});
|
|
172
206
|
};
|
|
173
|
-
const
|
|
207
|
+
const baseHeaders = {};
|
|
174
208
|
if (!usesLovableEndpoint) {
|
|
175
209
|
for (const [key, value] of Object.entries(config.headers)) {
|
|
176
210
|
if (key.toLowerCase() !== "content-type")
|
|
177
|
-
|
|
211
|
+
baseHeaders[key] = value;
|
|
178
212
|
}
|
|
179
213
|
}
|
|
180
|
-
|
|
181
|
-
if (apiKey)
|
|
182
|
-
headers.authorization = `Bearer ${apiKey}`;
|
|
214
|
+
baseHeaders["content-type"] = "application/json";
|
|
183
215
|
const flush = async () => {
|
|
216
|
+
void applyLazyLogLevel();
|
|
184
217
|
if (buffer.length === 0)
|
|
185
218
|
return;
|
|
186
219
|
const records = buffer.splice(0, buffer.length);
|
|
220
|
+
const dropped = records.length;
|
|
221
|
+
const headers = { ...baseHeaders };
|
|
222
|
+
if (usesLovableEndpoint) {
|
|
223
|
+
const key = await resolveApiKey();
|
|
224
|
+
if (!key) {
|
|
225
|
+
log.warn("metrics.disabled_no_api_key", {
|
|
226
|
+
envVar: config.apiKeyEnvVar,
|
|
227
|
+
endpoint: config.endpoint,
|
|
228
|
+
dropped
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
headers.authorization = `Bearer ${key}`;
|
|
233
|
+
}
|
|
187
234
|
try {
|
|
188
235
|
const res = await doFetch(config.endpoint, {
|
|
189
236
|
method: "POST",
|
|
@@ -191,10 +238,11 @@ function createMetricsRecorder(ctx, deps = {}) {
|
|
|
191
238
|
body: buildLogsPayload(records, server),
|
|
192
239
|
keepalive: true
|
|
193
240
|
});
|
|
194
|
-
if (!res.ok)
|
|
195
|
-
log.
|
|
241
|
+
if (!res.ok) {
|
|
242
|
+
log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
|
|
243
|
+
}
|
|
196
244
|
} catch (err) {
|
|
197
|
-
log.
|
|
245
|
+
log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
|
|
198
246
|
}
|
|
199
247
|
};
|
|
200
248
|
const ensureTimer = () => {
|
|
@@ -670,6 +718,7 @@ export {
|
|
|
670
718
|
headResponse,
|
|
671
719
|
methodNotAllowed,
|
|
672
720
|
nowMs,
|
|
721
|
+
reportInvocation,
|
|
673
722
|
createRecorderForRuntime,
|
|
674
723
|
resolveProtectedResource,
|
|
675
724
|
assertResourcePathShape,
|
|
@@ -6,12 +6,13 @@ import {
|
|
|
6
6
|
createRecorderForRuntime,
|
|
7
7
|
createRequestAuthorizer,
|
|
8
8
|
nowMs,
|
|
9
|
+
reportInvocation,
|
|
9
10
|
withCors
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6TZNGFRM.js";
|
|
11
12
|
import {
|
|
12
13
|
describeError,
|
|
13
14
|
log
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VQQZAFRA.js";
|
|
15
16
|
|
|
16
17
|
// src/protocols/mcp/protocol.ts
|
|
17
18
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -24,14 +25,24 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
24
25
|
try {
|
|
25
26
|
result = await tool.handler(args, new ToolContext(auth));
|
|
26
27
|
} catch {
|
|
27
|
-
recorder
|
|
28
|
+
reportInvocation(recorder, {
|
|
29
|
+
tool: tool.name,
|
|
30
|
+
method: "tools/call",
|
|
31
|
+
outcome: "handler_error",
|
|
32
|
+
durationMs: nowMs() - start
|
|
33
|
+
});
|
|
28
34
|
return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
|
|
29
35
|
}
|
|
30
36
|
if (result == null) {
|
|
31
|
-
recorder
|
|
37
|
+
reportInvocation(recorder, {
|
|
38
|
+
tool: tool.name,
|
|
39
|
+
method: "tools/call",
|
|
40
|
+
outcome: "handler_error",
|
|
41
|
+
durationMs: nowMs() - start
|
|
42
|
+
});
|
|
32
43
|
return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
|
|
33
44
|
}
|
|
34
|
-
recorder
|
|
45
|
+
reportInvocation(recorder, {
|
|
35
46
|
tool: tool.name,
|
|
36
47
|
method: "tools/call",
|
|
37
48
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
oauthConfigurationErrorResponse,
|
|
8
8
|
resolveProtectedResource,
|
|
9
9
|
withCors
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6TZNGFRM.js";
|
|
11
11
|
import {
|
|
12
12
|
describeError,
|
|
13
13
|
log
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VQQZAFRA.js";
|
|
15
15
|
|
|
16
16
|
// src/protocols/oauth-metadata.ts
|
|
17
17
|
function notFound() {
|
|
@@ -3,19 +3,29 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
|
3
3
|
function isLogLevel(value) {
|
|
4
4
|
return typeof value === "string" && value in LEVEL_RANK;
|
|
5
5
|
}
|
|
6
|
+
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
6
7
|
function readEnvLevel() {
|
|
7
8
|
try {
|
|
8
|
-
const raw = typeof process !== "undefined" ? process.env?.[
|
|
9
|
+
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
9
10
|
const normalized = raw?.trim().toLowerCase();
|
|
10
11
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
11
12
|
} catch {
|
|
12
13
|
return void 0;
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
|
-
var currentLevel = readEnvLevel() ?? "
|
|
16
|
+
var currentLevel = readEnvLevel() ?? "debug";
|
|
16
17
|
function setLogLevel(level) {
|
|
17
18
|
currentLevel = level;
|
|
18
19
|
}
|
|
20
|
+
function parseLogLevel(raw) {
|
|
21
|
+
const normalized = raw?.trim().toLowerCase();
|
|
22
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
23
|
+
}
|
|
24
|
+
function applyLogLevelFromEnv(raw) {
|
|
25
|
+
const level = parseLogLevel(raw);
|
|
26
|
+
if (level)
|
|
27
|
+
setLogLevel(level);
|
|
28
|
+
}
|
|
19
29
|
function enabled(level) {
|
|
20
30
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
21
31
|
}
|
|
@@ -115,7 +125,9 @@ export {
|
|
|
115
125
|
resolveMetricsConfig,
|
|
116
126
|
trimTrailingSlash,
|
|
117
127
|
parseSafeUrl,
|
|
128
|
+
LOG_LEVEL_ENV_VAR,
|
|
118
129
|
setLogLevel,
|
|
130
|
+
applyLogLevelFromEnv,
|
|
119
131
|
log,
|
|
120
132
|
describeError
|
|
121
133
|
};
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
createRequestAuthorizer,
|
|
10
10
|
methodNotAllowed,
|
|
11
11
|
nowMs,
|
|
12
|
+
reportInvocation,
|
|
12
13
|
withCors
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-6TZNGFRM.js";
|
|
14
15
|
|
|
15
16
|
// src/protocols/rest/invoke-tool.ts
|
|
16
17
|
import { getParseErrorMessage, objectFromShape, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
@@ -86,20 +87,30 @@ function createInvokeToolHandler(mcp, options = {}, recorder = createRecorderFor
|
|
|
86
87
|
try {
|
|
87
88
|
result = await tool.handler(args, new ToolContext(authResult.auth));
|
|
88
89
|
} catch {
|
|
89
|
-
recorder
|
|
90
|
+
reportInvocation(recorder, {
|
|
91
|
+
tool: tool.name,
|
|
92
|
+
method: "tools/call",
|
|
93
|
+
outcome: "handler_error",
|
|
94
|
+
durationMs: nowMs() - start
|
|
95
|
+
});
|
|
90
96
|
return new Response(JSON.stringify({ error: "handler threw", tool: toolName }), {
|
|
91
97
|
status: 500,
|
|
92
98
|
headers: JSON_HEADERS
|
|
93
99
|
});
|
|
94
100
|
}
|
|
95
101
|
if (result == null) {
|
|
96
|
-
recorder
|
|
102
|
+
reportInvocation(recorder, {
|
|
103
|
+
tool: tool.name,
|
|
104
|
+
method: "tools/call",
|
|
105
|
+
outcome: "handler_error",
|
|
106
|
+
durationMs: nowMs() - start
|
|
107
|
+
});
|
|
97
108
|
return new Response(JSON.stringify({ error: `tool "${toolName}" returned no result` }), {
|
|
98
109
|
status: 500,
|
|
99
110
|
headers: JSON_HEADERS
|
|
100
111
|
});
|
|
101
112
|
}
|
|
102
|
-
recorder
|
|
113
|
+
reportInvocation(recorder, {
|
|
103
114
|
tool: tool.name,
|
|
104
115
|
method: "tools/call",
|
|
105
116
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -13,7 +13,7 @@ function isFileMissing(err) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// package.json
|
|
16
|
-
var version = "0.
|
|
16
|
+
var version = "0.17.0";
|
|
17
17
|
|
|
18
18
|
// src/protocols/rest/list-tools.ts
|
|
19
19
|
var import_zod_compat = require("@modelcontextprotocol/sdk/server/zod-compat.js");
|
|
@@ -24,16 +24,17 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
|
24
24
|
function isLogLevel(value) {
|
|
25
25
|
return typeof value === "string" && value in LEVEL_RANK;
|
|
26
26
|
}
|
|
27
|
+
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
27
28
|
function readEnvLevel() {
|
|
28
29
|
try {
|
|
29
|
-
const raw = typeof process !== "undefined" ? process.env?.[
|
|
30
|
+
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
30
31
|
const normalized = raw?.trim().toLowerCase();
|
|
31
32
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
32
33
|
} catch {
|
|
33
34
|
return void 0;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
|
-
var currentLevel = readEnvLevel() ?? "
|
|
37
|
+
var currentLevel = readEnvLevel() ?? "debug";
|
|
37
38
|
|
|
38
39
|
// src/auth/verifier.ts
|
|
39
40
|
var import_jose = require("jose");
|
|
@@ -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-FW6YL5C5.js";
|
|
5
|
+
import "../chunk-6TZNGFRM.js";
|
|
6
|
+
import "../chunk-VQQZAFRA.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-J5BTCP6T.js";
|
|
14
14
|
|
|
15
15
|
// src/manifest/io.ts
|
|
16
16
|
import { randomUUID } from "crypto";
|
package/dist/index.cjs
CHANGED
|
@@ -319,16 +319,17 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
|
319
319
|
function isLogLevel(value) {
|
|
320
320
|
return typeof value === "string" && value in LEVEL_RANK;
|
|
321
321
|
}
|
|
322
|
+
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
322
323
|
function readEnvLevel() {
|
|
323
324
|
try {
|
|
324
|
-
const raw = typeof process !== "undefined" ? process.env?.[
|
|
325
|
+
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
325
326
|
const normalized = raw?.trim().toLowerCase();
|
|
326
327
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
327
328
|
} catch {
|
|
328
329
|
return void 0;
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
|
-
var currentLevel = readEnvLevel() ?? "
|
|
332
|
+
var currentLevel = readEnvLevel() ?? "debug";
|
|
332
333
|
function setLogLevel(level) {
|
|
333
334
|
currentLevel = level;
|
|
334
335
|
}
|
package/dist/index.js
CHANGED
|
@@ -36,16 +36,29 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
|
36
36
|
function isLogLevel(value) {
|
|
37
37
|
return typeof value === "string" && value in LEVEL_RANK;
|
|
38
38
|
}
|
|
39
|
+
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
39
40
|
function readEnvLevel() {
|
|
40
41
|
try {
|
|
41
|
-
const raw = typeof process !== "undefined" ? process.env?.[
|
|
42
|
+
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
42
43
|
const normalized = raw?.trim().toLowerCase();
|
|
43
44
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
44
45
|
} catch {
|
|
45
46
|
return void 0;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
var currentLevel = readEnvLevel() ?? "
|
|
49
|
+
var currentLevel = readEnvLevel() ?? "debug";
|
|
50
|
+
function setLogLevel(level) {
|
|
51
|
+
currentLevel = level;
|
|
52
|
+
}
|
|
53
|
+
function parseLogLevel(raw) {
|
|
54
|
+
const normalized = raw?.trim().toLowerCase();
|
|
55
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
56
|
+
}
|
|
57
|
+
function applyLogLevelFromEnv(raw) {
|
|
58
|
+
const level = parseLogLevel(raw);
|
|
59
|
+
if (level)
|
|
60
|
+
setLogLevel(level);
|
|
61
|
+
}
|
|
49
62
|
function enabled(level) {
|
|
50
63
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
51
64
|
}
|
|
@@ -105,7 +118,7 @@ function resolveMetricsConfig(config = true) {
|
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
// package.json
|
|
108
|
-
var version = "0.
|
|
121
|
+
var version = "0.17.0";
|
|
109
122
|
|
|
110
123
|
// src/metrics/otlp.ts
|
|
111
124
|
var SCOPE_NAME = "@lovable.dev/mcp-js";
|
|
@@ -170,6 +183,10 @@ function nowUnixNano() {
|
|
|
170
183
|
function nowMs() {
|
|
171
184
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
172
185
|
}
|
|
186
|
+
function reportInvocation(recorder, ev) {
|
|
187
|
+
log.info("tool.invoked", { tool: ev.tool, method: ev.method, outcome: ev.outcome, durationMs: ev.durationMs });
|
|
188
|
+
recorder.record(ev);
|
|
189
|
+
}
|
|
173
190
|
var NOOP_RECORDER = {
|
|
174
191
|
record() {
|
|
175
192
|
},
|
|
@@ -197,51 +214,92 @@ function readRuntimeEnv(name) {
|
|
|
197
214
|
}
|
|
198
215
|
return void 0;
|
|
199
216
|
}
|
|
217
|
+
var cloudflareEnvPromise;
|
|
218
|
+
async function readCloudflareEnv(name) {
|
|
219
|
+
try {
|
|
220
|
+
const moduleSpecifier = "cloudflare:workers";
|
|
221
|
+
cloudflareEnvPromise ??= import(
|
|
222
|
+
/* @vite-ignore */
|
|
223
|
+
moduleSpecifier
|
|
224
|
+
).then((m) => m.env).catch(() => void 0);
|
|
225
|
+
const env = await cloudflareEnvPromise;
|
|
226
|
+
const value = env?.[name];
|
|
227
|
+
return typeof value === "string" && value ? value : void 0;
|
|
228
|
+
} catch {
|
|
229
|
+
return void 0;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
200
232
|
function probeWaitUntil() {
|
|
201
233
|
const fn = globalThis.EdgeRuntime?.waitUntil;
|
|
202
234
|
return typeof fn === "function" ? fn.bind(globalThis.EdgeRuntime) : void 0;
|
|
203
235
|
}
|
|
204
236
|
function createMetricsRecorder(ctx, deps = {}) {
|
|
205
237
|
const { config, server } = ctx;
|
|
238
|
+
applyLogLevelFromEnv(readRuntimeEnv(LOG_LEVEL_ENV_VAR));
|
|
206
239
|
if (!config.enabled)
|
|
207
240
|
return NOOP_RECORDER;
|
|
208
241
|
const doFetch = deps.fetch ?? globalThis.fetch;
|
|
209
|
-
if (!doFetch)
|
|
210
|
-
|
|
211
|
-
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
212
|
-
const apiKey = usesLovableEndpoint ? (deps.getApiKey ?? (() => readRuntimeEnv(config.apiKeyEnvVar)))() : void 0;
|
|
213
|
-
if (usesLovableEndpoint && !apiKey) {
|
|
214
|
-
log.debug("metrics.disabled_no_api_key", { envVar: config.apiKeyEnvVar });
|
|
242
|
+
if (!doFetch) {
|
|
243
|
+
log.warn("metrics.disabled_no_fetch", { endpoint: config.endpoint });
|
|
215
244
|
return NOOP_RECORDER;
|
|
216
245
|
}
|
|
246
|
+
const usesLovableEndpoint = config.endpoint === DEFAULT_METRICS_ENDPOINT;
|
|
217
247
|
const waitUntil = deps.waitUntil ?? probeWaitUntil();
|
|
218
248
|
const buffer = [];
|
|
219
249
|
let timer;
|
|
250
|
+
let lazyLogLevelApplied = false;
|
|
251
|
+
const applyLazyLogLevel = async () => {
|
|
252
|
+
if (lazyLogLevelApplied)
|
|
253
|
+
return;
|
|
254
|
+
lazyLogLevelApplied = true;
|
|
255
|
+
applyLogLevelFromEnv(await readCloudflareEnv(LOG_LEVEL_ENV_VAR));
|
|
256
|
+
};
|
|
257
|
+
let apiKey;
|
|
258
|
+
const resolveApiKey = async () => {
|
|
259
|
+
if (apiKey)
|
|
260
|
+
return apiKey;
|
|
261
|
+
apiKey = (deps.getApiKey ? deps.getApiKey() : readRuntimeEnv(config.apiKeyEnvVar)) ?? await readCloudflareEnv(config.apiKeyEnvVar);
|
|
262
|
+
return apiKey;
|
|
263
|
+
};
|
|
220
264
|
const schedule = (p) => {
|
|
221
265
|
if (waitUntil) {
|
|
222
266
|
try {
|
|
223
267
|
waitUntil(p);
|
|
224
268
|
return;
|
|
225
|
-
} catch {
|
|
269
|
+
} catch (err) {
|
|
270
|
+
log.warn("metrics.wait_until_failed", describeError(err));
|
|
226
271
|
}
|
|
227
272
|
}
|
|
228
273
|
void p.catch(() => {
|
|
229
274
|
});
|
|
230
275
|
};
|
|
231
|
-
const
|
|
276
|
+
const baseHeaders = {};
|
|
232
277
|
if (!usesLovableEndpoint) {
|
|
233
278
|
for (const [key, value] of Object.entries(config.headers)) {
|
|
234
279
|
if (key.toLowerCase() !== "content-type")
|
|
235
|
-
|
|
280
|
+
baseHeaders[key] = value;
|
|
236
281
|
}
|
|
237
282
|
}
|
|
238
|
-
|
|
239
|
-
if (apiKey)
|
|
240
|
-
headers.authorization = `Bearer ${apiKey}`;
|
|
283
|
+
baseHeaders["content-type"] = "application/json";
|
|
241
284
|
const flush = async () => {
|
|
285
|
+
void applyLazyLogLevel();
|
|
242
286
|
if (buffer.length === 0)
|
|
243
287
|
return;
|
|
244
288
|
const records = buffer.splice(0, buffer.length);
|
|
289
|
+
const dropped = records.length;
|
|
290
|
+
const headers = { ...baseHeaders };
|
|
291
|
+
if (usesLovableEndpoint) {
|
|
292
|
+
const key = await resolveApiKey();
|
|
293
|
+
if (!key) {
|
|
294
|
+
log.warn("metrics.disabled_no_api_key", {
|
|
295
|
+
envVar: config.apiKeyEnvVar,
|
|
296
|
+
endpoint: config.endpoint,
|
|
297
|
+
dropped
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
headers.authorization = `Bearer ${key}`;
|
|
302
|
+
}
|
|
245
303
|
try {
|
|
246
304
|
const res = await doFetch(config.endpoint, {
|
|
247
305
|
method: "POST",
|
|
@@ -249,10 +307,11 @@ function createMetricsRecorder(ctx, deps = {}) {
|
|
|
249
307
|
body: buildLogsPayload(records, server),
|
|
250
308
|
keepalive: true
|
|
251
309
|
});
|
|
252
|
-
if (!res.ok)
|
|
253
|
-
log.
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
log.warn("metrics.flush_rejected", { status: res.status, dropped, endpoint: config.endpoint });
|
|
312
|
+
}
|
|
254
313
|
} catch (err) {
|
|
255
|
-
log.
|
|
314
|
+
log.warn("metrics.flush_failed", { ...describeError(err), dropped, endpoint: config.endpoint });
|
|
256
315
|
}
|
|
257
316
|
};
|
|
258
317
|
const ensureTimer = () => {
|
|
@@ -830,14 +889,24 @@ function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
|
830
889
|
try {
|
|
831
890
|
result = await tool.handler(args, new ToolContext(auth));
|
|
832
891
|
} catch {
|
|
833
|
-
recorder
|
|
892
|
+
reportInvocation(recorder, {
|
|
893
|
+
tool: tool.name,
|
|
894
|
+
method: "tools/call",
|
|
895
|
+
outcome: "handler_error",
|
|
896
|
+
durationMs: nowMs() - start
|
|
897
|
+
});
|
|
834
898
|
return { content: [{ type: "text", text: "tool execution failed" }], isError: true };
|
|
835
899
|
}
|
|
836
900
|
if (result == null) {
|
|
837
|
-
recorder
|
|
901
|
+
reportInvocation(recorder, {
|
|
902
|
+
tool: tool.name,
|
|
903
|
+
method: "tools/call",
|
|
904
|
+
outcome: "handler_error",
|
|
905
|
+
durationMs: nowMs() - start
|
|
906
|
+
});
|
|
838
907
|
return { content: [{ type: "text", text: `tool "${tool.name}" returned no result` }], isError: true };
|
|
839
908
|
}
|
|
840
|
-
recorder
|
|
909
|
+
reportInvocation(recorder, {
|
|
841
910
|
tool: tool.name,
|
|
842
911
|
method: "tools/call",
|
|
843
912
|
outcome: result.isError ? "tool_error" : "ok",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMcpProtocolHandler
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-EL6YNP2R.js";
|
|
4
4
|
import "../../chunk-MA5H6PSF.js";
|
|
5
|
-
import "../../chunk-
|
|
6
|
-
import "../../chunk-
|
|
5
|
+
import "../../chunk-6TZNGFRM.js";
|
|
6
|
+
import "../../chunk-VQQZAFRA.js";
|
|
7
7
|
import "../../chunk-6DXGZZA4.js";
|
|
8
|
-
import "../../chunk-
|
|
8
|
+
import "../../chunk-J5BTCP6T.js";
|
|
9
9
|
export {
|
|
10
10
|
createMcpProtocolHandler
|
|
11
11
|
};
|
|
@@ -41,16 +41,17 @@ var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
|
41
41
|
function isLogLevel(value) {
|
|
42
42
|
return typeof value === "string" && value in LEVEL_RANK;
|
|
43
43
|
}
|
|
44
|
+
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
44
45
|
function readEnvLevel() {
|
|
45
46
|
try {
|
|
46
|
-
const raw = typeof process !== "undefined" ? process.env?.[
|
|
47
|
+
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
47
48
|
const normalized = raw?.trim().toLowerCase();
|
|
48
49
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
49
50
|
} catch {
|
|
50
51
|
return void 0;
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
|
-
var currentLevel = readEnvLevel() ?? "
|
|
54
|
+
var currentLevel = readEnvLevel() ?? "debug";
|
|
54
55
|
function enabled(level) {
|
|
55
56
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
56
57
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createOAuthProtectedResourceMetadataHandler
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-H2HXKAMQ.js";
|
|
4
|
+
import "../chunk-6TZNGFRM.js";
|
|
5
|
+
import "../chunk-VQQZAFRA.js";
|
|
6
6
|
import "../chunk-6DXGZZA4.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-J5BTCP6T.js";
|
|
8
8
|
export {
|
|
9
9
|
createOAuthProtectedResourceMetadataHandler
|
|
10
10
|
};
|