@lovable.dev/mcp-js 0.5.1 → 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-VD6CS7Y6.js → chunk-53M2X7FU.js} +18 -6
- package/dist/{chunk-DDF63QWG.js → chunk-722HKLIU.js} +21 -22
- package/dist/{chunk-GLG5RZGE.js → chunk-G5GWSV5D.js} +16 -4
- package/dist/{chunk-XEDRJFAR.js → chunk-NTXHOUK6.js} +97 -17
- 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 +139 -16
- package/dist/protocols/mcp/index.js +3 -3
- package/dist/protocols/oauth-metadata.cjs +135 -30
- package/dist/protocols/oauth-metadata.js +3 -3
- package/dist/protocols/rest/index.cjs +145 -18
- package/dist/protocols/rest/index.js +3 -3
- package/dist/stacks/tanstack/index.cjs +166 -40
- package/dist/stacks/tanstack/index.js +5 -5
- package/package.json +1 -1
- package/dist/chunk-QA3FWDUV.js +0 -40
|
@@ -43,17 +43,71 @@ function methodNotAllowed(allow) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// src/core/logger.ts
|
|
47
|
+
var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
48
|
+
function isLogLevel(value) {
|
|
49
|
+
return typeof value === "string" && value in LEVEL_RANK;
|
|
50
|
+
}
|
|
51
|
+
function readEnvLevel() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = typeof process !== "undefined" ? process.env?.["LOVABLE_MCP_LOG_LEVEL"] : void 0;
|
|
54
|
+
const normalized = raw?.trim().toLowerCase();
|
|
55
|
+
return isLogLevel(normalized) ? normalized : void 0;
|
|
56
|
+
} catch {
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
var currentLevel = readEnvLevel() ?? "silent";
|
|
61
|
+
function enabled(level) {
|
|
62
|
+
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
63
|
+
}
|
|
64
|
+
function emit(level, method, event, fields) {
|
|
65
|
+
if (!enabled(level))
|
|
66
|
+
return;
|
|
67
|
+
const message = `[mcp-js] ${event}`;
|
|
68
|
+
if (fields)
|
|
69
|
+
console[method](message, fields);
|
|
70
|
+
else
|
|
71
|
+
console[method](message);
|
|
72
|
+
}
|
|
73
|
+
var log = {
|
|
74
|
+
error: (event, fields) => emit("error", "error", event, fields),
|
|
75
|
+
warn: (event, fields) => emit("warn", "warn", event, fields),
|
|
76
|
+
info: (event, fields) => emit("info", "info", event, fields),
|
|
77
|
+
debug: (event, fields) => emit("debug", "debug", event, fields)
|
|
78
|
+
};
|
|
79
|
+
function describeError(err) {
|
|
80
|
+
if (err instanceof Error) {
|
|
81
|
+
const code = err.code;
|
|
82
|
+
return { name: err.name, message: err.message, ...typeof code === "string" ? { code } : {} };
|
|
83
|
+
}
|
|
84
|
+
return { value: String(err) };
|
|
85
|
+
}
|
|
86
|
+
|
|
46
87
|
// src/core/promise.ts
|
|
47
|
-
function cachedPromise(load) {
|
|
88
|
+
function cachedPromise(load, label) {
|
|
48
89
|
let settled = false;
|
|
49
90
|
let value;
|
|
50
91
|
return async () => {
|
|
51
|
-
if (settled)
|
|
92
|
+
if (settled) {
|
|
93
|
+
if (label)
|
|
94
|
+
log.debug(`${label}.cache_hit`);
|
|
52
95
|
return value;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
96
|
+
}
|
|
97
|
+
if (label)
|
|
98
|
+
log.debug(`${label}.load_start`);
|
|
99
|
+
try {
|
|
100
|
+
const loaded = await load();
|
|
101
|
+
settled = true;
|
|
102
|
+
value = loaded;
|
|
103
|
+
if (label)
|
|
104
|
+
log.debug(`${label}.settled`);
|
|
105
|
+
return loaded;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (label)
|
|
108
|
+
log.debug(`${label}.load_failed`, describeError(err));
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
57
111
|
};
|
|
58
112
|
}
|
|
59
113
|
|
|
@@ -129,13 +183,16 @@ async function fetchFirstValidOAuthServerMetadata(metadataUrls, expectedIssuer)
|
|
|
129
183
|
try {
|
|
130
184
|
return await fetchOAuthServerMetadata(url, expectedIssuer);
|
|
131
185
|
} catch (err) {
|
|
186
|
+
log.debug("oauth.discovery.attempt_failed", { url, ...describeError(err) });
|
|
132
187
|
errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
133
188
|
}
|
|
134
189
|
}
|
|
190
|
+
log.error("oauth.discovery.exhausted", { expectedIssuer, urlsTried: metadataUrls, errors });
|
|
135
191
|
throw new Error(`failed to discover OAuth server metadata (${errors.join("; ")})`);
|
|
136
192
|
}
|
|
137
193
|
async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
138
|
-
|
|
194
|
+
log.debug("oauth.discovery.fetch", { url });
|
|
195
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS), redirect: "manual" });
|
|
139
196
|
if (!response.ok) {
|
|
140
197
|
throw new Error(String(response.status));
|
|
141
198
|
}
|
|
@@ -145,6 +202,7 @@ async function fetchOAuthServerMetadata(url, expectedIssuer) {
|
|
|
145
202
|
}
|
|
146
203
|
parseSafeUrl("discovered issuer", json.issuer);
|
|
147
204
|
if (trimTrailingSlash(json.issuer) !== expectedIssuer) {
|
|
205
|
+
log.warn("oauth.discovery.issuer_mismatch", { url, expectedIssuer, published: json.issuer });
|
|
148
206
|
throw new Error("issuer mismatch");
|
|
149
207
|
}
|
|
150
208
|
if (typeof json.jwks_uri !== "string") {
|
|
@@ -157,6 +215,11 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
157
215
|
try {
|
|
158
216
|
return await fetchFirstValidOAuthServerMetadata(oauthMetadataUrlsForIssuer(issuer), issuer);
|
|
159
217
|
} catch (err) {
|
|
218
|
+
log.error("oauth.discovery.config_error", {
|
|
219
|
+
issuer,
|
|
220
|
+
...describeError(err),
|
|
221
|
+
outcome: "500 oauth configuration error"
|
|
222
|
+
});
|
|
160
223
|
throw new OAuthConfigurationError(
|
|
161
224
|
`OAuth issuer discovery failed: ${err instanceof Error ? err.message : String(err)}`
|
|
162
225
|
);
|
|
@@ -164,10 +227,21 @@ async function fetchIssuerOAuthServerMetadata(issuer) {
|
|
|
164
227
|
}
|
|
165
228
|
function createOAuthDiscoveryResolver(auth) {
|
|
166
229
|
const configuredIssuer = trimTrailingSlash(auth.issuer);
|
|
167
|
-
const oauthServerMetadata = cachedPromise(
|
|
230
|
+
const oauthServerMetadata = cachedPromise(
|
|
231
|
+
() => fetchIssuerOAuthServerMetadata(configuredIssuer),
|
|
232
|
+
"oauth.discovery.metadata"
|
|
233
|
+
);
|
|
168
234
|
return {
|
|
169
235
|
resolveIssuer: async () => configuredIssuer,
|
|
170
|
-
resolveJwksUri: async () =>
|
|
236
|
+
resolveJwksUri: async () => {
|
|
237
|
+
if (auth.jwksUri) {
|
|
238
|
+
log.debug("oauth.jwks.resolved", { jwksUri: auth.jwksUri, source: "configured" });
|
|
239
|
+
return auth.jwksUri;
|
|
240
|
+
}
|
|
241
|
+
const jwksUri = (await oauthServerMetadata()).jwks_uri;
|
|
242
|
+
log.debug("oauth.jwks.resolved", { jwksUri, source: "discovered" });
|
|
243
|
+
return jwksUri;
|
|
244
|
+
}
|
|
171
245
|
};
|
|
172
246
|
}
|
|
173
247
|
|
|
@@ -230,6 +304,14 @@ var OAuthTokenError = class extends Error {
|
|
|
230
304
|
function resolveAcceptedAudiences(auth, resource) {
|
|
231
305
|
return auth.acceptedAudiences ?? [resource];
|
|
232
306
|
}
|
|
307
|
+
function tokenHeaderFields(token) {
|
|
308
|
+
try {
|
|
309
|
+
const header = (0, import_jose.decodeProtectedHeader)(token);
|
|
310
|
+
return { jwtAlg: header.alg, jwtKid: header.kid, tokenLength: token.length };
|
|
311
|
+
} catch {
|
|
312
|
+
return { tokenLength: token.length };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
233
315
|
function isJwksFetchFailure(err) {
|
|
234
316
|
if (err instanceof import_jose.errors.JWKSTimeout || err instanceof import_jose.errors.JWKSInvalid)
|
|
235
317
|
return true;
|
|
@@ -251,19 +333,23 @@ async function verifyJwtClaims(token, keySet, issuer, audience, auth) {
|
|
|
251
333
|
return payload;
|
|
252
334
|
} catch (err) {
|
|
253
335
|
if (isJwksFetchFailure(err)) {
|
|
336
|
+
log.error("oauth.jwks.fetch_failed", { ...describeError(err), outcome: "500 oauth configuration error" });
|
|
254
337
|
throw new OAuthConfigurationError(`JWKS fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
255
338
|
}
|
|
339
|
+
log.debug("oauth.verify.rejected", { ...describeError(err), outcome: "401 invalid_token" });
|
|
256
340
|
throw err;
|
|
257
341
|
}
|
|
258
342
|
}
|
|
259
343
|
function assertNonEmptySubject(claims) {
|
|
260
344
|
const sub = claims["sub"];
|
|
261
345
|
if (typeof sub !== "string" || sub.trim() === "") {
|
|
346
|
+
log.debug("oauth.verify.bad_subject", { subType: typeof sub });
|
|
262
347
|
throw new OAuthTokenError(401, "invalid_token", "token subject claim must be a non-empty string");
|
|
263
348
|
}
|
|
264
349
|
}
|
|
265
350
|
function assertOAuthClientClaim(auth, clientId) {
|
|
266
351
|
if (auth.requireOAuthClientClaim !== false && !clientId) {
|
|
352
|
+
log.debug("oauth.verify.missing_client_claim", { outcome: "401 invalid_token" });
|
|
267
353
|
throw new OAuthTokenError(401, "invalid_token", "OAuth client claim is required");
|
|
268
354
|
}
|
|
269
355
|
}
|
|
@@ -288,17 +374,23 @@ function buildMcpAuthContext(args) {
|
|
|
288
374
|
};
|
|
289
375
|
}
|
|
290
376
|
function createOAuthTokenVerifier(auth, discovery) {
|
|
291
|
-
const loadRemoteJwksKeySet = cachedPromise(
|
|
292
|
-
() => discovery.resolveJwksUri().then((jwksURI) => (0, import_jose.createRemoteJWKSet)(new URL(jwksURI)))
|
|
293
|
-
);
|
|
294
377
|
return async (token, request, options) => {
|
|
295
378
|
const resource = resolveProtectedResource(auth, request, options);
|
|
296
379
|
const issuer = await discovery.resolveIssuer();
|
|
297
380
|
const acceptedAudiences = resolveAcceptedAudiences(auth, resource);
|
|
298
|
-
|
|
381
|
+
log.debug("oauth.verify.start", { issuer, acceptedAudiences, resource, ...tokenHeaderFields(token) });
|
|
382
|
+
const jwksUri = await discovery.resolveJwksUri();
|
|
383
|
+
log.debug("oauth.jwks.keyset_created", { jwksUri });
|
|
384
|
+
const keySet = (0, import_jose.createRemoteJWKSet)(new URL(jwksUri));
|
|
385
|
+
const claims = await verifyJwtClaims(token, keySet, issuer, acceptedAudiences, auth);
|
|
299
386
|
assertNonEmptySubject(claims);
|
|
300
387
|
const context = buildMcpAuthContext({ token, claims, issuer, resource, acceptedAudiences });
|
|
301
388
|
assertOAuthClientClaim(auth, context.principal.clientId);
|
|
389
|
+
log.info("oauth.verify.ok", {
|
|
390
|
+
sub: context.principal.sub,
|
|
391
|
+
clientId: context.principal.clientId,
|
|
392
|
+
scopes: context.principal.scopes
|
|
393
|
+
});
|
|
302
394
|
return context;
|
|
303
395
|
};
|
|
304
396
|
}
|
|
@@ -398,22 +490,27 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
398
490
|
if (runtime.kind === "unconfigured")
|
|
399
491
|
return { ok: true };
|
|
400
492
|
const token = parseBearerToken(request);
|
|
401
|
-
if (!token)
|
|
493
|
+
if (!token) {
|
|
494
|
+
log.info("auth.no_bearer_token", { outcome: "401" });
|
|
402
495
|
return { ok: false, response: challengeResponse(runtime.auth, request, 401) };
|
|
496
|
+
}
|
|
403
497
|
try {
|
|
404
498
|
const auth = await runtime.verify(token, request, runtime.options);
|
|
405
499
|
assertRequiredScopes(runtime.auth, auth);
|
|
406
500
|
return { ok: true, auth };
|
|
407
501
|
} catch (err) {
|
|
408
502
|
if (err instanceof OAuthConfigurationError) {
|
|
503
|
+
log.error("auth.config_error", { ...describeError(err), outcome: "500" });
|
|
409
504
|
return { ok: false, response: oauthConfigurationErrorResponse() };
|
|
410
505
|
}
|
|
411
506
|
if (err instanceof OAuthTokenError) {
|
|
507
|
+
log.info("auth.token_rejected", { status: err.status, oauthError: err.oauthError });
|
|
412
508
|
return {
|
|
413
509
|
ok: false,
|
|
414
510
|
response: challengeResponse(runtime.auth, request, err.status, err.oauthError, err.message)
|
|
415
511
|
};
|
|
416
512
|
}
|
|
513
|
+
log.error("auth.unexpected_error", { ...describeError(err), outcome: "401" });
|
|
417
514
|
return {
|
|
418
515
|
ok: false,
|
|
419
516
|
response: challengeResponse(runtime.auth, request, 401, "invalid_token", "Invalid access token")
|
|
@@ -466,6 +563,26 @@ var ToolContext = class {
|
|
|
466
563
|
}
|
|
467
564
|
};
|
|
468
565
|
|
|
566
|
+
// src/core/cors.ts
|
|
567
|
+
var EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
|
|
568
|
+
var ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
|
|
569
|
+
function withCors(response) {
|
|
570
|
+
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
571
|
+
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
572
|
+
return response;
|
|
573
|
+
}
|
|
574
|
+
function corsPreflightResponse(allowMethods) {
|
|
575
|
+
return new Response(null, {
|
|
576
|
+
status: 204,
|
|
577
|
+
headers: {
|
|
578
|
+
"Access-Control-Allow-Origin": "*",
|
|
579
|
+
"Access-Control-Allow-Methods": allowMethods,
|
|
580
|
+
"Access-Control-Allow-Headers": ALLOW_HEADERS,
|
|
581
|
+
"Access-Control-Max-Age": "86400"
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
469
586
|
// src/protocols/mcp/protocol.ts
|
|
470
587
|
function adaptToolToSdkCallback(tool, auth) {
|
|
471
588
|
return async (first) => {
|
|
@@ -484,7 +601,7 @@ function adaptToolToSdkCallback(tool, auth) {
|
|
|
484
601
|
}
|
|
485
602
|
function createMcpProtocolHandler(mcp, options = {}) {
|
|
486
603
|
const authorizer = createRequestAuthorizer(mcp, options);
|
|
487
|
-
|
|
604
|
+
const handle = async (request) => {
|
|
488
605
|
const authResult = await authorizer.authorize(request);
|
|
489
606
|
if (!authResult.ok)
|
|
490
607
|
return authResult.response;
|
|
@@ -511,28 +628,31 @@ function createMcpProtocolHandler(mcp, options = {}) {
|
|
|
511
628
|
});
|
|
512
629
|
await server.connect(transport);
|
|
513
630
|
return await transport.handleRequest(request);
|
|
514
|
-
} catch {
|
|
631
|
+
} catch (err) {
|
|
632
|
+
log.error("mcp.transport_error", { ...describeError(err), outcome: "500 internal error" });
|
|
515
633
|
return Response.json(
|
|
516
634
|
{ jsonrpc: "2.0", id: null, error: { code: -32603, message: "internal error" } },
|
|
517
635
|
{ status: 500 }
|
|
518
636
|
);
|
|
519
637
|
}
|
|
520
638
|
};
|
|
639
|
+
return async (request) => {
|
|
640
|
+
if (request.method === "OPTIONS")
|
|
641
|
+
return corsPreflightResponse("GET, POST, DELETE, OPTIONS");
|
|
642
|
+
return withCors(await handle(request));
|
|
643
|
+
};
|
|
521
644
|
}
|
|
522
645
|
|
|
523
646
|
// src/protocols/oauth-metadata.ts
|
|
524
|
-
var CORS_ORIGIN = { "Access-Control-Allow-Origin": "*" };
|
|
525
|
-
function withCors(response) {
|
|
526
|
-
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
527
|
-
return response;
|
|
528
|
-
}
|
|
529
647
|
function notFound() {
|
|
530
|
-
return
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
648
|
+
return withCors(
|
|
649
|
+
new Response(JSON.stringify({ error: "not found" }), {
|
|
650
|
+
status: 404,
|
|
651
|
+
// `no-store` so a 404 (OAuth unconfigured) isn't heuristically cached and
|
|
652
|
+
// then served past a later deploy that enables OAuth and starts returning 200.
|
|
653
|
+
headers: { ...JSON_HEADERS, "Cache-Control": "no-store" }
|
|
654
|
+
})
|
|
655
|
+
);
|
|
536
656
|
}
|
|
537
657
|
async function buildProtectedResourceMetadata(mcp, auth, request, options, discovery) {
|
|
538
658
|
const issuer = await discovery.resolveIssuer();
|
|
@@ -557,17 +677,12 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
|
557
677
|
return async (request) => {
|
|
558
678
|
if (runtime.kind !== "configured" || runtime.auth.protectedResourceMetadataUrl !== void 0)
|
|
559
679
|
return notFound();
|
|
560
|
-
if (request.method === "OPTIONS")
|
|
561
|
-
return
|
|
562
|
-
status: 204,
|
|
563
|
-
headers: { ...CORS_ORIGIN, "Access-Control-Allow-Methods": "GET, HEAD, OPTIONS" }
|
|
564
|
-
});
|
|
565
|
-
}
|
|
680
|
+
if (request.method === "OPTIONS")
|
|
681
|
+
return corsPreflightResponse("GET, HEAD, OPTIONS");
|
|
566
682
|
if (request.method !== "GET" && request.method !== "HEAD")
|
|
567
683
|
return withCors(methodNotAllowed("GET, HEAD, OPTIONS"));
|
|
568
684
|
const headers = {
|
|
569
685
|
...JSON_HEADERS,
|
|
570
|
-
...CORS_ORIGIN,
|
|
571
686
|
"Cache-Control": "public, max-age=300",
|
|
572
687
|
Vary: "Host"
|
|
573
688
|
};
|
|
@@ -579,9 +694,10 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
|
579
694
|
runtime.options,
|
|
580
695
|
runtime.discovery
|
|
581
696
|
);
|
|
582
|
-
const response = Response.json(metadata, { headers });
|
|
697
|
+
const response = withCors(Response.json(metadata, { headers }));
|
|
583
698
|
return request.method === "HEAD" ? headResponse(response) : response;
|
|
584
|
-
} catch {
|
|
699
|
+
} catch (err) {
|
|
700
|
+
log.error("oauth.metadata.config_error", { ...describeError(err), outcome: "500 oauth configuration error" });
|
|
585
701
|
const response = withCors(oauthConfigurationErrorResponse());
|
|
586
702
|
return request.method === "HEAD" ? headResponse(response) : response;
|
|
587
703
|
}
|
|
@@ -603,12 +719,12 @@ function shapeToJsonSchema(shape) {
|
|
|
603
719
|
function createListToolsHandler(mcp, options = {}) {
|
|
604
720
|
assertRestResourceBinding(mcp, options);
|
|
605
721
|
const authorizer = createRequestAuthorizer(mcp, options);
|
|
606
|
-
|
|
722
|
+
const handle = async (request) => {
|
|
607
723
|
const authResult = await authorizer.authorize(request);
|
|
608
724
|
if (!authResult.ok)
|
|
609
725
|
return authResult.response;
|
|
610
726
|
if (request.method !== "GET" && request.method !== "HEAD")
|
|
611
|
-
return methodNotAllowed("GET, HEAD");
|
|
727
|
+
return methodNotAllowed("GET, HEAD, OPTIONS");
|
|
612
728
|
const body = {
|
|
613
729
|
server: { name: mcp.name, version: mcp.version, title: mcp.title },
|
|
614
730
|
tools: mcp.tools.map((tool) => ({
|
|
@@ -623,6 +739,11 @@ function createListToolsHandler(mcp, options = {}) {
|
|
|
623
739
|
const response = Response.json(body);
|
|
624
740
|
return request.method === "HEAD" ? headResponse(response) : response;
|
|
625
741
|
};
|
|
742
|
+
return async (request) => {
|
|
743
|
+
if (request.method === "OPTIONS")
|
|
744
|
+
return corsPreflightResponse("GET, HEAD, OPTIONS");
|
|
745
|
+
return withCors(await handle(request));
|
|
746
|
+
};
|
|
626
747
|
}
|
|
627
748
|
|
|
628
749
|
// src/protocols/rest/invoke-tool.ts
|
|
@@ -642,12 +763,12 @@ function isEmptyArgs(value) {
|
|
|
642
763
|
function createInvokeToolHandler(mcp, options = {}) {
|
|
643
764
|
assertRestResourceBinding(mcp, options);
|
|
644
765
|
const authorizer = createRequestAuthorizer(mcp, options);
|
|
645
|
-
|
|
766
|
+
const handle = async (request, toolName) => {
|
|
646
767
|
const authResult = await authorizer.authorize(request);
|
|
647
768
|
if (!authResult.ok)
|
|
648
769
|
return authResult.response;
|
|
649
770
|
if (request.method !== "POST")
|
|
650
|
-
return methodNotAllowed("POST");
|
|
771
|
+
return methodNotAllowed("POST, OPTIONS");
|
|
651
772
|
const tool = mcp.tools.find((t) => t.name === toolName);
|
|
652
773
|
if (!tool) {
|
|
653
774
|
return new Response(JSON.stringify({ error: `unknown tool: ${safeReflectName(toolName)}` }), {
|
|
@@ -715,6 +836,11 @@ function createInvokeToolHandler(mcp, options = {}) {
|
|
|
715
836
|
isError: result.isError
|
|
716
837
|
});
|
|
717
838
|
};
|
|
839
|
+
return async (request, toolName) => {
|
|
840
|
+
if (request.method === "OPTIONS")
|
|
841
|
+
return corsPreflightResponse("POST, OPTIONS");
|
|
842
|
+
return withCors(await handle(request, toolName));
|
|
843
|
+
};
|
|
718
844
|
}
|
|
719
845
|
|
|
720
846
|
// src/stacks/tanstack/handlers.ts
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMcpProtocolHandler
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-G5GWSV5D.js";
|
|
4
4
|
import {
|
|
5
5
|
createOAuthProtectedResourceMetadataHandler
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-722HKLIU.js";
|
|
7
7
|
import {
|
|
8
8
|
createInvokeToolHandler,
|
|
9
9
|
createListToolsHandler
|
|
10
|
-
} from "../../chunk-
|
|
10
|
+
} from "../../chunk-53M2X7FU.js";
|
|
11
11
|
import "../../chunk-MA5H6PSF.js";
|
|
12
|
-
import "../../chunk-
|
|
13
|
-
import "../../chunk-
|
|
12
|
+
import "../../chunk-NTXHOUK6.js";
|
|
13
|
+
import "../../chunk-QC3DXQTH.js";
|
|
14
14
|
import "../../chunk-6DXGZZA4.js";
|
|
15
15
|
|
|
16
16
|
// src/stacks/tanstack/handlers.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovable.dev/mcp-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Author MCP servers for Lovable apps. Declare tools with defineTool, register them in defineMcp, and the framework adapter (TanStack today, Supabase Edge Functions next) emits the route(s) at build time.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/dist/chunk-QA3FWDUV.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// src/core/url.ts
|
|
2
|
-
function trimTrailingSlash(value) {
|
|
3
|
-
return value.replace(/\/+$/, "");
|
|
4
|
-
}
|
|
5
|
-
function isLocalHTTPHost(hostname) {
|
|
6
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
7
|
-
}
|
|
8
|
-
function urlSafetyProblem(url) {
|
|
9
|
-
const isAllowedHTTP = url.protocol === "http:" && isLocalHTTPHost(url.hostname);
|
|
10
|
-
if (url.protocol !== "https:" && !isAllowedHTTP) {
|
|
11
|
-
return "must use https://, except localhost development URLs";
|
|
12
|
-
}
|
|
13
|
-
if (url.username || url.password) {
|
|
14
|
-
return "must not include credentials";
|
|
15
|
-
}
|
|
16
|
-
if (url.search || url.hash) {
|
|
17
|
-
return "must not include query or fragment";
|
|
18
|
-
}
|
|
19
|
-
return void 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// src/core/validation.ts
|
|
23
|
-
function parseSafeUrl(subject, raw, ErrorClass = Error) {
|
|
24
|
-
let url;
|
|
25
|
-
try {
|
|
26
|
-
url = new URL(raw);
|
|
27
|
-
} catch {
|
|
28
|
-
throw new ErrorClass(`${subject} must be an absolute URL`);
|
|
29
|
-
}
|
|
30
|
-
const problem = urlSafetyProblem(url);
|
|
31
|
-
if (problem) {
|
|
32
|
-
throw new ErrorClass(`${subject} ${problem}`);
|
|
33
|
-
}
|
|
34
|
-
return url;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export {
|
|
38
|
-
trimTrailingSlash,
|
|
39
|
-
parseSafeUrl
|
|
40
|
-
};
|