@ecommaps/mcp 1.0.0 → 1.0.2
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 +122 -42
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
5
|
-
const args = require('minimist')(process.argv.slice(2));
|
|
3
|
+
const { parseArgs } = require('util');
|
|
4
|
+
const readline = require('readline');
|
|
6
5
|
|
|
7
6
|
// Configuration
|
|
8
|
-
const
|
|
7
|
+
const DEFAULT_SSE_URL = "https://api.ecommaps.com/api/v1/mcp/sse";
|
|
9
8
|
|
|
10
|
-
//
|
|
11
|
-
|
|
9
|
+
// Parse Args manually or with util.parseArgs
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
let apiKey = null;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Simple arg parsing
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
if (args[i].startsWith('sk_eco_')) {
|
|
16
|
+
apiKey = args[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
14
19
|
|
|
15
20
|
if (!apiKey) {
|
|
16
21
|
console.error("❌ Error: API Key is required.");
|
|
@@ -18,41 +23,116 @@ if (!apiKey) {
|
|
|
18
23
|
process.exit(1);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
const SSE_URL = DEFAULT_SSE_URL;
|
|
27
|
+
const POST_URL = SSE_URL.replace('/sse', '/messages'); // Default, might be updated by endpoint event
|
|
28
|
+
|
|
29
|
+
console.error(`🔌 EcoBaseAI MCP Client (Native) Connecting...`);
|
|
22
30
|
console.error(` Target: ${SSE_URL}`);
|
|
23
|
-
console.error(` Key: ${apiKey.substring(0, 8)}...`);
|
|
24
|
-
|
|
25
|
-
// We use 'npx supergateway' internally, or if installed as dependency, call the bin.
|
|
26
|
-
// Since we want this to be a standalone package, we can just spawn 'npx supergateway'.
|
|
27
|
-
// Ideally, we would bundle supergateway code, but for this proof of concept, spawning is easiest.
|
|
28
|
-
|
|
29
|
-
const child = spawn('npx', [
|
|
30
|
-
'-y',
|
|
31
|
-
'supergateway',
|
|
32
|
-
'--sse', SSE_URL,
|
|
33
|
-
'--oauth2Bearer', apiKey
|
|
34
|
-
], {
|
|
35
|
-
stdio: 'inherit' // Connect stdio to the parent process (Claude/Cursor)
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Forward signals to child to prevent zombie processes
|
|
39
|
-
process.on('SIGINT', () => {
|
|
40
|
-
// console.error("\nReceived SIGINT, shutting down...");
|
|
41
|
-
child.kill('SIGINT');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
process.on('SIGTERM', () => {
|
|
45
|
-
child.kill('SIGTERM');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
child.on('error', (err) => {
|
|
49
|
-
console.error('Failed to start client:', err);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
});
|
|
52
31
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
async function main() {
|
|
33
|
+
const headers = {
|
|
34
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
35
|
+
'Accept': 'text/event-stream',
|
|
36
|
+
'Content-Type': 'application/json'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// 1. Start SSE Connection
|
|
40
|
+
let controller = new AbortController();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch(SSE_URL, {
|
|
44
|
+
headers: headers,
|
|
45
|
+
signal: controller.signal
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
console.error(`❌ SSE Connection failed: ${response.status} ${response.statusText}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle SSE Stream
|
|
54
|
+
const reader = response.body.getReader();
|
|
55
|
+
const decoder = new TextDecoder();
|
|
56
|
+
let buffer = '';
|
|
57
|
+
|
|
58
|
+
// Process SSE in background
|
|
59
|
+
(async () => {
|
|
60
|
+
try {
|
|
61
|
+
while (true) {
|
|
62
|
+
const { done, value } = await reader.read();
|
|
63
|
+
if (done) break;
|
|
64
|
+
|
|
65
|
+
buffer += decoder.decode(value, { stream: true });
|
|
66
|
+
const lines = buffer.split('\n');
|
|
67
|
+
buffer = lines.pop(); // Keep incomplete line
|
|
68
|
+
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
if (line.startsWith('data: ')) {
|
|
71
|
+
const data = line.slice(6).trim();
|
|
72
|
+
if (data === '[DONE]') continue;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Try to parse as JSON-RPC
|
|
76
|
+
const msg = JSON.parse(data);
|
|
77
|
+
// Forward to StdOut
|
|
78
|
+
console.log(JSON.stringify(msg));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// Not JSON (maybe endpoint url?)
|
|
81
|
+
// console.error('Received non-JSON data:', data);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (line.startsWith('event: endpoint')) {
|
|
85
|
+
// The next data line contains the endpoint.
|
|
86
|
+
// Current simple parser might miss context, but our server sends data immediately after.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('❌ SSE Stream Error:', err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
// 2. Setup Stdin Reader for POST requests
|
|
97
|
+
const rl = readline.createInterface({
|
|
98
|
+
input: process.stdin,
|
|
99
|
+
output: process.stderr, // Important: Don't write prompts to stdout
|
|
100
|
+
terminal: false
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
rl.on('line', async (line) => {
|
|
104
|
+
if (!line.trim()) return;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// Validate JSON
|
|
108
|
+
const msg = JSON.parse(line);
|
|
109
|
+
|
|
110
|
+
// Send POST
|
|
111
|
+
const postResp = await fetch(POST_URL, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: headers,
|
|
114
|
+
body: JSON.stringify(msg)
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!postResp.ok) {
|
|
118
|
+
const text = await postResp.text();
|
|
119
|
+
console.error(`❌ POST Error: ${postResp.status} - ${text}`);
|
|
120
|
+
} else {
|
|
121
|
+
// Some MCP servers return result in HTTP response
|
|
122
|
+
const text = await postResp.text();
|
|
123
|
+
if (text && postResp.headers.get('content-type')?.includes('application/json')) {
|
|
124
|
+
console.log(text);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error('❌ Failed to process input line:', e);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error('❌ Connection Error:', err);
|
|
134
|
+
process.exit(1);
|
|
57
135
|
}
|
|
58
|
-
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
main();
|
package/package.json
CHANGED