Skip to content

Commit 54e8b61

Browse files
Notify resources list_changed when removing a resource template (#1266)
McpAsyncServer#removeResourceTemplate removed the template but never sent notifications/resources/list_changed, unlike addResourceTemplate and removeResource. Send it when a template was removed and the server declares resources.listChanged.
1 parent b9c2053 commit 54e8b61

3 files changed

Lines changed: 39 additions & 0 deletions

File tree

‎mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,10 @@ public Mono<Void> removeResourceTemplate(String uriTemplate) {
723723
McpServerFeatures.AsyncResourceTemplateSpecification removed = this.resourceTemplates.remove(uriTemplate);
724724
if (removed != null) {
725725
logger.debug("Removed resource template: {}", uriTemplate);
726+
if (Boolean.TRUE.equals(this.serverCapabilities.resources().listChanged())) {
727+
return notifyResourcesListChanged();
728+
}
729+
return Mono.empty();
726730
}
727731
else {
728732
logger.warn("Failed to remove a resource template with URI '{}' (not found)", uriTemplate);

‎mcp-test/src/test/java/io/modelcontextprotocol/server/DerivedCapabilitiesRuntimeChangesTests.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ void addAndRemoveResourceAndTemplateWithDerivedCapabilities() {
7878
StepVerifier.create(server.addResource(asyncResource("test://added"))).verifyComplete();
7979
StepVerifier.create(server.removeResource("test://initial")).verifyComplete();
8080
StepVerifier.create(server.addResourceTemplate(asyncTemplate("test://items/{id}"))).verifyComplete();
81+
StepVerifier.create(server.removeResourceTemplate("test://items/{id}")).verifyComplete();
8182

8283
assertThat(transport.getAllSentMessages()).isEmpty();
8384
}

‎mcp-test/src/test/java/io/modelcontextprotocol/server/ResourceTemplateManagementTests.java‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import io.modelcontextprotocol.MockMcpServerTransport;
1111
import io.modelcontextprotocol.MockMcpServerTransportProvider;
12+
import io.modelcontextprotocol.spec.McpSchema;
13+
import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification;
1214
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
1315
import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate;
1416
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
@@ -146,6 +148,38 @@ void testRemoveNonexistentResourceTemplate() {
146148
StepVerifier.create(mcpAsyncServer.removeResourceTemplate("nonexistent://template/{id}")).verifyComplete();
147149
}
148150

151+
@Test
152+
void testRemoveResourceTemplateNotifiesListChanged() {
153+
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME).build();
154+
155+
McpServerFeatures.AsyncResourceTemplateSpecification specification = new McpServerFeatures.AsyncResourceTemplateSpecification(
156+
template, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
157+
158+
mcpAsyncServer = McpServer.async(mockTransportProvider)
159+
.serverInfo("test-server", "1.0.0")
160+
.capabilities(ServerCapabilities.builder().resources(false, true).build())
161+
.resourceTemplates(specification)
162+
.build();
163+
164+
StepVerifier.create(mcpAsyncServer.removeResourceTemplate(TEST_TEMPLATE_URI)).verifyComplete();
165+
166+
assertThat(mockTransportProvider.getTransport().getAllSentMessages()).singleElement()
167+
.isInstanceOfSatisfying(JSONRPCNotification.class, notification -> assertThat(notification.method())
168+
.isEqualTo(McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED));
169+
}
170+
171+
@Test
172+
void testRemoveNonexistentResourceTemplateDoesNotNotify() {
173+
mcpAsyncServer = McpServer.async(mockTransportProvider)
174+
.serverInfo("test-server", "1.0.0")
175+
.capabilities(ServerCapabilities.builder().resources(false, true).build())
176+
.build();
177+
178+
StepVerifier.create(mcpAsyncServer.removeResourceTemplate("nonexistent://template/{id}")).verifyComplete();
179+
180+
assertThat(mockTransportProvider.getTransport().getAllSentMessages()).isEmpty();
181+
}
182+
149183
@Test
150184
void testReplaceExistingResourceTemplate() {
151185
ResourceTemplate originalTemplate = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)

0 commit comments

Comments
 (0)