001/* 002 * Copyright 2022-2026 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.soklet; 018 019import org.jspecify.annotations.NonNull; 020import org.jspecify.annotations.Nullable; 021 022import javax.annotation.concurrent.NotThreadSafe; 023import java.time.Duration; 024import java.util.concurrent.ExecutorService; 025import java.util.function.Consumer; 026import java.util.function.Supplier; 027 028import static java.util.Objects.requireNonNull; 029 030/** 031 * Contract for MCP server implementations that are designed to be managed by a {@link Soklet} instance. 032 * 033 * @author <a href="https://www.revetkn.com">Mark Allen</a> 034 */ 035public interface McpServer extends AutoCloseable { 036 /** 037 * Starts the MCP server. 038 */ 039 void start(); 040 041 /** 042 * Stops the MCP server. 043 */ 044 void stop(); 045 046 /** 047 * Indicates whether the server has been started. 048 * 049 * @return {@code true} if the server is started 050 */ 051 @NonNull 052 Boolean isStarted(); 053 054 /** 055 * Initializes the server with Soklet-owned infrastructure. 056 * 057 * @param sokletConfig the owning Soklet configuration 058 * @param requestHandler the request handler callback Soklet will invoke for MCP requests 059 */ 060 void initialize(@NonNull SokletConfig sokletConfig, 061 @NonNull RequestHandler requestHandler); 062 063 /** 064 * Provides the MCP handler resolver. 065 * 066 * @return the handler resolver 067 */ 068 @NonNull 069 McpHandlerResolver getHandlerResolver(); 070 071 /** 072 * Provides the request admission policy. 073 * 074 * @return the request admission policy 075 */ 076 @NonNull 077 McpRequestAdmissionPolicy getRequestAdmissionPolicy(); 078 079 /** 080 * Provides the request interceptor. 081 * 082 * @return the request interceptor 083 */ 084 @NonNull 085 McpRequestInterceptor getRequestInterceptor(); 086 087 /** 088 * Provides the response marshaler used for tool structured content. 089 * 090 * @return the response marshaler 091 */ 092 @NonNull 093 McpResponseMarshaler getResponseMarshaler(); 094 095 /** 096 * Provides the MCP CORS authorizer. 097 * 098 * @return the CORS authorizer 099 */ 100 @NonNull 101 McpCorsAuthorizer getCorsAuthorizer(); 102 103 /** 104 * Provides the session store. 105 * 106 * @return the session store 107 */ 108 @NonNull 109 McpSessionStore getSessionStore(); 110 111 /** 112 * Provides the generator used for MCP session IDs. 113 * 114 * @return the session ID generator 115 */ 116 @NonNull 117 IdGenerator<String> getIdGenerator(); 118 119 @Override 120 default void close() throws Exception { 121 stop(); 122 } 123 124 /** 125 * Request callback used by {@link Soklet} to hand transport requests to an initialized MCP server. 126 */ 127 @FunctionalInterface 128 interface RequestHandler { 129 /** 130 * Handles an MCP transport request. 131 * 132 * @param request the transport request 133 * @param requestResultConsumer the consumer that should receive the completed request result 134 */ 135 void handleRequest(@NonNull Request request, 136 @NonNull Consumer<HttpRequestResult> requestResultConsumer); 137 } 138 139 /** 140 * Acquires a builder configured with the given port. 141 * 142 * @param port the port to bind 143 * @return a new MCP server builder 144 */ 145 @NonNull 146 static Builder withPort(@NonNull Integer port) { 147 requireNonNull(port); 148 return new Builder(port); 149 } 150 151 /** 152 * Creates a default MCP server bound to the given port. 153 * 154 * @param port the port to bind 155 * @return the built MCP server 156 */ 157 @NonNull 158 static McpServer fromPort(@NonNull Integer port) { 159 requireNonNull(port); 160 return withPort(port).build(); 161 } 162 163 /** 164 * Builder for {@link McpServer} instances. 165 */ 166 @NotThreadSafe 167 final class Builder { 168 @NonNull 169 Integer port; 170 @Nullable 171 String host; 172 @Nullable 173 McpHandlerResolver handlerResolver; 174 @Nullable 175 McpRequestAdmissionPolicy requestAdmissionPolicy; 176 @Nullable 177 McpRequestInterceptor requestInterceptor; 178 @Nullable 179 McpResponseMarshaler responseMarshaler; 180 @Nullable 181 McpCorsAuthorizer corsAuthorizer; 182 @Nullable 183 McpSessionStore sessionStore; 184 @Nullable 185 Duration requestTimeout; 186 @Nullable 187 Duration requestHandlerTimeout; 188 @Nullable 189 Integer requestHandlerConcurrency; 190 @Nullable 191 Integer requestHandlerQueueCapacity; 192 @Nullable 193 Supplier<ExecutorService> requestHandlerExecutorServiceSupplier; 194 @Nullable 195 Integer maximumRequestSizeInBytes; 196 @Nullable 197 Integer requestReadBufferSizeInBytes; 198 @Nullable 199 Integer concurrentConnectionLimit; 200 @Nullable 201 Integer connectionQueueCapacity; 202 @Nullable 203 Duration shutdownTimeout; 204 @Nullable 205 Duration writeTimeout; 206 @Nullable 207 Duration heartbeatInterval; 208 @Nullable 209 IdGenerator<String> idGenerator; 210 211 private Builder(@NonNull Integer port) { 212 requireNonNull(port); 213 this.port = port; 214 } 215 216 /** 217 * Sets the port to bind. 218 * 219 * @param port the port 220 * @return this builder 221 */ 222 @NonNull 223 public Builder port(@NonNull Integer port) { 224 requireNonNull(port); 225 this.port = port; 226 return this; 227 } 228 229 /** 230 * Sets the host to bind, or {@code null} to use the server default. 231 * 232 * @param host the host to bind 233 * @return this builder 234 */ 235 @NonNull 236 public Builder host(@Nullable String host) { 237 this.host = host; 238 return this; 239 } 240 241 /** 242 * Sets the handler resolver. 243 * 244 * @param handlerResolver the handler resolver 245 * @return this builder 246 */ 247 @NonNull 248 public Builder handlerResolver(@Nullable McpHandlerResolver handlerResolver) { 249 this.handlerResolver = handlerResolver; 250 return this; 251 } 252 253 /** 254 * Sets the request admission policy. 255 * 256 * @param requestAdmissionPolicy the admission policy 257 * @return this builder 258 */ 259 @NonNull 260 public Builder requestAdmissionPolicy(@Nullable McpRequestAdmissionPolicy requestAdmissionPolicy) { 261 this.requestAdmissionPolicy = requestAdmissionPolicy; 262 return this; 263 } 264 265 /** 266 * Sets the request interceptor. 267 * 268 * @param requestInterceptor the request interceptor 269 * @return this builder 270 */ 271 @NonNull 272 public Builder requestInterceptor(@Nullable McpRequestInterceptor requestInterceptor) { 273 this.requestInterceptor = requestInterceptor; 274 return this; 275 } 276 277 /** 278 * Sets the response marshaler used for tool structured content. 279 * 280 * @param responseMarshaler the response marshaler 281 * @return this builder 282 */ 283 @NonNull 284 public Builder responseMarshaler(@Nullable McpResponseMarshaler responseMarshaler) { 285 this.responseMarshaler = responseMarshaler; 286 return this; 287 } 288 289 /** 290 * Sets the MCP CORS authorizer. 291 * 292 * @param corsAuthorizer the CORS authorizer 293 * @return this builder 294 */ 295 @NonNull 296 public Builder corsAuthorizer(@Nullable McpCorsAuthorizer corsAuthorizer) { 297 this.corsAuthorizer = corsAuthorizer; 298 return this; 299 } 300 301 /** 302 * Sets the session store. 303 * 304 * @param sessionStore the session store 305 * @return this builder 306 */ 307 @NonNull 308 public Builder sessionStore(@Nullable McpSessionStore sessionStore) { 309 this.sessionStore = sessionStore; 310 return this; 311 } 312 313 /** 314 * Sets the end-to-end request timeout. 315 * 316 * @param requestTimeout the request timeout 317 * @return this builder 318 */ 319 @NonNull 320 public Builder requestTimeout(@Nullable Duration requestTimeout) { 321 this.requestTimeout = requestTimeout; 322 return this; 323 } 324 325 /** 326 * Sets the timeout for MCP handler execution. 327 * 328 * @param requestHandlerTimeout the handler timeout 329 * @return this builder 330 */ 331 @NonNull 332 public Builder requestHandlerTimeout(@Nullable Duration requestHandlerTimeout) { 333 this.requestHandlerTimeout = requestHandlerTimeout; 334 return this; 335 } 336 337 /** 338 * Sets the handler concurrency level. 339 * 340 * @param requestHandlerConcurrency the handler concurrency 341 * @return this builder 342 */ 343 @NonNull 344 public Builder requestHandlerConcurrency(@Nullable Integer requestHandlerConcurrency) { 345 this.requestHandlerConcurrency = requestHandlerConcurrency; 346 return this; 347 } 348 349 /** 350 * Sets the handler queue capacity. 351 * 352 * @param requestHandlerQueueCapacity the handler queue capacity 353 * @return this builder 354 */ 355 @NonNull 356 public Builder requestHandlerQueueCapacity(@Nullable Integer requestHandlerQueueCapacity) { 357 this.requestHandlerQueueCapacity = requestHandlerQueueCapacity; 358 return this; 359 } 360 361 /** 362 * Sets the supplier used to create the handler executor service. 363 * 364 * @param requestHandlerExecutorServiceSupplier the executor service supplier 365 * @return this builder 366 */ 367 @NonNull 368 public Builder requestHandlerExecutorServiceSupplier(@Nullable Supplier<ExecutorService> requestHandlerExecutorServiceSupplier) { 369 this.requestHandlerExecutorServiceSupplier = requestHandlerExecutorServiceSupplier; 370 return this; 371 } 372 373 /** 374 * Sets the maximum accepted MCP request size in bytes. 375 * 376 * @param maximumRequestSizeInBytes the maximum request size 377 * @return this builder 378 */ 379 @NonNull 380 public Builder maximumRequestSizeInBytes(@Nullable Integer maximumRequestSizeInBytes) { 381 this.maximumRequestSizeInBytes = maximumRequestSizeInBytes; 382 return this; 383 } 384 385 /** 386 * Sets the read buffer size used while reading MCP requests. 387 * 388 * @param requestReadBufferSizeInBytes the read buffer size 389 * @return this builder 390 */ 391 @NonNull 392 public Builder requestReadBufferSizeInBytes(@Nullable Integer requestReadBufferSizeInBytes) { 393 this.requestReadBufferSizeInBytes = requestReadBufferSizeInBytes; 394 return this; 395 } 396 397 /** 398 * Sets the concurrent connection limit for the MCP server. 399 * 400 * @param concurrentConnectionLimit the concurrent connection limit 401 * @return this builder 402 */ 403 @NonNull 404 public Builder concurrentConnectionLimit(@Nullable Integer concurrentConnectionLimit) { 405 this.concurrentConnectionLimit = concurrentConnectionLimit; 406 return this; 407 } 408 409 /** 410 * Sets the outbound queue capacity for live MCP streams. 411 * 412 * @param connectionQueueCapacity the outbound queue capacity 413 * @return this builder 414 */ 415 @NonNull 416 public Builder connectionQueueCapacity(@Nullable Integer connectionQueueCapacity) { 417 this.connectionQueueCapacity = connectionQueueCapacity; 418 return this; 419 } 420 421 /** 422 * Sets the shutdown timeout. 423 * 424 * @param shutdownTimeout the shutdown timeout 425 * @return this builder 426 */ 427 @NonNull 428 public Builder shutdownTimeout(@Nullable Duration shutdownTimeout) { 429 this.shutdownTimeout = shutdownTimeout; 430 return this; 431 } 432 433 /** 434 * Sets the transport write timeout. 435 * 436 * @param writeTimeout the write timeout 437 * @return this builder 438 */ 439 @NonNull 440 public Builder writeTimeout(@Nullable Duration writeTimeout) { 441 this.writeTimeout = writeTimeout; 442 return this; 443 } 444 445 /** 446 * Sets the heartbeat interval for long-lived MCP event streams. 447 * 448 * @param heartbeatInterval the heartbeat interval 449 * @return this builder 450 */ 451 @NonNull 452 public Builder heartbeatInterval(@Nullable Duration heartbeatInterval) { 453 this.heartbeatInterval = heartbeatInterval; 454 return this; 455 } 456 457 /** 458 * Sets the generator used for MCP session IDs. 459 * 460 * @param idGenerator the session ID generator 461 * @return this builder 462 */ 463 @NonNull 464 public Builder idGenerator(@Nullable IdGenerator<String> idGenerator) { 465 this.idGenerator = idGenerator; 466 return this; 467 } 468 469 /** 470 * Builds the MCP server. 471 * 472 * @return the built MCP server 473 */ 474 @NonNull 475 public McpServer build() { 476 return new DefaultMcpServer(this); 477 } 478 } 479}