nuwax-mcp-stdio-proxy 1.4.2 → 1.4.4
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/index.js +87 -77
- package/dist/modes/stdio.js +13 -2
- package/dist/types.d.ts +2 -0
- package/dist/types.js +8 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6812,7 +6812,10 @@ function isSseEntry(entry) {
|
|
|
6812
6812
|
return "url" in entry && entry.transport === "sse";
|
|
6813
6813
|
}
|
|
6814
6814
|
function isStreamableEntry(entry) {
|
|
6815
|
-
return "url" in entry && typeof entry.url === "string" &&
|
|
6815
|
+
return "url" in entry && typeof entry.url === "string" && entry.transport === "streamable-http";
|
|
6816
|
+
}
|
|
6817
|
+
function needsProtocolDetection(entry) {
|
|
6818
|
+
return "url" in entry && typeof entry.url === "string" && !isSseEntry(entry) && !isStreamableEntry(entry);
|
|
6816
6819
|
}
|
|
6817
6820
|
|
|
6818
6821
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -23761,7 +23764,7 @@ var StdioServerTransport = class {
|
|
|
23761
23764
|
|
|
23762
23765
|
// src/constants.ts
|
|
23763
23766
|
var PKG_NAME = "nuwax-mcp-stdio-proxy";
|
|
23764
|
-
var PKG_VERSION = "1.4.
|
|
23767
|
+
var PKG_VERSION = "1.4.4";
|
|
23765
23768
|
|
|
23766
23769
|
// src/shared.ts
|
|
23767
23770
|
async function discoverTools(client) {
|
|
@@ -23836,6 +23839,81 @@ function filterTools(tools, filter) {
|
|
|
23836
23839
|
return tools;
|
|
23837
23840
|
}
|
|
23838
23841
|
|
|
23842
|
+
// src/detect.ts
|
|
23843
|
+
async function detectProtocol(url2, headers) {
|
|
23844
|
+
logInfo(`Auto-detecting protocol for ${url2}...`);
|
|
23845
|
+
try {
|
|
23846
|
+
const reqHeaders = {
|
|
23847
|
+
"Content-Type": "application/json",
|
|
23848
|
+
Accept: "application/json, text/event-stream",
|
|
23849
|
+
...headers
|
|
23850
|
+
};
|
|
23851
|
+
const controller = new AbortController();
|
|
23852
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
23853
|
+
const res = await fetch(url2, {
|
|
23854
|
+
method: "POST",
|
|
23855
|
+
headers: reqHeaders,
|
|
23856
|
+
body: JSON.stringify({
|
|
23857
|
+
jsonrpc: "2.0",
|
|
23858
|
+
id: 1,
|
|
23859
|
+
method: "initialize",
|
|
23860
|
+
params: {
|
|
23861
|
+
protocolVersion: "2025-03-26",
|
|
23862
|
+
capabilities: {},
|
|
23863
|
+
clientInfo: { name: "nuwax-mcp-detect", version: "1.0.0" }
|
|
23864
|
+
}
|
|
23865
|
+
}),
|
|
23866
|
+
signal: controller.signal
|
|
23867
|
+
});
|
|
23868
|
+
clearTimeout(timeout);
|
|
23869
|
+
const ct = res.headers.get("content-type") || "";
|
|
23870
|
+
if (res.ok && (ct.includes("application/json") || ct.includes("text/event-stream"))) {
|
|
23871
|
+
logInfo(`Detected streamable-http protocol for ${url2}`);
|
|
23872
|
+
await res.text().catch(() => {
|
|
23873
|
+
});
|
|
23874
|
+
const sessionId = res.headers.get("mcp-session-id");
|
|
23875
|
+
if (sessionId) {
|
|
23876
|
+
fetch(url2, {
|
|
23877
|
+
method: "DELETE",
|
|
23878
|
+
headers: { "mcp-session-id": sessionId, ...headers },
|
|
23879
|
+
signal: AbortSignal.timeout(5e3)
|
|
23880
|
+
}).catch(() => {
|
|
23881
|
+
});
|
|
23882
|
+
}
|
|
23883
|
+
return "stream";
|
|
23884
|
+
}
|
|
23885
|
+
await res.text().catch(() => {
|
|
23886
|
+
});
|
|
23887
|
+
} catch {
|
|
23888
|
+
}
|
|
23889
|
+
try {
|
|
23890
|
+
const reqHeaders = {
|
|
23891
|
+
Accept: "text/event-stream",
|
|
23892
|
+
...headers
|
|
23893
|
+
};
|
|
23894
|
+
const controller = new AbortController();
|
|
23895
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
23896
|
+
const res = await fetch(url2, {
|
|
23897
|
+
method: "GET",
|
|
23898
|
+
headers: reqHeaders,
|
|
23899
|
+
signal: controller.signal
|
|
23900
|
+
});
|
|
23901
|
+
const ct = res.headers.get("content-type") || "";
|
|
23902
|
+
if (ct.includes("text/event-stream")) {
|
|
23903
|
+
clearTimeout(timeout);
|
|
23904
|
+
logInfo(`Detected SSE protocol for ${url2}`);
|
|
23905
|
+
controller.abort();
|
|
23906
|
+
return "sse";
|
|
23907
|
+
}
|
|
23908
|
+
clearTimeout(timeout);
|
|
23909
|
+
await res.text().catch(() => {
|
|
23910
|
+
});
|
|
23911
|
+
} catch {
|
|
23912
|
+
}
|
|
23913
|
+
logWarn(`Could not auto-detect protocol for ${url2}, defaulting to streamable-http`);
|
|
23914
|
+
return "stream";
|
|
23915
|
+
}
|
|
23916
|
+
|
|
23839
23917
|
// src/modes/stdio.ts
|
|
23840
23918
|
async function runStdio(config2, allowTools, denyTools) {
|
|
23841
23919
|
const entries = Object.entries(config2.mcpServers);
|
|
@@ -23859,6 +23937,13 @@ async function runStdio(config2, allowTools, denyTools) {
|
|
|
23859
23937
|
connected = await connectSse(id, entry);
|
|
23860
23938
|
} else if (isStreamableEntry(entry)) {
|
|
23861
23939
|
connected = await connectStreamable(id, entry);
|
|
23940
|
+
} else if (needsProtocolDetection(entry)) {
|
|
23941
|
+
const detected = await detectProtocol(entry.url, buildRequestHeaders(entry));
|
|
23942
|
+
if (detected === "sse") {
|
|
23943
|
+
connected = await connectSse(id, { ...entry, transport: "sse" });
|
|
23944
|
+
} else {
|
|
23945
|
+
connected = await connectStreamable(id, entry);
|
|
23946
|
+
}
|
|
23862
23947
|
} else {
|
|
23863
23948
|
connected = await connectStdio(id, entry, baseEnv);
|
|
23864
23949
|
}
|
|
@@ -23936,81 +24021,6 @@ async function runStdio(config2, allowTools, denyTools) {
|
|
|
23936
24021
|
});
|
|
23937
24022
|
}
|
|
23938
24023
|
|
|
23939
|
-
// src/detect.ts
|
|
23940
|
-
async function detectProtocol(url2, headers) {
|
|
23941
|
-
logInfo(`Auto-detecting protocol for ${url2}...`);
|
|
23942
|
-
try {
|
|
23943
|
-
const reqHeaders = {
|
|
23944
|
-
"Content-Type": "application/json",
|
|
23945
|
-
Accept: "application/json, text/event-stream",
|
|
23946
|
-
...headers
|
|
23947
|
-
};
|
|
23948
|
-
const controller = new AbortController();
|
|
23949
|
-
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
23950
|
-
const res = await fetch(url2, {
|
|
23951
|
-
method: "POST",
|
|
23952
|
-
headers: reqHeaders,
|
|
23953
|
-
body: JSON.stringify({
|
|
23954
|
-
jsonrpc: "2.0",
|
|
23955
|
-
id: 1,
|
|
23956
|
-
method: "initialize",
|
|
23957
|
-
params: {
|
|
23958
|
-
protocolVersion: "2025-03-26",
|
|
23959
|
-
capabilities: {},
|
|
23960
|
-
clientInfo: { name: "nuwax-mcp-detect", version: "1.0.0" }
|
|
23961
|
-
}
|
|
23962
|
-
}),
|
|
23963
|
-
signal: controller.signal
|
|
23964
|
-
});
|
|
23965
|
-
clearTimeout(timeout);
|
|
23966
|
-
const ct = res.headers.get("content-type") || "";
|
|
23967
|
-
if (res.ok && (ct.includes("application/json") || ct.includes("text/event-stream"))) {
|
|
23968
|
-
logInfo(`Detected streamable-http protocol for ${url2}`);
|
|
23969
|
-
await res.text().catch(() => {
|
|
23970
|
-
});
|
|
23971
|
-
const sessionId = res.headers.get("mcp-session-id");
|
|
23972
|
-
if (sessionId) {
|
|
23973
|
-
fetch(url2, {
|
|
23974
|
-
method: "DELETE",
|
|
23975
|
-
headers: { "mcp-session-id": sessionId, ...headers },
|
|
23976
|
-
signal: AbortSignal.timeout(5e3)
|
|
23977
|
-
}).catch(() => {
|
|
23978
|
-
});
|
|
23979
|
-
}
|
|
23980
|
-
return "stream";
|
|
23981
|
-
}
|
|
23982
|
-
await res.text().catch(() => {
|
|
23983
|
-
});
|
|
23984
|
-
} catch {
|
|
23985
|
-
}
|
|
23986
|
-
try {
|
|
23987
|
-
const reqHeaders = {
|
|
23988
|
-
Accept: "text/event-stream",
|
|
23989
|
-
...headers
|
|
23990
|
-
};
|
|
23991
|
-
const controller = new AbortController();
|
|
23992
|
-
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
23993
|
-
const res = await fetch(url2, {
|
|
23994
|
-
method: "GET",
|
|
23995
|
-
headers: reqHeaders,
|
|
23996
|
-
signal: controller.signal
|
|
23997
|
-
});
|
|
23998
|
-
const ct = res.headers.get("content-type") || "";
|
|
23999
|
-
if (ct.includes("text/event-stream")) {
|
|
24000
|
-
clearTimeout(timeout);
|
|
24001
|
-
logInfo(`Detected SSE protocol for ${url2}`);
|
|
24002
|
-
controller.abort();
|
|
24003
|
-
return "sse";
|
|
24004
|
-
}
|
|
24005
|
-
clearTimeout(timeout);
|
|
24006
|
-
await res.text().catch(() => {
|
|
24007
|
-
});
|
|
24008
|
-
} catch {
|
|
24009
|
-
}
|
|
24010
|
-
logWarn(`Could not auto-detect protocol for ${url2}, defaulting to streamable-http`);
|
|
24011
|
-
return "stream";
|
|
24012
|
-
}
|
|
24013
|
-
|
|
24014
24024
|
// src/modes/convert.ts
|
|
24015
24025
|
async function runConvert(args) {
|
|
24016
24026
|
let targetUrl;
|
package/dist/modes/stdio.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Aggregates multiple MCP servers (stdio + streamable-http + SSE)
|
|
5
5
|
* into a single stdio MCP endpoint.
|
|
6
6
|
*/
|
|
7
|
-
import { isSseEntry, isStreamableEntry } from '../types.js';
|
|
7
|
+
import { isSseEntry, isStreamableEntry, needsProtocolDetection } from '../types.js';
|
|
8
8
|
import { logInfo, logWarn, logError } from '../logger.js';
|
|
9
|
-
import { buildBaseEnv, connectStdio, connectStreamable, connectSse } from '../transport.js';
|
|
9
|
+
import { buildBaseEnv, connectStdio, connectStreamable, connectSse, buildRequestHeaders } from '../transport.js';
|
|
10
10
|
import { discoverTools, createToolProxyServer, setupGracefulShutdown } from '../shared.js';
|
|
11
11
|
import { filterTools } from '../filter.js';
|
|
12
|
+
import { detectProtocol } from '../detect.js';
|
|
12
13
|
export async function runStdio(config, allowTools, denyTools) {
|
|
13
14
|
const entries = Object.entries(config.mcpServers);
|
|
14
15
|
if (entries.length === 0) {
|
|
@@ -32,6 +33,16 @@ export async function runStdio(config, allowTools, denyTools) {
|
|
|
32
33
|
else if (isStreamableEntry(entry)) {
|
|
33
34
|
connected = await connectStreamable(id, entry);
|
|
34
35
|
}
|
|
36
|
+
else if (needsProtocolDetection(entry)) {
|
|
37
|
+
// No explicit transport — probe the URL to determine protocol
|
|
38
|
+
const detected = await detectProtocol(entry.url, buildRequestHeaders(entry));
|
|
39
|
+
if (detected === 'sse') {
|
|
40
|
+
connected = await connectSse(id, { ...entry, transport: 'sse' });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
connected = await connectStreamable(id, entry);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
else {
|
|
36
47
|
connected = await connectStdio(id, entry, baseEnv);
|
|
37
48
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface SseServerEntry {
|
|
|
45
45
|
export type McpServerEntry = StdioServerEntry | StreamableServerEntry | SseServerEntry;
|
|
46
46
|
export declare function isSseEntry(entry: McpServerEntry): entry is SseServerEntry;
|
|
47
47
|
export declare function isStreamableEntry(entry: McpServerEntry): entry is StreamableServerEntry;
|
|
48
|
+
/** Check if URL entry has no explicit transport → needs auto-detection */
|
|
49
|
+
export declare function needsProtocolDetection(entry: McpServerEntry): entry is StreamableServerEntry;
|
|
48
50
|
/** @deprecated Use StreamableServerEntry instead */
|
|
49
51
|
export type BridgeServerEntry = StreamableServerEntry;
|
|
50
52
|
/** @deprecated Use StreamableServerEntry instead */
|
package/dist/types.js
CHANGED
|
@@ -7,7 +7,14 @@ export function isSseEntry(entry) {
|
|
|
7
7
|
export function isStreamableEntry(entry) {
|
|
8
8
|
return ('url' in entry &&
|
|
9
9
|
typeof entry.url === 'string' &&
|
|
10
|
-
|
|
10
|
+
entry.transport === 'streamable-http');
|
|
11
|
+
}
|
|
12
|
+
/** Check if URL entry has no explicit transport → needs auto-detection */
|
|
13
|
+
export function needsProtocolDetection(entry) {
|
|
14
|
+
return ('url' in entry &&
|
|
15
|
+
typeof entry.url === 'string' &&
|
|
16
|
+
!isSseEntry(entry) &&
|
|
17
|
+
!isStreamableEntry(entry));
|
|
11
18
|
}
|
|
12
19
|
/** @deprecated Use isStreamableEntry instead */
|
|
13
20
|
export const isBridgeEntry = isStreamableEntry;
|
package/package.json
CHANGED