Skip to content

Commit 76ee33a

Browse files
committed
Replacing Registration classes with Specification classes
1 parent be61de3 commit 76ee33a

4 files changed

Lines changed: 1236 additions & 181 deletions

File tree

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

Lines changed: 89 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import java.util.concurrent.CopyOnWriteArrayList;
1515
import java.util.function.BiFunction;
1616
import java.util.function.Function;
17-
import java.util.stream.Collectors;
1817

1918
import com.fasterxml.jackson.core.type.TypeReference;
2019
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -333,13 +332,13 @@ private static class AsyncServerImpl extends McpAsyncServer {
333332
/**
334333
* Thread-safe list of tool handlers that can be modified at runtime.
335334
*/
336-
private final CopyOnWriteArrayList<McpServerFeatures.AsyncToolRegistration> tools = new CopyOnWriteArrayList<>();
335+
private final CopyOnWriteArrayList<McpServerFeatures.AsyncToolSpecification> tools = new CopyOnWriteArrayList<>();
337336

338337
private final CopyOnWriteArrayList<McpSchema.ResourceTemplate> resourceTemplates = new CopyOnWriteArrayList<>();
339338

340-
private final ConcurrentHashMap<String, McpServerFeatures.AsyncResourceRegistration> resources = new ConcurrentHashMap<>();
339+
private final ConcurrentHashMap<String, McpServerFeatures.AsyncResourceSpecification> resources = new ConcurrentHashMap<>();
341340

342-
private final ConcurrentHashMap<String, McpServerFeatures.AsyncPromptRegistration> prompts = new ConcurrentHashMap<>();
341+
private final ConcurrentHashMap<String, McpServerFeatures.AsyncPromptSpecification> prompts = new ConcurrentHashMap<>();
343342

344343
private LoggingLevel minLoggingLevel = LoggingLevel.DEBUG;
345344

@@ -540,15 +539,27 @@ private McpServerSession.NotificationHandler asyncRootsListChangedNotificationHa
540539
* Add a new tool registration at runtime.
541540
* @param toolRegistration The tool registration to add
542541
* @return Mono that completes when clients have been notified of the change
542+
* @deprecated This method will be removed in 0.9.0. Use
543+
* {@link #addTool(McpServerFeatures.AsyncToolSpecification)}.
543544
*/
545+
@Deprecated
544546
public Mono<Void> addTool(McpServerFeatures.AsyncToolRegistration toolRegistration) {
545-
if (toolRegistration == null) {
546-
return Mono.error(new McpError("Tool registration must not be null"));
547+
return this.addTool(toolRegistration.toSpecification());
548+
}
549+
550+
/**
551+
* Add a new tool registration at runtime.
552+
* @param toolSpecification The tool registration to add
553+
* @return Mono that completes when clients have been notified of the change
554+
*/
555+
public Mono<Void> addTool(McpServerFeatures.AsyncToolSpecification toolSpecification) {
556+
if (toolSpecification == null) {
557+
return Mono.error(new McpError("Tool specification must not be null"));
547558
}
548-
if (toolRegistration.tool() == null) {
559+
if (toolSpecification.tool() == null) {
549560
return Mono.error(new McpError("Tool must not be null"));
550561
}
551-
if (toolRegistration.call() == null) {
562+
if (toolSpecification.call() == null) {
552563
return Mono.error(new McpError("Tool call handler must not be null"));
553564
}
554565
if (this.serverCapabilities.tools() == null) {
@@ -557,13 +568,13 @@ public Mono<Void> addTool(McpServerFeatures.AsyncToolRegistration toolRegistrati
557568

558569
return Mono.defer(() -> {
559570
// Check for duplicate tool names
560-
if (this.tools.stream().anyMatch(th -> th.tool().name().equals(toolRegistration.tool().name()))) {
571+
if (this.tools.stream().anyMatch(th -> th.tool().name().equals(toolSpecification.tool().name()))) {
561572
return Mono
562-
.error(new McpError("Tool with name '" + toolRegistration.tool().name() + "' already exists"));
573+
.error(new McpError("Tool with name '" + toolSpecification.tool().name() + "' already exists"));
563574
}
564575

565-
this.tools.add(toolRegistration);
566-
logger.debug("Added tool handler: {}", toolRegistration.tool().name());
576+
this.tools.add(toolSpecification);
577+
logger.debug("Added tool handler: {}", toolSpecification.tool().name());
567578

568579
if (this.serverCapabilities.tools().listChanged()) {
569580
return notifyToolsListChanged();
@@ -609,7 +620,7 @@ public Mono<Void> notifyToolsListChanged() {
609620

610621
private McpServerSession.RequestHandler<McpSchema.ListToolsResult> toolsListRequestHandler() {
611622
return (exchange, params) -> {
612-
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolRegistration::tool).toList();
623+
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList();
613624

614625
return Mono.just(new McpSchema.ListToolsResult(tools, null));
615626
};
@@ -621,15 +632,15 @@ private McpServerSession.RequestHandler<CallToolResult> toolsCallRequestHandler(
621632
new TypeReference<McpSchema.CallToolRequest>() {
622633
});
623634

624-
Optional<McpServerFeatures.AsyncToolRegistration> toolRegistration = this.tools.stream()
635+
Optional<McpServerFeatures.AsyncToolSpecification> toolSpecification = this.tools.stream()
625636
.filter(tr -> callToolRequest.name().equals(tr.tool().name()))
626637
.findAny();
627638

628-
if (toolRegistration.isEmpty()) {
639+
if (toolSpecification.isEmpty()) {
629640
return Mono.error(new McpError("Tool not found: " + callToolRequest.name()));
630641
}
631642

632-
return toolRegistration.map(tool -> tool.call().apply(callToolRequest.arguments()))
643+
return toolSpecification.map(tool -> tool.call().apply(exchange, callToolRequest.arguments()))
633644
.orElse(Mono.error(new McpError("Tool not found: " + callToolRequest.name())));
634645
};
635646
}
@@ -642,9 +653,21 @@ private McpServerSession.RequestHandler<CallToolResult> toolsCallRequestHandler(
642653
* Add a new resource handler at runtime.
643654
* @param resourceHandler The resource handler to add
644655
* @return Mono that completes when clients have been notified of the change
656+
* @deprecated This method will be removed in 0.9.0. Use
657+
* {@link #addResource(McpServerFeatures.AsyncResourceSpecification)}.
645658
*/
659+
@Deprecated
646660
public Mono<Void> addResource(McpServerFeatures.AsyncResourceRegistration resourceHandler) {
647-
if (resourceHandler == null || resourceHandler.resource() == null) {
661+
return this.addResource(resourceHandler.toSpecification());
662+
}
663+
664+
/**
665+
* Add a new resource handler at runtime.
666+
* @param resourceSpecification The resource handler to add
667+
* @return Mono that completes when clients have been notified of the change
668+
*/
669+
public Mono<Void> addResource(McpServerFeatures.AsyncResourceSpecification resourceSpecification) {
670+
if (resourceSpecification == null || resourceSpecification.resource() == null) {
648671
return Mono.error(new McpError("Resource must not be null"));
649672
}
650673

@@ -653,11 +676,11 @@ public Mono<Void> addResource(McpServerFeatures.AsyncResourceRegistration resour
653676
}
654677

655678
return Mono.defer(() -> {
656-
if (this.resources.putIfAbsent(resourceHandler.resource().uri(), resourceHandler) != null) {
679+
if (this.resources.putIfAbsent(resourceSpecification.resource().uri(), resourceSpecification) != null) {
657680
return Mono.error(new McpError(
658-
"Resource with URI '" + resourceHandler.resource().uri() + "' already exists"));
681+
"Resource with URI '" + resourceSpecification.resource().uri() + "' already exists"));
659682
}
660-
logger.debug("Added resource handler: {}", resourceHandler.resource().uri());
683+
logger.debug("Added resource handler: {}", resourceSpecification.resource().uri());
661684
if (this.serverCapabilities.resources().listChanged()) {
662685
return notifyResourcesListChanged();
663686
}
@@ -679,7 +702,7 @@ public Mono<Void> removeResource(String resourceUri) {
679702
}
680703

681704
return Mono.defer(() -> {
682-
McpServerFeatures.AsyncResourceRegistration removed = this.resources.remove(resourceUri);
705+
McpServerFeatures.AsyncResourceSpecification removed = this.resources.remove(resourceUri);
683706
if (removed != null) {
684707
logger.debug("Removed resource handler: {}", resourceUri);
685708
if (this.serverCapabilities.resources().listChanged()) {
@@ -696,16 +719,14 @@ public Mono<Void> removeResource(String resourceUri) {
696719
* @return A Mono that completes when all clients have been notified
697720
*/
698721
public Mono<Void> notifyResourcesListChanged() {
699-
McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(
700-
McpSchema.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED, null);
701722
return this.mcpTransportProvider.notifyClients(McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED, null);
702723
}
703724

704725
private McpServerSession.RequestHandler<McpSchema.ListResourcesResult> resourcesListRequestHandler() {
705726
return (exchange, params) -> {
706727
var resourceList = this.resources.values()
707728
.stream()
708-
.map(McpServerFeatures.AsyncResourceRegistration::resource)
729+
.map(McpServerFeatures.AsyncResourceSpecification::resource)
709730
.toList();
710731
return Mono.just(new McpSchema.ListResourcesResult(resourceList, null));
711732
};
@@ -723,9 +744,9 @@ private McpServerSession.RequestHandler<McpSchema.ReadResourceResult> resourcesR
723744
new TypeReference<McpSchema.ReadResourceRequest>() {
724745
});
725746
var resourceUri = resourceRequest.uri();
726-
McpServerFeatures.AsyncResourceRegistration registration = this.resources.get(resourceUri);
747+
McpServerFeatures.AsyncResourceSpecification registration = this.resources.get(resourceUri);
727748
if (registration != null) {
728-
return registration.readHandler().apply(resourceRequest);
749+
return registration.readHandler().apply(exchange, resourceRequest);
729750
}
730751
return Mono.error(new McpError("Resource not found: " + resourceUri));
731752
};
@@ -739,24 +760,36 @@ private McpServerSession.RequestHandler<McpSchema.ReadResourceResult> resourcesR
739760
* Add a new prompt handler at runtime.
740761
* @param promptRegistration The prompt handler to add
741762
* @return Mono that completes when clients have been notified of the change
763+
* @deprecated This method will be removed in 0.9.0. Use
764+
* {@link #addPrompt(McpServerFeatures.AsyncPromptSpecification)}.
742765
*/
766+
@Deprecated
743767
public Mono<Void> addPrompt(McpServerFeatures.AsyncPromptRegistration promptRegistration) {
744-
if (promptRegistration == null) {
768+
return this.addPrompt(promptRegistration.toSpecification());
769+
}
770+
771+
/**
772+
* Add a new prompt handler at runtime.
773+
* @param promptSpecification The prompt handler to add
774+
* @return Mono that completes when clients have been notified of the change
775+
*/
776+
public Mono<Void> addPrompt(McpServerFeatures.AsyncPromptSpecification promptSpecification) {
777+
if (promptSpecification == null) {
745778
return Mono.error(new McpError("Prompt registration must not be null"));
746779
}
747780
if (this.serverCapabilities.prompts() == null) {
748781
return Mono.error(new McpError("Server must be configured with prompt capabilities"));
749782
}
750783

751784
return Mono.defer(() -> {
752-
McpServerFeatures.AsyncPromptRegistration registration = this.prompts
753-
.putIfAbsent(promptRegistration.prompt().name(), promptRegistration);
754-
if (registration != null) {
785+
McpServerFeatures.AsyncPromptSpecification specification = this.prompts
786+
.putIfAbsent(promptSpecification.prompt().name(), promptSpecification);
787+
if (specification != null) {
755788
return Mono.error(new McpError(
756-
"Prompt with name '" + promptRegistration.prompt().name() + "' already exists"));
789+
"Prompt with name '" + promptSpecification.prompt().name() + "' already exists"));
757790
}
758791

759-
logger.debug("Added prompt handler: {}", promptRegistration.prompt().name());
792+
logger.debug("Added prompt handler: {}", promptSpecification.prompt().name());
760793

761794
// Servers that declared the listChanged capability SHOULD send a
762795
// notification,
@@ -782,7 +815,7 @@ public Mono<Void> removePrompt(String promptName) {
782815
}
783816

784817
return Mono.defer(() -> {
785-
McpServerFeatures.AsyncPromptRegistration removed = this.prompts.remove(promptName);
818+
McpServerFeatures.AsyncPromptSpecification removed = this.prompts.remove(promptName);
786819

787820
if (removed != null) {
788821
logger.debug("Removed prompt handler: {}", promptName);
@@ -814,7 +847,7 @@ private McpServerSession.RequestHandler<McpSchema.ListPromptsResult> promptsList
814847

815848
var promptList = this.prompts.values()
816849
.stream()
817-
.map(McpServerFeatures.AsyncPromptRegistration::prompt)
850+
.map(McpServerFeatures.AsyncPromptSpecification::prompt)
818851
.toList();
819852

820853
return Mono.just(new McpSchema.ListPromptsResult(promptList, null));
@@ -828,12 +861,12 @@ private McpServerSession.RequestHandler<McpSchema.GetPromptResult> promptsGetReq
828861
});
829862

830863
// Implement prompt retrieval logic here
831-
McpServerFeatures.AsyncPromptRegistration registration = this.prompts.get(promptRequest.name());
832-
if (registration == null) {
864+
McpServerFeatures.AsyncPromptSpecification specification = this.prompts.get(promptRequest.name());
865+
if (specification == null) {
833866
return Mono.error(new McpError("Prompt not found: " + promptRequest.name()));
834867
}
835868

836-
return registration.promptHandler().apply(promptRequest);
869+
return specification.promptHandler().apply(exchange, promptRequest);
837870
};
838871
}
839872

@@ -938,13 +971,13 @@ private static final class LegacyAsyncServer extends McpAsyncServer {
938971
/**
939972
* Thread-safe list of tool handlers that can be modified at runtime.
940973
*/
941-
private final CopyOnWriteArrayList<McpServerFeatures.AsyncToolRegistration> tools = new CopyOnWriteArrayList<>();
974+
private final CopyOnWriteArrayList<McpServerFeatures.AsyncToolSpecification> tools = new CopyOnWriteArrayList<>();
942975

943976
private final CopyOnWriteArrayList<McpSchema.ResourceTemplate> resourceTemplates = new CopyOnWriteArrayList<>();
944977

945-
private final ConcurrentHashMap<String, McpServerFeatures.AsyncResourceRegistration> resources = new ConcurrentHashMap<>();
978+
private final ConcurrentHashMap<String, McpServerFeatures.AsyncResourceSpecification> resources = new ConcurrentHashMap<>();
946979

947-
private final ConcurrentHashMap<String, McpServerFeatures.AsyncPromptRegistration> prompts = new ConcurrentHashMap<>();
980+
private final ConcurrentHashMap<String, McpServerFeatures.AsyncPromptSpecification> prompts = new ConcurrentHashMap<>();
948981

949982
private LoggingLevel minLoggingLevel = LoggingLevel.DEBUG;
950983

@@ -1170,7 +1203,7 @@ public Mono<Void> addTool(McpServerFeatures.AsyncToolRegistration toolRegistrati
11701203
.error(new McpError("Tool with name '" + toolRegistration.tool().name() + "' already exists"));
11711204
}
11721205

1173-
this.tools.add(toolRegistration);
1206+
this.tools.add(toolRegistration.toSpecification());
11741207
logger.debug("Added tool handler: {}", toolRegistration.tool().name());
11751208

11761209
if (this.serverCapabilities.tools().listChanged()) {
@@ -1217,7 +1250,7 @@ public Mono<Void> notifyToolsListChanged() {
12171250

12181251
private DefaultMcpSession.RequestHandler<McpSchema.ListToolsResult> toolsListRequestHandler() {
12191252
return params -> {
1220-
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolRegistration::tool).toList();
1253+
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList();
12211254

12221255
return Mono.just(new McpSchema.ListToolsResult(tools, null));
12231256
};
@@ -1229,15 +1262,15 @@ private DefaultMcpSession.RequestHandler<CallToolResult> toolsCallRequestHandler
12291262
new TypeReference<McpSchema.CallToolRequest>() {
12301263
});
12311264

1232-
Optional<McpServerFeatures.AsyncToolRegistration> toolRegistration = this.tools.stream()
1265+
Optional<McpServerFeatures.AsyncToolSpecification> toolRegistration = this.tools.stream()
12331266
.filter(tr -> callToolRequest.name().equals(tr.tool().name()))
12341267
.findAny();
12351268

12361269
if (toolRegistration.isEmpty()) {
12371270
return Mono.error(new McpError("Tool not found: " + callToolRequest.name()));
12381271
}
12391272

1240-
return toolRegistration.map(tool -> tool.call().apply(callToolRequest.arguments()))
1273+
return toolRegistration.map(tool -> tool.call().apply(null, callToolRequest.arguments()))
12411274
.orElse(Mono.error(new McpError("Tool not found: " + callToolRequest.name())));
12421275
};
12431276
}
@@ -1261,7 +1294,8 @@ public Mono<Void> addResource(McpServerFeatures.AsyncResourceRegistration resour
12611294
}
12621295

12631296
return Mono.defer(() -> {
1264-
if (this.resources.putIfAbsent(resourceHandler.resource().uri(), resourceHandler) != null) {
1297+
if (this.resources.putIfAbsent(resourceHandler.resource().uri(),
1298+
resourceHandler.toSpecification()) != null) {
12651299
return Mono.error(new McpError(
12661300
"Resource with URI '" + resourceHandler.resource().uri() + "' already exists"));
12671301
}
@@ -1287,7 +1321,7 @@ public Mono<Void> removeResource(String resourceUri) {
12871321
}
12881322

12891323
return Mono.defer(() -> {
1290-
McpServerFeatures.AsyncResourceRegistration removed = this.resources.remove(resourceUri);
1324+
McpServerFeatures.AsyncResourceSpecification removed = this.resources.remove(resourceUri);
12911325
if (removed != null) {
12921326
logger.debug("Removed resource handler: {}", resourceUri);
12931327
if (this.serverCapabilities.resources().listChanged()) {
@@ -1311,7 +1345,7 @@ private DefaultMcpSession.RequestHandler<McpSchema.ListResourcesResult> resource
13111345
return params -> {
13121346
var resourceList = this.resources.values()
13131347
.stream()
1314-
.map(McpServerFeatures.AsyncResourceRegistration::resource)
1348+
.map(McpServerFeatures.AsyncResourceSpecification::resource)
13151349
.toList();
13161350
return Mono.just(new McpSchema.ListResourcesResult(resourceList, null));
13171351
};
@@ -1328,9 +1362,9 @@ private DefaultMcpSession.RequestHandler<McpSchema.ReadResourceResult> resources
13281362
new TypeReference<McpSchema.ReadResourceRequest>() {
13291363
});
13301364
var resourceUri = resourceRequest.uri();
1331-
McpServerFeatures.AsyncResourceRegistration registration = this.resources.get(resourceUri);
1365+
McpServerFeatures.AsyncResourceSpecification registration = this.resources.get(resourceUri);
13321366
if (registration != null) {
1333-
return registration.readHandler().apply(resourceRequest);
1367+
return registration.readHandler().apply(null, resourceRequest);
13341368
}
13351369
return Mono.error(new McpError("Resource not found: " + resourceUri));
13361370
};
@@ -1354,8 +1388,8 @@ public Mono<Void> addPrompt(McpServerFeatures.AsyncPromptRegistration promptRegi
13541388
}
13551389

13561390
return Mono.defer(() -> {
1357-
McpServerFeatures.AsyncPromptRegistration registration = this.prompts
1358-
.putIfAbsent(promptRegistration.prompt().name(), promptRegistration);
1391+
McpServerFeatures.AsyncPromptSpecification registration = this.prompts
1392+
.putIfAbsent(promptRegistration.prompt().name(), promptRegistration.toSpecification());
13591393
if (registration != null) {
13601394
return Mono.error(new McpError(
13611395
"Prompt with name '" + promptRegistration.prompt().name() + "' already exists"));
@@ -1387,7 +1421,7 @@ public Mono<Void> removePrompt(String promptName) {
13871421
}
13881422

13891423
return Mono.defer(() -> {
1390-
McpServerFeatures.AsyncPromptRegistration removed = this.prompts.remove(promptName);
1424+
McpServerFeatures.AsyncPromptSpecification removed = this.prompts.remove(promptName);
13911425

13921426
if (removed != null) {
13931427
logger.debug("Removed prompt handler: {}", promptName);
@@ -1419,7 +1453,7 @@ private DefaultMcpSession.RequestHandler<McpSchema.ListPromptsResult> promptsLis
14191453

14201454
var promptList = this.prompts.values()
14211455
.stream()
1422-
.map(McpServerFeatures.AsyncPromptRegistration::prompt)
1456+
.map(McpServerFeatures.AsyncPromptSpecification::prompt)
14231457
.toList();
14241458

14251459
return Mono.just(new McpSchema.ListPromptsResult(promptList, null));
@@ -1433,12 +1467,12 @@ private DefaultMcpSession.RequestHandler<McpSchema.GetPromptResult> promptsGetRe
14331467
});
14341468

14351469
// Implement prompt retrieval logic here
1436-
McpServerFeatures.AsyncPromptRegistration registration = this.prompts.get(promptRequest.name());
1470+
McpServerFeatures.AsyncPromptSpecification registration = this.prompts.get(promptRequest.name());
14371471
if (registration == null) {
14381472
return Mono.error(new McpError("Prompt not found: " + promptRequest.name()));
14391473
}
14401474

1441-
return registration.promptHandler().apply(promptRequest);
1475+
return registration.promptHandler().apply(null, promptRequest);
14421476
};
14431477
}
14441478

0 commit comments

Comments
 (0)