@ai-ecoverse/mcpcat 0.10.0 → 0.11.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/dist/host/mcpcat.mjs +114 -76
- package/dist/kernel/mcpcat.mjs +114 -76
- package/package.json +1 -1
package/dist/host/mcpcat.mjs
CHANGED
|
@@ -7773,6 +7773,14 @@ var McpAuthError = class extends Error {
|
|
|
7773
7773
|
this.target = target;
|
|
7774
7774
|
}
|
|
7775
7775
|
};
|
|
7776
|
+
var AuthRetryableError = class extends Error {
|
|
7777
|
+
response;
|
|
7778
|
+
constructor(response) {
|
|
7779
|
+
super(`HTTP ${response.status}: auth retry`);
|
|
7780
|
+
this.name = "AuthRetryableError";
|
|
7781
|
+
this.response = response;
|
|
7782
|
+
}
|
|
7783
|
+
};
|
|
7776
7784
|
function selectSseFrame(body, targetId) {
|
|
7777
7785
|
const text = new TextDecoder().decode(body);
|
|
7778
7786
|
const blocks = text.replace(/\r\n/g, "\n").split(/\n\n/);
|
|
@@ -7797,6 +7805,42 @@ function mcpNameForMethod(method, params) {
|
|
|
7797
7805
|
if (method === "resources/read") return params.uri;
|
|
7798
7806
|
return void 0;
|
|
7799
7807
|
}
|
|
7808
|
+
function makeAbort(timeoutMs, externalSignal) {
|
|
7809
|
+
const controller = new AbortController();
|
|
7810
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7811
|
+
if (externalSignal) {
|
|
7812
|
+
if (externalSignal.aborted) {
|
|
7813
|
+
clearTimeout(timer);
|
|
7814
|
+
controller.abort();
|
|
7815
|
+
} else {
|
|
7816
|
+
externalSignal.addEventListener(
|
|
7817
|
+
"abort",
|
|
7818
|
+
() => {
|
|
7819
|
+
clearTimeout(timer);
|
|
7820
|
+
controller.abort();
|
|
7821
|
+
},
|
|
7822
|
+
{ once: true }
|
|
7823
|
+
);
|
|
7824
|
+
}
|
|
7825
|
+
}
|
|
7826
|
+
return { controller, timer };
|
|
7827
|
+
}
|
|
7828
|
+
function logHeaders(verbose, dir, entries) {
|
|
7829
|
+
for (const [k, v] of entries) {
|
|
7830
|
+
if (dir === ">") verbose.headerOut(k, v);
|
|
7831
|
+
else verbose.headerIn(k, v);
|
|
7832
|
+
}
|
|
7833
|
+
}
|
|
7834
|
+
function buildServerInfo(discoverResult2) {
|
|
7835
|
+
return {
|
|
7836
|
+
name: discoverResult2?.serverInfo?.name,
|
|
7837
|
+
version: discoverResult2?.serverInfo?.version,
|
|
7838
|
+
title: discoverResult2?.serverInfo?.title,
|
|
7839
|
+
protocolVersion: STATELESS_PROTOCOL_VERSION,
|
|
7840
|
+
capabilities: discoverResult2?.capabilities ?? {},
|
|
7841
|
+
instructions: discoverResult2?.instructions
|
|
7842
|
+
};
|
|
7843
|
+
}
|
|
7800
7844
|
async function createStatelessConnection(options) {
|
|
7801
7845
|
const {
|
|
7802
7846
|
url,
|
|
@@ -7805,11 +7849,12 @@ async function createStatelessConnection(options) {
|
|
|
7805
7849
|
timeoutMs = 6e4,
|
|
7806
7850
|
clientInfo = { name: "mcpcat", version: "0.0.0" },
|
|
7807
7851
|
signal: externalSignal,
|
|
7808
|
-
verbose
|
|
7852
|
+
verbose,
|
|
7853
|
+
authProvider
|
|
7809
7854
|
} = options;
|
|
7810
7855
|
let nextId2 = 1;
|
|
7811
7856
|
let negotiatedVersion;
|
|
7812
|
-
function buildHeaders(method, params) {
|
|
7857
|
+
function buildHeaders(method, params, token) {
|
|
7813
7858
|
const h = {
|
|
7814
7859
|
"Content-Type": "application/json",
|
|
7815
7860
|
Accept: "application/json, text/event-stream",
|
|
@@ -7819,36 +7864,12 @@ async function createStatelessConnection(options) {
|
|
|
7819
7864
|
const n = mcpNameForMethod(method, params);
|
|
7820
7865
|
if (n) h["Mcp-Name"] = n;
|
|
7821
7866
|
if (negotiatedVersion) h["Mcp-Protocol-Version"] = negotiatedVersion;
|
|
7867
|
+
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
7822
7868
|
return h;
|
|
7823
7869
|
}
|
|
7824
|
-
function makeAbort() {
|
|
7825
|
-
const controller = new AbortController();
|
|
7826
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7827
|
-
if (externalSignal) {
|
|
7828
|
-
if (externalSignal.aborted) {
|
|
7829
|
-
clearTimeout(timer);
|
|
7830
|
-
controller.abort();
|
|
7831
|
-
} else {
|
|
7832
|
-
externalSignal.addEventListener(
|
|
7833
|
-
"abort",
|
|
7834
|
-
() => {
|
|
7835
|
-
clearTimeout(timer);
|
|
7836
|
-
controller.abort();
|
|
7837
|
-
},
|
|
7838
|
-
{ once: true }
|
|
7839
|
-
);
|
|
7840
|
-
}
|
|
7841
|
-
}
|
|
7842
|
-
return { controller, timer };
|
|
7843
|
-
}
|
|
7844
|
-
function logHeaders(dir, entries) {
|
|
7845
|
-
if (!verbose?.enabled) return;
|
|
7846
|
-
for (const [k, v] of entries) {
|
|
7847
|
-
if (dir === ">") verbose.headerOut(k, v);
|
|
7848
|
-
else verbose.headerIn(k, v);
|
|
7849
|
-
}
|
|
7850
|
-
}
|
|
7851
7870
|
async function handleErrorStatus(res, method) {
|
|
7871
|
+
if ((res.status === 401 || res.status === 403) && authProvider)
|
|
7872
|
+
throw new AuthRetryableError(res);
|
|
7852
7873
|
if (res.status === 401 || res.status === 403) throw new McpAuthError(res.status, url);
|
|
7853
7874
|
const text = await res.text();
|
|
7854
7875
|
let rpc;
|
|
@@ -7860,34 +7881,27 @@ async function createStatelessConnection(options) {
|
|
|
7860
7881
|
if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
|
|
7861
7882
|
throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
|
|
7862
7883
|
}
|
|
7863
|
-
async function
|
|
7864
|
-
const
|
|
7865
|
-
const headers = buildHeaders(method, params);
|
|
7866
|
-
const meta = {
|
|
7867
|
-
protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
|
|
7868
|
-
clientInfo
|
|
7869
|
-
};
|
|
7870
|
-
const body = JSON.stringify({
|
|
7871
|
-
jsonrpc: "2.0",
|
|
7872
|
-
id,
|
|
7873
|
-
method,
|
|
7874
|
-
params: { ...params, _meta: meta }
|
|
7875
|
-
});
|
|
7884
|
+
async function doFetch(method, params, body) {
|
|
7885
|
+
const token = await authProvider?.token();
|
|
7886
|
+
const headers = buildHeaders(method, params, token);
|
|
7876
7887
|
if (verbose?.enabled) {
|
|
7877
7888
|
verbose.request(method, body);
|
|
7878
|
-
logHeaders(">", Object.entries(headers));
|
|
7889
|
+
logHeaders(verbose, ">", Object.entries(headers));
|
|
7879
7890
|
}
|
|
7880
|
-
const { controller, timer } = makeAbort();
|
|
7881
|
-
let res;
|
|
7891
|
+
const { controller, timer } = makeAbort(timeoutMs, externalSignal);
|
|
7882
7892
|
try {
|
|
7883
|
-
res = await fetchImpl(url, {
|
|
7893
|
+
const res = await fetchImpl(url, {
|
|
7884
7894
|
method: "POST",
|
|
7885
7895
|
headers,
|
|
7886
7896
|
body,
|
|
7887
7897
|
signal: controller.signal
|
|
7888
7898
|
});
|
|
7899
|
+
if (verbose?.enabled) {
|
|
7900
|
+
verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
|
|
7901
|
+
logHeaders(verbose, "<", res.headers.entries());
|
|
7902
|
+
}
|
|
7903
|
+
return { res, token };
|
|
7889
7904
|
} catch (err) {
|
|
7890
|
-
clearTimeout(timer);
|
|
7891
7905
|
if (controller.signal.aborted && !externalSignal?.aborted) {
|
|
7892
7906
|
throw new McpTimeoutError2(method, timeoutMs);
|
|
7893
7907
|
}
|
|
@@ -7895,11 +7909,8 @@ async function createStatelessConnection(options) {
|
|
|
7895
7909
|
} finally {
|
|
7896
7910
|
clearTimeout(timer);
|
|
7897
7911
|
}
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
logHeaders("<", res.headers.entries());
|
|
7901
|
-
}
|
|
7902
|
-
if (res.status >= 400) await handleErrorStatus(res, method);
|
|
7912
|
+
}
|
|
7913
|
+
async function parseResponse(res, id) {
|
|
7903
7914
|
const ct = res.headers.get("content-type") ?? "";
|
|
7904
7915
|
let frame;
|
|
7905
7916
|
if (ct.toLowerCase().includes("text/event-stream")) {
|
|
@@ -7913,10 +7924,53 @@ async function createStatelessConnection(options) {
|
|
|
7913
7924
|
}
|
|
7914
7925
|
return frame.result;
|
|
7915
7926
|
}
|
|
7927
|
+
async function handleAuthError(err, token) {
|
|
7928
|
+
if (!authProvider?.onUnauthorized) throw new McpAuthError(err.response.status, url);
|
|
7929
|
+
try {
|
|
7930
|
+
await authProvider.onUnauthorized({
|
|
7931
|
+
response: err.response,
|
|
7932
|
+
serverUrl: new URL(url),
|
|
7933
|
+
fetch: fetchImpl,
|
|
7934
|
+
token
|
|
7935
|
+
});
|
|
7936
|
+
} catch (authErr) {
|
|
7937
|
+
if (authErr instanceof AuthStepUpError) throw authErr;
|
|
7938
|
+
throw new McpAuthError(err.response.status, url);
|
|
7939
|
+
}
|
|
7940
|
+
}
|
|
7941
|
+
async function request(method, params) {
|
|
7942
|
+
const id = nextId2++;
|
|
7943
|
+
const meta = {
|
|
7944
|
+
protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
|
|
7945
|
+
clientInfo
|
|
7946
|
+
};
|
|
7947
|
+
const body = JSON.stringify({
|
|
7948
|
+
jsonrpc: "2.0",
|
|
7949
|
+
id,
|
|
7950
|
+
method,
|
|
7951
|
+
params: { ...params, _meta: meta }
|
|
7952
|
+
});
|
|
7953
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
7954
|
+
const { res, token } = await doFetch(method, params, body);
|
|
7955
|
+
if (res.status >= 400) {
|
|
7956
|
+
try {
|
|
7957
|
+
await handleErrorStatus(res, method);
|
|
7958
|
+
} catch (err) {
|
|
7959
|
+
if (err instanceof AuthRetryableError && attempt === 0) {
|
|
7960
|
+
await handleAuthError(err, token);
|
|
7961
|
+
continue;
|
|
7962
|
+
}
|
|
7963
|
+
throw err;
|
|
7964
|
+
}
|
|
7965
|
+
}
|
|
7966
|
+
return parseResponse(res, id);
|
|
7967
|
+
}
|
|
7968
|
+
throw new McpAuthError(401, url);
|
|
7969
|
+
}
|
|
7916
7970
|
const discoverResult2 = await request("server/discover", {
|
|
7917
7971
|
protocolVersion: STATELESS_PROTOCOL_VERSION
|
|
7918
7972
|
});
|
|
7919
|
-
const supported =
|
|
7973
|
+
const supported = discoverResult2?.supportedVersions?.filter((v) => typeof v === "string") ?? [];
|
|
7920
7974
|
if (!supported.includes(STATELESS_PROTOCOL_VERSION)) {
|
|
7921
7975
|
throw new Error(
|
|
7922
7976
|
`Server does not support ${STATELESS_PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
|
|
@@ -7927,15 +7981,7 @@ async function createStatelessConnection(options) {
|
|
|
7927
7981
|
return {
|
|
7928
7982
|
async info() {
|
|
7929
7983
|
if (serverInfoCache) return serverInfoCache;
|
|
7930
|
-
|
|
7931
|
-
serverInfoCache = {
|
|
7932
|
-
name: r?.serverInfo?.name,
|
|
7933
|
-
version: r?.serverInfo?.version,
|
|
7934
|
-
title: r?.serverInfo?.title,
|
|
7935
|
-
protocolVersion: STATELESS_PROTOCOL_VERSION,
|
|
7936
|
-
capabilities: r?.capabilities ?? {},
|
|
7937
|
-
instructions: r?.instructions
|
|
7938
|
-
};
|
|
7984
|
+
serverInfoCache = buildServerInfo(discoverResult2);
|
|
7939
7985
|
return serverInfoCache;
|
|
7940
7986
|
},
|
|
7941
7987
|
async listTools() {
|
|
@@ -8085,23 +8131,12 @@ async function connect(platform2, options) {
|
|
|
8085
8131
|
});
|
|
8086
8132
|
}
|
|
8087
8133
|
if (!options.url) throw new Error("connect requires either a url or a command");
|
|
8088
|
-
if (options.authProvider) {
|
|
8089
|
-
const token = await options.authProvider.token();
|
|
8090
|
-
if (token) {
|
|
8091
|
-
return createPiConnection({
|
|
8092
|
-
url: options.url,
|
|
8093
|
-
headers: options.headers,
|
|
8094
|
-
fetchImpl: platform2.fetch,
|
|
8095
|
-
authProvider: options.authProvider,
|
|
8096
|
-
...shared
|
|
8097
|
-
});
|
|
8098
|
-
}
|
|
8099
|
-
}
|
|
8100
8134
|
if (options.protocolVersion && !options.protocolVersion.startsWith("2026")) {
|
|
8101
8135
|
return createPiConnection({
|
|
8102
8136
|
url: options.url,
|
|
8103
8137
|
headers: options.headers,
|
|
8104
8138
|
fetchImpl: platform2.fetch,
|
|
8139
|
+
authProvider: options.authProvider,
|
|
8105
8140
|
...shared
|
|
8106
8141
|
});
|
|
8107
8142
|
}
|
|
@@ -8113,11 +8148,14 @@ async function connect(platform2, options) {
|
|
|
8113
8148
|
clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" },
|
|
8114
8149
|
timeoutMs: options.timeoutMs,
|
|
8115
8150
|
signal: options.signal,
|
|
8116
|
-
verbose: options.verbose
|
|
8151
|
+
verbose: options.verbose,
|
|
8152
|
+
authProvider: options.authProvider
|
|
8117
8153
|
});
|
|
8118
8154
|
} catch (err) {
|
|
8119
8155
|
const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
|
|
8120
8156
|
if (options.protocolVersion?.startsWith("2026")) throw err;
|
|
8157
|
+
if (err instanceof McpAuthError || err instanceof AuthStepUpError) throw err;
|
|
8158
|
+
if (err.name === "McpOAuthAuthorizationRequiredError") throw err;
|
|
8121
8159
|
if (isMethodNotFound) {
|
|
8122
8160
|
return createPiConnection({
|
|
8123
8161
|
url: options.url,
|
|
@@ -14277,5 +14315,5 @@ function createHostPlatform() {
|
|
|
14277
14315
|
// src/host.ts
|
|
14278
14316
|
var platform = createHostPlatform();
|
|
14279
14317
|
var args = process.argv.slice(2);
|
|
14280
|
-
var code = await run(platform, "0.
|
|
14318
|
+
var code = await run(platform, "0.11.0", args);
|
|
14281
14319
|
process.exit(code);
|
package/dist/kernel/mcpcat.mjs
CHANGED
|
@@ -7386,6 +7386,14 @@ var McpAuthError = class extends Error {
|
|
|
7386
7386
|
this.target = target;
|
|
7387
7387
|
}
|
|
7388
7388
|
};
|
|
7389
|
+
var AuthRetryableError = class extends Error {
|
|
7390
|
+
response;
|
|
7391
|
+
constructor(response) {
|
|
7392
|
+
super(`HTTP ${response.status}: auth retry`);
|
|
7393
|
+
this.name = "AuthRetryableError";
|
|
7394
|
+
this.response = response;
|
|
7395
|
+
}
|
|
7396
|
+
};
|
|
7389
7397
|
function selectSseFrame(body, targetId) {
|
|
7390
7398
|
const text = new TextDecoder().decode(body);
|
|
7391
7399
|
const blocks = text.replace(/\r\n/g, "\n").split(/\n\n/);
|
|
@@ -7410,6 +7418,42 @@ function mcpNameForMethod(method, params) {
|
|
|
7410
7418
|
if (method === "resources/read") return params.uri;
|
|
7411
7419
|
return void 0;
|
|
7412
7420
|
}
|
|
7421
|
+
function makeAbort(timeoutMs, externalSignal) {
|
|
7422
|
+
const controller = new AbortController();
|
|
7423
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7424
|
+
if (externalSignal) {
|
|
7425
|
+
if (externalSignal.aborted) {
|
|
7426
|
+
clearTimeout(timer);
|
|
7427
|
+
controller.abort();
|
|
7428
|
+
} else {
|
|
7429
|
+
externalSignal.addEventListener(
|
|
7430
|
+
"abort",
|
|
7431
|
+
() => {
|
|
7432
|
+
clearTimeout(timer);
|
|
7433
|
+
controller.abort();
|
|
7434
|
+
},
|
|
7435
|
+
{ once: true }
|
|
7436
|
+
);
|
|
7437
|
+
}
|
|
7438
|
+
}
|
|
7439
|
+
return { controller, timer };
|
|
7440
|
+
}
|
|
7441
|
+
function logHeaders(verbose, dir, entries) {
|
|
7442
|
+
for (const [k, v] of entries) {
|
|
7443
|
+
if (dir === ">") verbose.headerOut(k, v);
|
|
7444
|
+
else verbose.headerIn(k, v);
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
function buildServerInfo(discoverResult2) {
|
|
7448
|
+
return {
|
|
7449
|
+
name: discoverResult2?.serverInfo?.name,
|
|
7450
|
+
version: discoverResult2?.serverInfo?.version,
|
|
7451
|
+
title: discoverResult2?.serverInfo?.title,
|
|
7452
|
+
protocolVersion: STATELESS_PROTOCOL_VERSION,
|
|
7453
|
+
capabilities: discoverResult2?.capabilities ?? {},
|
|
7454
|
+
instructions: discoverResult2?.instructions
|
|
7455
|
+
};
|
|
7456
|
+
}
|
|
7413
7457
|
async function createStatelessConnection(options) {
|
|
7414
7458
|
const {
|
|
7415
7459
|
url,
|
|
@@ -7418,11 +7462,12 @@ async function createStatelessConnection(options) {
|
|
|
7418
7462
|
timeoutMs = 6e4,
|
|
7419
7463
|
clientInfo = { name: "mcpcat", version: "0.0.0" },
|
|
7420
7464
|
signal: externalSignal,
|
|
7421
|
-
verbose
|
|
7465
|
+
verbose,
|
|
7466
|
+
authProvider
|
|
7422
7467
|
} = options;
|
|
7423
7468
|
let nextId2 = 1;
|
|
7424
7469
|
let negotiatedVersion;
|
|
7425
|
-
function buildHeaders(method, params) {
|
|
7470
|
+
function buildHeaders(method, params, token) {
|
|
7426
7471
|
const h = {
|
|
7427
7472
|
"Content-Type": "application/json",
|
|
7428
7473
|
Accept: "application/json, text/event-stream",
|
|
@@ -7432,36 +7477,12 @@ async function createStatelessConnection(options) {
|
|
|
7432
7477
|
const n = mcpNameForMethod(method, params);
|
|
7433
7478
|
if (n) h["Mcp-Name"] = n;
|
|
7434
7479
|
if (negotiatedVersion) h["Mcp-Protocol-Version"] = negotiatedVersion;
|
|
7480
|
+
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
7435
7481
|
return h;
|
|
7436
7482
|
}
|
|
7437
|
-
function makeAbort() {
|
|
7438
|
-
const controller = new AbortController();
|
|
7439
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7440
|
-
if (externalSignal) {
|
|
7441
|
-
if (externalSignal.aborted) {
|
|
7442
|
-
clearTimeout(timer);
|
|
7443
|
-
controller.abort();
|
|
7444
|
-
} else {
|
|
7445
|
-
externalSignal.addEventListener(
|
|
7446
|
-
"abort",
|
|
7447
|
-
() => {
|
|
7448
|
-
clearTimeout(timer);
|
|
7449
|
-
controller.abort();
|
|
7450
|
-
},
|
|
7451
|
-
{ once: true }
|
|
7452
|
-
);
|
|
7453
|
-
}
|
|
7454
|
-
}
|
|
7455
|
-
return { controller, timer };
|
|
7456
|
-
}
|
|
7457
|
-
function logHeaders(dir, entries) {
|
|
7458
|
-
if (!verbose?.enabled) return;
|
|
7459
|
-
for (const [k, v] of entries) {
|
|
7460
|
-
if (dir === ">") verbose.headerOut(k, v);
|
|
7461
|
-
else verbose.headerIn(k, v);
|
|
7462
|
-
}
|
|
7463
|
-
}
|
|
7464
7483
|
async function handleErrorStatus(res, method) {
|
|
7484
|
+
if ((res.status === 401 || res.status === 403) && authProvider)
|
|
7485
|
+
throw new AuthRetryableError(res);
|
|
7465
7486
|
if (res.status === 401 || res.status === 403) throw new McpAuthError(res.status, url);
|
|
7466
7487
|
const text = await res.text();
|
|
7467
7488
|
let rpc;
|
|
@@ -7473,34 +7494,27 @@ async function createStatelessConnection(options) {
|
|
|
7473
7494
|
if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
|
|
7474
7495
|
throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
|
|
7475
7496
|
}
|
|
7476
|
-
async function
|
|
7477
|
-
const
|
|
7478
|
-
const headers = buildHeaders(method, params);
|
|
7479
|
-
const meta = {
|
|
7480
|
-
protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
|
|
7481
|
-
clientInfo
|
|
7482
|
-
};
|
|
7483
|
-
const body = JSON.stringify({
|
|
7484
|
-
jsonrpc: "2.0",
|
|
7485
|
-
id,
|
|
7486
|
-
method,
|
|
7487
|
-
params: { ...params, _meta: meta }
|
|
7488
|
-
});
|
|
7497
|
+
async function doFetch(method, params, body) {
|
|
7498
|
+
const token = await authProvider?.token();
|
|
7499
|
+
const headers = buildHeaders(method, params, token);
|
|
7489
7500
|
if (verbose?.enabled) {
|
|
7490
7501
|
verbose.request(method, body);
|
|
7491
|
-
logHeaders(">", Object.entries(headers));
|
|
7502
|
+
logHeaders(verbose, ">", Object.entries(headers));
|
|
7492
7503
|
}
|
|
7493
|
-
const { controller, timer } = makeAbort();
|
|
7494
|
-
let res;
|
|
7504
|
+
const { controller, timer } = makeAbort(timeoutMs, externalSignal);
|
|
7495
7505
|
try {
|
|
7496
|
-
res = await fetchImpl(url, {
|
|
7506
|
+
const res = await fetchImpl(url, {
|
|
7497
7507
|
method: "POST",
|
|
7498
7508
|
headers,
|
|
7499
7509
|
body,
|
|
7500
7510
|
signal: controller.signal
|
|
7501
7511
|
});
|
|
7512
|
+
if (verbose?.enabled) {
|
|
7513
|
+
verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
|
|
7514
|
+
logHeaders(verbose, "<", res.headers.entries());
|
|
7515
|
+
}
|
|
7516
|
+
return { res, token };
|
|
7502
7517
|
} catch (err) {
|
|
7503
|
-
clearTimeout(timer);
|
|
7504
7518
|
if (controller.signal.aborted && !externalSignal?.aborted) {
|
|
7505
7519
|
throw new McpTimeoutError2(method, timeoutMs);
|
|
7506
7520
|
}
|
|
@@ -7508,11 +7522,8 @@ async function createStatelessConnection(options) {
|
|
|
7508
7522
|
} finally {
|
|
7509
7523
|
clearTimeout(timer);
|
|
7510
7524
|
}
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
logHeaders("<", res.headers.entries());
|
|
7514
|
-
}
|
|
7515
|
-
if (res.status >= 400) await handleErrorStatus(res, method);
|
|
7525
|
+
}
|
|
7526
|
+
async function parseResponse(res, id) {
|
|
7516
7527
|
const ct = res.headers.get("content-type") ?? "";
|
|
7517
7528
|
let frame;
|
|
7518
7529
|
if (ct.toLowerCase().includes("text/event-stream")) {
|
|
@@ -7526,10 +7537,53 @@ async function createStatelessConnection(options) {
|
|
|
7526
7537
|
}
|
|
7527
7538
|
return frame.result;
|
|
7528
7539
|
}
|
|
7540
|
+
async function handleAuthError(err, token) {
|
|
7541
|
+
if (!authProvider?.onUnauthorized) throw new McpAuthError(err.response.status, url);
|
|
7542
|
+
try {
|
|
7543
|
+
await authProvider.onUnauthorized({
|
|
7544
|
+
response: err.response,
|
|
7545
|
+
serverUrl: new URL(url),
|
|
7546
|
+
fetch: fetchImpl,
|
|
7547
|
+
token
|
|
7548
|
+
});
|
|
7549
|
+
} catch (authErr) {
|
|
7550
|
+
if (authErr instanceof AuthStepUpError) throw authErr;
|
|
7551
|
+
throw new McpAuthError(err.response.status, url);
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
async function request(method, params) {
|
|
7555
|
+
const id = nextId2++;
|
|
7556
|
+
const meta = {
|
|
7557
|
+
protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
|
|
7558
|
+
clientInfo
|
|
7559
|
+
};
|
|
7560
|
+
const body = JSON.stringify({
|
|
7561
|
+
jsonrpc: "2.0",
|
|
7562
|
+
id,
|
|
7563
|
+
method,
|
|
7564
|
+
params: { ...params, _meta: meta }
|
|
7565
|
+
});
|
|
7566
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
7567
|
+
const { res, token } = await doFetch(method, params, body);
|
|
7568
|
+
if (res.status >= 400) {
|
|
7569
|
+
try {
|
|
7570
|
+
await handleErrorStatus(res, method);
|
|
7571
|
+
} catch (err) {
|
|
7572
|
+
if (err instanceof AuthRetryableError && attempt === 0) {
|
|
7573
|
+
await handleAuthError(err, token);
|
|
7574
|
+
continue;
|
|
7575
|
+
}
|
|
7576
|
+
throw err;
|
|
7577
|
+
}
|
|
7578
|
+
}
|
|
7579
|
+
return parseResponse(res, id);
|
|
7580
|
+
}
|
|
7581
|
+
throw new McpAuthError(401, url);
|
|
7582
|
+
}
|
|
7529
7583
|
const discoverResult2 = await request("server/discover", {
|
|
7530
7584
|
protocolVersion: STATELESS_PROTOCOL_VERSION
|
|
7531
7585
|
});
|
|
7532
|
-
const supported =
|
|
7586
|
+
const supported = discoverResult2?.supportedVersions?.filter((v) => typeof v === "string") ?? [];
|
|
7533
7587
|
if (!supported.includes(STATELESS_PROTOCOL_VERSION)) {
|
|
7534
7588
|
throw new Error(
|
|
7535
7589
|
`Server does not support ${STATELESS_PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
|
|
@@ -7540,15 +7594,7 @@ async function createStatelessConnection(options) {
|
|
|
7540
7594
|
return {
|
|
7541
7595
|
async info() {
|
|
7542
7596
|
if (serverInfoCache) return serverInfoCache;
|
|
7543
|
-
|
|
7544
|
-
serverInfoCache = {
|
|
7545
|
-
name: r?.serverInfo?.name,
|
|
7546
|
-
version: r?.serverInfo?.version,
|
|
7547
|
-
title: r?.serverInfo?.title,
|
|
7548
|
-
protocolVersion: STATELESS_PROTOCOL_VERSION,
|
|
7549
|
-
capabilities: r?.capabilities ?? {},
|
|
7550
|
-
instructions: r?.instructions
|
|
7551
|
-
};
|
|
7597
|
+
serverInfoCache = buildServerInfo(discoverResult2);
|
|
7552
7598
|
return serverInfoCache;
|
|
7553
7599
|
},
|
|
7554
7600
|
async listTools() {
|
|
@@ -7698,23 +7744,12 @@ async function connect(platform2, options) {
|
|
|
7698
7744
|
});
|
|
7699
7745
|
}
|
|
7700
7746
|
if (!options.url) throw new Error("connect requires either a url or a command");
|
|
7701
|
-
if (options.authProvider) {
|
|
7702
|
-
const token = await options.authProvider.token();
|
|
7703
|
-
if (token) {
|
|
7704
|
-
return createPiConnection({
|
|
7705
|
-
url: options.url,
|
|
7706
|
-
headers: options.headers,
|
|
7707
|
-
fetchImpl: platform2.fetch,
|
|
7708
|
-
authProvider: options.authProvider,
|
|
7709
|
-
...shared
|
|
7710
|
-
});
|
|
7711
|
-
}
|
|
7712
|
-
}
|
|
7713
7747
|
if (options.protocolVersion && !options.protocolVersion.startsWith("2026")) {
|
|
7714
7748
|
return createPiConnection({
|
|
7715
7749
|
url: options.url,
|
|
7716
7750
|
headers: options.headers,
|
|
7717
7751
|
fetchImpl: platform2.fetch,
|
|
7752
|
+
authProvider: options.authProvider,
|
|
7718
7753
|
...shared
|
|
7719
7754
|
});
|
|
7720
7755
|
}
|
|
@@ -7726,11 +7761,14 @@ async function connect(platform2, options) {
|
|
|
7726
7761
|
clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" },
|
|
7727
7762
|
timeoutMs: options.timeoutMs,
|
|
7728
7763
|
signal: options.signal,
|
|
7729
|
-
verbose: options.verbose
|
|
7764
|
+
verbose: options.verbose,
|
|
7765
|
+
authProvider: options.authProvider
|
|
7730
7766
|
});
|
|
7731
7767
|
} catch (err) {
|
|
7732
7768
|
const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
|
|
7733
7769
|
if (options.protocolVersion?.startsWith("2026")) throw err;
|
|
7770
|
+
if (err instanceof McpAuthError || err instanceof AuthStepUpError) throw err;
|
|
7771
|
+
if (err.name === "McpOAuthAuthorizationRequiredError") throw err;
|
|
7734
7772
|
if (isMethodNotFound) {
|
|
7735
7773
|
return createPiConnection({
|
|
7736
7774
|
url: options.url,
|
|
@@ -13805,7 +13843,7 @@ async function main(ctx) {
|
|
|
13805
13843
|
packageDir: "/node_modules/@ai-ecoverse/mcpcat"
|
|
13806
13844
|
});
|
|
13807
13845
|
const args = ctx.argv.slice(1);
|
|
13808
|
-
return run(platform2, "0.
|
|
13846
|
+
return run(platform2, "0.11.0", args);
|
|
13809
13847
|
}
|
|
13810
13848
|
export {
|
|
13811
13849
|
main as default
|