@meldocio/mcp-stdio-proxy 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/bin/meldoc-mcp-proxy.js +125 -11
- package/package.json +2 -2
package/bin/meldoc-mcp-proxy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const axios = require('axios');
|
|
4
|
+
const https = require('https');
|
|
4
5
|
const { URL } = require('url');
|
|
5
6
|
|
|
6
7
|
// JSON-RPC error codes
|
|
@@ -40,8 +41,10 @@ process.stdin.on('data', (chunk) => {
|
|
|
40
41
|
|
|
41
42
|
// Process complete lines
|
|
42
43
|
for (const line of lines) {
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const trimmed = line.trim();
|
|
45
|
+
// Skip empty lines but don't exit
|
|
46
|
+
if (trimmed) {
|
|
47
|
+
handleLine(trimmed);
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
});
|
|
@@ -52,18 +55,33 @@ process.stdin.on('end', () => {
|
|
|
52
55
|
if (buffer.trim()) {
|
|
53
56
|
handleLine(buffer.trim());
|
|
54
57
|
}
|
|
58
|
+
// Don't exit - Claude Desktop may reconnect
|
|
59
|
+
// The process will be terminated by Claude Desktop when needed
|
|
55
60
|
});
|
|
56
61
|
|
|
57
|
-
// Handle errors
|
|
62
|
+
// Handle errors - don't exit, just log
|
|
58
63
|
process.stdin.on('error', (error) => {
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
// Log to stderr but don't exit - Claude Desktop may close stdin
|
|
65
|
+
// Silently handle stdin errors - they're normal when Claude Desktop closes the connection
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Handle stdout errors
|
|
69
|
+
process.stdout.on('error', (error) => {
|
|
70
|
+
// If stdout is closed (e.g., Claude Desktop disconnected), exit gracefully
|
|
71
|
+
if (error.code === 'EPIPE') {
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
61
74
|
});
|
|
62
75
|
|
|
63
76
|
/**
|
|
64
77
|
* Handle a single line from stdin
|
|
65
78
|
*/
|
|
66
79
|
function handleLine(line) {
|
|
80
|
+
// Skip empty lines
|
|
81
|
+
if (!line || !line.trim()) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
67
85
|
try {
|
|
68
86
|
const request = JSON.parse(line);
|
|
69
87
|
handleRequest(request);
|
|
@@ -81,11 +99,14 @@ function validateRequest(request) {
|
|
|
81
99
|
return { valid: false, error: 'Request must be an object' };
|
|
82
100
|
}
|
|
83
101
|
|
|
84
|
-
|
|
102
|
+
// Allow requests without jsonrpc for compatibility (some MCP clients may omit it)
|
|
103
|
+
if (request.jsonrpc && request.jsonrpc !== '2.0') {
|
|
85
104
|
return { valid: false, error: 'jsonrpc must be "2.0"' };
|
|
86
105
|
}
|
|
87
106
|
|
|
88
|
-
if (
|
|
107
|
+
// Allow requests without method if they're notifications (id is null/undefined)
|
|
108
|
+
// But batch requests must be arrays
|
|
109
|
+
if (!request.method && !Array.isArray(request) && request.id !== null && request.id !== undefined) {
|
|
89
110
|
return { valid: false, error: 'Request must have a method or be a batch array' };
|
|
90
111
|
}
|
|
91
112
|
|
|
@@ -96,12 +117,29 @@ function validateRequest(request) {
|
|
|
96
117
|
* Handle a JSON-RPC request
|
|
97
118
|
*/
|
|
98
119
|
async function handleRequest(request) {
|
|
120
|
+
// Handle null/undefined requests
|
|
121
|
+
if (!request) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
99
125
|
// Handle batch requests (array of requests)
|
|
100
126
|
if (Array.isArray(request)) {
|
|
101
127
|
// Process batch requests sequentially
|
|
102
128
|
for (const req of request) {
|
|
103
129
|
if (req) {
|
|
104
|
-
|
|
130
|
+
// Check if this is a protocol method that should be handled locally
|
|
131
|
+
const method = req.method;
|
|
132
|
+
if (method === 'initialize') {
|
|
133
|
+
handleInitialize(req);
|
|
134
|
+
} else if (method === 'initialized') {
|
|
135
|
+
// Notification - no response needed
|
|
136
|
+
continue;
|
|
137
|
+
} else if (method === 'ping') {
|
|
138
|
+
handlePing(req);
|
|
139
|
+
} else {
|
|
140
|
+
// Forward to backend
|
|
141
|
+
await processSingleRequest(req);
|
|
142
|
+
}
|
|
105
143
|
}
|
|
106
144
|
}
|
|
107
145
|
return;
|
|
@@ -114,22 +152,89 @@ async function handleRequest(request) {
|
|
|
114
152
|
return;
|
|
115
153
|
}
|
|
116
154
|
|
|
155
|
+
// Handle MCP protocol methods locally (not forwarded to backend)
|
|
156
|
+
const method = request.method;
|
|
157
|
+
if (method === 'initialize') {
|
|
158
|
+
handleInitialize(request);
|
|
159
|
+
return;
|
|
160
|
+
} else if (method === 'initialized') {
|
|
161
|
+
// Notification - no response needed per MCP spec
|
|
162
|
+
return;
|
|
163
|
+
} else if (method === 'ping') {
|
|
164
|
+
handlePing(request);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// All other methods (tools/*, resources/*, etc.) are forwarded to backend
|
|
117
169
|
await processSingleRequest(request);
|
|
118
170
|
}
|
|
119
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Handle MCP initialize method
|
|
174
|
+
* This is called by Claude Desktop to establish the connection
|
|
175
|
+
*/
|
|
176
|
+
function handleInitialize(request) {
|
|
177
|
+
const response = {
|
|
178
|
+
jsonrpc: '2.0',
|
|
179
|
+
id: request.id,
|
|
180
|
+
result: {
|
|
181
|
+
protocolVersion: '2025-06-18',
|
|
182
|
+
capabilities: {
|
|
183
|
+
tools: {},
|
|
184
|
+
resources: {}
|
|
185
|
+
},
|
|
186
|
+
serverInfo: {
|
|
187
|
+
name: '@meldocio/mcp-stdio-proxy',
|
|
188
|
+
version: '1.0.1'
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
process.stdout.write(JSON.stringify(response) + '\n');
|
|
194
|
+
if (process.stdout.isTTY) {
|
|
195
|
+
process.stdout.flush();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Handle MCP ping method (keep-alive)
|
|
201
|
+
*/
|
|
202
|
+
function handlePing(request) {
|
|
203
|
+
const response = {
|
|
204
|
+
jsonrpc: '2.0',
|
|
205
|
+
id: request.id,
|
|
206
|
+
result: {}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
process.stdout.write(JSON.stringify(response) + '\n');
|
|
210
|
+
if (process.stdout.isTTY) {
|
|
211
|
+
process.stdout.flush();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
120
215
|
/**
|
|
121
216
|
* Process a single JSON-RPC request
|
|
217
|
+
* Forwards the request to the backend MCP API
|
|
122
218
|
*/
|
|
123
219
|
async function processSingleRequest(request) {
|
|
124
220
|
try {
|
|
221
|
+
// Ensure request has jsonrpc field
|
|
222
|
+
const requestWithJsonRpc = {
|
|
223
|
+
...request,
|
|
224
|
+
jsonrpc: request.jsonrpc || '2.0'
|
|
225
|
+
};
|
|
226
|
+
|
|
125
227
|
// Make HTTP request to MCP API
|
|
126
|
-
const response = await axios.post(rpcEndpoint,
|
|
228
|
+
const response = await axios.post(rpcEndpoint, requestWithJsonRpc, {
|
|
127
229
|
headers: {
|
|
128
230
|
'Authorization': `Bearer ${token}`,
|
|
129
|
-
'Content-Type': 'application/json'
|
|
231
|
+
'Content-Type': 'application/json',
|
|
232
|
+
'User-Agent': '@meldocio/mcp-stdio-proxy/1.0.0'
|
|
130
233
|
},
|
|
131
234
|
timeout: REQUEST_TIMEOUT,
|
|
132
|
-
validateStatus: (status) => status < 500 // Don't throw on 4xx errors
|
|
235
|
+
validateStatus: (status) => status < 500, // Don't throw on 4xx errors
|
|
236
|
+
// Keep connection alive for better performance
|
|
237
|
+
httpsAgent: new https.Agent({ keepAlive: true, keepAliveMsecs: 1000 })
|
|
133
238
|
});
|
|
134
239
|
|
|
135
240
|
// Handle successful response
|
|
@@ -146,7 +251,12 @@ async function processSingleRequest(request) {
|
|
|
146
251
|
}
|
|
147
252
|
}
|
|
148
253
|
|
|
254
|
+
// Ensure stdout is flushed immediately
|
|
149
255
|
process.stdout.write(JSON.stringify(responseData) + '\n');
|
|
256
|
+
// Flush stdout to ensure data is sent immediately
|
|
257
|
+
if (process.stdout.isTTY) {
|
|
258
|
+
process.stdout.flush();
|
|
259
|
+
}
|
|
150
260
|
} else {
|
|
151
261
|
// HTTP error status
|
|
152
262
|
const errorMessage = response.data?.error?.message ||
|
|
@@ -202,4 +312,8 @@ function sendError(id, code, message) {
|
|
|
202
312
|
};
|
|
203
313
|
|
|
204
314
|
process.stdout.write(JSON.stringify(errorResponse) + '\n');
|
|
315
|
+
// Flush stdout to ensure data is sent immediately
|
|
316
|
+
if (process.stdout.isTTY) {
|
|
317
|
+
process.stdout.flush();
|
|
318
|
+
}
|
|
205
319
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meldocio/mcp-stdio-proxy",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "MCP stdio proxy for meldoc - connects Claude Desktop to meldoc MCP API",
|
|
5
5
|
"main": "bin/meldoc-mcp-proxy.js",
|
|
6
6
|
"bin": {
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"bin/**/*.js"
|
|
47
47
|
]
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|