Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@

<dependencies>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-mcp-server-properties</artifactId>
<version>${project.parent.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@

import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerChangeNotificationProperties;
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand Down Expand Up @@ -71,23 +71,64 @@
* @since 1.0.0
* @see McpServerProperties
*/
@AutoConfiguration(afterName = {
"org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebFluxAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebMvcAutoConfiguration",
"org.springframework.ai.mcp.server.streamable.webflux.autoconfigure.McpServerStreamableHttpWebMvcAutoConfiguration",
"org.springframework.ai.mcp.server.streamable.webflux.autoconfigure.McpServerStreamableHttpWebFluxAutoConfiguration" })
@ConditionalOnClass({ McpSchema.class, McpSyncServer.class })
@AutoConfiguration(
afterName = { "org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebFluxAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerSseWebMvcAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerStreamableHttpWebMvcAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerStreamableHttpWebFluxAutoConfiguration" })
@ConditionalOnClass({ McpSchema.class })
@EnableConfigurationProperties({ McpServerProperties.class, McpServerChangeNotificationProperties.class })
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
@Conditional(McpServerAutoConfiguration.NonStatlessServerCondition.class)
public class McpServerAutoConfiguration {

private static final LogAccessor logger = new LogAccessor(McpServerAutoConfiguration.class);

public static class EnabledNonStatlessServerCondition extends AllNestedConditions {
public static class NonStatlessServerCondition extends AnyNestedCondition {

public EnabledNonStatlessServerCondition() {
public NonStatlessServerCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol", havingValue = "SSE",
matchIfMissing = true)
static class SseEnabledCondition {

}

@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol",
havingValue = "STREAMABLE", matchIfMissing = false)
static class StreamableEnabledCondition {

}

}

public static class EnabledSseServerCondition extends AllNestedConditions {

public EnabledSseServerCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
static class McpServerEnabledCondition {

}

@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol", havingValue = "SSE",
matchIfMissing = true)
static class SseEnabledCondition {

}

}

public static class EnabledStreamableServerCondition extends AllNestedConditions {

public EnabledStreamableServerCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

Expand All @@ -97,9 +138,9 @@ static class McpServerEnabledCondition {

}

@ConditionalOnProperty(prefix = McpServerStreamableHttpProperties.CONFIG_PREFIX, name = "stateless",
havingValue = "false", matchIfMissing = true)
static class StatelessEnabledCondition {
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol",
havingValue = "STREAMABLE", matchIfMissing = false)
static class StreamableEnabledCondition {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,21 @@
* limitations under the License.
*/

package org.springframework.ai.mcp.server.stateless.autoconfigure;
package org.springframework.ai.mcp.server.common.autoconfigure;

import java.util.ArrayList;
import java.util.List;

import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.env.Environment;
import org.springframework.core.log.LogAccessor;
import org.springframework.util.CollectionUtils;
Expand All @@ -53,17 +55,37 @@
* @author Christian Tzolov
*/
@AutoConfiguration(afterName = {
"org.springframework.ai.mcp.server.stateless.autoconfigure.ToolCallbackConverterAutoConfiguration",
"org.springframework.ai.mcp.server.stateless.webflux.autoconfigure.McpServerStatelessWebFluxAutoConfiguration",
"org.springframework.ai.mcp.server.stateless.webmvc.autoconfigure.McpServerStatelessWebMvcAutoConfiguration" })
"org.springframework.ai.mcp.server.common.autoconfigure.StatelessToolCallbackConverterAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerStatelessWebFluxAutoConfiguration",
"org.springframework.ai.mcp.server.autoconfigure.McpServerStatelessWebMvcAutoConfiguration" })
@ConditionalOnClass({ McpSchema.class })
@EnableConfigurationProperties(McpServerProperties.class)
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
@Conditional({ McpServerStdioDisabledCondition.class,
McpServerStatelessAutoConfiguration.EnabledStatelessServerCondition.class })
public class McpServerStatelessAutoConfiguration {

private static final LogAccessor logger = new LogAccessor(McpServerStatelessAutoConfiguration.class);

public static class EnabledStatelessServerCondition extends AllNestedConditions {

public EnabledStatelessServerCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
static class McpServerEnabledCondition {

}

@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "protocol", havingValue = "STATELESS",
matchIfMissing = false)
static class StatelessEnabledCondition {

}

}

@Bean
@ConditionalOnMissingBean
public McpSchema.ServerCapabilities.Builder capabilitiesBuilder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package org.springframework.ai.mcp.server.stateless.autoconfigure;
package org.springframework.ai.mcp.server.common.autoconfigure;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -41,8 +41,10 @@
*/
@AutoConfiguration
@EnableConfigurationProperties(McpServerProperties.class)
@Conditional(ToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class)
public class ToolCallbackConverterAutoConfiguration {
@Conditional({ McpServerStdioDisabledCondition.class,
McpServerStatelessAutoConfiguration.EnabledStatelessServerCondition.class,
StatelessToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class })
public class StatelessToolCallbackConverterAutoConfiguration {

public static class ToolCallbackConverterCondition extends AllNestedConditions {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeType;

Expand All @@ -42,7 +41,8 @@
*/
@AutoConfiguration
@EnableConfigurationProperties(McpServerProperties.class)
@Conditional(ToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class)
@Conditional({ ToolCallbackConverterAutoConfiguration.ToolCallbackConverterCondition.class,
McpServerAutoConfiguration.NonStatlessServerCondition.class })
public class ToolCallbackConverterAutoConfiguration {

public static class ToolCallbackConverterCondition extends AllNestedConditions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ public class McpServerProperties {
* <li>ASYNC - Asynchronous server</li>
* </ul>
*/
private ServerType type = ServerType.SYNC;
private ApiType type = ApiType.SYNC;

private Capabilities capabilities = new Capabilities();

private ServerProtocol protocol = ServerProtocol.SSE;

/**
* Sets the duration to wait for server responses before timing out requests. This
* timeout applies to all requests made through the client, including tool calls,
Expand All @@ -112,10 +114,16 @@ public Capabilities getCapabilities() {
return this.capabilities;
}

public enum ServerProtocol {

SSE, STREAMABLE, STATELESS

}

/**
* Server types supported by the MCP server.
* API types supported by the MCP server.
*/
public enum ServerType {
public enum ApiType {

/**
* Synchronous (McpSyncServer) server
Expand Down Expand Up @@ -176,11 +184,11 @@ public void setInstructions(String instructions) {
this.instructions = instructions;
}

public ServerType getType() {
public ApiType getType() {
return this.type;
}

public void setType(ServerType serverType) {
public void setType(ApiType serverType) {
Assert.notNull(serverType, "Server type must not be null");
this.type = serverType;
}
Expand All @@ -189,6 +197,15 @@ public Map<String, String> getToolResponseMimeType() {
return this.toolResponseMimeType;
}

public ServerProtocol getProtocol() {
return this.protocol;
}

public void setProtocol(ServerProtocol serverMode) {
Assert.notNull(serverMode, "Server mode must not be null");
this.protocol = serverMode;
}

public static class Capabilities {

private boolean resource = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package org.springframework.ai.mcp.server.autoconfigure;
package org.springframework.ai.mcp.server.common.autoconfigure.properties;

import java.time.Duration;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
#
org.springframework.ai.mcp.server.common.autoconfigure.McpServerAutoConfiguration
org.springframework.ai.mcp.server.common.autoconfigure.ToolCallbackConverterAutoConfiguration
org.springframework.ai.mcp.server.common.autoconfigure.McpServerStatelessAutoConfiguration
org.springframework.ai.mcp.server.common.autoconfigure.StatelessToolCallbackConverterAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ void defaultConfiguration() {
McpServerProperties properties = context.getBean(McpServerProperties.class);
assertThat(properties.getName()).isEqualTo("mcp-server");
assertThat(properties.getVersion()).isEqualTo("1.0.0");
assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.SYNC);
assertThat(properties.getType()).isEqualTo(McpServerProperties.ApiType.SYNC);
assertThat(properties.getRequestTimeout().getSeconds()).isEqualTo(20);

// Check capabilities
Expand Down Expand Up @@ -102,7 +102,7 @@ void asyncConfiguration() {
assertThat(properties.getName()).isEqualTo("test-server");
assertThat(properties.getVersion()).isEqualTo("2.0.0");
assertThat(properties.getInstructions()).isEqualTo("My MCP Server");
assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.ASYNC);
assertThat(properties.getType()).isEqualTo(McpServerProperties.ApiType.ASYNC);
assertThat(properties.getRequestTimeout().getSeconds()).isEqualTo(30);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package org.springframework.ai.mcp.server.stateless.autoconfigure;
package org.springframework.ai.mcp.server.common.autoconfigure;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -54,9 +54,9 @@
public class McpStatelessServerAutoConfigurationIT {

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()

.withPropertyValues("spring.ai.mcp.server.protocol=STATELESS")
.withConfiguration(AutoConfigurations.of(McpServerStatelessAutoConfiguration.class,
ToolCallbackConverterAutoConfiguration.class))
StatelessToolCallbackConverterAutoConfiguration.class))
.withUserConfiguration(TestStatelessTransportConfiguration.class);

@Test
Expand All @@ -68,7 +68,7 @@ void defaultConfiguration() {
McpServerProperties properties = context.getBean(McpServerProperties.class);
assertThat(properties.getName()).isEqualTo("mcp-server");
assertThat(properties.getVersion()).isEqualTo("1.0.0");
assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.SYNC);
assertThat(properties.getType()).isEqualTo(McpServerProperties.ApiType.SYNC);
assertThat(properties.getRequestTimeout().getSeconds()).isEqualTo(20);
// assertThat(properties.getMcpEndpoint()).isEqualTo("/mcp");

Expand All @@ -94,7 +94,7 @@ void asyncConfiguration() {
assertThat(properties.getName()).isEqualTo("test-server");
assertThat(properties.getVersion()).isEqualTo("2.0.0");
assertThat(properties.getInstructions()).isEqualTo("My MCP Server");
assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.ASYNC);
assertThat(properties.getType()).isEqualTo(McpServerProperties.ApiType.ASYNC);
assertThat(properties.getRequestTimeout().getSeconds()).isEqualTo(30);
});
}
Expand Down
Loading