* Send a server-initiated request to the client and await its response. * * MCP is bidirectional: the server can ask the client questions too. We use * this for `roots/list` — the spec-blessed way to learn the workspace root * when the client didn't pass one in `initialize` (see issue #1
(method: string, params?: unknown, timeoutMs = 5000)
| 112 | * on timeout so callers can fall back rather than hang forever. |
| 113 | */ |
| 114 | request(method: string, params?: unknown, timeoutMs = 5000): Promise<unknown> { |
| 115 | const id = `${this.idPrefix()}-${this.nextRequestId++}`; |
| 116 | return new Promise<unknown>((resolve, reject) => { |
| 117 | const timer = setTimeout(() => { |
| 118 | this.pending.delete(id); |
| 119 | reject(new Error(`Timed out after ${timeoutMs}ms waiting for "${method}" response`)); |
| 120 | }, timeoutMs); |
| 121 | // Don't let a pending request keep the process alive on shutdown. |
| 122 | timer.unref?.(); |
| 123 | this.pending.set(id, { |
| 124 | resolve: (value) => { clearTimeout(timer); resolve(value); }, |
| 125 | reject: (error) => { clearTimeout(timer); reject(error); }, |
| 126 | }); |
| 127 | this.write(JSON.stringify({ jsonrpc: '2.0', id, method, params })); |
| 128 | }); |
| 129 | } |
| 130 | |
| 131 | send(response: JsonRpcResponse): void { |
| 132 | this.write(JSON.stringify(response)); |