toolcraft 0.0.745 → 0.0.746
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/composition.json +3 -3
- package/dist/composition.json +3 -3
- package/node_modules/auth-store/dist/transaction-lock.js +2 -2
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/client-registration.js +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +2 -9
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.js +8 -48
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/redirect-target.d.ts +10 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/redirect-target.js +42 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js +2 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/session-transaction.js +4 -3
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.d.ts +6 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.js +8 -0
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/dist/index.browser.js +75 -11
- package/node_modules/tiny-mcp-client/dist/index.js +431 -424
- package/node_modules/tiny-stdio-mcp-server/dist/base64.js +6 -1
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/headers.js +7 -6
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
|
@@ -1323,7 +1323,11 @@ var JSON_RPC_ERROR_CODES = Object.freeze({
|
|
|
1323
1323
|
function isBase64(value) {
|
|
1324
1324
|
if (value.length % 4 !== 0)
|
|
1325
1325
|
return false;
|
|
1326
|
-
|
|
1326
|
+
try {
|
|
1327
|
+
return btoa(atob(value)) === value;
|
|
1328
|
+
} catch {
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1327
1331
|
}
|
|
1328
1332
|
|
|
1329
1333
|
// ../tiny-stdio-mcp-server/dist/uri.js
|
|
@@ -3566,141 +3570,8 @@ function copyBoundedOAuthJson(value, message) {
|
|
|
3566
3570
|
}
|
|
3567
3571
|
}
|
|
3568
3572
|
|
|
3569
|
-
// ../mcp-oauth/dist/client/
|
|
3570
|
-
import http from "node:http";
|
|
3571
|
-
|
|
3572
|
-
// ../mcp-oauth/dist/client/authorization-state.js
|
|
3573
|
-
import crypto2 from "node:crypto";
|
|
3574
|
-
function createAuthorizationState(input) {
|
|
3575
|
-
const payload = {
|
|
3576
|
-
v: 1,
|
|
3577
|
-
n: crypto2.randomBytes(16).toString("base64url"),
|
|
3578
|
-
i: input.issuer,
|
|
3579
|
-
r: input.requireIssuer
|
|
3580
|
-
};
|
|
3581
|
-
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
3582
|
-
}
|
|
3583
|
-
function parseAuthorizationState(value) {
|
|
3584
|
-
if (value === null || value.length === 0) {
|
|
3585
|
-
return null;
|
|
3586
|
-
}
|
|
3587
|
-
try {
|
|
3588
|
-
const decoded = Buffer.from(value, "base64url").toString("utf8");
|
|
3589
|
-
const parsed = JSON.parse(decoded);
|
|
3590
|
-
if (!isObjectRecord(parsed)) {
|
|
3591
|
-
return null;
|
|
3592
|
-
}
|
|
3593
|
-
const version = getOwnEntry(parsed, "v");
|
|
3594
|
-
const nonce = getOwnEntry(parsed, "n");
|
|
3595
|
-
const issuer = getOwnEntry(parsed, "i");
|
|
3596
|
-
const requireIssuer = getOwnEntry(parsed, "r");
|
|
3597
|
-
if (version !== 1 || typeof nonce !== "string" || nonce.length === 0 || typeof issuer !== "string" || issuer.length === 0 || typeof requireIssuer !== "boolean") {
|
|
3598
|
-
return null;
|
|
3599
|
-
}
|
|
3600
|
-
return {
|
|
3601
|
-
issuer,
|
|
3602
|
-
requireIssuer
|
|
3603
|
-
};
|
|
3604
|
-
} catch {
|
|
3605
|
-
return null;
|
|
3606
|
-
}
|
|
3607
|
-
}
|
|
3608
|
-
function isObjectRecord(value) {
|
|
3609
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3610
|
-
}
|
|
3611
|
-
function getOwnEntry(record2, key2) {
|
|
3612
|
-
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
3613
|
-
}
|
|
3614
|
-
|
|
3615
|
-
// ../mcp-oauth/dist/client/authorization-error.js
|
|
3616
|
-
var authorizationErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
3617
|
-
var OAuthAuthorizationError = class extends Error {
|
|
3618
|
-
error;
|
|
3619
|
-
errorDescription;
|
|
3620
|
-
/** Recognize errors from separately bundled copies of this package. */
|
|
3621
|
-
static is(value) {
|
|
3622
|
-
return value instanceof Error && Object.getOwnPropertyDescriptor(value, authorizationErrorBrand)?.value === true;
|
|
3623
|
-
}
|
|
3624
|
-
constructor(error, errorDescription) {
|
|
3625
|
-
super(`OAuth authorization failed: ${error} \u2014 ${errorDescription}`);
|
|
3626
|
-
this.error = error;
|
|
3627
|
-
this.errorDescription = errorDescription;
|
|
3628
|
-
this.name = "OAuthAuthorizationError";
|
|
3629
|
-
Object.defineProperty(this, authorizationErrorBrand, { value: true });
|
|
3630
|
-
}
|
|
3631
|
-
};
|
|
3632
|
-
|
|
3633
|
-
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
3573
|
+
// ../mcp-oauth/dist/client/redirect-target.js
|
|
3634
3574
|
var oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
|
|
3635
|
-
function snapshotLoopbackAuthorizationOptions(options) {
|
|
3636
|
-
return {
|
|
3637
|
-
...options,
|
|
3638
|
-
openBrowser: options.openBrowser?.bind(options),
|
|
3639
|
-
readLine: options.readLine?.bind(options),
|
|
3640
|
-
waitForCallback: options.waitForCallback?.bind(options),
|
|
3641
|
-
createServer: options.createServer?.bind(options),
|
|
3642
|
-
callbackPath: options.callbackPath,
|
|
3643
|
-
redirectUri: options.redirectUri,
|
|
3644
|
-
signal: options.signal,
|
|
3645
|
-
timeoutMs: options.timeoutMs,
|
|
3646
|
-
landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage, title: options.landingPage.title, body: options.landingPage.body }
|
|
3647
|
-
};
|
|
3648
|
-
}
|
|
3649
|
-
async function createLoopbackAuthorizationSession(options = {}) {
|
|
3650
|
-
options = snapshotLoopbackAuthorizationOptions(options);
|
|
3651
|
-
options.signal?.throwIfAborted();
|
|
3652
|
-
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
3653
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
3654
|
-
throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
|
|
3655
|
-
if (options.waitForCallback !== void 0)
|
|
3656
|
-
return createHostedAuthorizationSession(options, timeoutMs);
|
|
3657
|
-
const target = loopbackTarget(options);
|
|
3658
|
-
const server = options.createServer ? options.createServer() : http.createServer();
|
|
3659
|
-
const controller = new AbortController();
|
|
3660
|
-
let closed = false;
|
|
3661
|
-
let used = false;
|
|
3662
|
-
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
3663
|
-
const teardown = () => {
|
|
3664
|
-
if (closed)
|
|
3665
|
-
return;
|
|
3666
|
-
closed = true;
|
|
3667
|
-
clearTimeout(timer);
|
|
3668
|
-
options.signal?.removeEventListener("abort", callerAbort);
|
|
3669
|
-
server.closeAllConnections?.();
|
|
3670
|
-
server.close();
|
|
3671
|
-
};
|
|
3672
|
-
const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
3673
|
-
timer.unref?.();
|
|
3674
|
-
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
3675
|
-
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
3676
|
-
if (options.signal?.aborted)
|
|
3677
|
-
callerAbort();
|
|
3678
|
-
let port;
|
|
3679
|
-
try {
|
|
3680
|
-
port = await startServer(server, target.port, target.host, controller.signal);
|
|
3681
|
-
} catch (error) {
|
|
3682
|
-
controller.abort(error);
|
|
3683
|
-
throw error;
|
|
3684
|
-
}
|
|
3685
|
-
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
3686
|
-
return {
|
|
3687
|
-
redirectUri,
|
|
3688
|
-
async waitForCode(authorizationUrl) {
|
|
3689
|
-
controller.signal.throwIfAborted();
|
|
3690
|
-
if (used)
|
|
3691
|
-
throw new Error("OAuth authorization session has already been used");
|
|
3692
|
-
used = true;
|
|
3693
|
-
try {
|
|
3694
|
-
return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
|
|
3695
|
-
} finally {
|
|
3696
|
-
clearTimeout(timer);
|
|
3697
|
-
}
|
|
3698
|
-
},
|
|
3699
|
-
close() {
|
|
3700
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
3701
|
-
}
|
|
3702
|
-
};
|
|
3703
|
-
}
|
|
3704
3575
|
function loopbackTarget(options) {
|
|
3705
3576
|
if (options.redirectUri !== void 0) {
|
|
3706
3577
|
let url;
|
|
@@ -3733,258 +3604,14 @@ function validateHostedOAuthRedirect(redirectUri, callbackPath) {
|
|
|
3733
3604
|
if (url.protocol !== "https:" || url.username || url.password || url.href.includes("#") || url.port === "0" || oauthCallbackParameters.some((name) => url.searchParams.has(name)) || [...redirectUri].some((char) => char.codePointAt(0) <= 32) || callbackPath !== void 0 && callbackPath !== url.pathname)
|
|
3734
3605
|
throw new Error("Invalid hosted OAuth redirect URI");
|
|
3735
3606
|
}
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
clearTimeout(timer);
|
|
3745
|
-
options.signal?.removeEventListener("abort", aborted);
|
|
3746
|
-
};
|
|
3747
|
-
controller.signal.addEventListener("abort", cleanup, { once: true });
|
|
3748
|
-
options.signal?.addEventListener("abort", aborted, { once: true });
|
|
3749
|
-
if (options.signal?.aborted)
|
|
3750
|
-
aborted();
|
|
3751
|
-
return {
|
|
3752
|
-
redirectUri,
|
|
3753
|
-
async waitForCode(authorizationUrl) {
|
|
3754
|
-
controller.signal.throwIfAborted();
|
|
3755
|
-
if (used)
|
|
3756
|
-
throw new Error("OAuth authorization session has already been used");
|
|
3757
|
-
used = true;
|
|
3758
|
-
try {
|
|
3759
|
-
const authorization = new URL(authorizationUrl);
|
|
3760
|
-
if (authorization.searchParams.getAll("redirect_uri").length !== 1 || authorization.searchParams.get("redirect_uri") !== redirectUri)
|
|
3761
|
-
throw new Error("OAuth authorization redirect mismatch");
|
|
3762
|
-
const callback = await new Promise((resolve, reject) => {
|
|
3763
|
-
const onAbort = () => reject(controller.signal.reason);
|
|
3764
|
-
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
3765
|
-
void Promise.resolve().then(() => {
|
|
3766
|
-
controller.signal.throwIfAborted();
|
|
3767
|
-
return options.waitForCallback({ authorizationUrl, redirectUri, signal: controller.signal });
|
|
3768
|
-
}).then(resolve, reject).finally(() => controller.signal.removeEventListener("abort", onAbort));
|
|
3769
|
-
});
|
|
3770
|
-
controller.signal.throwIfAborted();
|
|
3771
|
-
const url = new URL(callback), base = new URL(redirectUri);
|
|
3772
|
-
if (url.origin !== base.origin || url.pathname !== base.pathname || url.username || url.password || url.href.includes("#") || [...callback].some((char) => char.codePointAt(0) <= 32))
|
|
3773
|
-
throw new Error("OAuth callback redirect mismatch");
|
|
3774
|
-
const params = readAuthorizationCallbackParameters(url);
|
|
3775
|
-
for (const name of oauthCallbackParameters)
|
|
3776
|
-
url.searchParams.delete(name);
|
|
3777
|
-
if (url.searchParams.toString() !== base.searchParams.toString())
|
|
3778
|
-
throw new Error("OAuth callback redirect mismatch");
|
|
3779
|
-
const expected = readExpectedAuthorizationCallback(authorizationUrl);
|
|
3780
|
-
if (expected.state === null)
|
|
3781
|
-
throw new Error("OAuth authorization missing state");
|
|
3782
|
-
validateAuthorizationCallbackBinding(params, expected);
|
|
3783
|
-
if (params.error !== null)
|
|
3784
|
-
throw new OAuthAuthorizationError(params.error, params.errorDescription ?? params.error);
|
|
3785
|
-
return validateAuthorizationCallbackParameters(params, expected);
|
|
3786
|
-
} finally {
|
|
3787
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
3788
|
-
}
|
|
3789
|
-
},
|
|
3790
|
-
close() {
|
|
3791
|
-
controller.abort(new Error("OAuth authorization session closed"));
|
|
3792
|
-
}
|
|
3793
|
-
};
|
|
3794
|
-
}
|
|
3795
|
-
async function startServer(server, port, host, signal) {
|
|
3796
|
-
signal.throwIfAborted();
|
|
3797
|
-
return new Promise((resolve, reject) => {
|
|
3798
|
-
const cleanup = () => {
|
|
3799
|
-
server.off("error", handleError);
|
|
3800
|
-
signal.removeEventListener("abort", aborted);
|
|
3801
|
-
};
|
|
3802
|
-
const handleError = (error) => {
|
|
3803
|
-
cleanup();
|
|
3804
|
-
reject(error);
|
|
3805
|
-
};
|
|
3806
|
-
const aborted = () => {
|
|
3807
|
-
cleanup();
|
|
3808
|
-
reject(signal.reason);
|
|
3809
|
-
};
|
|
3810
|
-
server.once("error", handleError);
|
|
3811
|
-
signal.addEventListener("abort", aborted, { once: true });
|
|
3812
|
-
try {
|
|
3813
|
-
server.listen(port, host, () => {
|
|
3814
|
-
cleanup();
|
|
3815
|
-
if (signal.aborted) {
|
|
3816
|
-
server.close();
|
|
3817
|
-
reject(signal.reason);
|
|
3818
|
-
return;
|
|
3819
|
-
}
|
|
3820
|
-
const address = server.address();
|
|
3821
|
-
if (address === null || typeof address === "string") {
|
|
3822
|
-
reject(new Error("OAuth listener has no TCP address"));
|
|
3823
|
-
return;
|
|
3824
|
-
}
|
|
3825
|
-
resolve(address.port);
|
|
3826
|
-
});
|
|
3827
|
-
} catch (error) {
|
|
3828
|
-
cleanup();
|
|
3829
|
-
reject(error);
|
|
3830
|
-
}
|
|
3831
|
-
});
|
|
3832
|
-
}
|
|
3833
|
-
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
|
|
3834
|
-
signal.throwIfAborted();
|
|
3835
|
-
const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
|
|
3836
|
-
return new Promise((resolve, reject) => {
|
|
3837
|
-
let settled = false;
|
|
3838
|
-
const settle = (fn) => {
|
|
3839
|
-
if (settled)
|
|
3840
|
-
return;
|
|
3841
|
-
settled = true;
|
|
3842
|
-
server.off("request", request);
|
|
3843
|
-
signal.removeEventListener("abort", aborted);
|
|
3844
|
-
fn();
|
|
3845
|
-
};
|
|
3846
|
-
const aborted = () => settle(() => reject(signal.reason));
|
|
3847
|
-
const request = (req, res) => {
|
|
3848
|
-
let url;
|
|
3849
|
-
try {
|
|
3850
|
-
url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
3851
|
-
} catch {
|
|
3852
|
-
res.writeHead(400);
|
|
3853
|
-
res.end("Invalid callback URL");
|
|
3854
|
-
return;
|
|
3855
|
-
}
|
|
3856
|
-
if (url.pathname !== callbackPath) {
|
|
3857
|
-
res.writeHead(404);
|
|
3858
|
-
res.end("Not found");
|
|
3859
|
-
return;
|
|
3860
|
-
}
|
|
3861
|
-
try {
|
|
3862
|
-
const callbackParameters = readAuthorizationCallbackParameters(url);
|
|
3863
|
-
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3864
|
-
if (callbackParameters.error !== null)
|
|
3865
|
-
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
3866
|
-
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
3867
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
3868
|
-
res.end(buildSuccessPage(options.landingPage));
|
|
3869
|
-
settle(() => resolve(code));
|
|
3870
|
-
} catch (error) {
|
|
3871
|
-
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8", "X-Content-Type-Options": "nosniff" });
|
|
3872
|
-
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
3873
|
-
settle(() => reject(error));
|
|
3874
|
-
}
|
|
3875
|
-
};
|
|
3876
|
-
server.on("request", request);
|
|
3877
|
-
signal.addEventListener("abort", aborted, { once: true });
|
|
3878
|
-
if (options.readLine !== void 0) {
|
|
3879
|
-
void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
|
|
3880
|
-
if (settled)
|
|
3881
|
-
return;
|
|
3882
|
-
const callbackParameters = extractCallbackParametersFromInput(input);
|
|
3883
|
-
if (callbackParameters === null)
|
|
3884
|
-
throw new Error("OAuth callback missing authorization code");
|
|
3885
|
-
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3886
|
-
if (callbackParameters.error !== null)
|
|
3887
|
-
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
3888
|
-
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
3889
|
-
settle(() => resolve(code));
|
|
3890
|
-
}).catch((error) => settle(() => reject(error)));
|
|
3891
|
-
}
|
|
3892
|
-
if (options.openBrowser !== void 0) {
|
|
3893
|
-
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
3894
|
-
}
|
|
3895
|
-
});
|
|
3896
|
-
}
|
|
3897
|
-
function extractCallbackParametersFromInput(input) {
|
|
3898
|
-
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
3899
|
-
if (trimmed.length === 0) {
|
|
3900
|
-
return null;
|
|
3901
|
-
}
|
|
3902
|
-
let url;
|
|
3903
|
-
try {
|
|
3904
|
-
url = new URL(trimmed);
|
|
3905
|
-
} catch {
|
|
3906
|
-
return {
|
|
3907
|
-
code: trimmed,
|
|
3908
|
-
error: null,
|
|
3909
|
-
errorDescription: null,
|
|
3910
|
-
state: null,
|
|
3911
|
-
iss: null
|
|
3912
|
-
};
|
|
3913
|
-
}
|
|
3914
|
-
return readAuthorizationCallbackParameters(url);
|
|
3915
|
-
}
|
|
3916
|
-
function readAuthorizationCallbackParameters(url) {
|
|
3917
|
-
for (const parameter of oauthCallbackParameters) {
|
|
3918
|
-
if (url.searchParams.getAll(parameter).length > 1)
|
|
3919
|
-
throw new Error(`OAuth callback parameter '${parameter}' must occur only once`);
|
|
3920
|
-
}
|
|
3921
|
-
return {
|
|
3922
|
-
code: url.searchParams.get("code"),
|
|
3923
|
-
error: url.searchParams.get("error"),
|
|
3924
|
-
errorDescription: url.searchParams.get("error_description"),
|
|
3925
|
-
state: url.searchParams.get("state"),
|
|
3926
|
-
iss: url.searchParams.get("iss")
|
|
3927
|
-
};
|
|
3928
|
-
}
|
|
3929
|
-
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
3930
|
-
const url = new URL(authorizationUrl);
|
|
3931
|
-
const state = url.searchParams.get("state");
|
|
3932
|
-
const parsedState = parseAuthorizationState(state);
|
|
3933
|
-
return {
|
|
3934
|
-
state,
|
|
3935
|
-
issuer: parsedState?.issuer ?? null,
|
|
3936
|
-
requireIssuer: parsedState?.requireIssuer ?? false
|
|
3937
|
-
};
|
|
3938
|
-
}
|
|
3939
|
-
function validateAuthorizationCallbackParameters(callback, expected) {
|
|
3940
|
-
validateAuthorizationCallbackBinding(callback, expected);
|
|
3941
|
-
if (callback.code === null || callback.code.length === 0) {
|
|
3942
|
-
throw new Error("OAuth callback missing authorization code");
|
|
3943
|
-
}
|
|
3944
|
-
return callback.code;
|
|
3945
|
-
}
|
|
3946
|
-
function validateAuthorizationCallbackBinding(callback, expected) {
|
|
3947
|
-
if (expected.state !== null) {
|
|
3948
|
-
if (callback.state === null || callback.state.length === 0) {
|
|
3949
|
-
throw new Error("OAuth callback missing state");
|
|
3950
|
-
}
|
|
3951
|
-
if (callback.state !== expected.state) {
|
|
3952
|
-
throw new Error("OAuth callback state mismatch");
|
|
3953
|
-
}
|
|
3954
|
-
}
|
|
3955
|
-
if (expected.requireIssuer) {
|
|
3956
|
-
if (callback.iss === null || callback.iss.length === 0) {
|
|
3957
|
-
throw new Error("OAuth callback missing issuer");
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
if (callback.iss !== null && callback.iss.length > 0 && expected.issuer !== null && callback.iss !== expected.issuer) {
|
|
3961
|
-
throw new Error("OAuth callback issuer mismatch");
|
|
3962
|
-
}
|
|
3963
|
-
}
|
|
3964
|
-
function escapeHtml(text) {
|
|
3965
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
3966
|
-
}
|
|
3967
|
-
function buildSuccessPage(landingPage) {
|
|
3968
|
-
const title = landingPage?.title ?? "Connected";
|
|
3969
|
-
const body = landingPage?.body ?? "You can close this tab and return to your terminal.";
|
|
3970
|
-
return [
|
|
3971
|
-
"<!DOCTYPE html>",
|
|
3972
|
-
`<html><head><meta charset=utf-8><title>${escapeHtml(title)}</title></head>`,
|
|
3973
|
-
'<body style="font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0">',
|
|
3974
|
-
'<div style="text-align:center">',
|
|
3975
|
-
`<h1>${escapeHtml(title)}</h1>`,
|
|
3976
|
-
`<p style="color:#666">${escapeHtml(body)}</p>`,
|
|
3977
|
-
"</div></body></html>"
|
|
3978
|
-
].join("");
|
|
3979
|
-
}
|
|
3980
|
-
|
|
3981
|
-
// ../mcp-oauth/dist/client/token-auth-method.js
|
|
3982
|
-
function normalizeOAuthTokenEndpointAuthMethod(value) {
|
|
3983
|
-
if (value === void 0 || value === null)
|
|
3984
|
-
return void 0;
|
|
3985
|
-
if (value !== "none" && value !== "client_secret_post" && value !== "client_secret_basic")
|
|
3986
|
-
throw new Error("Unsupported OAuth token endpoint authentication method");
|
|
3987
|
-
return value;
|
|
3607
|
+
|
|
3608
|
+
// ../mcp-oauth/dist/client/token-auth-method.js
|
|
3609
|
+
function normalizeOAuthTokenEndpointAuthMethod(value) {
|
|
3610
|
+
if (value === void 0 || value === null)
|
|
3611
|
+
return void 0;
|
|
3612
|
+
if (value !== "none" && value !== "client_secret_post" && value !== "client_secret_basic")
|
|
3613
|
+
throw new Error("Unsupported OAuth token endpoint authentication method");
|
|
3614
|
+
return value;
|
|
3988
3615
|
}
|
|
3989
3616
|
|
|
3990
3617
|
// ../mcp-oauth/dist/client/client-registration.js
|
|
@@ -4114,7 +3741,7 @@ function registrationMatchesRedirect(client, requestedUri, fresh = false) {
|
|
|
4114
3741
|
}
|
|
4115
3742
|
|
|
4116
3743
|
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
4117
|
-
import
|
|
3744
|
+
import crypto2 from "node:crypto";
|
|
4118
3745
|
import path4 from "node:path";
|
|
4119
3746
|
|
|
4120
3747
|
// ../auth-store/dist/encrypted-file-store.js
|
|
@@ -4133,8 +3760,8 @@ import { randomUUID } from "node:crypto";
|
|
|
4133
3760
|
import path from "node:path";
|
|
4134
3761
|
async function withSecretStoreFileLock(fs2, lockDirectory, operation, options = {}) {
|
|
4135
3762
|
options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
|
|
4136
|
-
const timeoutMs = options.timeoutMs ??
|
|
4137
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647)
|
|
3763
|
+
const timeoutMs = options.timeoutMs ?? Infinity;
|
|
3764
|
+
if (timeoutMs !== Infinity && (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647))
|
|
4138
3765
|
throw new Error("Invalid secret-store transaction lock timeout");
|
|
4139
3766
|
options.signal?.throwIfAborted();
|
|
4140
3767
|
const deadline = performance.now() + timeoutMs;
|
|
@@ -4540,10 +4167,10 @@ function parseEncryptedDocument(raw) {
|
|
|
4540
4167
|
if (!isRecord2(parsed)) {
|
|
4541
4168
|
return null;
|
|
4542
4169
|
}
|
|
4543
|
-
const version =
|
|
4544
|
-
const iv =
|
|
4545
|
-
const authTag =
|
|
4546
|
-
const ciphertext =
|
|
4170
|
+
const version = getOwnEntry(parsed, "version");
|
|
4171
|
+
const iv = getOwnEntry(parsed, "iv");
|
|
4172
|
+
const authTag = getOwnEntry(parsed, "authTag");
|
|
4173
|
+
const ciphertext = getOwnEntry(parsed, "ciphertext");
|
|
4547
4174
|
if (version !== ENCRYPTION_VERSION) {
|
|
4548
4175
|
return null;
|
|
4549
4176
|
}
|
|
@@ -4563,7 +4190,7 @@ function parseEncryptedDocument(raw) {
|
|
|
4563
4190
|
function isRecord2(value) {
|
|
4564
4191
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
4565
4192
|
}
|
|
4566
|
-
function
|
|
4193
|
+
function getOwnEntry(record2, key2) {
|
|
4567
4194
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4568
4195
|
}
|
|
4569
4196
|
function isNotFoundError(error) {
|
|
@@ -4724,14 +4351,14 @@ function createSecurityCliFailure(operation, result) {
|
|
|
4724
4351
|
return new Error(`Failed to ${operation}: security exited with code ${exitCode}`);
|
|
4725
4352
|
}
|
|
4726
4353
|
function getCommandExitCode(result) {
|
|
4727
|
-
const value =
|
|
4354
|
+
const value = getOwnEntry2(result, "exitCode");
|
|
4728
4355
|
return typeof value === "number" && Number.isInteger(value) ? value : 1;
|
|
4729
4356
|
}
|
|
4730
4357
|
function getCommandOutput(result, key2) {
|
|
4731
|
-
const value =
|
|
4358
|
+
const value = getOwnEntry2(result, key2);
|
|
4732
4359
|
return typeof value === "string" ? value : "";
|
|
4733
4360
|
}
|
|
4734
|
-
function
|
|
4361
|
+
function getOwnEntry2(record2, key2) {
|
|
4735
4362
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4736
4363
|
}
|
|
4737
4364
|
|
|
@@ -4850,7 +4477,7 @@ function createAuthStoreClientStore(options, namespace) {
|
|
|
4850
4477
|
} catch {
|
|
4851
4478
|
throw new Error("Stored OAuth client must be valid JSON; reset the store explicitly to recover");
|
|
4852
4479
|
}
|
|
4853
|
-
if (
|
|
4480
|
+
if (isObjectRecord(parsed) && typeof getOwnEntry3(parsed, "clientId") === "string")
|
|
4854
4481
|
return parsed;
|
|
4855
4482
|
throw new Error("Stored OAuth client must be a JSON object with clientId");
|
|
4856
4483
|
},
|
|
@@ -4871,7 +4498,7 @@ function snapshotPersistenceOptions(options) {
|
|
|
4871
4498
|
}
|
|
4872
4499
|
function createNamedSecretStore(key2, options, defaults, namespace) {
|
|
4873
4500
|
options = snapshotOAuthPersistenceOptions(options);
|
|
4874
|
-
const hash =
|
|
4501
|
+
const hash = crypto2.createHash("sha256").update(namespace === void 0 ? key2 : JSON.stringify([namespace, key2])).digest("hex");
|
|
4875
4502
|
const configuredFilePath = options.fileStore?.filePath;
|
|
4876
4503
|
const parsedFilePath = configuredFilePath === void 0 ? null : path4.parse(configuredFilePath);
|
|
4877
4504
|
const fileStore = {
|
|
@@ -4909,47 +4536,47 @@ function assertPersistenceNamespace(namespace) {
|
|
|
4909
4536
|
if (namespace !== void 0 && (typeof namespace !== "string" || namespace.trim() === "" || Buffer.byteLength(namespace, "utf8") > 1024))
|
|
4910
4537
|
throw new Error("OAuth persistence namespace must be a nonempty string within 1024 bytes");
|
|
4911
4538
|
}
|
|
4912
|
-
function
|
|
4539
|
+
function isObjectRecord(value) {
|
|
4913
4540
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4914
4541
|
}
|
|
4915
|
-
function
|
|
4542
|
+
function getOwnEntry3(record2, key2) {
|
|
4916
4543
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4917
4544
|
}
|
|
4918
4545
|
function getOwnString(record2, key2) {
|
|
4919
|
-
const value =
|
|
4546
|
+
const value = getOwnEntry3(record2, key2);
|
|
4920
4547
|
return typeof value === "string" ? value : void 0;
|
|
4921
4548
|
}
|
|
4922
4549
|
function isStoredOAuthSession(value) {
|
|
4923
|
-
if (!
|
|
4550
|
+
if (!isObjectRecord(value)) {
|
|
4924
4551
|
return false;
|
|
4925
4552
|
}
|
|
4926
|
-
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && normalizeStoredOAuthClient(
|
|
4553
|
+
return isNonBlankOwnString(value, "resource") && isNonBlankOwnString(value, "authorizationServer") && normalizeStoredOAuthClient(getOwnEntry3(value, "client")) !== null && isStoredOAuthDiscovery(getOwnEntry3(value, "discovery")) && (getOwnEntry3(value, "requestedScope") === void 0 || isNonBlankOwnString(value, "requestedScope")) && (getOwnEntry3(value, "refreshState") === void 0 || getOwnEntry3(value, "refreshState") === "pending" && getOwnEntry3(value, "tokens") === void 0) && isStoredOAuthTokensOrMissing(getOwnEntry3(value, "tokens"));
|
|
4927
4554
|
}
|
|
4928
4555
|
function isStoredOAuthDiscovery(value) {
|
|
4929
|
-
if (!
|
|
4556
|
+
if (!isObjectRecord(value)) {
|
|
4930
4557
|
return false;
|
|
4931
4558
|
}
|
|
4932
|
-
return isNonBlankOwnString(value, "resourceMetadataUrl") &&
|
|
4559
|
+
return isNonBlankOwnString(value, "resourceMetadataUrl") && isObjectRecord(getOwnEntry3(value, "resourceMetadata")) && isObjectRecord(getOwnEntry3(value, "authorizationServerMetadata"));
|
|
4933
4560
|
}
|
|
4934
4561
|
function isStoredOAuthTokensOrMissing(value) {
|
|
4935
4562
|
if (value === void 0) {
|
|
4936
4563
|
return true;
|
|
4937
4564
|
}
|
|
4938
|
-
if (!
|
|
4565
|
+
if (!isObjectRecord(value)) {
|
|
4939
4566
|
return false;
|
|
4940
4567
|
}
|
|
4941
4568
|
if (!isNonBlankOwnString(value, "accessToken") || getOwnString(value, "tokenType") !== "Bearer") {
|
|
4942
4569
|
return false;
|
|
4943
4570
|
}
|
|
4944
|
-
const expiresAt =
|
|
4571
|
+
const expiresAt = getOwnEntry3(value, "expiresAt");
|
|
4945
4572
|
if (expiresAt !== null && (typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt > MAX_JS_DATE_MS || !Number.isFinite(new Date(expiresAt).getTime()))) {
|
|
4946
4573
|
return false;
|
|
4947
4574
|
}
|
|
4948
|
-
const refreshToken =
|
|
4575
|
+
const refreshToken = getOwnEntry3(value, "refreshToken");
|
|
4949
4576
|
if (refreshToken !== void 0 && (typeof refreshToken !== "string" || refreshToken.trim().length === 0)) {
|
|
4950
4577
|
return false;
|
|
4951
4578
|
}
|
|
4952
|
-
const scope =
|
|
4579
|
+
const scope = getOwnEntry3(value, "scope");
|
|
4953
4580
|
return scope === void 0 || scope === "" || typeof scope === "string" && scope.trim().length > 0;
|
|
4954
4581
|
}
|
|
4955
4582
|
function isNonBlankOwnString(record2, key2) {
|
|
@@ -5016,8 +4643,8 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
5016
4643
|
async function replace(record2, options2) {
|
|
5017
4644
|
options2 = { ...options2, signal: options2.signal, timeoutMs: options2.timeoutMs };
|
|
5018
4645
|
options2.signal?.throwIfAborted();
|
|
5019
|
-
const timeoutMs = options2.timeoutMs ??
|
|
5020
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
4646
|
+
const timeoutMs = options2.timeoutMs ?? Infinity;
|
|
4647
|
+
if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647))
|
|
5021
4648
|
throw new Error("OAuth replacement timeoutMs must be a positive supported timer interval");
|
|
5022
4649
|
if (store.withLock === void 0)
|
|
5023
4650
|
throw new Error("OAuth resource identity backend must support transaction locks");
|
|
@@ -5183,6 +4810,385 @@ async function fetchMcpResponse(fetchImplementation, input, init = {}) {
|
|
|
5183
4810
|
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
5184
4811
|
import { URL as URL2 } from "node:url";
|
|
5185
4812
|
|
|
4813
|
+
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
4814
|
+
import http from "node:http";
|
|
4815
|
+
|
|
4816
|
+
// ../mcp-oauth/dist/client/authorization-state.js
|
|
4817
|
+
import crypto3 from "node:crypto";
|
|
4818
|
+
function createAuthorizationState(input) {
|
|
4819
|
+
const payload = {
|
|
4820
|
+
v: 1,
|
|
4821
|
+
n: crypto3.randomBytes(16).toString("base64url"),
|
|
4822
|
+
i: input.issuer,
|
|
4823
|
+
r: input.requireIssuer
|
|
4824
|
+
};
|
|
4825
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
4826
|
+
}
|
|
4827
|
+
function parseAuthorizationState(value) {
|
|
4828
|
+
if (value === null || value.length === 0) {
|
|
4829
|
+
return null;
|
|
4830
|
+
}
|
|
4831
|
+
try {
|
|
4832
|
+
const decoded = Buffer.from(value, "base64url").toString("utf8");
|
|
4833
|
+
const parsed = JSON.parse(decoded);
|
|
4834
|
+
if (!isObjectRecord2(parsed)) {
|
|
4835
|
+
return null;
|
|
4836
|
+
}
|
|
4837
|
+
const version = getOwnEntry4(parsed, "v");
|
|
4838
|
+
const nonce = getOwnEntry4(parsed, "n");
|
|
4839
|
+
const issuer = getOwnEntry4(parsed, "i");
|
|
4840
|
+
const requireIssuer = getOwnEntry4(parsed, "r");
|
|
4841
|
+
if (version !== 1 || typeof nonce !== "string" || nonce.length === 0 || typeof issuer !== "string" || issuer.length === 0 || typeof requireIssuer !== "boolean") {
|
|
4842
|
+
return null;
|
|
4843
|
+
}
|
|
4844
|
+
return {
|
|
4845
|
+
issuer,
|
|
4846
|
+
requireIssuer
|
|
4847
|
+
};
|
|
4848
|
+
} catch {
|
|
4849
|
+
return null;
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
function isObjectRecord2(value) {
|
|
4853
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4854
|
+
}
|
|
4855
|
+
function getOwnEntry4(record2, key2) {
|
|
4856
|
+
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4859
|
+
// ../mcp-oauth/dist/client/authorization-error.js
|
|
4860
|
+
var authorizationErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
4861
|
+
var OAuthAuthorizationError = class extends Error {
|
|
4862
|
+
error;
|
|
4863
|
+
errorDescription;
|
|
4864
|
+
/** Recognize errors from separately bundled copies of this package. */
|
|
4865
|
+
static is(value) {
|
|
4866
|
+
return value instanceof Error && Object.getOwnPropertyDescriptor(value, authorizationErrorBrand)?.value === true;
|
|
4867
|
+
}
|
|
4868
|
+
constructor(error, errorDescription) {
|
|
4869
|
+
super(`OAuth authorization failed: ${error} \u2014 ${errorDescription}`);
|
|
4870
|
+
this.error = error;
|
|
4871
|
+
this.errorDescription = errorDescription;
|
|
4872
|
+
this.name = "OAuthAuthorizationError";
|
|
4873
|
+
Object.defineProperty(this, authorizationErrorBrand, { value: true });
|
|
4874
|
+
}
|
|
4875
|
+
};
|
|
4876
|
+
|
|
4877
|
+
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
4878
|
+
function snapshotLoopbackAuthorizationOptions(options) {
|
|
4879
|
+
return {
|
|
4880
|
+
...options,
|
|
4881
|
+
openBrowser: options.openBrowser?.bind(options),
|
|
4882
|
+
readLine: options.readLine?.bind(options),
|
|
4883
|
+
waitForCallback: options.waitForCallback?.bind(options),
|
|
4884
|
+
createServer: options.createServer?.bind(options),
|
|
4885
|
+
callbackPath: options.callbackPath,
|
|
4886
|
+
redirectUri: options.redirectUri,
|
|
4887
|
+
signal: options.signal,
|
|
4888
|
+
timeoutMs: options.timeoutMs,
|
|
4889
|
+
landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage, title: options.landingPage.title, body: options.landingPage.body }
|
|
4890
|
+
};
|
|
4891
|
+
}
|
|
4892
|
+
async function createLoopbackAuthorizationSession(options = {}) {
|
|
4893
|
+
options = snapshotLoopbackAuthorizationOptions(options);
|
|
4894
|
+
options.signal?.throwIfAborted();
|
|
4895
|
+
const timeoutMs = options.timeoutMs ?? Infinity;
|
|
4896
|
+
if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647))
|
|
4897
|
+
throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
|
|
4898
|
+
if (options.waitForCallback !== void 0)
|
|
4899
|
+
return createHostedAuthorizationSession(options, timeoutMs);
|
|
4900
|
+
const target = loopbackTarget(options);
|
|
4901
|
+
const server = options.createServer ? options.createServer() : http.createServer();
|
|
4902
|
+
const controller = new AbortController();
|
|
4903
|
+
let closed = false;
|
|
4904
|
+
let used = false;
|
|
4905
|
+
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
4906
|
+
const teardown = () => {
|
|
4907
|
+
if (closed)
|
|
4908
|
+
return;
|
|
4909
|
+
closed = true;
|
|
4910
|
+
clearTimeout(timer);
|
|
4911
|
+
options.signal?.removeEventListener("abort", callerAbort);
|
|
4912
|
+
server.closeAllConnections?.();
|
|
4913
|
+
server.close();
|
|
4914
|
+
};
|
|
4915
|
+
const timer = timeoutMs === Infinity ? void 0 : setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
4916
|
+
timer?.unref?.();
|
|
4917
|
+
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
4918
|
+
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
4919
|
+
if (options.signal?.aborted)
|
|
4920
|
+
callerAbort();
|
|
4921
|
+
let port;
|
|
4922
|
+
try {
|
|
4923
|
+
port = await startServer(server, target.port, target.host, controller.signal);
|
|
4924
|
+
} catch (error) {
|
|
4925
|
+
controller.abort(error);
|
|
4926
|
+
throw error;
|
|
4927
|
+
}
|
|
4928
|
+
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
4929
|
+
return {
|
|
4930
|
+
redirectUri,
|
|
4931
|
+
async waitForCode(authorizationUrl) {
|
|
4932
|
+
controller.signal.throwIfAborted();
|
|
4933
|
+
if (used)
|
|
4934
|
+
throw new Error("OAuth authorization session has already been used");
|
|
4935
|
+
used = true;
|
|
4936
|
+
try {
|
|
4937
|
+
return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
|
|
4938
|
+
} finally {
|
|
4939
|
+
clearTimeout(timer);
|
|
4940
|
+
}
|
|
4941
|
+
},
|
|
4942
|
+
close() {
|
|
4943
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
4944
|
+
}
|
|
4945
|
+
};
|
|
4946
|
+
}
|
|
4947
|
+
function createHostedAuthorizationSession(options, timeoutMs) {
|
|
4948
|
+
validateAuthorizationRedirect(options);
|
|
4949
|
+
const redirectUri = options.redirectUri, controller = new AbortController();
|
|
4950
|
+
let used = false;
|
|
4951
|
+
const aborted = () => controller.abort(options.signal?.reason);
|
|
4952
|
+
const timer = timeoutMs === Infinity ? void 0 : setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
4953
|
+
timer?.unref?.();
|
|
4954
|
+
const cleanup = () => {
|
|
4955
|
+
clearTimeout(timer);
|
|
4956
|
+
options.signal?.removeEventListener("abort", aborted);
|
|
4957
|
+
};
|
|
4958
|
+
controller.signal.addEventListener("abort", cleanup, { once: true });
|
|
4959
|
+
options.signal?.addEventListener("abort", aborted, { once: true });
|
|
4960
|
+
if (options.signal?.aborted)
|
|
4961
|
+
aborted();
|
|
4962
|
+
return {
|
|
4963
|
+
redirectUri,
|
|
4964
|
+
async waitForCode(authorizationUrl) {
|
|
4965
|
+
controller.signal.throwIfAborted();
|
|
4966
|
+
if (used)
|
|
4967
|
+
throw new Error("OAuth authorization session has already been used");
|
|
4968
|
+
used = true;
|
|
4969
|
+
try {
|
|
4970
|
+
const authorization = new URL(authorizationUrl);
|
|
4971
|
+
if (authorization.searchParams.getAll("redirect_uri").length !== 1 || authorization.searchParams.get("redirect_uri") !== redirectUri)
|
|
4972
|
+
throw new Error("OAuth authorization redirect mismatch");
|
|
4973
|
+
const callback = await new Promise((resolve, reject) => {
|
|
4974
|
+
const onAbort = () => reject(controller.signal.reason);
|
|
4975
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
4976
|
+
void Promise.resolve().then(() => {
|
|
4977
|
+
controller.signal.throwIfAborted();
|
|
4978
|
+
return options.waitForCallback({ authorizationUrl, redirectUri, signal: controller.signal });
|
|
4979
|
+
}).then(resolve, reject).finally(() => controller.signal.removeEventListener("abort", onAbort));
|
|
4980
|
+
});
|
|
4981
|
+
controller.signal.throwIfAborted();
|
|
4982
|
+
const url = new URL(callback), base = new URL(redirectUri);
|
|
4983
|
+
if (url.origin !== base.origin || url.pathname !== base.pathname || url.username || url.password || url.href.includes("#") || [...callback].some((char) => char.codePointAt(0) <= 32))
|
|
4984
|
+
throw new Error("OAuth callback redirect mismatch");
|
|
4985
|
+
const params = readAuthorizationCallbackParameters(url);
|
|
4986
|
+
for (const name of oauthCallbackParameters)
|
|
4987
|
+
url.searchParams.delete(name);
|
|
4988
|
+
if (url.searchParams.toString() !== base.searchParams.toString())
|
|
4989
|
+
throw new Error("OAuth callback redirect mismatch");
|
|
4990
|
+
const expected = readExpectedAuthorizationCallback(authorizationUrl);
|
|
4991
|
+
if (expected.state === null)
|
|
4992
|
+
throw new Error("OAuth authorization missing state");
|
|
4993
|
+
validateAuthorizationCallbackBinding(params, expected);
|
|
4994
|
+
if (params.error !== null)
|
|
4995
|
+
throw new OAuthAuthorizationError(params.error, params.errorDescription ?? params.error);
|
|
4996
|
+
return validateAuthorizationCallbackParameters(params, expected);
|
|
4997
|
+
} finally {
|
|
4998
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
4999
|
+
}
|
|
5000
|
+
},
|
|
5001
|
+
close() {
|
|
5002
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
5003
|
+
}
|
|
5004
|
+
};
|
|
5005
|
+
}
|
|
5006
|
+
async function startServer(server, port, host, signal) {
|
|
5007
|
+
signal.throwIfAborted();
|
|
5008
|
+
return new Promise((resolve, reject) => {
|
|
5009
|
+
const cleanup = () => {
|
|
5010
|
+
server.off("error", handleError);
|
|
5011
|
+
signal.removeEventListener("abort", aborted);
|
|
5012
|
+
};
|
|
5013
|
+
const handleError = (error) => {
|
|
5014
|
+
cleanup();
|
|
5015
|
+
reject(error);
|
|
5016
|
+
};
|
|
5017
|
+
const aborted = () => {
|
|
5018
|
+
cleanup();
|
|
5019
|
+
reject(signal.reason);
|
|
5020
|
+
};
|
|
5021
|
+
server.once("error", handleError);
|
|
5022
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
5023
|
+
try {
|
|
5024
|
+
server.listen(port, host, () => {
|
|
5025
|
+
cleanup();
|
|
5026
|
+
if (signal.aborted) {
|
|
5027
|
+
server.close();
|
|
5028
|
+
reject(signal.reason);
|
|
5029
|
+
return;
|
|
5030
|
+
}
|
|
5031
|
+
const address = server.address();
|
|
5032
|
+
if (address === null || typeof address === "string") {
|
|
5033
|
+
reject(new Error("OAuth listener has no TCP address"));
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
5036
|
+
resolve(address.port);
|
|
5037
|
+
});
|
|
5038
|
+
} catch (error) {
|
|
5039
|
+
cleanup();
|
|
5040
|
+
reject(error);
|
|
5041
|
+
}
|
|
5042
|
+
});
|
|
5043
|
+
}
|
|
5044
|
+
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
|
|
5045
|
+
signal.throwIfAborted();
|
|
5046
|
+
const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
|
|
5047
|
+
return new Promise((resolve, reject) => {
|
|
5048
|
+
let settled = false;
|
|
5049
|
+
const settle = (fn) => {
|
|
5050
|
+
if (settled)
|
|
5051
|
+
return;
|
|
5052
|
+
settled = true;
|
|
5053
|
+
server.off("request", request);
|
|
5054
|
+
signal.removeEventListener("abort", aborted);
|
|
5055
|
+
fn();
|
|
5056
|
+
};
|
|
5057
|
+
const aborted = () => settle(() => reject(signal.reason));
|
|
5058
|
+
const request = (req, res) => {
|
|
5059
|
+
let url;
|
|
5060
|
+
try {
|
|
5061
|
+
url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
5062
|
+
} catch {
|
|
5063
|
+
res.writeHead(400);
|
|
5064
|
+
res.end("Invalid callback URL");
|
|
5065
|
+
return;
|
|
5066
|
+
}
|
|
5067
|
+
if (url.pathname !== callbackPath) {
|
|
5068
|
+
res.writeHead(404);
|
|
5069
|
+
res.end("Not found");
|
|
5070
|
+
return;
|
|
5071
|
+
}
|
|
5072
|
+
try {
|
|
5073
|
+
const callbackParameters = readAuthorizationCallbackParameters(url);
|
|
5074
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
5075
|
+
if (callbackParameters.error !== null)
|
|
5076
|
+
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
5077
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
5078
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
5079
|
+
res.end(buildSuccessPage(options.landingPage));
|
|
5080
|
+
settle(() => resolve(code));
|
|
5081
|
+
} catch (error) {
|
|
5082
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8", "X-Content-Type-Options": "nosniff" });
|
|
5083
|
+
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
5084
|
+
settle(() => reject(error));
|
|
5085
|
+
}
|
|
5086
|
+
};
|
|
5087
|
+
server.on("request", request);
|
|
5088
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
5089
|
+
if (options.readLine !== void 0) {
|
|
5090
|
+
void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
|
|
5091
|
+
if (settled)
|
|
5092
|
+
return;
|
|
5093
|
+
const callbackParameters = extractCallbackParametersFromInput(input);
|
|
5094
|
+
if (callbackParameters === null)
|
|
5095
|
+
throw new Error("OAuth callback missing authorization code");
|
|
5096
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
5097
|
+
if (callbackParameters.error !== null)
|
|
5098
|
+
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
5099
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
5100
|
+
settle(() => resolve(code));
|
|
5101
|
+
}).catch((error) => settle(() => reject(error)));
|
|
5102
|
+
}
|
|
5103
|
+
if (options.openBrowser !== void 0) {
|
|
5104
|
+
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
5105
|
+
}
|
|
5106
|
+
});
|
|
5107
|
+
}
|
|
5108
|
+
function extractCallbackParametersFromInput(input) {
|
|
5109
|
+
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
5110
|
+
if (trimmed.length === 0) {
|
|
5111
|
+
return null;
|
|
5112
|
+
}
|
|
5113
|
+
let url;
|
|
5114
|
+
try {
|
|
5115
|
+
url = new URL(trimmed);
|
|
5116
|
+
} catch {
|
|
5117
|
+
return {
|
|
5118
|
+
code: trimmed,
|
|
5119
|
+
error: null,
|
|
5120
|
+
errorDescription: null,
|
|
5121
|
+
state: null,
|
|
5122
|
+
iss: null
|
|
5123
|
+
};
|
|
5124
|
+
}
|
|
5125
|
+
return readAuthorizationCallbackParameters(url);
|
|
5126
|
+
}
|
|
5127
|
+
function readAuthorizationCallbackParameters(url) {
|
|
5128
|
+
for (const parameter of oauthCallbackParameters) {
|
|
5129
|
+
if (url.searchParams.getAll(parameter).length > 1)
|
|
5130
|
+
throw new Error(`OAuth callback parameter '${parameter}' must occur only once`);
|
|
5131
|
+
}
|
|
5132
|
+
return {
|
|
5133
|
+
code: url.searchParams.get("code"),
|
|
5134
|
+
error: url.searchParams.get("error"),
|
|
5135
|
+
errorDescription: url.searchParams.get("error_description"),
|
|
5136
|
+
state: url.searchParams.get("state"),
|
|
5137
|
+
iss: url.searchParams.get("iss")
|
|
5138
|
+
};
|
|
5139
|
+
}
|
|
5140
|
+
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
5141
|
+
const url = new URL(authorizationUrl);
|
|
5142
|
+
const state = url.searchParams.get("state");
|
|
5143
|
+
const parsedState = parseAuthorizationState(state);
|
|
5144
|
+
return {
|
|
5145
|
+
state,
|
|
5146
|
+
issuer: parsedState?.issuer ?? null,
|
|
5147
|
+
requireIssuer: parsedState?.requireIssuer ?? false
|
|
5148
|
+
};
|
|
5149
|
+
}
|
|
5150
|
+
function validateAuthorizationCallbackParameters(callback, expected) {
|
|
5151
|
+
validateAuthorizationCallbackBinding(callback, expected);
|
|
5152
|
+
if (callback.code === null || callback.code.length === 0) {
|
|
5153
|
+
throw new Error("OAuth callback missing authorization code");
|
|
5154
|
+
}
|
|
5155
|
+
return callback.code;
|
|
5156
|
+
}
|
|
5157
|
+
function validateAuthorizationCallbackBinding(callback, expected) {
|
|
5158
|
+
if (expected.state !== null) {
|
|
5159
|
+
if (callback.state === null || callback.state.length === 0) {
|
|
5160
|
+
throw new Error("OAuth callback missing state");
|
|
5161
|
+
}
|
|
5162
|
+
if (callback.state !== expected.state) {
|
|
5163
|
+
throw new Error("OAuth callback state mismatch");
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
if (expected.requireIssuer) {
|
|
5167
|
+
if (callback.iss === null || callback.iss.length === 0) {
|
|
5168
|
+
throw new Error("OAuth callback missing issuer");
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5171
|
+
if (callback.iss !== null && callback.iss.length > 0 && expected.issuer !== null && callback.iss !== expected.issuer) {
|
|
5172
|
+
throw new Error("OAuth callback issuer mismatch");
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
function escapeHtml(text) {
|
|
5176
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
5177
|
+
}
|
|
5178
|
+
function buildSuccessPage(landingPage) {
|
|
5179
|
+
const title = landingPage?.title ?? "Connected";
|
|
5180
|
+
const body = landingPage?.body ?? "You can close this tab and return to your terminal.";
|
|
5181
|
+
return [
|
|
5182
|
+
"<!DOCTYPE html>",
|
|
5183
|
+
`<html><head><meta charset=utf-8><title>${escapeHtml(title)}</title></head>`,
|
|
5184
|
+
'<body style="font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0">',
|
|
5185
|
+
'<div style="text-align:center">',
|
|
5186
|
+
`<h1>${escapeHtml(title)}</h1>`,
|
|
5187
|
+
`<p style="color:#666">${escapeHtml(body)}</p>`,
|
|
5188
|
+
"</div></body></html>"
|
|
5189
|
+
].join("");
|
|
5190
|
+
}
|
|
5191
|
+
|
|
5186
5192
|
// ../mcp-oauth/dist/client/pkce.js
|
|
5187
5193
|
import crypto4 from "node:crypto";
|
|
5188
5194
|
function generateCodeVerifier() {
|
|
@@ -5439,8 +5445,8 @@ function normalizeBearerTokenType(value) {
|
|
|
5439
5445
|
// ../mcp-oauth/dist/client/session-transaction.js
|
|
5440
5446
|
var queues = /* @__PURE__ */ new WeakMap();
|
|
5441
5447
|
async function withOAuthSessionTransaction(store, resource, operation, options = {}) {
|
|
5442
|
-
const timeoutMs = options.timeoutMs ??
|
|
5443
|
-
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
5448
|
+
const timeoutMs = options.timeoutMs ?? Infinity;
|
|
5449
|
+
if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647))
|
|
5444
5450
|
throw new Error("sessionLockTimeoutMs must be an integer from 1 to 2147483647 milliseconds");
|
|
5445
5451
|
options.signal?.throwIfAborted();
|
|
5446
5452
|
const started = performance.now();
|
|
@@ -5463,7 +5469,8 @@ async function withOAuthSessionTransaction(store, resource, operation, options =
|
|
|
5463
5469
|
});
|
|
5464
5470
|
const abort = () => rejectWait(options.signal?.reason);
|
|
5465
5471
|
try {
|
|
5466
|
-
|
|
5472
|
+
if (timeoutMs !== Infinity)
|
|
5473
|
+
timer = setTimeout(() => rejectWait(new Error("Timed out waiting for OAuth session transaction lock")), timeoutMs);
|
|
5467
5474
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
5468
5475
|
if (options.signal?.aborted)
|
|
5469
5476
|
abort();
|
|
@@ -6313,15 +6320,15 @@ function shouldReRegisterStoredDynamicClient(error, client, alreadyAttempted) {
|
|
|
6313
6320
|
|
|
6314
6321
|
// ../tiny-stdio-mcp-server/dist/headers.js
|
|
6315
6322
|
function encodeHeaderValue(value) {
|
|
6316
|
-
const bytes =
|
|
6317
|
-
if (
|
|
6323
|
+
const bytes = new TextEncoder().encode(value);
|
|
6324
|
+
if (new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes) !== value)
|
|
6318
6325
|
throw new Error("MCP header value must contain valid Unicode");
|
|
6319
6326
|
const sentinel = value.startsWith("=?base64?") && value.endsWith("?=");
|
|
6320
6327
|
const unsafe = value.trim() !== value || [...value].some((character) => {
|
|
6321
6328
|
const code = character.charCodeAt(0);
|
|
6322
6329
|
return code !== 9 && (code < 32 || code > 126);
|
|
6323
6330
|
});
|
|
6324
|
-
return sentinel || unsafe ? `=?base64?${bytes.
|
|
6331
|
+
return sentinel || unsafe ? `=?base64?${btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join(""))}?=` : value;
|
|
6325
6332
|
}
|
|
6326
6333
|
var schemaMaps = /* @__PURE__ */ new Set([
|
|
6327
6334
|
"properties",
|
|
@@ -8221,7 +8228,7 @@ var HttpTransport = class {
|
|
|
8221
8228
|
if (this.legacyProtocolVersion !== void 0) headers.set("MCP-Protocol-Version", this.legacyProtocolVersion);
|
|
8222
8229
|
if (this.lastEventId !== void 0) {
|
|
8223
8230
|
if (this.lastEventId === "") headers.delete("Last-Event-ID");
|
|
8224
|
-
else headers.set("Last-Event-ID",
|
|
8231
|
+
else headers.set("Last-Event-ID", Array.from(new TextEncoder().encode(this.lastEventId), (byte) => String.fromCharCode(byte)).join(""));
|
|
8225
8232
|
}
|
|
8226
8233
|
return this.authorizeRequestHeaders(headers, signal);
|
|
8227
8234
|
}
|
|
@@ -8650,7 +8657,7 @@ async function* readLines(stream, maxLineBytes = 16 * 1024 * 1024) {
|
|
|
8650
8657
|
let bytes = 0;
|
|
8651
8658
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
8652
8659
|
const append = (part) => {
|
|
8653
|
-
bytes +=
|
|
8660
|
+
bytes += new TextEncoder().encode(part).byteLength;
|
|
8654
8661
|
if (bytes > maxLineBytes) throw new Error(`Stdio input line byte limit exceeded (${maxLineBytes} bytes)`);
|
|
8655
8662
|
if (part.length > 0) parts.push(part);
|
|
8656
8663
|
};
|
|
@@ -8711,7 +8718,7 @@ var SseParser = class {
|
|
|
8711
8718
|
else if (index === this.buffer.length - 1) this.skipLf = true;
|
|
8712
8719
|
}
|
|
8713
8720
|
start = index + 1;
|
|
8714
|
-
if (
|
|
8721
|
+
if (new TextEncoder().encode(line).byteLength > this.maxEventBytes)
|
|
8715
8722
|
throw new Error(`SSE event exceeds ${this.maxEventBytes} bytes`);
|
|
8716
8723
|
this.consumeLine(line, messages);
|
|
8717
8724
|
this.assertEventSize("");
|
|
@@ -8743,7 +8750,7 @@ var SseParser = class {
|
|
|
8743
8750
|
return;
|
|
8744
8751
|
}
|
|
8745
8752
|
if (field === "data") {
|
|
8746
|
-
this.dataBytes +=
|
|
8753
|
+
this.dataBytes += new TextEncoder().encode(value).byteLength + (this.dataLines.length > 0 ? 1 : 0);
|
|
8747
8754
|
this.dataLines.push(value);
|
|
8748
8755
|
return;
|
|
8749
8756
|
}
|
|
@@ -8782,7 +8789,7 @@ var SseParser = class {
|
|
|
8782
8789
|
this.dataBytes = 0;
|
|
8783
8790
|
}
|
|
8784
8791
|
assertEventSize(partialLine) {
|
|
8785
|
-
if (this.dataBytes +
|
|
8792
|
+
if (this.dataBytes + new TextEncoder().encode(this.eventType ?? "").byteLength + new TextEncoder().encode(this.eventId).byteLength + new TextEncoder().encode(partialLine).byteLength > this.maxEventBytes)
|
|
8786
8793
|
throw new Error(`SSE event exceeds ${this.maxEventBytes} bytes`);
|
|
8787
8794
|
}
|
|
8788
8795
|
};
|