@lovable.dev/mcp-js 0.28.0 → 0.29.0-rc.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 +7 -1
- package/dist/{authorize-Cs1yyLrq.d.ts → authorize-BjNE3LPr.d.ts} +1 -1
- package/dist/{authorize-BcNuNPtP.d.cts → authorize-BzZFS0zi.d.cts} +1 -1
- package/dist/{base-CUkmyaXJ.d.cts → base-DSH8NnPj.d.cts} +4 -0
- package/dist/{base-CUkmyaXJ.d.ts → base-DSH8NnPj.d.ts} +4 -0
- package/dist/cli/extract-manifest.cjs +2 -2
- package/dist/cli/extract-manifest.js +2 -2
- package/dist/{authorize-ChY44o_1.cjs → cors-CmKzDN7k.cjs} +73 -29
- package/dist/{authorize--rUeMOGU.js → cors-D4YbODs-.js} +68 -24
- package/dist/{forwarded-BlLF3dfc.js → forwarded-B3EBjHnA.js} +3 -3
- package/dist/{forwarded-Bhwcj22u.cjs → forwarded-BC_RHnn5.cjs} +3 -3
- package/dist/{gate-CGZl0gjJ.cjs → gate-n_HX6eQ5.cjs} +6 -4
- package/dist/{gate-BiM6EBeA.js → gate-pGsVlY_4.js} +5 -3
- package/dist/index.cjs +37 -0
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +37 -0
- package/dist/{io-VBHO8YH_.d.cts → io-BEwvgN8s.d.cts} +1 -1
- package/dist/{io-SMlD5WeX.d.ts → io-CvALf9NE.d.ts} +1 -1
- package/dist/list-tools-Dkyyje7j.cjs +48 -0
- package/dist/{list-tools-BLGr7mot.js → list-tools-ePP9tXND.js} +9 -13
- package/dist/{list-tools-BAqQVIHk.d.ts → list-tools-qIvVJeWZ.d.ts} +3 -3
- package/dist/{list-tools-BN97L2Yv.d.cts → list-tools-zagoI477.d.cts} +3 -3
- package/dist/mcp-Cs4zfvSi.js +336 -0
- package/dist/mcp-DQ1Vdv4v.cjs +341 -0
- package/dist/{package-CGLPZwz-.cjs → package-C3YVYgxp.cjs} +1 -1
- package/dist/{package-DgiA0xuY.js → package-DzB_5Hur.js} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +3 -3
- package/dist/protocols/mcp/index.d.ts +3 -3
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +11 -9
- package/dist/protocols/oauth-metadata.d.cts +2 -2
- package/dist/protocols/oauth-metadata.d.ts +2 -2
- package/dist/protocols/oauth-metadata.js +5 -3
- package/dist/protocols/rest/index.cjs +2 -2
- package/dist/protocols/rest/index.d.cts +4 -4
- package/dist/protocols/rest/index.d.ts +4 -4
- package/dist/protocols/rest/index.js +2 -2
- package/dist/{rest-BS7bBOd9.js → rest-DSclWpIt.js} +13 -12
- package/dist/{rest-BE05MUNS.cjs → rest-P6XZWP42.cjs} +28 -27
- package/dist/schema-CW1jKvio.js +65 -0
- package/dist/schema-D3vClxb7.cjs +76 -0
- package/dist/stacks/supabase/index.cjs +7 -7
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +5 -5
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.d.cts +1 -1
- package/dist/stacks/supabase/vite.d.ts +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +8 -8
- package/dist/stacks/tanstack/index.d.cts +2 -2
- package/dist/stacks/tanstack/index.d.ts +2 -2
- package/dist/stacks/tanstack/index.js +5 -5
- package/dist/stacks/tanstack/vite.cjs +8 -4
- package/dist/stacks/tanstack/vite.d.cts +11 -1
- package/dist/stacks/tanstack/vite.d.ts +11 -1
- package/dist/stacks/tanstack/vite.js +8 -4
- package/dist/tool-result-CK6hXO43.cjs +89 -0
- package/dist/tool-result-DfCk445b.js +78 -0
- package/dist/{types-C0Wgm9zp.d.cts → types-D1cgsnPu.d.cts} +15 -7
- package/dist/{types-C0Wgm9zp.d.ts → types-D1cgsnPu.d.ts} +15 -7
- package/package.json +2 -1
- package/dist/content-D5LJAHyM.js +0 -12
- package/dist/content-NCYIGgOb.cjs +0 -17
- package/dist/list-tools-DCF6G4fn.cjs +0 -52
- package/dist/mcp-CUGwXkxb.cjs +0 -153
- package/dist/mcp-Czp-hHjN.js +0 -148
package/README.md
CHANGED
|
@@ -51,7 +51,13 @@ That's the whole authoring surface. No imperative server construction, no route
|
|
|
51
51
|
|
|
52
52
|
The table shows the default `path: "/mcp"`. The REST companions co-locate under the parent of the configured `path`, so `mcpPlugin({ path: "/api/public/mcp" })` emits them at `/api/public/.mcp/list-tools` and `/api/public/.mcp/invoke-tool/<tool>` (file: `src/routes/api/public/[.mcp]/...`). Pass `restRoutes: false` to drop them.
|
|
53
53
|
|
|
54
|
-
MCP (`POST <path>`, `/mcp` by default) is the public wire format clients speak directly. REST (the `.mcp/*` companions, co-located under the `path` parent) is internal RPC — only an upstream MCP proxy calls it; never a browser or a hand-rolled client. All runtime routes import the same `defineMcp` result, so they stay in sync on which tools exist and which auth
|
|
54
|
+
MCP (`POST <path>`, `/mcp` by default) is the public wire format clients speak directly. REST (the `.mcp/*` companions, co-located under the `path` parent) is internal RPC — only an upstream MCP proxy calls it; never a browser or a hand-rolled client. All runtime routes import the same `defineMcp` result, so they stay in sync on which tools exist and which auth and browser-origin policies protect them. If `defineMcp({ auth: ... })` is omitted, the handlers stay unauthenticated; if OAuth auth is configured, MCP and REST both require the proxy/client to pass `Authorization: Bearer <token>`. Rate limiting belongs at the app host or edge.
|
|
55
|
+
|
|
56
|
+
The MCP endpoint supports both protocol eras on one URL. Finalized `2026-07-28` clients send the version, client capabilities, and mirrored HTTP headers on every request; the server returns typed results plus cache metadata on discovery and list responses without initialization or protocol sessions. Initialization-era clients continue through a stateless compatibility path: `initialize`, `tools/list`, and `tools/call` work as independent requests, and the endpoint never mints `Mcp-Session-Id`. `GET` and `DELETE` return `405` because there is no standalone stream or session to manage.
|
|
57
|
+
|
|
58
|
+
`outputSchema` accepts either the existing object-shape shorthand (`{ value: z.string() }`) or a JSON-Schema-representable Zod schema such as `z.array(z.string())`; `structuredContent` may be any JSON value. The server returns the schema's parsed output, so coercions are reflected in the result; transforms with no truthful JSON Schema are rejected when `defineMcp` runs. Modern clients receive the natural schema and value. The compatibility path wraps non-object schemas and values under `result` for initialization-era clients whose wire format requires an object root.
|
|
59
|
+
|
|
60
|
+
Browser-origin checks apply to the MCP, REST companion, and OAuth metadata routes before authentication. Omit `allowedOrigins` to allow requests with no `Origin` header (Go, desktop, CLI, and server clients) plus same-origin browser requests. Set `allowedOrigins: ["https://client.example.com"]` for exact additional origins, or `allowedOrigins: "any"` to intentionally restore wildcard CORS. Exact and same origins are reflected with `Vary: Origin`; other browser origins receive `403` without an OAuth challenge. Reverse-proxy adapters compare against the effective public origin after applying only their configured trusted forwarded headers.
|
|
55
61
|
|
|
56
62
|
The OAuth metadata route is emitted by default and returns `404` until OAuth auth is configured. Disable it with `mcpPlugin({ protectedResourceMetadataRoute: false })` only if the app owns `/.well-known/oauth-protected-resource` itself.
|
|
57
63
|
|
|
@@ -16,6 +16,10 @@ interface InvocationRecord {
|
|
|
16
16
|
/** HTTP request method. Set on `auth_challenge` so probe traffic (GET vs
|
|
17
17
|
* POST discovery) is distinguishable; absent on tool-call records. */
|
|
18
18
|
httpMethod?: string;
|
|
19
|
+
/** Wire surface that served the call: the modern stateless revision, the
|
|
20
|
+
* initialization-era path, or the REST companion. Absent on records emitted
|
|
21
|
+
* before protocol classification (auth gate, transport faults). */
|
|
22
|
+
protocol?: "2026-07-28" | "legacy" | "rest";
|
|
19
23
|
/** The app end-user that made the call (the access token `sub`), or
|
|
20
24
|
* `undefined` when the request is unauthenticated. Not a Lovable identity. */
|
|
21
25
|
endUserId?: string;
|
|
@@ -16,6 +16,10 @@ interface InvocationRecord {
|
|
|
16
16
|
/** HTTP request method. Set on `auth_challenge` so probe traffic (GET vs
|
|
17
17
|
* POST discovery) is distinguishable; absent on tool-call records. */
|
|
18
18
|
httpMethod?: string;
|
|
19
|
+
/** Wire surface that served the call: the modern stateless revision, the
|
|
20
|
+
* initialization-era path, or the REST companion. Absent on records emitted
|
|
21
|
+
* before protocol classification (auth gate, transport faults). */
|
|
22
|
+
protocol?: "2026-07-28" | "legacy" | "rest";
|
|
19
23
|
/** The app end-user that made the call (the access token `sub`), or
|
|
20
24
|
* `undefined` when the request is unauthenticated. Not a Lovable identity. */
|
|
21
25
|
endUserId?: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_package = require("../package-
|
|
3
|
-
const require_list_tools = require("../list-tools-
|
|
2
|
+
const require_package = require("../package-C3YVYgxp.cjs");
|
|
3
|
+
const require_list_tools = require("../list-tools-Dkyyje7j.cjs");
|
|
4
4
|
const require_fs_errors = require("../fs-errors-CWNzOV75.cjs");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
6
6
|
let node_path = require("node:path");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "../package-
|
|
3
|
-
import { t as buildMcpListing } from "../list-tools-
|
|
2
|
+
import { t as version } from "../package-DzB_5Hur.js";
|
|
3
|
+
import { t as buildMcpListing } from "../list-tools-ePP9tXND.js";
|
|
4
4
|
import { t as isFileMissing } from "../fs-errors-PA2t1TIp.js";
|
|
5
5
|
import { lstatSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, resolve } from "node:path";
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
const require_logger = require("./logger-CN1q-KNn.cjs");
|
|
2
|
-
const require_package = require("./package-
|
|
2
|
+
const require_package = require("./package-C3YVYgxp.cjs");
|
|
3
3
|
require("./metadata-path-zd7NSNoa.cjs");
|
|
4
4
|
let jose = require("jose");
|
|
5
|
-
//#region src/core/cors.ts
|
|
6
|
-
const EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
|
|
7
|
-
const ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
|
|
8
|
-
/** Add permissive CORS to a built response, preserving its other headers. */
|
|
9
|
-
function withCors(response) {
|
|
10
|
-
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
11
|
-
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
12
|
-
return response;
|
|
13
|
-
}
|
|
14
|
-
function corsPreflightResponse(allowMethods) {
|
|
15
|
-
return new Response(null, {
|
|
16
|
-
status: 204,
|
|
17
|
-
headers: {
|
|
18
|
-
"Access-Control-Allow-Origin": "*",
|
|
19
|
-
"Access-Control-Allow-Methods": allowMethods,
|
|
20
|
-
"Access-Control-Allow-Headers": ALLOW_HEADERS,
|
|
21
|
-
"Access-Control-Max-Age": "86400"
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
5
|
//#region src/core/http.ts
|
|
27
6
|
const JSON_HEADERS = { "Content-Type": "application/json" };
|
|
28
7
|
/** Strip the body from a built response, preserving status and headers, for HEAD. */
|
|
@@ -34,7 +13,7 @@ function headResponse(response) {
|
|
|
34
13
|
});
|
|
35
14
|
}
|
|
36
15
|
function finalizeWireResponse(request, response) {
|
|
37
|
-
return
|
|
16
|
+
return request.method === "HEAD" ? headResponse(response) : response;
|
|
38
17
|
}
|
|
39
18
|
function methodNotAllowed(allow) {
|
|
40
19
|
return new Response(JSON.stringify({ error: "method not allowed" }), {
|
|
@@ -74,6 +53,7 @@ function toLogRecord(rec) {
|
|
|
74
53
|
if (rec.reqBytes !== void 0) attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
|
|
75
54
|
if (rec.resBytes !== void 0) attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
|
|
76
55
|
if (rec.httpMethod !== void 0) attributes.push(strAttr("http.request.method", rec.httpMethod));
|
|
56
|
+
if (rec.protocol !== void 0) attributes.push(strAttr("mcp.protocol", rec.protocol));
|
|
77
57
|
attributes.push(strAttr("mcp.stack", rec.stack));
|
|
78
58
|
if (rec.endUserId !== void 0) attributes.push(strAttr("mcp.end_user_id", rec.endUserId));
|
|
79
59
|
return {
|
|
@@ -177,6 +157,7 @@ var BaseMetricRecorder = class {
|
|
|
177
157
|
outcome: ev.outcome,
|
|
178
158
|
durationMs: ev.durationMs,
|
|
179
159
|
stack: this.stack,
|
|
160
|
+
...ev.protocol !== void 0 && { protocol: ev.protocol },
|
|
180
161
|
...ev.errorText !== void 0 && { errorText: ev.errorText }
|
|
181
162
|
});
|
|
182
163
|
if (!this.config.enabled) return;
|
|
@@ -959,6 +940,69 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
959
940
|
} };
|
|
960
941
|
}
|
|
961
942
|
//#endregion
|
|
943
|
+
//#region src/core/cors.ts
|
|
944
|
+
const EXPOSE_HEADERS = "WWW-Authenticate, MCP-Protocol-Version, Mcp-Session-Id";
|
|
945
|
+
const ALLOW_HEADERS = "Authorization, Content-Type, MCP-Protocol-Version, Mcp-Method, Mcp-Name, Mcp-Session-Id, Last-Event-ID";
|
|
946
|
+
const MCP_PARAM_HEADER = /^mcp-param-[!#$%&'*+.^_`|~0-9A-Za-z-]+$/i;
|
|
947
|
+
function appendVary(headers, value) {
|
|
948
|
+
const values = new Set((headers.get("Vary") ?? "").split(",").map((entry) => entry.trim()).filter(Boolean));
|
|
949
|
+
values.add(value);
|
|
950
|
+
headers.set("Vary", [...values].join(", "));
|
|
951
|
+
}
|
|
952
|
+
function addCorsHeaders(response, allowedOrigin, variesByOrigin) {
|
|
953
|
+
if (allowedOrigin !== void 0) {
|
|
954
|
+
response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
|
955
|
+
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
956
|
+
}
|
|
957
|
+
if (variesByOrigin) appendVary(response.headers, "Origin");
|
|
958
|
+
return response;
|
|
959
|
+
}
|
|
960
|
+
function allowedRequestHeaders(request) {
|
|
961
|
+
const allowed = new Map(ALLOW_HEADERS.split(", ").map((header) => [header.toLowerCase(), header]));
|
|
962
|
+
for (const header of (request.headers.get("Access-Control-Request-Headers") ?? "").split(",")) {
|
|
963
|
+
const normalized = header.trim();
|
|
964
|
+
if (MCP_PARAM_HEADER.test(normalized)) allowed.set(normalized.toLowerCase(), normalized);
|
|
965
|
+
}
|
|
966
|
+
return [...allowed.values()].join(", ");
|
|
967
|
+
}
|
|
968
|
+
function evaluateCors(request, allowedOrigins) {
|
|
969
|
+
const origin = request.headers.get("Origin");
|
|
970
|
+
const allowAny = allowedOrigins === "any";
|
|
971
|
+
let allowedOrigin;
|
|
972
|
+
if (allowAny) allowedOrigin = "*";
|
|
973
|
+
else if (origin === null) allowedOrigin = void 0;
|
|
974
|
+
else if (origin === new URL(request.url).origin || allowedOrigins?.includes(origin)) allowedOrigin = origin;
|
|
975
|
+
else allowedOrigin = null;
|
|
976
|
+
if (allowedOrigin === null) {
|
|
977
|
+
const response = Response.json({ error: "origin not allowed" }, {
|
|
978
|
+
status: 403,
|
|
979
|
+
headers: { "Cache-Control": "no-store" }
|
|
980
|
+
});
|
|
981
|
+
appendVary(response.headers, "Origin");
|
|
982
|
+
return {
|
|
983
|
+
ok: false,
|
|
984
|
+
response
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
ok: true,
|
|
989
|
+
withHeaders: (response) => addCorsHeaders(response, allowedOrigin, !allowAny),
|
|
990
|
+
preflight: (allowMethods) => {
|
|
991
|
+
const requestedHeaders = request.headers.get("Access-Control-Request-Headers");
|
|
992
|
+
const response = new Response(null, {
|
|
993
|
+
status: 204,
|
|
994
|
+
headers: {
|
|
995
|
+
"Access-Control-Allow-Methods": allowMethods,
|
|
996
|
+
"Access-Control-Allow-Headers": allowedRequestHeaders(request),
|
|
997
|
+
"Access-Control-Max-Age": "86400"
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
if (requestedHeaders) appendVary(response.headers, "Access-Control-Request-Headers");
|
|
1001
|
+
return addCorsHeaders(response, allowedOrigin, !allowAny);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
962
1006
|
Object.defineProperty(exports, "JSON_HEADERS", {
|
|
963
1007
|
enumerable: true,
|
|
964
1008
|
get: function() {
|
|
@@ -977,12 +1021,6 @@ Object.defineProperty(exports, "assertRestResourceBinding", {
|
|
|
977
1021
|
return assertRestResourceBinding;
|
|
978
1022
|
}
|
|
979
1023
|
});
|
|
980
|
-
Object.defineProperty(exports, "corsPreflightResponse", {
|
|
981
|
-
enumerable: true,
|
|
982
|
-
get: function() {
|
|
983
|
-
return corsPreflightResponse;
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
1024
|
Object.defineProperty(exports, "createNoopRecorder", {
|
|
987
1025
|
enumerable: true,
|
|
988
1026
|
get: function() {
|
|
@@ -1001,6 +1039,12 @@ Object.defineProperty(exports, "createRequestAuthorizer", {
|
|
|
1001
1039
|
return createRequestAuthorizer;
|
|
1002
1040
|
}
|
|
1003
1041
|
});
|
|
1042
|
+
Object.defineProperty(exports, "evaluateCors", {
|
|
1043
|
+
enumerable: true,
|
|
1044
|
+
get: function() {
|
|
1045
|
+
return evaluateCors;
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1004
1048
|
Object.defineProperty(exports, "finalizeWireResponse", {
|
|
1005
1049
|
enumerable: true,
|
|
1006
1050
|
get: function() {
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
import { i as log, n as applyLogLevelFromEnv, o as parseSafeUrl, r as describeError, s as trimTrailingSlash, t as LOG_LEVEL_ENV_VAR, u as resolveMetricsConfig } from "./logger-Ctv5xUSD.js";
|
|
2
|
-
import { t as version } from "./package-
|
|
2
|
+
import { t as version } from "./package-DzB_5Hur.js";
|
|
3
3
|
import "./metadata-path-CAkNcfyY.js";
|
|
4
4
|
import { createLocalJWKSet, decodeProtectedHeader, errors, jwtVerify } from "jose";
|
|
5
|
-
//#region src/core/cors.ts
|
|
6
|
-
const EXPOSE_HEADERS = "WWW-Authenticate, Mcp-Session-Id, Mcp-Protocol-Version";
|
|
7
|
-
const ALLOW_HEADERS = "Authorization, Content-Type, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID";
|
|
8
|
-
/** Add permissive CORS to a built response, preserving its other headers. */
|
|
9
|
-
function withCors(response) {
|
|
10
|
-
response.headers.set("Access-Control-Allow-Origin", "*");
|
|
11
|
-
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
12
|
-
return response;
|
|
13
|
-
}
|
|
14
|
-
function corsPreflightResponse(allowMethods) {
|
|
15
|
-
return new Response(null, {
|
|
16
|
-
status: 204,
|
|
17
|
-
headers: {
|
|
18
|
-
"Access-Control-Allow-Origin": "*",
|
|
19
|
-
"Access-Control-Allow-Methods": allowMethods,
|
|
20
|
-
"Access-Control-Allow-Headers": ALLOW_HEADERS,
|
|
21
|
-
"Access-Control-Max-Age": "86400"
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
5
|
//#region src/core/http.ts
|
|
27
6
|
const JSON_HEADERS = { "Content-Type": "application/json" };
|
|
28
7
|
/** Strip the body from a built response, preserving status and headers, for HEAD. */
|
|
@@ -34,7 +13,7 @@ function headResponse(response) {
|
|
|
34
13
|
});
|
|
35
14
|
}
|
|
36
15
|
function finalizeWireResponse(request, response) {
|
|
37
|
-
return
|
|
16
|
+
return request.method === "HEAD" ? headResponse(response) : response;
|
|
38
17
|
}
|
|
39
18
|
function methodNotAllowed(allow) {
|
|
40
19
|
return new Response(JSON.stringify({ error: "method not allowed" }), {
|
|
@@ -74,6 +53,7 @@ function toLogRecord(rec) {
|
|
|
74
53
|
if (rec.reqBytes !== void 0) attributes.push(intAttr("mcp.request_size_bytes", rec.reqBytes));
|
|
75
54
|
if (rec.resBytes !== void 0) attributes.push(intAttr("mcp.response_size_bytes", rec.resBytes));
|
|
76
55
|
if (rec.httpMethod !== void 0) attributes.push(strAttr("http.request.method", rec.httpMethod));
|
|
56
|
+
if (rec.protocol !== void 0) attributes.push(strAttr("mcp.protocol", rec.protocol));
|
|
77
57
|
attributes.push(strAttr("mcp.stack", rec.stack));
|
|
78
58
|
if (rec.endUserId !== void 0) attributes.push(strAttr("mcp.end_user_id", rec.endUserId));
|
|
79
59
|
return {
|
|
@@ -177,6 +157,7 @@ var BaseMetricRecorder = class {
|
|
|
177
157
|
outcome: ev.outcome,
|
|
178
158
|
durationMs: ev.durationMs,
|
|
179
159
|
stack: this.stack,
|
|
160
|
+
...ev.protocol !== void 0 && { protocol: ev.protocol },
|
|
180
161
|
...ev.errorText !== void 0 && { errorText: ev.errorText }
|
|
181
162
|
});
|
|
182
163
|
if (!this.config.enabled) return;
|
|
@@ -959,4 +940,67 @@ function createRequestAuthorizer(mcp, options = {}) {
|
|
|
959
940
|
} };
|
|
960
941
|
}
|
|
961
942
|
//#endregion
|
|
962
|
-
|
|
943
|
+
//#region src/core/cors.ts
|
|
944
|
+
const EXPOSE_HEADERS = "WWW-Authenticate, MCP-Protocol-Version, Mcp-Session-Id";
|
|
945
|
+
const ALLOW_HEADERS = "Authorization, Content-Type, MCP-Protocol-Version, Mcp-Method, Mcp-Name, Mcp-Session-Id, Last-Event-ID";
|
|
946
|
+
const MCP_PARAM_HEADER = /^mcp-param-[!#$%&'*+.^_`|~0-9A-Za-z-]+$/i;
|
|
947
|
+
function appendVary(headers, value) {
|
|
948
|
+
const values = new Set((headers.get("Vary") ?? "").split(",").map((entry) => entry.trim()).filter(Boolean));
|
|
949
|
+
values.add(value);
|
|
950
|
+
headers.set("Vary", [...values].join(", "));
|
|
951
|
+
}
|
|
952
|
+
function addCorsHeaders(response, allowedOrigin, variesByOrigin) {
|
|
953
|
+
if (allowedOrigin !== void 0) {
|
|
954
|
+
response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
|
955
|
+
response.headers.set("Access-Control-Expose-Headers", EXPOSE_HEADERS);
|
|
956
|
+
}
|
|
957
|
+
if (variesByOrigin) appendVary(response.headers, "Origin");
|
|
958
|
+
return response;
|
|
959
|
+
}
|
|
960
|
+
function allowedRequestHeaders(request) {
|
|
961
|
+
const allowed = new Map(ALLOW_HEADERS.split(", ").map((header) => [header.toLowerCase(), header]));
|
|
962
|
+
for (const header of (request.headers.get("Access-Control-Request-Headers") ?? "").split(",")) {
|
|
963
|
+
const normalized = header.trim();
|
|
964
|
+
if (MCP_PARAM_HEADER.test(normalized)) allowed.set(normalized.toLowerCase(), normalized);
|
|
965
|
+
}
|
|
966
|
+
return [...allowed.values()].join(", ");
|
|
967
|
+
}
|
|
968
|
+
function evaluateCors(request, allowedOrigins) {
|
|
969
|
+
const origin = request.headers.get("Origin");
|
|
970
|
+
const allowAny = allowedOrigins === "any";
|
|
971
|
+
let allowedOrigin;
|
|
972
|
+
if (allowAny) allowedOrigin = "*";
|
|
973
|
+
else if (origin === null) allowedOrigin = void 0;
|
|
974
|
+
else if (origin === new URL(request.url).origin || allowedOrigins?.includes(origin)) allowedOrigin = origin;
|
|
975
|
+
else allowedOrigin = null;
|
|
976
|
+
if (allowedOrigin === null) {
|
|
977
|
+
const response = Response.json({ error: "origin not allowed" }, {
|
|
978
|
+
status: 403,
|
|
979
|
+
headers: { "Cache-Control": "no-store" }
|
|
980
|
+
});
|
|
981
|
+
appendVary(response.headers, "Origin");
|
|
982
|
+
return {
|
|
983
|
+
ok: false,
|
|
984
|
+
response
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
ok: true,
|
|
989
|
+
withHeaders: (response) => addCorsHeaders(response, allowedOrigin, !allowAny),
|
|
990
|
+
preflight: (allowMethods) => {
|
|
991
|
+
const requestedHeaders = request.headers.get("Access-Control-Request-Headers");
|
|
992
|
+
const response = new Response(null, {
|
|
993
|
+
status: 204,
|
|
994
|
+
headers: {
|
|
995
|
+
"Access-Control-Allow-Methods": allowMethods,
|
|
996
|
+
"Access-Control-Allow-Headers": allowedRequestHeaders(request),
|
|
997
|
+
"Access-Control-Max-Age": "86400"
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
if (requestedHeaders) appendVary(response.headers, "Access-Control-Request-Headers");
|
|
1001
|
+
return addCorsHeaders(response, allowedOrigin, !allowAny);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
1006
|
+
export { oauthConfigurationErrorResponse as a, createRecorderForRuntime as c, JSON_HEADERS as d, finalizeWireResponse as f, getOAuthRuntime as i, createNoopRecorder as l, assertRestResourceBinding as n, assertResourcePathShape as o, methodNotAllowed as p, createRequestAuthorizer as r, resolveProtectedResource as s, evaluateCors as t, nowMs as u };
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* proxy which *overwrites* the header (Lovable's web-proxy does), never by a
|
|
16
16
|
* bare SDK consumer.
|
|
17
17
|
* - `X-Forwarded-Proto` only selects http/https, so it is gated separately:
|
|
18
|
-
* Supabase
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Supabase's gateway stamps it (that stack always trusts it), and Lovable's
|
|
19
|
+
* preview proxy overwrites it before the TanStack dev server, so the TanStack
|
|
20
|
+
* Vite plugin emits the flag by default.
|
|
21
21
|
*/
|
|
22
22
|
function applyForwardedOrigin(request, options) {
|
|
23
23
|
const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* proxy which *overwrites* the header (Lovable's web-proxy does), never by a
|
|
16
16
|
* bare SDK consumer.
|
|
17
17
|
* - `X-Forwarded-Proto` only selects http/https, so it is gated separately:
|
|
18
|
-
* Supabase
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Supabase's gateway stamps it (that stack always trusts it), and Lovable's
|
|
19
|
+
* preview proxy overwrites it before the TanStack dev server, so the TanStack
|
|
20
|
+
* Vite plugin emits the flag by default.
|
|
21
21
|
*/
|
|
22
22
|
function applyForwardedOrigin(request, options) {
|
|
23
23
|
const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_cors = require("./cors-CmKzDN7k.cjs");
|
|
2
2
|
//#region src/protocols/gate.ts
|
|
3
3
|
function createGatedHandler(gate) {
|
|
4
4
|
const allow = [...gate.methods, "OPTIONS"].join(", ");
|
|
@@ -8,9 +8,11 @@ function createGatedHandler(gate) {
|
|
|
8
8
|
if (!gate.methods.includes(request.method)) return gate.methodNotAllowed(allow);
|
|
9
9
|
return gate.handle(request, authResult, recorder, ...extra);
|
|
10
10
|
};
|
|
11
|
-
return async (request, recorder =
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
return async (request, recorder = require_cors.createNoopRecorder(), ...extra) => {
|
|
12
|
+
const cors = require_cors.evaluateCors(request, gate.allowedOrigins);
|
|
13
|
+
if (!cors.ok) return require_cors.finalizeWireResponse(request, cors.response);
|
|
14
|
+
if (request.method === "OPTIONS") return cors.preflight(allow);
|
|
15
|
+
const final = require_cors.finalizeWireResponse(request, cors.withHeaders(await respond(request, recorder, ...extra)));
|
|
14
16
|
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
15
17
|
return final;
|
|
16
18
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as finalizeWireResponse, l as createNoopRecorder, t as evaluateCors } from "./cors-D4YbODs-.js";
|
|
2
2
|
//#region src/protocols/gate.ts
|
|
3
3
|
function createGatedHandler(gate) {
|
|
4
4
|
const allow = [...gate.methods, "OPTIONS"].join(", ");
|
|
@@ -9,8 +9,10 @@ function createGatedHandler(gate) {
|
|
|
9
9
|
return gate.handle(request, authResult, recorder, ...extra);
|
|
10
10
|
};
|
|
11
11
|
return async (request, recorder = createNoopRecorder(), ...extra) => {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const cors = evaluateCors(request, gate.allowedOrigins);
|
|
13
|
+
if (!cors.ok) return finalizeWireResponse(request, cors.response);
|
|
14
|
+
if (request.method === "OPTIONS") return cors.preflight(allow);
|
|
15
|
+
const final = finalizeWireResponse(request, cors.withHeaders(await respond(request, recorder, ...extra)));
|
|
14
16
|
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
15
17
|
return final;
|
|
16
18
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_logger = require("./logger-CN1q-KNn.cjs");
|
|
3
|
+
const require_schema = require("./schema-D3vClxb7.cjs");
|
|
3
4
|
const require_errors = require("./errors-CN6Da8iz.cjs");
|
|
4
5
|
//#region src/core/define.ts
|
|
5
6
|
function assertUniqueNames(mcp) {
|
|
@@ -79,6 +80,39 @@ function freezeAuth(auth) {
|
|
|
79
80
|
if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
|
|
80
81
|
Object.freeze(auth);
|
|
81
82
|
}
|
|
83
|
+
function assertAllowedOrigins(allowedOrigins) {
|
|
84
|
+
if (allowedOrigins === void 0 || allowedOrigins === "any") return;
|
|
85
|
+
if (!Array.isArray(allowedOrigins)) throw new Error(`@lovable.dev/mcp-js: allowedOrigins must be "any" or an array of exact origins`);
|
|
86
|
+
for (const origin of allowedOrigins) {
|
|
87
|
+
if (typeof origin !== "string") throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain strings`);
|
|
88
|
+
let parsed;
|
|
89
|
+
try {
|
|
90
|
+
parsed = new URL(origin);
|
|
91
|
+
} catch {
|
|
92
|
+
throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain valid exact origins`);
|
|
93
|
+
}
|
|
94
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.origin !== origin) throw new Error(`@lovable.dev/mcp-js: allowedOrigins entry "${origin}" must be an exact http(s) origin without a path, query, fragment, or trailing slash`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function assertToolSchemas(mcp) {
|
|
98
|
+
for (const tool of mcp.tools) for (const [name, definition, strategy] of [[
|
|
99
|
+
"inputSchema",
|
|
100
|
+
tool.inputSchema,
|
|
101
|
+
"input"
|
|
102
|
+
], [
|
|
103
|
+
"outputSchema",
|
|
104
|
+
tool.outputSchema,
|
|
105
|
+
"output"
|
|
106
|
+
]]) {
|
|
107
|
+
if (!definition) continue;
|
|
108
|
+
try {
|
|
109
|
+
require_schema.jsonSchemaFromDefinition(definition, strategy);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
112
|
+
throw new Error(`@lovable.dev/mcp-js: tool "${tool.name}" ${name} cannot be represented as JSON Schema${detail}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
82
116
|
/**
|
|
83
117
|
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
84
118
|
* `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
|
|
@@ -101,8 +135,11 @@ function defineMcp(def) {
|
|
|
101
135
|
assertNonEmptyString("version", def.version);
|
|
102
136
|
assertUniqueNames(def);
|
|
103
137
|
if (def.auth) assertOAuthConfig(def.auth);
|
|
138
|
+
assertAllowedOrigins(def.allowedOrigins);
|
|
139
|
+
assertToolSchemas(def);
|
|
104
140
|
require_logger.resolveMetricsConfig(def.metrics);
|
|
105
141
|
freezeAuth(def.auth);
|
|
142
|
+
if (Array.isArray(def.allowedOrigins)) Object.freeze(def.allowedOrigins);
|
|
106
143
|
Object.freeze(def.tools);
|
|
107
144
|
for (const tool of def.tools) Object.freeze(tool);
|
|
108
145
|
return def;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as McpAuthConfig, S as JwtClaims, _ as
|
|
1
|
+
import { C as MetricsOptions, E as McpAuthConfig, S as MetricsConfig, T as JwtClaims, _ as ToolDefinition, a as EmbeddedResource, b as ZodRawShape, c as JsonValue, d as McpDefinitionInput, f as ResourceLink, g as ToolContent, h as ToolAnnotations, i as EmbeddedBlobResource, l as McpAllowedOrigins, m as TextContent, n as ContentAnnotations, o as EmbeddedTextResource, p as ResourceLinkIcon, r as ContentBlock, s as ImageContent, t as AudioContent, u as McpDefinition, v as ToolHandlerResult, w as ToolContext, x as ZodSchema, y as ZodOutputSchema } from "./types-D1cgsnPu.cjs";
|
|
2
2
|
//#region src/core/define.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
@@ -8,7 +8,7 @@ import { C as McpAuthConfig, S as JwtClaims, _ as ZodRawShape, a as EmbeddedReso
|
|
|
8
8
|
* full `z.object(...)` — the MCP SDK builds the object wrapper internally
|
|
9
9
|
* and the raw shape is what the JSON-RPC `tools/list` payload exposes.
|
|
10
10
|
*/
|
|
11
|
-
declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends
|
|
11
|
+
declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends ZodOutputSchema | undefined = undefined>(def: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
12
12
|
/**
|
|
13
13
|
* Declare the MCP server. `export default` the result from your MCP
|
|
14
14
|
* entrypoint (default `lib/mcp/index.ts`); the Vite plugin reads it to
|
|
@@ -71,4 +71,4 @@ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
|
|
|
71
71
|
*/
|
|
72
72
|
declare function setLogLevel(level: LogLevel): LogLevel;
|
|
73
73
|
//#endregion
|
|
74
|
-
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JwtClaims, type LogLevel, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
|
|
74
|
+
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JsonValue, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodOutputSchema, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as McpAuthConfig, S as JwtClaims, _ as
|
|
1
|
+
import { C as MetricsOptions, E as McpAuthConfig, S as MetricsConfig, T as JwtClaims, _ as ToolDefinition, a as EmbeddedResource, b as ZodRawShape, c as JsonValue, d as McpDefinitionInput, f as ResourceLink, g as ToolContent, h as ToolAnnotations, i as EmbeddedBlobResource, l as McpAllowedOrigins, m as TextContent, n as ContentAnnotations, o as EmbeddedTextResource, p as ResourceLinkIcon, r as ContentBlock, s as ImageContent, t as AudioContent, u as McpDefinition, v as ToolHandlerResult, w as ToolContext, x as ZodSchema, y as ZodOutputSchema } from "./types-D1cgsnPu.js";
|
|
2
2
|
//#region src/core/define.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
@@ -8,7 +8,7 @@ import { C as McpAuthConfig, S as JwtClaims, _ as ZodRawShape, a as EmbeddedReso
|
|
|
8
8
|
* full `z.object(...)` — the MCP SDK builds the object wrapper internally
|
|
9
9
|
* and the raw shape is what the JSON-RPC `tools/list` payload exposes.
|
|
10
10
|
*/
|
|
11
|
-
declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends
|
|
11
|
+
declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends ZodOutputSchema | undefined = undefined>(def: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
12
12
|
/**
|
|
13
13
|
* Declare the MCP server. `export default` the result from your MCP
|
|
14
14
|
* entrypoint (default `lib/mcp/index.ts`); the Vite plugin reads it to
|
|
@@ -71,4 +71,4 @@ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
|
|
|
71
71
|
*/
|
|
72
72
|
declare function setLogLevel(level: LogLevel): LogLevel;
|
|
73
73
|
//#endregion
|
|
74
|
-
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JwtClaims, type LogLevel, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
|
|
74
|
+
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JsonValue, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodOutputSchema, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { a as setLogLevel, o as parseSafeUrl, s as trimTrailingSlash, u as resolveMetricsConfig } from "./logger-Ctv5xUSD.js";
|
|
2
|
+
import { t as jsonSchemaFromDefinition } from "./schema-CW1jKvio.js";
|
|
2
3
|
import { i as ToolContext, t as ToolError } from "./errors-DSe96Ffd.js";
|
|
3
4
|
//#region src/core/define.ts
|
|
4
5
|
function assertUniqueNames(mcp) {
|
|
@@ -78,6 +79,39 @@ function freezeAuth(auth) {
|
|
|
78
79
|
if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
|
|
79
80
|
Object.freeze(auth);
|
|
80
81
|
}
|
|
82
|
+
function assertAllowedOrigins(allowedOrigins) {
|
|
83
|
+
if (allowedOrigins === void 0 || allowedOrigins === "any") return;
|
|
84
|
+
if (!Array.isArray(allowedOrigins)) throw new Error(`@lovable.dev/mcp-js: allowedOrigins must be "any" or an array of exact origins`);
|
|
85
|
+
for (const origin of allowedOrigins) {
|
|
86
|
+
if (typeof origin !== "string") throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain strings`);
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = new URL(origin);
|
|
90
|
+
} catch {
|
|
91
|
+
throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain valid exact origins`);
|
|
92
|
+
}
|
|
93
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.origin !== origin) throw new Error(`@lovable.dev/mcp-js: allowedOrigins entry "${origin}" must be an exact http(s) origin without a path, query, fragment, or trailing slash`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function assertToolSchemas(mcp) {
|
|
97
|
+
for (const tool of mcp.tools) for (const [name, definition, strategy] of [[
|
|
98
|
+
"inputSchema",
|
|
99
|
+
tool.inputSchema,
|
|
100
|
+
"input"
|
|
101
|
+
], [
|
|
102
|
+
"outputSchema",
|
|
103
|
+
tool.outputSchema,
|
|
104
|
+
"output"
|
|
105
|
+
]]) {
|
|
106
|
+
if (!definition) continue;
|
|
107
|
+
try {
|
|
108
|
+
jsonSchemaFromDefinition(definition, strategy);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
111
|
+
throw new Error(`@lovable.dev/mcp-js: tool "${tool.name}" ${name} cannot be represented as JSON Schema${detail}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
81
115
|
/**
|
|
82
116
|
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
83
117
|
* `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
|
|
@@ -100,8 +134,11 @@ function defineMcp(def) {
|
|
|
100
134
|
assertNonEmptyString("version", def.version);
|
|
101
135
|
assertUniqueNames(def);
|
|
102
136
|
if (def.auth) assertOAuthConfig(def.auth);
|
|
137
|
+
assertAllowedOrigins(def.allowedOrigins);
|
|
138
|
+
assertToolSchemas(def);
|
|
103
139
|
resolveMetricsConfig(def.metrics);
|
|
104
140
|
freezeAuth(def.auth);
|
|
141
|
+
if (Array.isArray(def.allowedOrigins)) Object.freeze(def.allowedOrigins);
|
|
105
142
|
Object.freeze(def.tools);
|
|
106
143
|
for (const tool of def.tools) Object.freeze(tool);
|
|
107
144
|
return def;
|