@lovable.dev/mcp-js 0.6.0 → 0.7.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/README.md +21 -0
- package/dist/{chunk-NJ5WBRYI.js → chunk-3XPKR4G6.js} +7 -2
- package/dist/{chunk-ZKKLOL2C.js → chunk-HRMLGCXV.js} +85 -25
- package/dist/{chunk-SYI32IRK.js → chunk-LMT6RXUF.js} +1 -1
- package/dist/chunk-QC3DXQTH.js +87 -0
- package/dist/{chunk-K2T4WTKX.js → chunk-W7F6JRDB.js} +7 -2
- 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 +124 -24
- package/dist/protocols/mcp/index.js +3 -3
- package/dist/protocols/oauth-metadata.cjs +118 -23
- package/dist/protocols/oauth-metadata.js +3 -3
- package/dist/protocols/rest/index.cjs +122 -23
- package/dist/protocols/rest/index.js +3 -3
- package/dist/stacks/tanstack/index.cjs +126 -25
- 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 |
|
|
@@ -5,7 +5,11 @@ import {
|
|
|
5
5
|
corsPreflightResponse,
|
|
6
6
|
createRequestAuthorizer,
|
|
7
7
|
withCors
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-HRMLGCXV.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 {
|
|
173
|
+
import { createLocalJWKSet, decodeProtectedHeader, jwtVerify } from "jose";
|
|
139
174
|
|
|
140
175
|
// src/auth/claims.ts
|
|
141
176
|
function readString(value) {
|
|
@@ -155,6 +190,7 @@ function stringClaim(claims, name) {
|
|
|
155
190
|
// src/auth/verifier.ts
|
|
156
191
|
var DEFAULT_JWT_ALGORITHMS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"];
|
|
157
192
|
var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
193
|
+
var JWKS_FETCH_TIMEOUT_MS = 5e3;
|
|
158
194
|
var OAuthTokenError = class extends Error {
|
|
159
195
|
constructor(status, oauthError, message) {
|
|
160
196
|
super(message);
|
|
@@ -166,12 +202,25 @@ var OAuthTokenError = class extends Error {
|
|
|
166
202
|
function resolveAcceptedAudiences(auth, resource) {
|
|
167
203
|
return auth.acceptedAudiences ?? [resource];
|
|
168
204
|
}
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
205
|
+
function tokenHeaderFields(token) {
|
|
206
|
+
try {
|
|
207
|
+
const header = decodeProtectedHeader(token);
|
|
208
|
+
return { jwtAlg: header.alg, jwtKid: header.kid, tokenLength: token.length };
|
|
209
|
+
} catch {
|
|
210
|
+
return { tokenLength: token.length };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function fetchVerificationKeySet(jwksUri) {
|
|
214
|
+
try {
|
|
215
|
+
const response = await fetch(jwksUri, { signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
216
|
+
if (!response.ok)
|
|
217
|
+
throw new Error(`JWKS endpoint returned ${response.status}`);
|
|
218
|
+
const json = await response.json();
|
|
219
|
+
return createLocalJWKSet(json);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
|
|
222
|
+
throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
223
|
+
}
|
|
175
224
|
}
|
|
176
225
|
async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
|
|
177
226
|
try {
|
|
@@ -186,20 +235,20 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
|
|
|
186
235
|
});
|
|
187
236
|
return payload;
|
|
188
237
|
} catch (err) {
|
|
189
|
-
|
|
190
|
-
throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
191
|
-
}
|
|
238
|
+
log.debug("oauth.verify.rejected", { ...describeError(err), outcome: "401 invalid_token" });
|
|
192
239
|
throw err;
|
|
193
240
|
}
|
|
194
241
|
}
|
|
195
242
|
function assertNonEmptySubject(claims) {
|
|
196
243
|
const sub = claims["sub"];
|
|
197
244
|
if (typeof sub !== "string" || sub.trim() === "") {
|
|
245
|
+
log.debug("oauth.verify.bad_subject", { subType: typeof sub });
|
|
198
246
|
throw new OAuthTokenError(401, "invalid_token", "token subject claim must be a non-empty string");
|
|
199
247
|
}
|
|
200
248
|
}
|
|
201
249
|
function assertOAuthClientClaim(auth, clientId) {
|
|
202
250
|
if (auth.requireOAuthClientClaim !== false && !clientId) {
|
|
251
|
+
log.debug("oauth.verify.missing_client_claim", { outcome: "401 invalid_token" });
|
|
203
252
|
throw new OAuthTokenError(401, "invalid_token", "OAuth client claim is required");
|
|
204
253
|
}
|
|
205
254
|
}
|
|
@@ -224,17 +273,23 @@ function buildMcpAuthContext(args) {
|
|
|
224
273
|
};
|
|
225
274
|
}
|
|
226
275
|
function createOAuthTokenVerifier(auth, discovery) {
|
|
227
|
-
const loadRemoteJwksKeySet = cachedPromise(
|
|
228
|
-
() => discovery.resolveJwksUri().then((jwksURI) => createRemoteJWKSet(new URL(jwksURI)))
|
|
229
|
-
);
|
|
230
276
|
return async (token, request, options) => {
|
|
231
277
|
const resource = resolveProtectedResource(auth, request, options);
|
|
232
278
|
const issuer = await discovery.resolveIssuer();
|
|
233
279
|
const acceptedAudiences = resolveAcceptedAudiences(auth, resource);
|
|
234
|
-
|
|
280
|
+
log.debug("oauth.verify.start", { issuer, acceptedAudiences, resource, ...tokenHeaderFields(token) });
|
|
281
|
+
const jwksUri = await discovery.resolveJwksUri();
|
|
282
|
+
log.debug("oauth.jwks.fetch", { jwksUri });
|
|
283
|
+
const keySet = await fetchVerificationKeySet(jwksUri);
|
|
284
|
+
const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
|
|
235
285
|
assertNonEmptySubject(claims);
|
|
236
286
|
const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
|
|
237
287
|
assertOAuthClientClaim(auth, context.principal.clientId);
|
|
288
|
+
log.info("oauth.verify.ok", {
|
|
289
|
+
sub: context.principal.sub,
|
|
290
|
+
clientId: context.principal.clientId,
|
|
291
|
+
scopes: context.principal.scopes
|
|
292
|
+
});
|
|
238
293
|
return context;
|
|
239
294
|
};
|
|
240
295
|
}
|
|
@@ -334,22 +389,27 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
334
389
|
if (runtime.kind === "unconfigured")
|
|
335
390
|
return { ok: true };
|
|
336
391
|
const token = parseBearerToken(request);
|
|
337
|
-
if (!token)
|
|
392
|
+
if (!token) {
|
|
393
|
+
log.info("auth.no_bearer_token", { outcome: "401" });
|
|
338
394
|
return { ok: false, response: challengeResponse(runtime.auth, request, 401) };
|
|
395
|
+
}
|
|
339
396
|
try {
|
|
340
397
|
const auth = await runtime.verify(token, request, runtime.options);
|
|
341
398
|
assertRequiredScopes(runtime.auth, auth);
|
|
342
399
|
return { ok: true, auth };
|
|
343
400
|
} catch (err) {
|
|
344
401
|
if (err instanceof OAuthConfigurationError) {
|
|
402
|
+
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
345
403
|
return { ok: false, response: oauthConfigurationErrorResponse() };
|
|
346
404
|
}
|
|
347
405
|
if (err instanceof OAuthTokenError) {
|
|
406
|
+
log.info("auth.token_rejected", { status: err.status, oauthError: err.oauthError });
|
|
348
407
|
return {
|
|
349
408
|
ok: false,
|
|
350
409
|
response: challengeResponse(runtime.auth, request, err.status, err.oauthError, err.message)
|
|
351
410
|
};
|
|
352
411
|
}
|
|
412
|
+
log.error("auth.unexpected_error", { ...describeError(err), outcome: "401" });
|
|
353
413
|
return {
|
|
354
414
|
ok: false,
|
|
355
415
|
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
|
+
};
|
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
oauthConfigurationErrorResponse,
|
|
8
8
|
resolveProtectedResource,
|
|
9
9
|
withCors
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HRMLGCXV.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
|
}
|
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
|
};
|