Skip to content

Commit 1cbace3

Browse files
committed
feat: add pagination support for listRoots operation (#336)
- Add nextCursor field to ListRootsResult for cursor-based pagination - Implement automatic pagination in McpAsyncServerExchange.listRoots() - Automatically fetches and combines all pages into single result - Refactor McpAsyncServerExchange.listRoots() to return Collections.unmodifiableList - Update tests to verify pagination functionality - Add test suite for McpAsyncServerExchange covering: - listRoots() pagination scenarios and edge cases - Logging notification with level filtering - Elicitation creation with various capabilities - Message creation with sampling capabilities - Error handling and validation scenarios - Verify sendNotification calls in logging notification tests - Verify sendRequest is never called when capabilities are missing Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
1 parent 5bd64c2 commit 1cbace3

File tree

4 files changed

+770
-4
lines changed

4 files changed

+770
-4
lines changed

mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServerExchange.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
package io.modelcontextprotocol.server;
66

7+
import java.util.ArrayList;
8+
import java.util.Collections;
9+
710
import com.fasterxml.jackson.core.type.TypeReference;
811
import io.modelcontextprotocol.spec.McpError;
912
import io.modelcontextprotocol.spec.McpSchema;
@@ -126,7 +129,19 @@ public Mono<McpSchema.ElicitResult> createElicitation(McpSchema.ElicitRequest el
126129
* @return A Mono that emits the list of roots result.
127130
*/
128131
public Mono<McpSchema.ListRootsResult> listRoots() {
129-
return this.listRoots(null);
132+
133+
// @formatter:off
134+
return this.listRoots(McpSchema.FIRST_PAGE)
135+
.expand(result -> (result.nextCursor() != null) ?
136+
this.listRoots(result.nextCursor()) : Mono.empty())
137+
.reduce(new McpSchema.ListRootsResult(new ArrayList<>(), null),
138+
(allRootsResult, result) -> {
139+
allRootsResult.roots().addAll(result.roots());
140+
return allRootsResult;
141+
})
142+
.map(result -> new McpSchema.ListRootsResult(Collections.unmodifiableList(result.roots()),
143+
result.nextCursor()));
144+
// @formatter:on
130145
}
131146

132147
/**

mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1628,11 +1628,19 @@ public record Root( // @formatter:off
16281628
*
16291629
* @param roots An array of Root objects, each representing a root directory or file
16301630
* that the server can operate on.
1631+
* @param nextCursor An optional cursor for pagination. If present, indicates there
1632+
* are more roots available. The client can use this cursor to request the next page
1633+
* of results by sending a roots/list request with the cursor parameter set to this
16311634
*/
16321635
@JsonInclude(JsonInclude.Include.NON_ABSENT)
16331636
@JsonIgnoreProperties(ignoreUnknown = true)
16341637
public record ListRootsResult( // @formatter:off
1635-
@JsonProperty("roots") List<Root> roots) {
1638+
@JsonProperty("roots") List<Root> roots,
1639+
@JsonProperty("nextCursor") String nextCursor) {
1640+
1641+
public ListRootsResult(List<Root> roots) {
1642+
this(roots, null);
1643+
}
16361644
} // @formatter:on
16371645

16381646
}

0 commit comments

Comments
 (0)