001package org.nasdanika.ai.mcp; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Collections; 006import java.util.List; 007import java.util.concurrent.CompletionStage; 008import java.util.stream.Collectors; 009 010import org.nasdanika.capability.CapabilityProvider; 011import org.nasdanika.capability.ServiceCapabilityFactory; 012import org.nasdanika.cli.SubCommandCapabilityFactory; 013import org.nasdanika.common.ProgressMonitor; 014 015import io.modelcontextprotocol.server.McpServerFeatures.AsyncPromptSpecification; 016import io.modelcontextprotocol.server.McpServerFeatures.AsyncResourceSpecification; 017import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification; 018import io.modelcontextprotocol.server.McpServerFeatures.SyncPromptSpecification; 019import io.modelcontextprotocol.server.McpServerFeatures.SyncResourceSpecification; 020import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; 021import io.opentelemetry.api.OpenTelemetry; 022import picocli.CommandLine; 023 024public class McpServerCommandFactory extends SubCommandCapabilityFactory<McpServerCommand> { 025 026 @Override 027 protected Class<McpServerCommand> getCommandType() { 028 return McpServerCommand.class; 029 } 030 031 private record ServerConfig(OpenTelemetry openTelemetry, 032 Collection<SyncPromptSpecification> syncPromptSpecifications, 033 Collection<AsyncPromptSpecification> asyncPromptSpecifications, 034 Collection<SyncResourceSpecification> syncResourceSpecifications, 035 Collection<AsyncResourceSpecification> asyncResourceSpecifications, 036 Collection<SyncToolSpecification> syncToolSpecifications, 037 Collection<AsyncToolSpecification> asyncToolSpecifications) { 038 039 public ServerConfig(OpenTelemetry openTelemetry) { 040 this( 041 openTelemetry, 042 Collections.synchronizedList(new ArrayList<>()), 043 Collections.synchronizedList(new ArrayList<>()), 044 Collections.synchronizedList(new ArrayList<>()), 045 Collections.synchronizedList(new ArrayList<>()), 046 Collections.synchronizedList(new ArrayList<>()), 047 Collections.synchronizedList(new ArrayList<>())); 048 } 049 } 050 051 @Override 052 protected CompletionStage<McpServerCommand> doCreateCommand( 053 List<CommandLine> parentPath, 054 Loader loader, 055 ProgressMonitor progressMonitor) { 056 057 Requirement<Object, OpenTelemetry> openTelemetryRequirement = ServiceCapabilityFactory.createRequirement(OpenTelemetry.class); 058 CompletionStage<OpenTelemetry> openTelemetryCS = loader.loadOne(openTelemetryRequirement, progressMonitor); 059 060 return openTelemetryCS 061 .thenApply(ServerConfig::new) 062 .thenCompose(config -> syncPromptSpecifications(config, loader, progressMonitor)) 063 .thenCompose(config -> asyncPromptSpecifications(config, loader, progressMonitor)) 064 .thenCompose(config -> syncResourceSpecifications(config, loader, progressMonitor)) 065 .thenCompose(config -> asyncResourceSpecifications(config, loader, progressMonitor)) 066 .thenCompose(config -> syncToolSpecifications(config, loader, progressMonitor)) 067 .thenCompose(config -> asyncToolSpecifications(config, loader, progressMonitor)) 068 .thenApply(this::createCommand); 069 } 070 071 protected CompletionStage<ServerConfig> syncPromptSpecifications( 072 ServerConfig config, 073 Loader loader, 074 ProgressMonitor progressMonitor) { 075 076 Requirement<Void, SyncPromptSpecification> requirement = ServiceCapabilityFactory.createRequirement(SyncPromptSpecification.class); 077 CompletionStage<Iterable<CapabilityProvider<SyncPromptSpecification>>> cs = loader.load(requirement, progressMonitor); 078 return cs.thenApply(capabilityProviders -> { 079 for (CapabilityProvider<SyncPromptSpecification> capabilityProvider: capabilityProviders) { 080 config.syncPromptSpecifications().addAll(capabilityProvider.getPublisher().collect(Collectors.toList()).block()); 081 } 082 return config; 083 }); 084 } 085 086 protected CompletionStage<ServerConfig> asyncPromptSpecifications( 087 ServerConfig config, 088 Loader loader, 089 ProgressMonitor progressMonitor) { 090 091 Requirement<Void, AsyncPromptSpecification> requirement = ServiceCapabilityFactory.createRequirement(AsyncPromptSpecification.class); 092 CompletionStage<Iterable<CapabilityProvider<AsyncPromptSpecification>>> cs = loader.load(requirement, progressMonitor); 093 return cs.thenApply(capabilityProviders -> { 094 for (CapabilityProvider<AsyncPromptSpecification> capabilityProvider: capabilityProviders) { 095 config.asyncPromptSpecifications().addAll(capabilityProvider.getPublisher().collect(Collectors.toList()).block()); 096 } 097 return config; 098 }); 099 } 100 101 protected CompletionStage<ServerConfig> syncResourceSpecifications( 102 ServerConfig config, 103 Loader loader, 104 ProgressMonitor progressMonitor) { 105 106 Requirement<Void, SyncResourceSpecification> requirement = ServiceCapabilityFactory.createRequirement(SyncResourceSpecification.class); 107 CompletionStage<Iterable<CapabilityProvider<SyncResourceSpecification>>> cs = loader.load(requirement, progressMonitor); 108 return cs.thenApply(capabilityProviders -> { 109 for (CapabilityProvider<SyncResourceSpecification> capabilityProvider: capabilityProviders) { 110 config.syncResourceSpecifications().addAll(capabilityProvider.getPublisher().collect(Collectors.toList()).block()); 111 } 112 return config; 113 }); 114 } 115 116 protected CompletionStage<ServerConfig> asyncResourceSpecifications( 117 ServerConfig config, 118 Loader loader, 119 ProgressMonitor progressMonitor) { 120 121 Requirement<Void, AsyncResourceSpecification> requirement = ServiceCapabilityFactory.createRequirement(AsyncResourceSpecification.class); 122 CompletionStage<Iterable<CapabilityProvider<AsyncResourceSpecification>>> cs = loader.load(requirement, progressMonitor); 123 return cs.thenApply(capabilityProviders -> { 124 for (CapabilityProvider<AsyncResourceSpecification> capabilityProvider: capabilityProviders) { 125 config.asyncResourceSpecifications().addAll(capabilityProvider.getPublisher().collect(Collectors.toList()).block()); 126 } 127 return config; 128 }); 129 } 130 131 protected CompletionStage<ServerConfig> syncToolSpecifications( 132 ServerConfig config, 133 Loader loader, 134 ProgressMonitor progressMonitor) { 135 136 Requirement<Void, SyncToolSpecification> requirement = ServiceCapabilityFactory.createRequirement(SyncToolSpecification.class); 137 CompletionStage<Iterable<CapabilityProvider<SyncToolSpecification>>> cs = loader.load(requirement, progressMonitor); 138 return cs.thenApply(capabilityProviders -> { 139 for (CapabilityProvider<SyncToolSpecification> capabilityProvider: capabilityProviders) { 140 config.syncToolSpecifications().addAll(capabilityProvider.getPublisher().collect(Collectors.toList()).block()); 141 } 142 return config; 143 }); 144 } 145 146 protected CompletionStage<ServerConfig> asyncToolSpecifications( 147 ServerConfig config, 148 Loader loader, 149 ProgressMonitor progressMonitor) { 150 151 Requirement<Void, AsyncToolSpecification> requirement = ServiceCapabilityFactory.createRequirement(AsyncToolSpecification.class); 152 CompletionStage<Iterable<CapabilityProvider<AsyncToolSpecification>>> cs = loader.load(requirement, progressMonitor); 153 return cs.thenApply(capabilityProviders -> { 154 for (CapabilityProvider<AsyncToolSpecification> capabilityProvider: capabilityProviders) { 155 config.asyncToolSpecifications().addAll(capabilityProvider.getPublisher().collect(Collectors.toList()).block()); 156 } 157 return config; 158 }); 159 } 160 161 private McpServerCommand createCommand(ServerConfig config) { 162 163 McpServerCommand server = new McpServerCommand( 164 config.openTelemetry(), 165 config.syncPromptSpecifications(), 166 config.asyncPromptSpecifications(), 167 config.syncResourceSpecifications(), 168 config.asyncResourceSpecifications(), 169 config.syncToolSpecifications(), 170 config.asyncToolSpecifications()); 171 172 return server.isEmpty() ? null : server; 173 } 174 175}