Commit Graph
13 Commits
Author SHA1 Message Date
Edgar ArakelyanandClaude Opus 5.5 b6bb74ed9f fix(mcp): close the listener before dropping open connections
closeAllConnections() ran before close(), so a connection accepted in
between missed the forced-close pass and close() could wait for its
request timeout. close() now starts first and closeAllConnections()
follows immediately, as Node documents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:25:57 -07:00
Edgar ArakelyanandClaude Opus 5.5 2deed2eb77 fix(mcp): ignore zero-quality Accept ranges in the session takeover guard
Addresses a thirteenth automated review finding: `application/json;q=0`
explicitly refuses JSON, so it no longer counts toward the exact Accept ranges
a session takeover requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:27:04 -07:00
Edgar ArakelyanandClaude Fable 5.1 a1da7ffef1 fix(mcp): close the connection unless the incoming body was consumed to its end
Addresses the twelfth automated review finding: bodyUsed only means a body
was disturbed, so a route that read one chunk of an upload left the rest
paused on a keep-alive socket. The close decision now follows the incoming
message's end of stream.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 13:00:32 -07:00
Edgar ArakelyanandClaude Fable 5.1 0c9701334f fix(mcp): take request targets verbatim, require a lone initialize and keep failed hooks retryable
Addresses the tenth automated review round: the bridge joins an origin-form
request target onto the listener's origin so a network-path target stays a
path and absolute-form targets are refused; only a lone initialize can replace
an open session; and the shutdown hook records completion only after it
returns, so a hook that threw stays eligible for the next close.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 12:21:29 -07:00
Edgar ArakelyanandClaude Fable 5.1 24301be4fb fix(mcp): guard request conversion and close after any unread declared body
Addresses the ninth automated review round: a method Fetch refuses, such as
TRACE, is answered with 405 instead of escaping the request guard; the
unread-body decision now comes from the incoming message's own body
declaration, so a GET or HEAD carrying a body closes too; and the regression
uses a keep-alive client and checks that the socket actually closes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 11:22:42 -07:00
Edgar ArakelyanandClaude Fable 5.1 a23f5f1352 fix(mcp): close the connection after a reply sent before the body was read
Addresses the eighth automated review finding: a refused Host or Origin, an
unknown route or the startup 503 answered a POST while its body stayed unread
on the paused bridged stream; such replies now carry Connection: close so an
unread upload cannot keep a socket open.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 11:04:29 -07:00
Edgar ArakelyanandClaude Fable 5.1 174188279b fix(mcp): fire the shutdown hook once, stop awaiting the probe's tee and keep every Set-Cookie
Addresses the fifth automated review round: the instance's shutdown hook runs
exactly once even when the listener closes without a live session; the
initialize probe no longer awaits cancelling its cloned body, which would wait
for the original request to drain; and the node:http bridge passes repeated
Set-Cookie values through as an array.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 09:53:56 -07:00
Edgar ArakelyanandClaude Fable 5.1 54a7475602 refactor(mcp): release the listener through one helper
Replaces a comma expression left by the previous commit with plain
statements and moves the port release into a small function, so the listener
body stays within the lint limits without changing behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 09:37:23 -07:00
Edgar ArakelyanandClaude Fable 5.1 fb22df370b fix(mcp): match Accept ranges exactly and keep a session whose shutdown failed
Addresses the fourth automated review round: a sessionless POST replaces a
session only when its Accept header lists application/json and
text/event-stream as exact media ranges, and a session whose transport
shutdown rejected stays owned so the next close retries it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 09:36:21 -07:00
Edgar ArakelyanandClaude Fable 5.1 b76f354d76 fix(mcp): classify loopback from the bound address and keep startup failures out of onerror
Addresses the third automated review round: loopback is classified from the
address the listener bound with the configured name added to the Host and
Origin allowlist; scoped IPv6 literals are refused before binding and a URL
that cannot be formed closes the listener; listener errors reach onerror only
after a successful bind; the MCP runtime keeps its factory owner as the single
owner of instance cleanup through `closeInstance: false`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 09:18:08 -07:00
Edgar ArakelyanandClaude Fable 5.1 9ed8f06f75 fix(mcp): compare the media type proper and bind before creating the server
Addresses the second automated review round: a sessionless POST replaces a
session only when its media type proper is application/json, and the server
instance is created after the listener binds so a bind failure leaves nothing
to clean up and a factory failure releases the listener.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 08:38:34 -07:00
Edgar ArakelyanandClaude Fable 5.1 b0ec63cc03 fix(mcp): harden the streamable HTTP listener's session replacement and shutdown
Addresses the automated review findings on the PR: refuse requests once the
session slot is closed and join an arming initialize during shutdown; retry
failed close stages instead of replaying the rejection; classify loopback
binds by address class and add the bind's own hostname to the Host and Origin
allowlist; let only a POST with the streamable headers replace an open
session; announce the bound URL at every log level; route `mcp -h --http` to
the HTTP entry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 08:14:01 -07:00
Edgar ArakelyanandClaude Fable 5.1 46dd8f0e2b feat(mcp): serve the MCP server over streamable HTTP
Add rea mcp --http [--host] [--port]: the same server rea speaks over stdio,
served over MCP Streamable HTTP on a loopback listener by default, using the
SDK's web-standard transport already in the pinned @modelcontextprotocol/server
behind a small node:http bridge. One client and one session at a time; DELETE
ends the session without running provider teardown, a new client's initialize
replaces a session left open, foreign Host headers are refused on loopback
binds, and shutdown is signals-only. Stdio stays the default and registered
transport; server.json and client registrations are unchanged.

Refs morluto/rea#4933

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-17 21:05:10 -07:00