(server, method, params)
| 271 | } |
| 272 | |
| 273 | _sendRequest(server, method, params) { |
| 274 | return new Promise((resolve) => { |
| 275 | if (!server.process || !server.process.stdin || !server.demuxer) { |
| 276 | resolve(null); |
| 277 | return; |
| 278 | } |
| 279 | |
| 280 | const id = this._requestId++; |
| 281 | const request = JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n'; |
| 282 | |
| 283 | let timer = null; |
| 284 | const finish = (val) => { |
| 285 | if (timer) clearTimeout(timer); |
| 286 | server.demuxer.unregister(id); |
| 287 | resolve(val); |
| 288 | }; |
| 289 | |
| 290 | server.demuxer.register(id, (line) => { |
| 291 | try { |
| 292 | const resp = JSON.parse(line); |
| 293 | if (resp.id === id) { |
| 294 | finish(resp.result || null); |
| 295 | } |
| 296 | } catch { /* not our line; demuxer keeps trying others */ } |
| 297 | }); |
| 298 | |
| 299 | try { |
| 300 | server.process.stdin.write(request); |
| 301 | } catch { |
| 302 | finish(null); |
| 303 | return; |
| 304 | } |
| 305 | |
| 306 | // Timeout after 10s. Note: we resolve null rather than reject so |
| 307 | // callers don't blow up — MCP errors are operational, not fatal. |
| 308 | timer = setTimeout(() => finish(null), 10000); |
| 309 | }); |
| 310 | } |
| 311 | |
| 312 | _sendNotification(server, method, params) { |
| 313 | if (!server.process || !server.process.stdin) return; |
no test coverage detected