Skip to content

test(vmcp): cover client response posts - #5885

Open
jstar0 wants to merge 2 commits into
stacklok:mainfrom
jstar0:test/vmcp-client-response-accepted
Open

test(vmcp): cover client response posts#5885
jstar0 wants to merge 2 commits into
stacklok:mainfrom
jstar0:test/vmcp-client-response-accepted

Conversation

@jstar0

@jstar0 jstar0 commented Jul 20, 2026

Copy link
Copy Markdown

Summary

Valid JSON-RPC responses sent by Streamable HTTP clients used to pass through a
request-only authorization layer, which rejected them as malformed and broke
server-initiated MCP requests. The production vMCP handler no longer installs
that legacy layer after #5556, but the client-response path had no end-to-end
regression coverage.

This adds a production-handler integration test that opens a real Streamable
HTTP client, triggers server-initiated elicitation during a tool call, and sends
the correlated client response back through the /mcp endpoint.

Changes

The test verifies that:

  • the client response POST returns 202 Accepted with an empty body;
  • the elicitation response is correlated and delivered to the server;
  • the outer tool call completes with the expected result; and
  • configuring the retired request-only authorization middleware does not put it
    back into the production handler chain.

The change is test-only and does not modify runtime behavior.

Verification

go test -race ./pkg/vmcp/server -run '^TestHandler_AcceptsClientResponsePostWithLegacyAuthzConfigured$' -count=1
go test -race ./pkg/vmcp/server -count=1

Closes #5009

Signed-off-by: King Star <mcxin.y@gmail.com>
@github-actions github-actions Bot added the size/S Small PR: 100-299 lines changed label Jul 28, 2026
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.45%. Comparing base (8efeeb3) to head (5aaa720).
⚠️ Report is 84 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5885      +/-   ##
==========================================
- Coverage   71.48%   71.45%   -0.03%     
==========================================
  Files         694      694              
  Lines       70971    70971              
==========================================
- Hits        50735    50714      -21     
- Misses      16577    16598      +21     
  Partials     3659     3659              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChrisJBurns ChrisJBurns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test-only PR with genuine regression value for the client-response → 202 path. I applied it to current main and it passes under -race. A few small things would make it clearer/stronger:

Scope of Closes #5009: the issue's "where to fix" points at pkg/transport/proxy/streamable/ and its impact section mentions non-vMCP proxy pings too. This test only covers the vMCP server path. Can you confirm the general thv run / thv proxy streamable transport also returns 202 for client-response POSTs? If not, this closes only half of #5009 — worth either adding coverage there or noting the remaining gap on the issue before it's auto-closed.

Housekeeping: the branch is ~84 commits behind main and CI hasn't reported. Mind rebasing so the parity/lint suites run green before merge? (It still applies cleanly and passes locally, so no real conflict expected.)

// these two layers together. The request-only authz layer treated a JSON-RPC
// response as malformed because the parser intentionally returned no request.
srv.config.AuthMiddleware = mcpparser.ParsingMiddleware
srv.config.AuthzMiddleware = legacyRequestAuthorization

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inert by construction, not an active gate. The production Handler (server.go:634-637) no longer reads config.AuthzMiddleware at all — the whole HTTP authz block was deleted in #5556, not just guarded on nil. So this assignment does nothing today; the assertion passes because the field is ignored. That's still a useful canary (it would fail if someone re-added an if config.AuthzMiddleware != nil { apply } block to Handler), but the preceding comment frames it as if the layer is live in the chain. Suggest making the intent explicit, e.g.:

// Regression canary: Handler no longer reads config.AuthzMiddleware (the HTTP
// authz block was removed in #5556). Setting it here proves that re-introducing
// an `if config.AuthzMiddleware != nil { apply }` block would break response POSTs.

}
}

func TestHandler_AcceptsClientResponsePostWithLegacyAuthzConfigured(t *testing.T) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name ...WithLegacyAuthzConfigured reads as if the legacy middleware is live in the request chain, but it's structurally unreachable (see comment below). Consider a name/doc that says it guards against re-adding the authz block rather than running "with … configured", or add a short top-of-function comment stating the two distinct things this proves: (1) a client-response POST returns 202, and (2) a configured AuthzMiddleware is not reinstalled by the Serve-path Handler.

readErr: readErr,
}
select {
case t.observed <- observation:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: the default silently drops the observation if this buffered (size 1) channel is already full. Fine for the single-response happy path, but if a retry ever POSTs a second response, the test could hang on <-observed waiting for one that was dropped. A one-line comment noting "only one response POST is expected; extras are intentionally discarded" would save a future debugger some time.

@github-actions github-actions Bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Small PR: 100-299 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vMCP streamable-http returns 400 on valid client responses to server-initiated requests

2 participants