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.
- package/index.js +57 -25
- package/package.json +1 -2
- 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
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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.
|
|
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);
|