| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
McpAsyncServerExchange#createMessage and #createElicitation both fail fast when the client is not initialized or has not declared the matching capability. listRoots sent the request regardless. Add the same two guards to listRoots(String cursor). The no-arg listRoots() delegates to it, so both overloads are covered by the single check.
| Back | FazBrowse Home | New Git URL |
Closes #1067.
McpAsyncServerExchange#createMessage and #createElicitation both fail fast when the client is not initialized or has not declared the matching capability. listRoots sent the request regardless.
This adds the same two guards to listRoots(String cursor). The no-arg listRoots() delegates to that overload, so a single check covers both entry points.
Motivation and Context
A server calling listRoots() against a client that never declared roots had no way to tell that up front: the request went out and failed somewhere downstream, or hung, depending on how the client handled an unsupported method. The two neighbouring methods on the same class already guard against exactly this, so the inconsistency was the bug.
How Has This Been Tested?
Unit tests only — not exercised in a real application.
Two tests added to McpAsyncServerExchangeTests, mirroring the existing elicitation guard tests:
Both call the no-arg listRoots(), so the delegation path is covered too, and both assert verify(mockSession, never()).sendRequest(eq(METHOD_ROOTS_LIST), ...) — the point of the issue is that no request is sent at all, not merely that an error surfaces.
Verified locally with ./mvnw -pl mcp-core -am test: 420 tests, green. Reverting only the main-code change makes exactly the two new tests fail, and nothing else.
Breaking Changes
Behaviour changes, though in the direction the class already established. A server that previously called listRoots() against a client which supports roots but does not declare the capability would have gotten a response; it now gets an IllegalStateException. That matches what createMessage and createElicitation already do for sampling and elicitation.
Types of changes
Checklist
Additional context
The issue suggested only the roots() == null guard. I added the null-capabilities guard alongside it so the method matches the shape of createMessage and createElicitation — happy to drop it if you would rather keep the change minimal.
No documentation change: the guard makes an undocumented failure mode explicit rather than altering the documented contract. Say the word if the Javadoc on listRoots should call out the new precondition.