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 CHANGED
@@ -6809,12 +6809,13 @@ var logError = (msg) => log("ERROR", msg);
6809
6809
 
6810
6810
  // src/types.ts
6811
6811
  function isSseEntry(entry) {
6812
- if (!("url" in entry)) return false;
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" && !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);
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.3";
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;
@@ -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
- if (!('url' in entry))
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
- !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));
15
18
  }
16
19
  /** @deprecated Use isStreamableEntry instead */
17
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.3",
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": {