Summary
The remaining MCP proxy hydration/forwarding fixes plus the per-upstream bootstrap
credential — the third and final binding-mcp extraction from the examples/mcp.proxy
branch (#1793), after cache refresh (#1813) and the single session-id contract (#1815).
Changes
- Content-length framing for request bodies —
fix: frame client HTTP request bodies
with Content-Length; feat: frame streamed tools/call / prompts/get /
resources/read request bodies — the server stamps a guesstimate contentLength on
McpBeginEx and the client declares a south Content-Length and whitespace-pads (no
buffering / chunked).
- Grant the proxy reply window when the request reply begins (fixes a north
tools/list hang from a reply-window race).
- Strip the toolkit prefix from the forwarded tool name in the proxy request body.
- Degrade gracefully when a south route fails cache hydration — skip a never-established
route (sessionId == null) from hydration and isolate its per-route lifecycle failure
from the shared hydrater lifecycle, so the healthy routes still aggregate.
- Per-upstream bootstrap credential on the mcp client binding
(options.authorization.credentials) — used when there is no inbound client token, so the
cache hydrater and token-less calls can authorize a route that requires it.
- Widen the redirect hash unsigned —
RedirectFW carried sessionId.hashCode()
sign-extended; binding-http folds it via Long.hashCode, so a negative hash routed the
cross-worker redirect to the wrong worker → re-redirect → HTTP 500 for ~half of sessions
under multiple workers. Widen with Integer.toUnsignedLong so the fold round-trips.
Notes
General binding-mcp behavior, independent of the example (the example-config edits in two of
these commits are excluded and stay on the example branch). Final binding-mcp extraction;
after this, only the example remains on #1793.
Summary
The remaining MCP proxy hydration/forwarding fixes plus the per-upstream bootstrap
credential — the third and final binding-mcp extraction from the
examples/mcp.proxybranch (#1793), after cache refresh (#1813) and the single session-id contract (#1815).
Changes
fix: frame client HTTP request bodieswith
Content-Length;feat: frame streamedtools/call/prompts/get/resources/readrequest bodies — the server stamps a guesstimatecontentLengthonMcpBeginExand the client declares a southContent-Lengthand whitespace-pads (nobuffering / chunked).
tools/listhang from a reply-window race).route (
sessionId == null) from hydration and isolate its per-route lifecycle failurefrom the shared hydrater lifecycle, so the healthy routes still aggregate.
(
options.authorization.credentials) — used when there is no inbound client token, so thecache hydrater and token-less calls can authorize a route that requires it.
RedirectFWcarriedsessionId.hashCode()sign-extended; binding-http folds it via
Long.hashCode, so a negative hash routed thecross-worker redirect to the wrong worker → re-redirect → HTTP 500 for ~half of sessions
under multiple workers. Widen with
Integer.toUnsignedLongso the fold round-trips.Notes
General binding-mcp behavior, independent of the example (the example-config edits in two of
these commits are excluded and stay on the example branch). Final binding-mcp extraction;
after this, only the example remains on #1793.