@lovable.dev/mcp-js 0.6.0 → 0.7.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/README.md +21 -0
- package/dist/{chunk-SYI32IRK.js → chunk-53M2X7FU.js} +1 -1
- package/dist/{chunk-K2T4WTKX.js → chunk-722HKLIU.js} +7 -2
- package/dist/{chunk-NJ5WBRYI.js → chunk-G5GWSV5D.js} +7 -2
- package/dist/{chunk-ZKKLOL2C.js → chunk-NTXHOUK6.js} +74 -16
- package/dist/chunk-QC3DXQTH.js +87 -0
- package/dist/index.cjs +23 -2
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +5 -3
- package/dist/protocols/mcp/index.cjs +113 -15
- package/dist/protocols/mcp/index.js +3 -3
- package/dist/protocols/oauth-metadata.cjs +107 -14
- package/dist/protocols/oauth-metadata.js +3 -3
- package/dist/protocols/rest/index.cjs +111 -14
- package/dist/protocols/rest/index.js +3 -3
- package/dist/stacks/tanstack/index.cjs +115 -16
- package/dist/stacks/tanstack/index.js +5 -5
- package/package.json +1 -1
- package/dist/chunk-QA3FWDUV.js +0 -40
package/README.md
CHANGED
|
@@ -182,6 +182,27 @@ The request-derived `resource` default trusts the incoming `Host` to name this s
|
|
|
182
182
|
|
|
183
183
|
Use `auth.oauth.issuer(...)`, set `resource` or `acceptedAudiences` to anchor the accepted audience, and optionally set `jwksUri` and `requiredScopes`. For Supabase project auth, set `acceptedAudiences: "authenticated"`, keep app/business checks in app code, and forward `ctx.getToken()` to Supabase for RLS-backed data access.
|
|
184
184
|
|
|
185
|
+
## Debug logging
|
|
186
|
+
|
|
187
|
+
The runtime is silent by default. Raise the log level to trace OAuth discovery, JWKS resolution, and token verification when a deployed MCP server returns `401`/`500` and you need to see why. Logs go to `console` (so they land in your platform's function logs) and **never include the bearer token, full claims, or PII** — only non-secret fields such as the JWT header `alg`/`kid`, `issuer`, `jwks_uri`, the token `sub`/`client_id`/`scopes`, and `jose` error codes.
|
|
188
|
+
|
|
189
|
+
Enable it either way:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// Programmatic — works on every runtime, including Workers that pass env via bindings.
|
|
193
|
+
import { setLogLevel } from "@lovable.dev/mcp-js";
|
|
194
|
+
setLogLevel("debug"); // "silent" | "error" | "warn" | "info" | "debug"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Or via env var, read once at startup. Node/Deno only: it reads `process.env`,
|
|
199
|
+
# which is absent on a deployed Cloudflare Worker (env arrives via bindings), so
|
|
200
|
+
# the var is silently ignored there — use `setLogLevel()` on Workers.
|
|
201
|
+
LOVABLE_MCP_LOG_LEVEL=debug
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A `500` on an OAuth-protected route is always one of two causes, logged at `error`: an `OAuthConfigurationError` from issuer-metadata discovery or a JWKS *fetch* failure (`oauth.discovery.config_error` / `oauth.jwks.fetch_failed` → `auth.config_error`), or a transport-level fault in the MCP handler (`mcp.transport_error`). Auth outcomes are logged at `info`: a granted request as `oauth.verify.ok`, and a `401`/`403` as `auth.token_rejected` (with the `jose` reason) or `auth.no_bearer_token` — so a request rejected for insufficient scope shows both `oauth.verify.ok` and the `auth.token_rejected` that follows it.
|
|
205
|
+
|
|
185
206
|
## Subpath exports
|
|
186
207
|
|
|
187
208
|
| Subpath | Contents |
|
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
oauthConfigurationErrorResponse,
|
|
8
8
|
resolveProtectedResource,
|
|
9
9
|
withCors
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-NTXHOUK6.js";
|
|
11
|
+
import {
|
|
12
|
+
describeError,
|
|
13
|
+
log
|
|
14
|
+
} from "./chunk-QC3DXQTH.js";
|
|
11
15
|
|
|
12
16
|
// src/protocols/oauth-metadata.ts
|
|
13
17
|
function notFound() {
|
|
@@ -62,7 +66,8 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
|
62
66
|
);
|
|
63
67
|
const response = withCors(Response.json(metadata, { headers }));
|
|
64
68
|
return request.method === "HEAD" ? headResponse(response) : response;
|
|
65
|
-
} catch {
|
|
69
|
+
} catch (err) {
|
|
70
|
+
log.error("oauth.metadata.config_error", { ...describeError(err), outcome: "500 oauth configuration error" });
|
|
66
71
|
const response = withCors(oauthConfigurationErrorResponse());
|
|
67
72
|
return request.method === "HEAD" ? headResponse(response) : response;
|
|
68
73
|
}
|
|
@@ -5,7 +5,11 @@ import {
|
|
|
5
5
|
corsPreflightResponse,
|
|
6
6
|
createRequestAuthorizer,
|
|
7
7
|
withCors
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NTXHOUK6.js";
|
|
9
|
+
import {
|
|
10
|
+
describeError,
|
|
11
|
+
log
|
|
12
|
+
} from "./chunk-QC3DXQTH.js";
|
|
9
13
|
|
|
10
14
|
// src/protocols/mcp/protocol.ts
|
|
11
15
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -54,7 +58,8 @@ function createMcpProtocolHandler(mcp, options = {}) {
|
|
|
54
58
|
});
|
|
55
59
|
await server.connect(transport);
|
|
56
60
|
return await transport.handleRequest(request);
|
|
57
|
-
} catch {
|
|
61
|
+
} catch (err) {
|
|
62
|
+
log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
|
|
58
63
|
return Response.json(
|
|
59
64
|
{ jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
|
|
60
65
|
{ status: 500 }
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
describeError,
|
|
3
|
+
log,
|
|
2
4
|
parseSafeUrl,
|
|
3
5
|
trimTrailingSlash
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QC3DXQTH.js";
|
|
5
7
|
import {
|
|
6
8
|
OAUTH_PROTECTED_RESOURCE_METADATA_PATH
|
|
7
9
|
} from "./chunk-6DXGZZA4.js";
|
|
@@ -43,16 +45,29 @@ function resolveResourcePath(resourcePath, request) {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
// src/core/promise.ts
|
|
46
|
-
function cachedPromise(load) {
|
|
48
|
+
function cachedPromise(load, label) {
|
|
47
49
|
let settled = false;
|
|
48
50
|
let value;
|
|
49
51
|
return async () => {
|
|
50
|
-
if (settled)
|
|
52
|
+
if (settled) {
|
|
53
|
+
if (label)
|
|
54
|
+
log.debug(`${label}.cache_hit`);
|
|
51
55
|
return value;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
}
|
|
57
|
+
if (label)
|
|
58
|
+
log.debug(`${label}.load_start`);
|
|
59
|
+
try {
|
|
60
|
+
const loaded = await load();
|
|
61
|
+
settled = true;
|
|
62
|
+
value = loaded;
|
|
63
|
+
if (label)
|
|
64
|
+
log.debug(`${label}.settled`);
|
|
65
|
+
return loaded;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (label)
|
|
68
|
+
log.debug(`${label}.load_failed`, describeError(err));
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
56
71
|
};
|
|
57
72
|
}
|
|
58
73
|
|
|
@@ -92,13 +107,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
|
|
|
92
107
|
try {
|
|
93
108
|
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
94
109
|
} catch (err) {
|
|
110
|
+
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
95
111
|
errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
96
112
|
}
|
|
97
113
|
}
|
|
114
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
|
|
98
115
|
throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
|
|
99
116
|
}
|
|
100
117
|
async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
101
|
-
|
|
118
|
+
log.debug("oauth.discovery.fetch", { url });
|
|
119
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
102
120
|
if (!response.ok) {
|
|
103
121
|
throw new Error(String(response.status));
|
|
104
122
|
}
|
|
@@ -108,6 +126,7 @@ async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
|
108
126
|
}
|
|
109
127
|
parseSafeUrl("discovered issuer", json.issuer);
|
|
110
128
|
if (trimTrailingSlash(json.issuer) !== expectedIssuer) {
|
|
129
|
+
log.warn("oauth.discovery.issuer_mismatch", { url, expectedIssuer, published: json.issuer });
|
|
111
130
|
throw new Error("issuer mismatch");
|
|
112
131
|
}
|
|
113
132
|
if (typeof json.jwks_uri !== "string") {
|
|
@@ -120,6 +139,11 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
120
139
|
try {
|
|
121
140
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
122
141
|
} catch (err) {
|
|
142
|
+
log.error("oauth.discovery.config_error", {
|
|
143
|
+
issuer,
|
|
144
|
+
...describeError(err),
|
|
145
|
+
outcome: "500 oauth configuration error"
|
|
146
|
+
});
|
|
123
147
|
throw new OAuthConfigurationError(
|
|
124
148
|
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
125
149
|
);
|
|
@@ -127,15 +151,26 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
127
151
|
}
|
|
128
152
|
function createOAuthDiscoveryResolver(auth) {
|
|
129
153
|
const configuredIssuer = trimTrailingSlash(auth.issuer);
|
|
130
|
-
const oauthServerMetadata = cachedPromise(
|
|
154
|
+
const oauthServerMetadata = cachedPromise(
|
|
155
|
+
() => fetchIssuerOAuthServerMetadata(configuredIssuer),
|
|
156
|
+
"oauth.discovery.metadata"
|
|
157
|
+
);
|
|
131
158
|
return {
|
|
132
159
|
resolveIssuer: async () => configuredIssuer,
|
|
133
|
-
resolveJwksUri: async () =>
|
|
160
|
+
resolveJwksUri: async () => {
|
|
161
|
+
if (auth.jwksUri) {
|
|
162
|
+
log.debug("oauth.jwks.resolved", { jwksUri: auth.jwksUri, source: "configured" });
|
|
163
|
+
return auth.jwksUri;
|
|
164
|
+
}
|
|
165
|
+
const jwksUri = (await oauthServerMetadata()).jwks_uri;
|
|
166
|
+
log.debug("oauth.jwks.resolved", { jwksUri, source: "discovered" });
|
|
167
|
+
return jwksUri;
|
|
168
|
+
}
|
|
134
169
|
};
|
|
135
170
|
}
|
|
136
171
|
|
|
137
172
|
// src/auth/verifier.ts
|
|
138
|
-
import { createRemoteJWKSet, errors as joseErrors, jwtVerify } from "jose";
|
|
173
|
+
import { createRemoteJWKSet, decodeProtectedHeader, errors as joseErrors, jwtVerify } from "jose";
|
|
139
174
|
|
|
140
175
|
// src/auth/claims.ts
|
|
141
176
|
function readString(value) {
|
|
@@ -166,6 +201,14 @@ var OAuthTokenError = class extends Error {
|
|
|
166
201
|
function resolveAcceptedAudiences(auth, resource) {
|
|
167
202
|
return auth.acceptedAudiences ?? [resource];
|
|
168
203
|
}
|
|
204
|
+
function tokenHeaderFields(token) {
|
|
205
|
+
try {
|
|
206
|
+
const header = decodeProtectedHeader(token);
|
|
207
|
+
return { jwtAlg: header.alg, jwtKid: header.kid, tokenLength: token.length };
|
|
208
|
+
} catch {
|
|
209
|
+
return { tokenLength: token.length };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
169
212
|
function isJwksFetchFailure(err) {
|
|
170
213
|
if (err instanceof joseErrors.JWKSTimeout || err instanceof joseErrors.JWKSInvalid)
|
|
171
214
|
return true;
|
|
@@ -187,19 +230,23 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
|
|
|
187
230
|
return payload;
|
|
188
231
|
} catch (err) {
|
|
189
232
|
if (isJwksFetchFailure(err)) {
|
|
233
|
+
log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
|
|
190
234
|
throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
191
235
|
}
|
|
236
|
+
log.debug("oauth.verify.rejected", { ...describeError(err), outcome: "401 invalid_token" });
|
|
192
237
|
throw err;
|
|
193
238
|
}
|
|
194
239
|
}
|
|
195
240
|
function assertNonEmptySubject(claims) {
|
|
196
241
|
const sub = claims["sub"];
|
|
197
242
|
if (typeof sub !== "string" || sub.trim() === "") {
|
|
243
|
+
log.debug("oauth.verify.bad_subject", { subType: typeof sub });
|
|
198
244
|
throw new OAuthTokenError(401, "invalid_token", "token subject claim must be a non-empty string");
|
|
199
245
|
}
|
|
200
246
|
}
|
|
201
247
|
function assertOAuthClientClaim(auth, clientId) {
|
|
202
248
|
if (auth.requireOAuthClientClaim !== false && !clientId) {
|
|
249
|
+
log.debug("oauth.verify.missing_client_claim", { outcome: "401 invalid_token" });
|
|
203
250
|
throw new OAuthTokenError(401, "invalid_token", "OAuth client claim is required");
|
|
204
251
|
}
|
|
205
252
|
}
|
|
@@ -224,17 +271,23 @@ function buildMcpAuthContext(args) {
|
|
|
224
271
|
};
|
|
225
272
|
}
|
|
226
273
|
function createOAuthTokenVerifier(auth, discovery) {
|
|
227
|
-
const loadRemoteJwksKeySet = cachedPromise(
|
|
228
|
-
() => discovery.resolveJwksUri().then((jwksURI) => createRemoteJWKSet(new URL(jwksURI)))
|
|
229
|
-
);
|
|
230
274
|
return async (token, request, options) => {
|
|
231
275
|
const resource = resolveProtectedResource(auth, request, options);
|
|
232
276
|
const issuer = await discovery.resolveIssuer();
|
|
233
277
|
const acceptedAudiences = resolveAcceptedAudiences(auth, resource);
|
|
234
|
-
|
|
278
|
+
log.debug("oauth.verify.start", { issuer, acceptedAudiences, resource, ...tokenHeaderFields(token) });
|
|
279
|
+
const jwksUri = await discovery.resolveJwksUri();
|
|
280
|
+
log.debug("oauth.jwks.keyset_created", { jwksUri });
|
|
281
|
+
const keySet = createRemoteJWKSet(new URL(jwksUri));
|
|
282
|
+
const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
|
|
235
283
|
assertNonEmptySubject(claims);
|
|
236
284
|
const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
|
|
237
285
|
assertOAuthClientClaim(auth, context.principal.clientId);
|
|
286
|
+
log.info("oauth.verify.ok", {
|
|
287
|
+
sub: context.principal.sub,
|
|
288
|
+
clientId: context.principal.clientId,
|
|
289
|
+
scopes: context.principal.scopes
|
|
290
|
+
});
|
|
238
291
|
return context;
|
|
239
292
|
};
|
|
240
293
|
}
|
|
@@ -334,22 +387,27 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
334
387
|
if (runtime.kind === "unconfigured")
|
|
335
388
|
return { ok: true };
|
|
336
389
|
const token = parseBearerToken(request);
|
|
337
|
-
if (!token)
|
|
390
|
+
if (!token) {
|
|
391
|
+
log.info("auth.no_bearer_token", { outcome: "401" });
|
|
338
392
|
return { ok: false, response: challengeResponse(runtime.auth, request, 401) };
|
|
393
|
+
}
|
|
339
394
|
try {
|
|
340
395
|
const auth = await runtime.verify(token, request, runtime.options);
|
|
341
396
|
assertRequiredScopes(runtime.auth, auth);
|
|
342
397
|
return { ok: true, auth };
|
|
343
398
|
} catch (err) {
|
|
344
399
|
if (err instanceof OAuthConfigurationError) {
|
|
400
|
+
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
345
401
|
return { ok: false, response: oauthConfigurationErrorResponse() };
|
|
346
402
|
}
|
|
347
403
|
if (err instanceof OAuthTokenError) {
|
|
404
|
+
log.info("auth.token_rejected", { status: err.status, oauthError: err.oauthError });
|
|
348
405
|
return {
|
|
349
406
|
ok: false,
|
|
350
407
|
response: challengeResponse(runtime.auth, request, err.status, err.oauthError, err.message)
|
|
351
408
|
};
|
|
352
409
|
}
|
|
410
|
+
log.error("auth.unexpected_error", { ...describeError(err), outcome: "401" });
|
|
353
411
|
return {
|
|
354
412
|
ok: false,
|
|
355
413
|
response: challengeResponse(runtime.auth, request, 401, "invalid_token", "Invalid access token")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/core/logger.ts
|
|
2
|
+
var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
3
|
+
function isLogLevel(value) {
|
|
4
|
+
return typeof value === "string" && value in LEVEL_RANK;
|
|
5
|
+
}
|
|
6
|
+
function readEnvLevel() {
|
|
7
|
+
try {
|
|
8
|
+
const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
|
|
9
|
+
const normalized = raw?.trim().toLowerCase();
|
|
10
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
11
|
+
} catch {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
var currentLevel = readEnvLevel() ?? "silent";
|
|
16
|
+
function setLogLevel(level) {
|
|
17
|
+
currentLevel = level;
|
|
18
|
+
}
|
|
19
|
+
function enabled(level) {
|
|
20
|
+
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
21
|
+
}
|
|
22
|
+
function emit(level, method, event, fields) {
|
|
23
|
+
if (!enabled(level))
|
|
24
|
+
return;
|
|
25
|
+
const message = `[mcp-js] ${event}`;
|
|
26
|
+
if (fields)
|
|
27
|
+
console[method](message, fields);
|
|
28
|
+
else
|
|
29
|
+
console[method](message);
|
|
30
|
+
}
|
|
31
|
+
var log = {
|
|
32
|
+
error: (event, fields) => emit("error", "error", event, fields),
|
|
33
|
+
warn: (event, fields) => emit("warn", "warn", event, fields),
|
|
34
|
+
info: (event, fields) => emit("info", "info", event, fields),
|
|
35
|
+
debug: (event, fields) => emit("debug", "debug", event, fields)
|
|
36
|
+
};
|
|
37
|
+
function describeError(err) {
|
|
38
|
+
if (err instanceof Error) {
|
|
39
|
+
const code = err.code;
|
|
40
|
+
return { name: err.name, message: err.message, ...typeof code === "string" ? { code } : {} };
|
|
41
|
+
}
|
|
42
|
+
return { value: String(err) };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/core/url.ts
|
|
46
|
+
function trimTrailingSlash(value) {
|
|
47
|
+
return value.replace(/\/+$/, "");
|
|
48
|
+
}
|
|
49
|
+
function isLocalHTTPHost(hostname) {
|
|
50
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
51
|
+
}
|
|
52
|
+
function urlSafetyProblem(url) {
|
|
53
|
+
const isAllowedHTTP = url.protocol === "http:" && isLocalHTTPHost(url.hostname);
|
|
54
|
+
if (url.protocol !== "https:" && !isAllowedHTTP) {
|
|
55
|
+
return "must use https://, except localhost development URLs";
|
|
56
|
+
}
|
|
57
|
+
if (url.username || url.password) {
|
|
58
|
+
return "must not include credentials";
|
|
59
|
+
}
|
|
60
|
+
if (url.search || url.hash) {
|
|
61
|
+
return "must not include query or fragment";
|
|
62
|
+
}
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/core/validation.ts
|
|
67
|
+
function parseSafeUrl(subject, raw, ErrorClass = Error) {
|
|
68
|
+
let url;
|
|
69
|
+
try {
|
|
70
|
+
url = new URL(raw);
|
|
71
|
+
} catch {
|
|
72
|
+
throw new ErrorClass(`${subject} must be an absolute URL`);
|
|
73
|
+
}
|
|
74
|
+
const problem = urlSafetyProblem(url);
|
|
75
|
+
if (problem) {
|
|
76
|
+
throw new ErrorClass(`${subject} ${problem}`);
|
|
77
|
+
}
|
|
78
|
+
return url;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
trimTrailingSlash,
|
|
83
|
+
parseSafeUrl,
|
|
84
|
+
setLogLevel,
|
|
85
|
+
log,
|
|
86
|
+
describeError
|
|
87
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -23,7 +23,8 @@ __export(src_exports, {
|
|
|
23
23
|
ToolContext: () => ToolContext,
|
|
24
24
|
auth: () => auth,
|
|
25
25
|
defineMcp: () => defineMcp,
|
|
26
|
-
defineTool: () => defineTool
|
|
26
|
+
defineTool: () => defineTool,
|
|
27
|
+
setLogLevel: () => setLogLevel
|
|
27
28
|
});
|
|
28
29
|
module.exports = __toCommonJS(src_exports);
|
|
29
30
|
|
|
@@ -262,10 +263,30 @@ var ToolContext = class {
|
|
|
262
263
|
return this.#auth?.principal.claims;
|
|
263
264
|
}
|
|
264
265
|
};
|
|
266
|
+
|
|
267
|
+
// src/core/logger.ts
|
|
268
|
+
var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
269
|
+
function isLogLevel(value) {
|
|
270
|
+
return typeof value === "string" && value in LEVEL_RANK;
|
|
271
|
+
}
|
|
272
|
+
function readEnvLevel() {
|
|
273
|
+
try {
|
|
274
|
+
const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
|
|
275
|
+
const normalized = raw?.trim().toLowerCase();
|
|
276
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
277
|
+
} catch {
|
|
278
|
+
return void 0;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
var currentLevel = readEnvLevel() ?? "silent";
|
|
282
|
+
function setLogLevel(level) {
|
|
283
|
+
currentLevel = level;
|
|
284
|
+
}
|
|
265
285
|
// Annotate the CommonJS export names for ESM import in node:
|
|
266
286
|
0 && (module.exports = {
|
|
267
287
|
ToolContext,
|
|
268
288
|
auth,
|
|
269
289
|
defineMcp,
|
|
270
|
-
defineTool
|
|
290
|
+
defineTool,
|
|
291
|
+
setLogLevel
|
|
271
292
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -53,4 +53,12 @@ declare const auth: Readonly<{
|
|
|
53
53
|
}>;
|
|
54
54
|
}>;
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
|
|
57
|
+
/**
|
|
58
|
+
* Set the runtime log level programmatically. Use this when the deployment can't
|
|
59
|
+
* surface `LOVABLE_MCP_LOG_LEVEL` to the package (e.g. a Cloudflare Worker that
|
|
60
|
+
* passes vars via bindings): call `setLogLevel("debug")` once at server startup.
|
|
61
|
+
*/
|
|
62
|
+
declare function setLogLevel(level: LogLevel): void;
|
|
63
|
+
|
|
64
|
+
export { type IssuerOAuthOptions, type LogLevel, McpDefinitionInput, ToolDefinition, ZodRawShape, auth, defineMcp, defineTool, setLogLevel };
|
package/dist/index.d.ts
CHANGED
|
@@ -53,4 +53,12 @@ declare const auth: Readonly<{
|
|
|
53
53
|
}>;
|
|
54
54
|
}>;
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
|
|
57
|
+
/**
|
|
58
|
+
* Set the runtime log level programmatically. Use this when the deployment can't
|
|
59
|
+
* surface `LOVABLE_MCP_LOG_LEVEL` to the package (e.g. a Cloudflare Worker that
|
|
60
|
+
* passes vars via bindings): call `setLogLevel("debug")` once at server startup.
|
|
61
|
+
*/
|
|
62
|
+
declare function setLogLevel(level: LogLevel): void;
|
|
63
|
+
|
|
64
|
+
export { type IssuerOAuthOptions, type LogLevel, McpDefinitionInput, ToolDefinition, ZodRawShape, auth, defineMcp, defineTool, setLogLevel };
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
ToolContext
|
|
3
3
|
} from "./chunk-MA5H6PSF.js";
|
|
4
4
|
import {
|
|
5
|
-
parseSafeUrl
|
|
6
|
-
|
|
5
|
+
parseSafeUrl,
|
|
6
|
+
setLogLevel
|
|
7
|
+
} from "./chunk-QC3DXQTH.js";
|
|
7
8
|
|
|
8
9
|
// src/core/define.ts
|
|
9
10
|
function assertUniqueNames(mcp) {
|
|
@@ -168,5 +169,6 @@ export {
|
|
|
168
169
|
ToolContext,
|
|
169
170
|
auth,
|
|
170
171
|
defineMcp,
|
|
171
|
-
defineTool
|
|
172
|
+
defineTool,
|
|
173
|
+
setLogLevel
|
|
172
174
|
};
|