@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.
Files changed (2) hide show
  1. package/index.js +122 -42
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -1,16 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawn } = require('child_process');
4
- const path = require('path');
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 SSE_URL = "https://api.ecommaps.com/api/v1/mcp/sse";
7
+ const DEFAULT_SSE_URL = "https://api.ecommaps.com/api/v1/mcp/sse";
9
8
 
10
- // Usage: npx @ecommaps/mcp <API_KEY>
11
- // Or: npx @ecommaps/mcp --key <API_KEY>
9
+ // Parse Args manually or with util.parseArgs
10
+ const args = process.argv.slice(2);
11
+ let apiKey = null;
12
12
 
13
- const apiKey = args.key || args._[0];
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
- console.error(`🔌 EcoBaseAI MCP Client Connecting...`);
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
- child.on('close', (code) => {
54
- if (code !== 0 && code !== null) {
55
- console.error(`Client exited with code ${code}`);
56
- process.exit(code);
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
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@ecommaps/mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "EcoBaseAI Official MCP Client",
5
5
  "bin": {
6
- "ecommaps-mcp": "./index.js"
6
+ "mcp": "./index.js"
7
7
  },
8
8
  "main": "index.js",
9
9
  "dependencies": {