nuwax-mcp-stdio-proxy 1.4.3 → 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 +88 -80
- package/dist/modes/stdio.js +13 -2
- package/dist/types.d.ts +2 -0
- package/dist/types.js +9 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6809,12 +6809,13 @@ var logError = (msg) => log("ERROR", msg);
|
|
|
6809
6809
|
|
|
6810
6810
|
// src/types.ts
|
|
6811
6811
|
function isSseEntry(entry) {
|
|
6812
|
-
|
|
6813
|
-
const e = entry;
|
|
6814
|
-
return e.transport === "sse" || !e.transport && /\/sse(?:\?|$)/i.test(e.url);
|
|
6812
|
+
return "url" in entry && entry.transport === "sse";
|
|
6815
6813
|
}
|
|
6816
6814
|
function isStreamableEntry(entry) {
|
|
6817
|
-
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);
|
|
6818
6819
|
}
|
|
6819
6820
|
|
|
6820
6821
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -23763,7 +23764,7 @@ var StdioServerTransport = class {
|
|
|
23763
23764
|
|
|
23764
23765
|
// src/constants.ts
|
|
23765
23766
|
var PKG_NAME = "nuwax-mcp-stdio-proxy";
|
|
23766
|
-
var PKG_VERSION = "1.4.
|
|
23767
|
+
var PKG_VERSION = "1.4.4";
|
|
23767
23768
|
|
|
23768
23769
|
// src/shared.ts
|
|
23769
23770
|
async function discoverTools(client) {
|
|
@@ -23838,6 +23839,81 @@ function filterTools(tools, filter) {
|
|
|
23838
23839
|
return tools;
|
|
23839
23840
|
}
|
|
23840
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
|
+
|
|
23841
23917
|
// src/modes/stdio.ts
|
|
23842
23918
|
async function runStdio(config2, allowTools, denyTools) {
|
|
23843
23919
|
const entries = Object.entries(config2.mcpServers);
|
|
@@ -23861,6 +23937,13 @@ async function runStdio(config2, allowTools, denyTools) {
|
|
|
23861
23937
|
connected = await connectSse(id, entry);
|
|
23862
23938
|
} else if (isStreamableEntry(entry)) {
|
|
23863
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
|
+
}
|
|
23864
23947
|
} else {
|
|
23865
23948
|
connected = await connectStdio(id, entry, baseEnv);
|
|
23866
23949
|
}
|
|
@@ -23938,81 +24021,6 @@ async function runStdio(config2, allowTools, denyTools) {
|
|
|
23938
24021
|
});
|
|
23939
24022
|
}
|
|
23940
24023
|
|
|
23941
|
-
// src/detect.ts
|
|
23942
|
-
async function detectProtocol(url2, headers) {
|
|
23943
|
-
logInfo(`Auto-detecting protocol for ${url2}...`);
|
|
23944
|
-
try {
|
|
23945
|
-
const reqHeaders = {
|
|
23946
|
-
"Content-Type": "application/json",
|
|
23947
|
-
Accept: "application/json, text/event-stream",
|
|
23948
|
-
...headers
|
|
23949
|
-
};
|
|
23950
|
-
const controller = new AbortController();
|
|
23951
|
-
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
23952
|
-
const res = await fetch(url2, {
|
|
23953
|
-
method: "POST",
|
|
23954
|
-
headers: reqHeaders,
|
|
23955
|
-
body: JSON.stringify({
|
|
23956
|
-
jsonrpc: "2.0",
|
|
23957
|
-
id: 1,
|
|
23958
|
-
method: "initialize",
|
|
23959
|
-
params: {
|
|
23960
|
-
protocolVersion: "2025-03-26",
|
|
23961
|
-
capabilities: {},
|
|
23962
|
-
clientInfo: { name: "nuwax-mcp-detect", version: "1.0.0" }
|
|
23963
|
-
}
|
|
23964
|
-
}),
|
|
23965
|
-
signal: controller.signal
|
|
23966
|
-
});
|
|
23967
|
-
clearTimeout(timeout);
|
|
23968
|
-
const ct = res.headers.get("content-type") || "";
|
|
23969
|
-
if (res.ok && (ct.includes("application/json") || ct.includes("text/event-stream"))) {
|
|
23970
|
-
logInfo(`Detected streamable-http protocol for ${url2}`);
|
|
23971
|
-
await res.text().catch(() => {
|
|
23972
|
-
});
|
|
23973
|
-
const sessionId = res.headers.get("mcp-session-id");
|
|
23974
|
-
if (sessionId) {
|
|
23975
|
-
fetch(url2, {
|
|
23976
|
-
method: "DELETE",
|
|
23977
|
-
headers: { "mcp-session-id": sessionId, ...headers },
|
|
23978
|
-
signal: AbortSignal.timeout(5e3)
|
|
23979
|
-
}).catch(() => {
|
|
23980
|
-
});
|
|
23981
|
-
}
|
|
23982
|
-
return "stream";
|
|
23983
|
-
}
|
|
23984
|
-
await res.text().catch(() => {
|
|
23985
|
-
});
|
|
23986
|
-
} catch {
|
|
23987
|
-
}
|
|
23988
|
-
try {
|
|
23989
|
-
const reqHeaders = {
|
|
23990
|
-
Accept: "text/event-stream",
|
|
23991
|
-
...headers
|
|
23992
|
-
};
|
|
23993
|
-
const controller = new AbortController();
|
|
23994
|
-
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
23995
|
-
const res = await fetch(url2, {
|
|
23996
|
-
method: "GET",
|
|
23997
|
-
headers: reqHeaders,
|
|
23998
|
-
signal: controller.signal
|
|
23999
|
-
});
|
|
24000
|
-
const ct = res.headers.get("content-type") || "";
|
|
24001
|
-
if (ct.includes("text/event-stream")) {
|
|
24002
|
-
clearTimeout(timeout);
|
|
24003
|
-
logInfo(`Detected SSE protocol for ${url2}`);
|
|
24004
|
-
controller.abort();
|
|
24005
|
-
return "sse";
|
|
24006
|
-
}
|
|
24007
|
-
clearTimeout(timeout);
|
|
24008
|
-
await res.text().catch(() => {
|
|
24009
|
-
});
|
|
24010
|
-
} catch {
|
|
24011
|
-
}
|
|
24012
|
-
logWarn(`Could not auto-detect protocol for ${url2}, defaulting to streamable-http`);
|
|
24013
|
-
return "stream";
|
|
24014
|
-
}
|
|
24015
|
-
|
|
24016
24024
|
// src/modes/convert.ts
|
|
24017
24025
|
async function runConvert(args) {
|
|
24018
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
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
* Types for MCP server configuration
|
|
3
3
|
*/
|
|
4
4
|
export function isSseEntry(entry) {
|
|
5
|
-
|
|
6
|
-
return false;
|
|
7
|
-
const e = entry;
|
|
8
|
-
// Explicit transport: 'sse' or URL path contains /sse (auto-detect)
|
|
9
|
-
return e.transport === 'sse' || (!e.transport && /\/sse(?:\?|$)/i.test(e.url));
|
|
5
|
+
return 'url' in entry && entry.transport === 'sse';
|
|
10
6
|
}
|
|
11
7
|
export function isStreamableEntry(entry) {
|
|
12
8
|
return ('url' in entry &&
|
|
13
9
|
typeof entry.url === 'string' &&
|
|
14
|
-
|
|
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));
|
|
15
18
|
}
|
|
16
19
|
/** @deprecated Use isStreamableEntry instead */
|
|
17
20
|
export const isBridgeEntry = isStreamableEntry;
|
package/package.json
CHANGED