@lovable.dev/mcp-js 0.22.0 → 0.22.1-rc.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/dist/authorize-CumVyk7p.d.cts +27 -0
- package/dist/authorize-D6p4-9_D.d.ts +27 -0
- package/dist/base-DCe0kP79.d.cts +29 -0
- package/dist/base-DCe0kP79.d.ts +29 -0
- package/dist/cli/extract-manifest.cjs +116 -201
- package/dist/cli/extract-manifest.d.cts +1 -1
- package/dist/cli/extract-manifest.d.ts +1 -1
- package/dist/cli/extract-manifest.js +117 -156
- package/dist/content-D5LJAHyM.js +12 -0
- package/dist/content-NCYIGgOb.cjs +17 -0
- package/dist/context-BweeEcoA.cjs +56 -0
- package/dist/context-CIpPzN7P.js +51 -0
- package/dist/cors-BxwVxCm3.cjs +913 -0
- package/dist/cors-CJRL7tyh.js +830 -0
- package/dist/forwarded--h4efJy-.js +43 -0
- package/dist/forwarded-Pi8Hs5Ps.cjs +48 -0
- package/dist/fs-errors-CWNzOV75.cjs +11 -0
- package/dist/fs-errors-PA2t1TIp.js +6 -0
- package/dist/index.cjs +129 -329
- package/dist/index.d.cts +26 -26
- package/dist/index.d.ts +26 -26
- package/dist/index.js +118 -177
- package/dist/{io-D4srzRKc.d.ts → io-CfSPxeBN.d.ts} +6 -4
- package/dist/io-DPYvBqET.d.cts +15 -0
- package/dist/list-tools-B4TTPEl1.d.cts +8 -0
- package/dist/list-tools-Duj1zT0T.js +46 -0
- package/dist/list-tools-_Hh_o_mD.cjs +57 -0
- package/dist/list-tools-bh6-eqZM.d.ts +8 -0
- package/dist/logger-2OXtfiIW.js +141 -0
- package/dist/logger-BqPOa_E1.cjs +194 -0
- package/dist/mcp--iewDKrV.cjs +128 -0
- package/dist/mcp-CALHyhDi.js +123 -0
- package/dist/metadata-path-CAkNcfyY.js +12 -0
- package/dist/metadata-path-zd7NSNoa.cjs +17 -0
- package/dist/package-CLTXgGyU.cjs +9 -0
- package/dist/package-g7-GHkOW.js +4 -0
- package/dist/paths-IS65L6TA.js +7 -0
- package/dist/paths-OP318a6c.cjs +18 -0
- package/dist/protocols/mcp/index.cjs +3 -750
- package/dist/protocols/mcp/index.d.cts +6 -7
- package/dist/protocols/mcp/index.d.ts +6 -7
- package/dist/protocols/mcp/index.js +2 -12
- package/dist/protocols/oauth-metadata.cjs +48 -539
- package/dist/protocols/oauth-metadata.d.cts +5 -6
- package/dist/protocols/oauth-metadata.d.ts +5 -6
- package/dist/protocols/oauth-metadata.js +52 -10
- package/dist/protocols/rest/index.cjs +5 -843
- package/dist/protocols/rest/index.d.cts +7 -10
- package/dist/protocols/rest/index.d.ts +7 -10
- package/dist/protocols/rest/index.js +3 -16
- package/dist/rest-BcS3SYjx.cjs +122 -0
- package/dist/rest-Dv9Xq9cV.js +117 -0
- package/dist/stacks/supabase/index.cjs +76 -1318
- package/dist/stacks/supabase/index.d.cts +29 -30
- package/dist/stacks/supabase/index.d.ts +29 -30
- package/dist/stacks/supabase/index.js +73 -89
- package/dist/stacks/supabase/vite.cjs +258 -287
- package/dist/stacks/supabase/vite.d.cts +24 -24
- package/dist/stacks/supabase/vite.d.ts +24 -24
- package/dist/stacks/supabase/vite.js +252 -252
- package/dist/stacks/tanstack/index.cjs +42 -1272
- package/dist/stacks/tanstack/index.d.cts +8 -9
- package/dist/stacks/tanstack/index.d.ts +8 -9
- package/dist/stacks/tanstack/index.js +36 -41
- package/dist/stacks/tanstack/vite.cjs +223 -312
- package/dist/stacks/tanstack/vite.d.cts +80 -81
- package/dist/stacks/tanstack/vite.d.ts +80 -81
- package/dist/stacks/tanstack/vite.js +216 -282
- package/dist/types-BYdYYTjk.d.cts +321 -0
- package/dist/types-BYdYYTjk.d.ts +321 -0
- package/package.json +69 -28
- package/dist/authorize-BMeXd_Sh.d.ts +0 -25
- package/dist/base-CM-KCpp6.d.ts +0 -28
- package/dist/chunk-4EHZUUS5.js +0 -141
- package/dist/chunk-6DXGZZA4.js +0 -6
- package/dist/chunk-6KNM7RGD.js +0 -79
- package/dist/chunk-6QFZYYUV.js +0 -11
- package/dist/chunk-EOFPMSND.js +0 -6
- package/dist/chunk-H37EB22A.js +0 -125
- package/dist/chunk-MA5H6PSF.js +0 -46
- package/dist/chunk-P43FIUMZ.js +0 -114
- package/dist/chunk-RK62EMPY.js +0 -741
- package/dist/chunk-RLE4UE63.js +0 -58
- package/dist/chunk-UQK5UO6C.js +0 -26
- package/dist/chunk-XQWJN6DC.js +0 -14
- package/dist/chunk-Y3ZFPEQH.js +0 -8
- package/dist/types-qqrmJ736.d.ts +0 -321
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/core/forwarded.ts
|
|
2
|
+
/**
|
|
3
|
+
* Rewrites a request's URL to the public origin a reverse proxy forwards via
|
|
4
|
+
* `X-Forwarded-*`, so SDK-derived OAuth `resource`/metadata/`WWW-Authenticate`
|
|
5
|
+
* URLs name the host the client actually connected to — not the internal
|
|
6
|
+
* backend origin the proxy rewrites `Host` to. Without this, an app on a custom
|
|
7
|
+
* domain advertises its internal `*.lovable.dev`/`*.supabase.co` host and every
|
|
8
|
+
* MCP call 401s on an audience the client can't satisfy.
|
|
9
|
+
*
|
|
10
|
+
* Each header is trusted independently, because a forwarded value is only as
|
|
11
|
+
* trustworthy as the proxy that sets it:
|
|
12
|
+
* - `X-Forwarded-Host` can redirect the advertised resource to an attacker
|
|
13
|
+
* origin (the threat `auth/resource.ts` guards against), so it is honored only
|
|
14
|
+
* when `trustForwardedHost` is set — enabled on emit paths that sit behind a
|
|
15
|
+
* proxy which *overwrites* the header (Lovable's web-proxy does), never by a
|
|
16
|
+
* bare SDK consumer.
|
|
17
|
+
* - `X-Forwarded-Proto` only selects http/https, so it is gated separately:
|
|
18
|
+
* Supabase terminates TLS upstream and forwards over http with a
|
|
19
|
+
* gateway-stamped proto, so that stack trusts it; the TanStack proxy does not
|
|
20
|
+
* sanitize proto, so that stack leaves it alone.
|
|
21
|
+
*/
|
|
22
|
+
function applyForwardedOrigin(request, options) {
|
|
23
|
+
const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
|
|
24
|
+
const host = options.trustForwardedHost ? firstForwardedValue(request, "x-forwarded-host") : void 0;
|
|
25
|
+
if (proto === void 0 && host === void 0) return request;
|
|
26
|
+
const url = new URL(request.url);
|
|
27
|
+
let changed = false;
|
|
28
|
+
if (proto !== void 0 && `${proto}:` !== url.protocol) {
|
|
29
|
+
url.protocol = `${proto}:`;
|
|
30
|
+
changed = true;
|
|
31
|
+
}
|
|
32
|
+
if (host !== void 0 && host !== url.host) {
|
|
33
|
+
url.host = host;
|
|
34
|
+
changed = true;
|
|
35
|
+
}
|
|
36
|
+
return changed ? new Request(url.href, request) : request;
|
|
37
|
+
}
|
|
38
|
+
function firstForwardedValue(request, header) {
|
|
39
|
+
const value = request.headers.get(header)?.split(",")[0]?.trim();
|
|
40
|
+
return value ? value : void 0;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { applyForwardedOrigin as t };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/core/forwarded.ts
|
|
2
|
+
/**
|
|
3
|
+
* Rewrites a request's URL to the public origin a reverse proxy forwards via
|
|
4
|
+
* `X-Forwarded-*`, so SDK-derived OAuth `resource`/metadata/`WWW-Authenticate`
|
|
5
|
+
* URLs name the host the client actually connected to — not the internal
|
|
6
|
+
* backend origin the proxy rewrites `Host` to. Without this, an app on a custom
|
|
7
|
+
* domain advertises its internal `*.lovable.dev`/`*.supabase.co` host and every
|
|
8
|
+
* MCP call 401s on an audience the client can't satisfy.
|
|
9
|
+
*
|
|
10
|
+
* Each header is trusted independently, because a forwarded value is only as
|
|
11
|
+
* trustworthy as the proxy that sets it:
|
|
12
|
+
* - `X-Forwarded-Host` can redirect the advertised resource to an attacker
|
|
13
|
+
* origin (the threat `auth/resource.ts` guards against), so it is honored only
|
|
14
|
+
* when `trustForwardedHost` is set — enabled on emit paths that sit behind a
|
|
15
|
+
* proxy which *overwrites* the header (Lovable's web-proxy does), never by a
|
|
16
|
+
* bare SDK consumer.
|
|
17
|
+
* - `X-Forwarded-Proto` only selects http/https, so it is gated separately:
|
|
18
|
+
* Supabase terminates TLS upstream and forwards over http with a
|
|
19
|
+
* gateway-stamped proto, so that stack trusts it; the TanStack proxy does not
|
|
20
|
+
* sanitize proto, so that stack leaves it alone.
|
|
21
|
+
*/
|
|
22
|
+
function applyForwardedOrigin(request, options) {
|
|
23
|
+
const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
|
|
24
|
+
const host = options.trustForwardedHost ? firstForwardedValue(request, "x-forwarded-host") : void 0;
|
|
25
|
+
if (proto === void 0 && host === void 0) return request;
|
|
26
|
+
const url = new URL(request.url);
|
|
27
|
+
let changed = false;
|
|
28
|
+
if (proto !== void 0 && `${proto}:` !== url.protocol) {
|
|
29
|
+
url.protocol = `${proto}:`;
|
|
30
|
+
changed = true;
|
|
31
|
+
}
|
|
32
|
+
if (host !== void 0 && host !== url.host) {
|
|
33
|
+
url.host = host;
|
|
34
|
+
changed = true;
|
|
35
|
+
}
|
|
36
|
+
return changed ? new Request(url.href, request) : request;
|
|
37
|
+
}
|
|
38
|
+
function firstForwardedValue(request, header) {
|
|
39
|
+
const value = request.headers.get(header)?.split(",")[0]?.trim();
|
|
40
|
+
return value ? value : void 0;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
Object.defineProperty(exports, "applyForwardedOrigin", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function() {
|
|
46
|
+
return applyForwardedOrigin;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/core/fs-errors.ts
|
|
2
|
+
function isFileMissing(err) {
|
|
3
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
4
|
+
}
|
|
5
|
+
//#endregion
|
|
6
|
+
Object.defineProperty(exports, "isFileMissing", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function() {
|
|
9
|
+
return isFileMissing;
|
|
10
|
+
}
|
|
11
|
+
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,349 +1,149 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var src_exports = {};
|
|
22
|
-
__export(src_exports, {
|
|
23
|
-
ToolContext: () => ToolContext,
|
|
24
|
-
auth: () => auth,
|
|
25
|
-
defineMcp: () => defineMcp,
|
|
26
|
-
defineTool: () => defineTool,
|
|
27
|
-
setLogLevel: () => setLogLevel
|
|
28
|
-
});
|
|
29
|
-
module.exports = __toCommonJS(src_exports);
|
|
30
|
-
|
|
31
|
-
// src/metrics/config.ts
|
|
32
|
-
var DEFAULT_METRICS_ENDPOINT = "https://api.lovable.dev/v1/app-mcp-usage";
|
|
33
|
-
var METRICS_API_KEY_ENV_VAR = "LOVABLE_API_KEY";
|
|
34
|
-
function assertMetricsEndpoint(endpoint) {
|
|
35
|
-
let url;
|
|
36
|
-
try {
|
|
37
|
-
url = new URL(endpoint);
|
|
38
|
-
} catch {
|
|
39
|
-
throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be an absolute URL, got ${JSON.stringify(endpoint)}`);
|
|
40
|
-
}
|
|
41
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
42
|
-
throw new Error(`@lovable.dev/mcp-js: metrics.endpoint must be http(s), got ${JSON.stringify(endpoint)}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function resolveMetricsConfig(config = true) {
|
|
46
|
-
const options = typeof config === "boolean" ? { enabled: config } : config;
|
|
47
|
-
const endpoint = options.endpoint ?? DEFAULT_METRICS_ENDPOINT;
|
|
48
|
-
assertMetricsEndpoint(endpoint);
|
|
49
|
-
return Object.freeze({
|
|
50
|
-
enabled: options.enabled ?? true,
|
|
51
|
-
endpoint,
|
|
52
|
-
headers: options.headers ?? {},
|
|
53
|
-
apiKeyEnvVar: METRICS_API_KEY_ENV_VAR
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// src/core/url.ts
|
|
58
|
-
function trimTrailingSlash(value) {
|
|
59
|
-
return value.replace(/\/+$/, "");
|
|
60
|
-
}
|
|
61
|
-
function isLocalHTTPHost(hostname) {
|
|
62
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
63
|
-
}
|
|
64
|
-
function urlSafetyProblem(url) {
|
|
65
|
-
const isAllowedHTTP = url.protocol === "http:" && isLocalHTTPHost(url.hostname);
|
|
66
|
-
if (url.protocol !== "https:" && !isAllowedHTTP) {
|
|
67
|
-
return "must use https://, except localhost development URLs";
|
|
68
|
-
}
|
|
69
|
-
if (url.username || url.password) {
|
|
70
|
-
return "must not include credentials";
|
|
71
|
-
}
|
|
72
|
-
if (url.search || url.hash) {
|
|
73
|
-
return "must not include query or fragment";
|
|
74
|
-
}
|
|
75
|
-
return void 0;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/core/validation.ts
|
|
79
|
-
function parseSafeUrl(subject, raw, ErrorClass = Error) {
|
|
80
|
-
let url;
|
|
81
|
-
try {
|
|
82
|
-
url = new URL(raw);
|
|
83
|
-
} catch {
|
|
84
|
-
throw new ErrorClass(`${subject} must be an absolute URL`);
|
|
85
|
-
}
|
|
86
|
-
const problem = urlSafetyProblem(url);
|
|
87
|
-
if (problem) {
|
|
88
|
-
throw new ErrorClass(`${subject} ${problem}`);
|
|
89
|
-
}
|
|
90
|
-
return url;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// src/core/define.ts
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_logger = require("./logger-BqPOa_E1.cjs");
|
|
3
|
+
const require_context = require("./context-BweeEcoA.cjs");
|
|
4
|
+
//#region src/core/define.ts
|
|
94
5
|
function assertUniqueNames(mcp) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
seen.add(tool.name);
|
|
101
|
-
}
|
|
6
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7
|
+
for (const tool of mcp.tools) {
|
|
8
|
+
if (seen.has(tool.name)) throw new Error(`@lovable.dev/mcp-js: duplicate tool name "${tool.name}"`);
|
|
9
|
+
seen.add(tool.name);
|
|
10
|
+
}
|
|
102
11
|
}
|
|
103
12
|
function assertNonEmptyString(label, value) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
if (value !== value.trim()) {
|
|
108
|
-
throw new Error(`@lovable.dev/mcp-js: ${label} must not have leading or trailing whitespace`);
|
|
109
|
-
}
|
|
13
|
+
if (value.trim() === "") throw new Error(`@lovable.dev/mcp-js: ${label} must not be empty`);
|
|
14
|
+
if (value !== value.trim()) throw new Error(`@lovable.dev/mcp-js: ${label} must not have leading or trailing whitespace`);
|
|
110
15
|
}
|
|
111
16
|
function assertHttpsUrlField(name, value) {
|
|
112
|
-
|
|
113
|
-
|
|
17
|
+
assertNonEmptyString(`auth.${name}`, value);
|
|
18
|
+
require_logger.parseSafeUrl(`@lovable.dev/mcp-js: auth.${name}`, value);
|
|
114
19
|
}
|
|
115
20
|
function assertScope(scope) {
|
|
116
|
-
|
|
117
|
-
throw new Error(`@lovable.dev/mcp-js: OAuth scopes must be non-empty space-free tokens`);
|
|
118
|
-
}
|
|
21
|
+
if (scope.trim() === "" || /\s/.test(scope)) throw new Error(`@lovable.dev/mcp-js: OAuth scopes must be non-empty space-free tokens`);
|
|
119
22
|
}
|
|
120
23
|
function assertJwtAlgorithm(algorithm) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
if (/^HS\d+$/i.test(algorithm) || algorithm.toLowerCase() === "none") {
|
|
125
|
-
throw new Error(`@lovable.dev/mcp-js: auth.algorithms cannot include "${algorithm}"; this verifier is JWKS-only`);
|
|
126
|
-
}
|
|
24
|
+
if (algorithm.trim() === "" || /\s/.test(algorithm)) throw new Error(`@lovable.dev/mcp-js: auth.algorithms must contain non-empty space-free tokens`);
|
|
25
|
+
if (/^HS\d+$/i.test(algorithm) || algorithm.toLowerCase() === "none") throw new Error(`@lovable.dev/mcp-js: auth.algorithms cannot include "${algorithm}"; this verifier is JWKS-only`);
|
|
127
26
|
}
|
|
128
|
-
|
|
27
|
+
const MAX_CLOCK_TOLERANCE_SECONDS = 300;
|
|
129
28
|
function assertClockToleranceSeconds(value) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (!Array.isArray(auth2.algorithms)) {
|
|
192
|
-
throw new Error(`@lovable.dev/mcp-js: auth.algorithms must be an array`);
|
|
193
|
-
}
|
|
194
|
-
if (auth2.algorithms.length === 0) {
|
|
195
|
-
throw new Error(`@lovable.dev/mcp-js: auth.algorithms must not be empty`);
|
|
196
|
-
}
|
|
197
|
-
for (const algorithm of auth2.algorithms)
|
|
198
|
-
assertJwtAlgorithm(algorithm);
|
|
199
|
-
}
|
|
200
|
-
if (auth2.accessTokenTyp !== void 0) {
|
|
201
|
-
if (!Array.isArray(auth2.accessTokenTyp)) {
|
|
202
|
-
throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must be an array`);
|
|
203
|
-
}
|
|
204
|
-
if (auth2.accessTokenTyp.length === 0) {
|
|
205
|
-
throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must not be empty`);
|
|
206
|
-
}
|
|
207
|
-
for (const typ of auth2.accessTokenTyp) {
|
|
208
|
-
if (typeof typ !== "string") {
|
|
209
|
-
throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must contain strings`);
|
|
210
|
-
}
|
|
211
|
-
assertNonEmptyString("auth.accessTokenTyp", typ);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (auth2.clockToleranceSeconds !== void 0)
|
|
215
|
-
assertClockToleranceSeconds(auth2.clockToleranceSeconds);
|
|
216
|
-
if (auth2.resource === void 0 && auth2.acceptedAudiences === void 0) {
|
|
217
|
-
throw new Error(`@lovable.dev/mcp-js: auth.resource or auth.acceptedAudiences is required`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function freezeAuth(auth2) {
|
|
221
|
-
if (!auth2)
|
|
222
|
-
return;
|
|
223
|
-
if (auth2.acceptedAudiences)
|
|
224
|
-
Object.freeze(auth2.acceptedAudiences);
|
|
225
|
-
if (auth2.requiredScopes)
|
|
226
|
-
Object.freeze(auth2.requiredScopes);
|
|
227
|
-
if (auth2.algorithms)
|
|
228
|
-
Object.freeze(auth2.algorithms);
|
|
229
|
-
if (auth2.accessTokenTyp)
|
|
230
|
-
Object.freeze(auth2.accessTokenTyp);
|
|
231
|
-
Object.freeze(auth2);
|
|
232
|
-
}
|
|
29
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > MAX_CLOCK_TOLERANCE_SECONDS) throw new Error(`@lovable.dev/mcp-js: auth.clockToleranceSeconds must be a non-negative number of seconds no greater than ${MAX_CLOCK_TOLERANCE_SECONDS}`);
|
|
30
|
+
}
|
|
31
|
+
function assertOAuthConfig(auth) {
|
|
32
|
+
if (auth.type !== "oauth") {
|
|
33
|
+
const authType = String(auth.type);
|
|
34
|
+
throw new Error(`@lovable.dev/mcp-js: unsupported auth type "${authType}"`);
|
|
35
|
+
}
|
|
36
|
+
if (auth.issuer === void 0) throw new Error(`@lovable.dev/mcp-js: auth.issuer is required`);
|
|
37
|
+
assertHttpsUrlField("issuer", auth.issuer);
|
|
38
|
+
if (auth.jwksUri !== void 0) assertHttpsUrlField("jwksUri", auth.jwksUri);
|
|
39
|
+
if (auth.resource !== void 0) assertHttpsUrlField("resource", auth.resource);
|
|
40
|
+
if (auth.resourceName !== void 0) assertNonEmptyString("auth.resourceName", auth.resourceName);
|
|
41
|
+
if (auth.resourceDocumentation !== void 0) assertHttpsUrlField("resourceDocumentation", auth.resourceDocumentation);
|
|
42
|
+
if (auth.protectedResourceMetadataUrl !== void 0) assertHttpsUrlField("protectedResourceMetadataUrl", auth.protectedResourceMetadataUrl);
|
|
43
|
+
if (auth.acceptedAudiences !== void 0) {
|
|
44
|
+
if (!Array.isArray(auth.acceptedAudiences)) throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences must be an array`);
|
|
45
|
+
if (auth.acceptedAudiences.length === 0) throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences must not be empty`);
|
|
46
|
+
for (const audience of auth.acceptedAudiences) {
|
|
47
|
+
if (typeof audience !== "string") throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences must contain strings`);
|
|
48
|
+
assertNonEmptyString("auth.acceptedAudiences", audience);
|
|
49
|
+
if (require_logger.trimTrailingSlash(audience) === "") throw new Error(`@lovable.dev/mcp-js: auth.acceptedAudiences entries must not be only slashes`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (auth.requireOAuthClientClaim !== void 0 && typeof auth.requireOAuthClientClaim !== "boolean") throw new Error(`@lovable.dev/mcp-js: auth.requireOAuthClientClaim must be a boolean`);
|
|
53
|
+
if (auth.acceptResourceClaim !== void 0 && typeof auth.acceptResourceClaim !== "boolean") throw new Error(`@lovable.dev/mcp-js: auth.acceptResourceClaim must be a boolean`);
|
|
54
|
+
if (auth.requiredScopes !== void 0) {
|
|
55
|
+
if (!Array.isArray(auth.requiredScopes)) throw new Error(`@lovable.dev/mcp-js: auth.requiredScopes must be an array`);
|
|
56
|
+
for (const scope of auth.requiredScopes) assertScope(scope);
|
|
57
|
+
}
|
|
58
|
+
if (auth.algorithms !== void 0) {
|
|
59
|
+
if (!Array.isArray(auth.algorithms)) throw new Error(`@lovable.dev/mcp-js: auth.algorithms must be an array`);
|
|
60
|
+
if (auth.algorithms.length === 0) throw new Error(`@lovable.dev/mcp-js: auth.algorithms must not be empty`);
|
|
61
|
+
for (const algorithm of auth.algorithms) assertJwtAlgorithm(algorithm);
|
|
62
|
+
}
|
|
63
|
+
if (auth.accessTokenTyp !== void 0) {
|
|
64
|
+
if (!Array.isArray(auth.accessTokenTyp)) throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must be an array`);
|
|
65
|
+
if (auth.accessTokenTyp.length === 0) throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must not be empty`);
|
|
66
|
+
for (const typ of auth.accessTokenTyp) {
|
|
67
|
+
if (typeof typ !== "string") throw new Error(`@lovable.dev/mcp-js: auth.accessTokenTyp must contain strings`);
|
|
68
|
+
assertNonEmptyString("auth.accessTokenTyp", typ);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (auth.clockToleranceSeconds !== void 0) assertClockToleranceSeconds(auth.clockToleranceSeconds);
|
|
72
|
+
if (auth.resource === void 0 && auth.acceptedAudiences === void 0) throw new Error(`@lovable.dev/mcp-js: auth.resource or auth.acceptedAudiences is required`);
|
|
73
|
+
}
|
|
74
|
+
function freezeAuth(auth) {
|
|
75
|
+
if (!auth) return;
|
|
76
|
+
if (auth.acceptedAudiences) Object.freeze(auth.acceptedAudiences);
|
|
77
|
+
if (auth.requiredScopes) Object.freeze(auth.requiredScopes);
|
|
78
|
+
if (auth.algorithms) Object.freeze(auth.algorithms);
|
|
79
|
+
if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
|
|
80
|
+
Object.freeze(auth);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
84
|
+
* `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
|
|
85
|
+
*
|
|
86
|
+
* `inputSchema` is a raw zod shape (e.g. `{ text: z.string() }`), not a
|
|
87
|
+
* full `z.object(...)` — the MCP SDK builds the object wrapper internally
|
|
88
|
+
* and the raw shape is what the JSON-RPC `tools/list` payload exposes.
|
|
89
|
+
*/
|
|
233
90
|
function defineTool(def) {
|
|
234
|
-
|
|
91
|
+
return def;
|
|
235
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Declare the MCP server. `export default` the result from your MCP
|
|
95
|
+
* entrypoint (default `lib/mcp/index.ts`); the Vite plugin reads it to
|
|
96
|
+
* emit the framework-specific route(s) at build time.
|
|
97
|
+
*/
|
|
236
98
|
function defineMcp(def) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// src/auth/config.ts
|
|
99
|
+
assertNonEmptyString("name", def.name);
|
|
100
|
+
assertNonEmptyString("title", def.title);
|
|
101
|
+
assertNonEmptyString("version", def.version);
|
|
102
|
+
assertUniqueNames(def);
|
|
103
|
+
if (def.auth) assertOAuthConfig(def.auth);
|
|
104
|
+
require_logger.resolveMetricsConfig(def.metrics);
|
|
105
|
+
freezeAuth(def.auth);
|
|
106
|
+
Object.freeze(def.tools);
|
|
107
|
+
for (const tool of def.tools) Object.freeze(tool);
|
|
108
|
+
return def;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/auth/config.ts
|
|
252
112
|
function frozenArray(value) {
|
|
253
|
-
|
|
113
|
+
return value === void 0 ? void 0 : Object.freeze([...value]);
|
|
254
114
|
}
|
|
255
115
|
function frozenAudiences(value) {
|
|
256
|
-
|
|
116
|
+
return frozenArray(typeof value === "string" ? [value] : value);
|
|
257
117
|
}
|
|
118
|
+
/** Mutates `value` in place; callers pass a fresh literal so deleting keys is safe. */
|
|
258
119
|
function stripUndefined(value) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
120
|
+
for (const key of Object.keys(value)) if (value[key] === void 0) delete value[key];
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Configure OAuth against an issuer. `acceptedAudiences` (or `resource`) is the
|
|
125
|
+
* token-acceptance policy, not a per-request authorization decision: any token
|
|
126
|
+
* carrying an accepted `aud` passes. When an issuer mints broad project-wide
|
|
127
|
+
* audiences (e.g. Supabase's `aud: "authenticated"`), a token for one resource
|
|
128
|
+
* is structurally accepted by another sharing that audience — so gate per-tool
|
|
129
|
+
* authorization on `ctx.getClaims()` / `ctx.getClientId()`, never on the
|
|
130
|
+
* accepted audience.
|
|
131
|
+
*/
|
|
265
132
|
function issuer(options) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
constructor(auth2) {
|
|
284
|
-
this.#auth = auth2;
|
|
285
|
-
}
|
|
286
|
-
/** Whether the in-flight tool call carries a verified auth context. */
|
|
287
|
-
isAuthenticated() {
|
|
288
|
-
return this.#auth !== void 0;
|
|
289
|
-
}
|
|
290
|
-
/** The verified bearer token, or `undefined` when unauthenticated. Pass it to downstream APIs; never return or log it. */
|
|
291
|
-
getToken() {
|
|
292
|
-
return this.#auth?.bearer.token;
|
|
293
|
-
}
|
|
294
|
-
/** The verified user id (the token `sub`), or `undefined`. */
|
|
295
|
-
getUserId() {
|
|
296
|
-
return this.#auth?.principal.sub;
|
|
297
|
-
}
|
|
298
|
-
/** The verified user email, or `undefined` when absent. */
|
|
299
|
-
getUserEmail() {
|
|
300
|
-
return this.#auth?.principal.email;
|
|
301
|
-
}
|
|
302
|
-
/** The verified OAuth `client_id`, or `undefined`. */
|
|
303
|
-
getClientId() {
|
|
304
|
-
return this.#auth?.principal.clientId;
|
|
305
|
-
}
|
|
306
|
-
/** The verified OAuth scopes, or `undefined` when unauthenticated. */
|
|
307
|
-
getScopes() {
|
|
308
|
-
return this.#auth?.principal.scopes;
|
|
309
|
-
}
|
|
310
|
-
/** The verified token issuer, or `undefined`. */
|
|
311
|
-
getIssuer() {
|
|
312
|
-
return this.#auth?.principal.issuer;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* The full verified JWT claims, or `undefined`. Use this for app/business
|
|
316
|
-
* authorization on issuer-specific claims that have no dedicated accessor.
|
|
317
|
-
*/
|
|
318
|
-
getClaims() {
|
|
319
|
-
return this.#auth?.principal.claims;
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
// src/core/logger.ts
|
|
324
|
-
var LEVEL_RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
|
|
325
|
-
function isLogLevel(value) {
|
|
326
|
-
return typeof value === "string" && value in LEVEL_RANK;
|
|
327
|
-
}
|
|
328
|
-
var LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
329
|
-
function readEnvLevel() {
|
|
330
|
-
try {
|
|
331
|
-
const raw = typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0;
|
|
332
|
-
const normalized = raw?.trim().toLowerCase();
|
|
333
|
-
return isLogLevel(normalized) ? normalized : void 0;
|
|
334
|
-
} catch {
|
|
335
|
-
return void 0;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
var currentLevel = readEnvLevel() ?? "debug";
|
|
339
|
-
function setLogLevel(level) {
|
|
340
|
-
currentLevel = level;
|
|
341
|
-
}
|
|
342
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
343
|
-
0 && (module.exports = {
|
|
344
|
-
ToolContext,
|
|
345
|
-
auth,
|
|
346
|
-
defineMcp,
|
|
347
|
-
defineTool,
|
|
348
|
-
setLogLevel
|
|
349
|
-
});
|
|
133
|
+
return Object.freeze(stripUndefined({
|
|
134
|
+
type: "oauth",
|
|
135
|
+
...options,
|
|
136
|
+
acceptedAudiences: frozenAudiences(options.acceptedAudiences),
|
|
137
|
+
requiredScopes: frozenArray(options.requiredScopes),
|
|
138
|
+
algorithms: frozenArray(options.algorithms),
|
|
139
|
+
accessTokenTyp: frozenArray(options.accessTokenTyp)
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
const oauth = Object.freeze({ issuer });
|
|
143
|
+
const auth = Object.freeze({ oauth });
|
|
144
|
+
//#endregion
|
|
145
|
+
exports.ToolContext = require_context.ToolContext;
|
|
146
|
+
exports.auth = auth;
|
|
147
|
+
exports.defineMcp = defineMcp;
|
|
148
|
+
exports.defineTool = defineTool;
|
|
149
|
+
exports.setLogLevel = require_logger.setLogLevel;
|