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 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" && !isSseEntry(entry);
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.2";
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;
@@ -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
- !isSseEntry(entry));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuwax-mcp-stdio-proxy",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "TypeScript MCP proxy — aggregates multiple MCP servers (stdio + streamable-http + SSE) with convert & proxy modes",
5
5
  "type": "module",
6
6
  "bin": {