@ecommaps/mcp 1.0.6 → 1.0.7
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/README.md +63 -21
- package/cli.js +142 -134
- package/package.json +4 -7
package/README.md
CHANGED
|
@@ -1,34 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ecommaps/mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official Ecommaps MCP **stdio bridge**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package lets MCP-capable editors connect to Ecommaps over the modern
|
|
6
|
+
Streamable HTTP endpoint (`/api/v1/mcp`) while speaking stdio locally.
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
Run directly with your store key:
|
|
6
11
|
|
|
7
12
|
```bash
|
|
8
|
-
|
|
13
|
+
npx @ecommaps/mcp <YOUR_STORE_MCP_KEY>
|
|
9
14
|
```
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
Use a custom endpoint (production or local):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @ecommaps/mcp <YOUR_STORE_MCP_KEY> --url https://api.ecommaps.com/api/v1/mcp
|
|
20
|
+
```
|
|
12
21
|
|
|
13
22
|
```bash
|
|
14
|
-
npx @ecommaps/mcp <
|
|
23
|
+
npx @ecommaps/mcp <YOUR_STORE_MCP_KEY> --url http://127.0.0.1:8001/api/v1/mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## CLI Options
|
|
27
|
+
|
|
28
|
+
- `--url <MCP_URL>`: Override the MCP endpoint URL.
|
|
29
|
+
- `--api-key <KEY>`: Pass key explicitly instead of positional arg.
|
|
30
|
+
- `--help`: Show usage.
|
|
31
|
+
|
|
32
|
+
## Environment Variables
|
|
33
|
+
|
|
34
|
+
- `MCP_BEARER_TOKEN`: Preferred token source.
|
|
35
|
+
- `MCP_API_KEY`: Backward-compatible token source.
|
|
36
|
+
- `ECOMMAPS_MCP_URL`: Default endpoint if `--url` is not provided.
|
|
37
|
+
|
|
38
|
+
## Editor Configuration (Generic MCP stdio)
|
|
39
|
+
|
|
40
|
+
Use the following shape in any editor that supports stdio MCP servers:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": [
|
|
46
|
+
"@ecommaps/mcp",
|
|
47
|
+
"sk_eco_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
48
|
+
"--url",
|
|
49
|
+
"https://api.ecommaps.com/api/v1/mcp"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
15
52
|
```
|
|
16
53
|
|
|
17
|
-
|
|
54
|
+
Or with env var:
|
|
18
55
|
|
|
19
|
-
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"command": "npx",
|
|
59
|
+
"args": ["@ecommaps/mcp", "--url", "https://api.ecommaps.com/api/v1/mcp"],
|
|
60
|
+
"env": {
|
|
61
|
+
"MCP_BEARER_TOKEN": "sk_eco_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
20
65
|
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npm login
|
|
24
|
-
```
|
|
25
|
-
2. Navigate to this directory:
|
|
26
|
-
```bash
|
|
27
|
-
cd packages/mcp-cli
|
|
28
|
-
```
|
|
29
|
-
3. Publish with public access:
|
|
30
|
-
```bash
|
|
31
|
-
npm publish --access public
|
|
32
|
-
```
|
|
66
|
+
## Transport Notes
|
|
33
67
|
|
|
34
|
-
|
|
68
|
+
- Recommended endpoint: `POST /api/v1/mcp`
|
|
69
|
+
- Legacy compatibility endpoints still exist during migration but are not recommended for new integrations.
|
|
70
|
+
|
|
71
|
+
## Release
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd packages/mcp-cli
|
|
75
|
+
npm publish --access public
|
|
76
|
+
```
|
package/cli.js
CHANGED
|
@@ -1,150 +1,158 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { parseArgs } = require('util');
|
|
4
3
|
const readline = require('readline');
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
const DEFAULT_SSE_URL = "https://api.ecommaps.com/api/v1/mcp/sse";
|
|
5
|
+
const DEFAULT_MCP_URL = process.env.ECOMMAPS_MCP_URL || 'https://api.ecommaps.com/api/v1/mcp';
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
const args =
|
|
11
|
-
let apiKey = null;
|
|
7
|
+
function parseCliArgs(argv) {
|
|
8
|
+
const args = argv.slice(2);
|
|
9
|
+
let apiKey = process.env.MCP_BEARER_TOKEN || process.env.MCP_API_KEY || null;
|
|
10
|
+
let mcpUrl = DEFAULT_MCP_URL;
|
|
11
|
+
let showHelp = false;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (args[i]
|
|
20
|
-
|
|
13
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg === '--help' || arg === '-h') {
|
|
16
|
+
showHelp = true;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (arg === '--url' && args[i + 1]) {
|
|
20
|
+
mcpUrl = args[i + 1];
|
|
21
|
+
i += 1;
|
|
22
|
+
continue;
|
|
21
23
|
}
|
|
24
|
+
if (arg === '--api-key' && args[i + 1]) {
|
|
25
|
+
apiKey = args[i + 1];
|
|
26
|
+
i += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!arg.startsWith('--') && !apiKey) {
|
|
30
|
+
apiKey = arg;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { apiKey, mcpUrl, showHelp };
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
function printUsageAndExit() {
|
|
38
|
+
console.error('Usage: npx @ecommaps/mcp <YOUR_API_KEY> [--url https://api.ecommaps.com/api/v1/mcp]');
|
|
39
|
+
console.error(' npx @ecommaps/mcp --api-key <YOUR_API_KEY> [--url <MCP_URL>]');
|
|
40
|
+
console.error('');
|
|
41
|
+
console.error('Environment variables:');
|
|
42
|
+
console.error(' MCP_BEARER_TOKEN Preferred auth token');
|
|
43
|
+
console.error(' MCP_API_KEY Backward-compatible auth token');
|
|
44
|
+
console.error(' ECOMMAPS_MCP_URL Default MCP endpoint URL');
|
|
45
|
+
process.exit(0);
|
|
28
46
|
}
|
|
29
47
|
|
|
30
|
-
|
|
31
|
-
|
|
48
|
+
function normalizeResponsePayload(rawBody, contentType) {
|
|
49
|
+
if (!rawBody) return null;
|
|
50
|
+
|
|
51
|
+
if (contentType && contentType.includes('text/event-stream')) {
|
|
52
|
+
// Streamable HTTP can return SSE; convert first data frame to JSON object if possible.
|
|
53
|
+
const lines = rawBody.split('\n');
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
if (!line.startsWith('data:')) continue;
|
|
56
|
+
const value = line.slice(5).trim();
|
|
57
|
+
if (!value || value === '[DONE]') continue;
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(value);
|
|
60
|
+
} catch (_) {
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(rawBody);
|
|
69
|
+
} catch (_) {
|
|
70
|
+
return rawBody;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
32
73
|
|
|
33
|
-
|
|
34
|
-
|
|
74
|
+
async function postRpc(mcpUrl, apiKey, rpcPayload) {
|
|
75
|
+
const response = await fetch(mcpUrl, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
Authorization: `Bearer ${apiKey}`,
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
Accept: 'application/json, text/event-stream',
|
|
81
|
+
'MCP-Protocol-Version': '2025-06-18',
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify(rpcPayload),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const rawBody = await response.text();
|
|
87
|
+
const contentType = response.headers.get('content-type') || '';
|
|
88
|
+
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
throw new Error(`MCP HTTP ${response.status}: ${rawBody}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const payload = normalizeResponsePayload(rawBody, contentType);
|
|
94
|
+
if (payload === null) {
|
|
95
|
+
throw new Error('MCP response was empty.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof payload === 'object' && payload && payload.error) {
|
|
99
|
+
throw new Error(`MCP JSON-RPC error: ${payload.error.message || 'Unknown error'}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return payload;
|
|
103
|
+
}
|
|
35
104
|
|
|
36
105
|
async function main() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// so we filter it out here to keep the client happy.
|
|
85
|
-
if (msg.method === 'notifications/initialized') {
|
|
86
|
-
return; // Skip this message
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Forward to StdOut
|
|
90
|
-
console.log(JSON.stringify(msg));
|
|
91
|
-
} catch (e) {
|
|
92
|
-
// Not JSON (maybe endpoint url?)
|
|
93
|
-
// console.error('Received non-JSON data:', data);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (line.startsWith('event: endpoint')) {
|
|
97
|
-
// The next data line contains the endpoint.
|
|
98
|
-
// Current simple parser might miss context, but our server sends data immediately after.
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} catch (err) {
|
|
103
|
-
console.error('❌ SSE Stream Error:', err);
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
})();
|
|
107
|
-
|
|
108
|
-
// 2. Setup Stdin Reader for POST requests
|
|
109
|
-
const rl = readline.createInterface({
|
|
110
|
-
input: process.stdin,
|
|
111
|
-
output: process.stderr, // Important: Don't write prompts to stdout
|
|
112
|
-
terminal: false
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
rl.on('line', async (line) => {
|
|
116
|
-
if (!line.trim()) return;
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
// Validate JSON
|
|
120
|
-
const msg = JSON.parse(line);
|
|
121
|
-
|
|
122
|
-
// Send POST
|
|
123
|
-
const postResp = await fetch(POST_URL, {
|
|
124
|
-
method: 'POST',
|
|
125
|
-
headers: headers,
|
|
126
|
-
body: JSON.stringify(msg)
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
if (!postResp.ok) {
|
|
130
|
-
const text = await postResp.text();
|
|
131
|
-
console.error(`❌ POST Error: ${postResp.status} - ${text}`);
|
|
132
|
-
} else {
|
|
133
|
-
// Some MCP servers return result in HTTP response
|
|
134
|
-
const text = await postResp.text();
|
|
135
|
-
if (text && postResp.headers.get('content-type')?.includes('application/json')) {
|
|
136
|
-
console.log(text);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (e) {
|
|
140
|
-
console.error('❌ Failed to process input line:', e);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
} catch (err) {
|
|
145
|
-
console.error('❌ Connection Error:', err);
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
106
|
+
const { apiKey, mcpUrl, showHelp } = parseCliArgs(process.argv);
|
|
107
|
+
if (showHelp) {
|
|
108
|
+
printUsageAndExit();
|
|
109
|
+
}
|
|
110
|
+
if (!apiKey) {
|
|
111
|
+
console.error('Error: API key is required.');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.error('Ecommaps MCP stdio bridge started');
|
|
116
|
+
console.error(`Target: ${mcpUrl}`);
|
|
117
|
+
|
|
118
|
+
const rl = readline.createInterface({
|
|
119
|
+
input: process.stdin,
|
|
120
|
+
output: process.stderr,
|
|
121
|
+
terminal: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
let queue = Promise.resolve();
|
|
125
|
+
|
|
126
|
+
rl.on('line', (line) => {
|
|
127
|
+
const trimmed = line.trim();
|
|
128
|
+
if (!trimmed) return;
|
|
129
|
+
|
|
130
|
+
queue = queue.then(async () => {
|
|
131
|
+
let payload;
|
|
132
|
+
try {
|
|
133
|
+
payload = JSON.parse(trimmed);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error(`Invalid JSON input: ${error.message}`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const result = await postRpc(mcpUrl, apiKey, payload);
|
|
141
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
const message = error && error.message ? error.message : String(error);
|
|
144
|
+
console.error(message);
|
|
145
|
+
// Best-effort JSON-RPC error response so MCP clients stay synchronized.
|
|
146
|
+
const id = payload && Object.prototype.hasOwnProperty.call(payload, 'id') ? payload.id : null;
|
|
147
|
+
process.stdout.write(
|
|
148
|
+
`${JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32000, message } })}\n`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
main()
|
|
155
|
+
main().catch((error) => {
|
|
156
|
+
console.error(error && error.message ? error.message : String(error));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
});
|
package/package.json
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecommaps/mcp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "Official Ecommaps MCP stdio bridge",
|
|
5
5
|
"bin": {
|
|
6
|
-
"mcp": "
|
|
6
|
+
"mcp": "cli.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"cli.js",
|
|
10
10
|
"README.md"
|
|
11
11
|
],
|
|
12
12
|
"main": "cli.js",
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"minimist": "^1.2.8"
|
|
15
|
-
},
|
|
16
13
|
"keywords": [
|
|
17
14
|
"mcp",
|
|
18
15
|
"ai",
|
|
@@ -20,4 +17,4 @@
|
|
|
20
17
|
],
|
|
21
18
|
"author": "EcoBaseAI",
|
|
22
19
|
"license": "ISC"
|
|
23
|
-
}
|
|
20
|
+
}
|