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}