toolbox-co-mcp-bridge 1.0.2 → 1.0.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.
Files changed (3) hide show
  1. package/index.js +57 -25
  2. package/package.json +1 -2
  3. package/test_sse.mjs +15 -0
package/index.js CHANGED
@@ -1,20 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import EventSource from "eventsource";
3
2
  import fetch from "node-fetch";
4
3
  import readline from "readline";
5
4
 
6
- // The hardcoded remote SSE endpoint for ToolBox Co. SaaS tools
7
5
  const sseUrl = "https://toolbox-mcp-1058072229791.us-central1.run.app/sse";
6
+ const finalSseUrl = `${sseUrl}?t=${Date.now()}`;
8
7
 
9
- const es = new EventSource(sseUrl);
10
8
  let postEndpoint = '';
11
9
  const messageQueue = [];
10
+ let gcrCookie = '';
12
11
 
13
12
  async function sendPost(msg) {
14
13
  try {
14
+ const headers = { 'Content-Type': 'application/json' };
15
+ if (gcrCookie) {
16
+ headers['Cookie'] = gcrCookie;
17
+ }
18
+
15
19
  const response = await fetch(postEndpoint, {
16
20
  method: 'POST',
17
- headers: { 'Content-Type': 'application/json' },
21
+ headers,
18
22
  body: msg
19
23
  });
20
24
  if (!response.ok) {
@@ -27,31 +31,59 @@ async function sendPost(msg) {
27
31
  }
28
32
  }
29
33
 
30
- // When the MCP server initializes the connection, it emits an 'endpoint' event
31
- // containing the specific URI we must POST messages to.
32
- es.addEventListener("endpoint", async (e) => {
33
- try {
34
- postEndpoint = new URL(e.data, sseUrl).toString();
35
-
36
- // Flush any queued messages that arrived before we got the endpoint
37
- for (const msg of messageQueue) {
38
- await sendPost(msg);
34
+ async function connectSSE() {
35
+ console.error(`[ToolBox Bridge] Opening strict bypass connection to: ${finalSseUrl}`);
36
+ const res = await fetch(finalSseUrl);
37
+
38
+ // Extract Cloud Run session affinity cookie
39
+ const cookies = res.headers.raw()['set-cookie'] || [];
40
+ for (const c of cookies) {
41
+ if (c.startsWith('GCRAffinity=')) {
42
+ gcrCookie = c.split(';')[0];
43
+ console.error(`[ToolBox Bridge] Captured Cloud Run Affinity Cookie: ${gcrCookie.substring(0, 20)}...`);
39
44
  }
40
- messageQueue.length = 0;
41
- } catch (err) {
42
- console.error("Failed to parse POST endpoint:", err);
43
45
  }
44
- });
45
46
 
46
- // Any messages received from the remote MCP server must be forwarded
47
- // locally to Claude Desktop via standard output (stdout).
48
- es.onmessage = (e) => {
49
- process.stdout.write(e.data + '\n');
50
- };
47
+ let buffer = '';
48
+ for await (const chunk of res.body) {
49
+ buffer += chunk.toString();
50
+
51
+ // Simple SSE parser
52
+ const events = buffer.split('\n\n');
53
+ buffer = events.pop() || ''; // Keep the last incomplete chunk in buffer
54
+
55
+ for (const eventBlock of events) {
56
+ if (!eventBlock.trim()) continue;
57
+
58
+ let eventType = 'message';
59
+ let data = '';
60
+
61
+ for (const line of eventBlock.split('\n')) {
62
+ if (line.startsWith('event:')) {
63
+ eventType = line.substring(6).trim();
64
+ } else if (line.startsWith('data:')) {
65
+ // MCP JSON RPC payloads might contain multiple chunks
66
+ data += line.substring(5).trim();
67
+ }
68
+ }
69
+
70
+ if (eventType === 'endpoint') {
71
+ postEndpoint = new URL(data, sseUrl).toString();
72
+ console.error(`[ToolBox Bridge] Successfully received dynamic POST endpoint: ${postEndpoint}`);
73
+
74
+ for (const msg of messageQueue) {
75
+ await sendPost(msg);
76
+ }
77
+ messageQueue.length = 0;
78
+ } else if (eventType === 'message') {
79
+ process.stdout.write(data + '\n');
80
+ }
81
+ }
82
+ }
83
+ console.error("[ToolBox Bridge] SSE connection closed by server.");
84
+ }
51
85
 
52
- es.onerror = (e) => {
53
- console.error("SSE connection error:", e);
54
- };
86
+ connectSSE().catch(e => console.error("SSE connection error:", e));
55
87
 
56
88
  // Use readline for perfect line-delimited stream reading, immune to TCP chunk truncation
57
89
  const rl = readline.createInterface({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolbox-co-mcp-bridge",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A bridge proxy to connect local STDIO-based MCP clients (like Claude Desktop) to the remote ToolBox Co. SSE endpoint.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,7 +19,6 @@
19
19
  "author": "ToolBox Co.",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "eventsource": "^2.0.2",
23
22
  "node-fetch": "^3.3.2"
24
23
  }
25
24
  }
package/test_sse.mjs ADDED
@@ -0,0 +1,15 @@
1
+ import fetch from "node-fetch";
2
+
3
+ async function main() {
4
+ const sseUrl = "https://toolbox-mcp-1058072229791.us-central1.run.app/sse";
5
+ console.log("Fetching", sseUrl);
6
+ const res = await fetch(sseUrl);
7
+ console.log("Status:", res.status);
8
+ console.log("Headers:", JSON.stringify([...res.headers.entries()]));
9
+
10
+ for await (const chunk of res.body) {
11
+ console.log("Chunk:", chunk.toString());
12
+ break; // just read the first chunk to see the endpoint event
13
+ }
14
+ }
15
+ main().catch(console.error);