feat(mcp): trigger lazy auth on JSON-RPC body errors - #6
Merged
Conversation
lorem-dev
force-pushed
the
feat/json-rpc-lazy-auth
branch
from
May 22, 2026 22:33
6d25c11 to
c80677e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extend the lazy-auth retry path so it fires not only on HTTP 401/403,
but also when the remote MCP server returns a JSON-RPC body error or
a tool-result with
isError: true. Until now, a server that alwaysanswered HTTP 200 with a body-level error would never trigger the
auth-header resolver and the agent would loop forever without
credentials. This PR closes that gap.
What's in this branch
internal/mcp/jsonrpc_error.go(+tests) - minimal JSON-RPC 2.0envelope parser with
inspectResponse(raw []byte) boolthatreturns
truefor responses carrying a top-levelerrororresult.isError, after sanity-checkingjsonrpc == "2.0"and anon-null
id.extractID(raw)returns the raw bytes of the idfor use as a map key (so two responses share the same logical id
iff their
extractIDoutputs match).internal/mcp/auth_state.go(+tests) - sharedauthStatethatguarantees one-shot header resolution. The transport and the body
inspector both route through this state so a single resolve
covers both retry triggers.
internal/mcp/proxy.gorewritten to track in-flight request idsand re-send the original request through the auth-resolved
transport when
inspectResponseflags the response as retry-worthy.authStatefor theauth-retry path instead of inlining the resolver.
docs/architecture.mddescribes the new body-level trigger.## Development.Context: PR #3
A prior PR #3 from the same branch was merged into
developon2026-05-15 (merge commit
6becdfc9), butdevelopwas subsequentlyrewritten and the merge commit + the seven feature commits are no
longer reachable from any released history. The branch was also
deleted from GitHub at PR-merge time. This PR resubmits the same
work against the current
developtip so the change actually lands.Test plan
make verifypasses (all 7 gates).proxy_retry_test.gocovers: body-level errorresponse triggers one-shot resolver, retried request gets
the resolved headers, second response forwarded unchanged.
jsonrpc_error_test.gocovers: notifications, id:null,malformed JSON, valid responses with
errorand withresult.isError, no-error happy path.that answers 200 +
{"error":{...}}on first call,observe header resolution + retry.