Follow-up from #206 / PR #210.
Message validation currently differs by transport:
- ndjson (stdio):
ndJsonStream skips non-object JSON lines and passes any object-shaped message to the connection layer, which handles lenient shapes (resolves responses carrying error: null alongside result, fast-rejects structurally invalid responses with RequestError.invalidRequest, dispatches requests missing the jsonrpc field).
- WebSocket / SSE:
ws-stream.ts:205, ws-server.ts:144, and sse.ts validate with the strict isJsonRpcMessage guard and silently drop anything that fails it.
Consequences of the strict-drop policy on WS/SSE:
- A lenient peer's response (e.g.
{"jsonrpc":"2.0","id":1,"result":{...},"error":null}) is dropped, so the caller's sendRequest promise never settles — there is no request timeout, so it hangs until the connection closes. The same message works over stdio.
- A post-initialize request that fails validation gets neither processing nor the spec-mandated
-32600 error response, so the remote caller hangs too. Only the connection layer (which owns responders) can generate that reply.
PR #210 hardened the connection layer (receiveMessage guards non-objects; handleResponse fast-rejects malformed error members), so a reasonable resolution is to relax the per-transport guards to an object check and let Connection own the accept/reject policy in one place — ideally answering invalid requests with RequestError.invalidRequest instead of silently discarding them.
🤖 Generated with Claude Code
Follow-up from #206 / PR #210.
Message validation currently differs by transport:
ndJsonStreamskips non-object JSON lines and passes any object-shaped message to the connection layer, which handles lenient shapes (resolves responses carryingerror: nullalongsideresult, fast-rejects structurally invalid responses withRequestError.invalidRequest, dispatches requests missing thejsonrpcfield).ws-stream.ts:205,ws-server.ts:144, andsse.tsvalidate with the strictisJsonRpcMessageguard and silently drop anything that fails it.Consequences of the strict-drop policy on WS/SSE:
{"jsonrpc":"2.0","id":1,"result":{...},"error":null}) is dropped, so the caller'ssendRequestpromise never settles — there is no request timeout, so it hangs until the connection closes. The same message works over stdio.-32600error response, so the remote caller hangs too. Only the connection layer (which owns responders) can generate that reply.PR #210 hardened the connection layer (
receiveMessageguards non-objects;handleResponsefast-rejects malformederrormembers), so a reasonable resolution is to relax the per-transport guards to an object check and letConnectionown the accept/reject policy in one place — ideally answering invalid requests withRequestError.invalidRequestinstead of silently discarding them.🤖 Generated with Claude Code