Skip to content

Commit 6fe71e1

Browse files
authored
Added Model Context Protocol (#1093)
1 parent 1290771 commit 6fe71e1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+630
-152
lines changed

api/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ dependencies {
6262
implementation libs.netty.common
6363
implementation libs.netty.handler
6464

65+
implementation libs.modelcontextprotocol.spring.webflux
66+
implementation libs.victools.jsonschema.generator
6567

6668
// Google Managed Service for Kafka IAM support
6769
implementation (libs.google.managed.kafka.login.handler) {

api/src/main/java/io/kafbat/ui/config/CustomWebFilter.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ public class CustomWebFilter implements WebFilter {
1717

1818
final String path = exchange.getRequest().getPath().pathWithinApplication().value();
1919

20+
ServerWebExchange filterExchange = exchange;
21+
2022
if (path.startsWith("/ui") || path.isEmpty() || path.equals("/")) {
21-
return chain.filter(
22-
exchange.mutate().request(
23-
exchange.getRequest().mutate()
24-
.path(basePath + "/index.html")
25-
.contextPath(basePath)
26-
.build()
27-
).build()
28-
);
23+
filterExchange = exchange.mutate().request(
24+
exchange.getRequest().mutate()
25+
.path(basePath + "/index.html")
26+
.contextPath(basePath)
27+
.build()
28+
).build();
2929
}
3030

31-
return chain.filter(exchange);
31+
return chain.filter(filterExchange).contextWrite(ctx -> ctx.put(ServerWebExchange.class, exchange));
3232
}
3333
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.kafbat.ui.config;
2+
3+
import com.github.victools.jsonschema.generator.OptionPreset;
4+
import com.github.victools.jsonschema.generator.SchemaGenerator;
5+
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
6+
import com.github.victools.jsonschema.generator.SchemaVersion;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
@Configuration
11+
public class JsonSchemaConfig {
12+
@Bean
13+
public SchemaGenerator schemaGenerator() {
14+
SchemaGeneratorConfigBuilder configBuilder =
15+
new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
16+
return new SchemaGenerator(configBuilder.build());
17+
}
18+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.kafbat.ui.config;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import io.kafbat.ui.service.mcp.McpSpecificationGenerator;
5+
import io.kafbat.ui.service.mcp.McpTool;
6+
import io.modelcontextprotocol.server.McpAsyncServer;
7+
import io.modelcontextprotocol.server.McpServer;
8+
import io.modelcontextprotocol.server.McpServerFeatures.AsyncPromptSpecification;
9+
import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification;
10+
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider;
11+
import io.modelcontextprotocol.spec.McpSchema;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
16+
import org.springframework.context.annotation.Bean;
17+
import org.springframework.context.annotation.Configuration;
18+
import org.springframework.web.reactive.function.server.RouterFunction;
19+
20+
@Configuration
21+
@RequiredArgsConstructor
22+
@ConditionalOnProperty(value = "mcp.enabled", havingValue = "true")
23+
public class McpConfig {
24+
25+
private final List<McpTool> mcpTools;
26+
private final McpSpecificationGenerator mcpSpecificationGenerator;
27+
28+
// SSE transport
29+
@Bean
30+
public WebFluxSseServerTransportProvider sseServerTransport(ObjectMapper mapper) {
31+
return new WebFluxSseServerTransportProvider(mapper, "/mcp/message", "/mcp/sse");
32+
}
33+
34+
// Router function for SSE transport used by Spring WebFlux to start an HTTP
35+
// server.
36+
37+
@Bean
38+
public RouterFunction<?> mcpRouterFunction(WebFluxSseServerTransportProvider transport) {
39+
return transport.getRouterFunction();
40+
}
41+
42+
@Bean
43+
public McpAsyncServer mcpServer(WebFluxSseServerTransportProvider transport) {
44+
45+
// Configure server capabilities with resource support
46+
var capabilities = McpSchema.ServerCapabilities.builder()
47+
.resources(false, true)
48+
.tools(true) // Tool support with list changes notifications
49+
.prompts(false) // Prompt support with list changes notifications
50+
.logging() // Logging support
51+
.build();
52+
53+
// Create the server with both tool and resource capabilities
54+
return McpServer.async(transport)
55+
.serverInfo("Kafka UI MCP", "0.0.1")
56+
.capabilities(capabilities)
57+
.tools(tools())
58+
.build();
59+
}
60+
61+
private List<AsyncToolSpecification> tools() {
62+
List<AsyncToolSpecification> tools = new ArrayList<>();
63+
for (McpTool mcpTool : mcpTools) {
64+
tools.addAll(mcpSpecificationGenerator.convertTool(mcpTool));
65+
}
66+
return tools;
67+
}
68+
}

api/src/main/java/io/kafbat/ui/controller/AclsController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.kafbat.ui.model.rbac.AccessContext;
1212
import io.kafbat.ui.model.rbac.permission.AclAction;
1313
import io.kafbat.ui.service.acl.AclsService;
14+
import io.kafbat.ui.service.mcp.McpTool;
1415
import java.util.Optional;
1516
import lombok.RequiredArgsConstructor;
1617
import org.apache.kafka.common.resource.PatternType;
@@ -24,7 +25,7 @@
2425

2526
@RestController
2627
@RequiredArgsConstructor
27-
public class AclsController extends AbstractController implements AclsApi {
28+
public class AclsController extends AbstractController implements AclsApi, McpTool {
2829

2930
private final AclsService aclsService;
3031

api/src/main/java/io/kafbat/ui/controller/BrokersController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.kafbat.ui.model.rbac.AccessContext;
1212
import io.kafbat.ui.model.rbac.permission.ClusterConfigAction;
1313
import io.kafbat.ui.service.BrokerService;
14+
import io.kafbat.ui.service.mcp.McpTool;
1415
import java.util.List;
1516
import java.util.Map;
1617
import javax.annotation.Nullable;
@@ -25,7 +26,7 @@
2526
@RestController
2627
@RequiredArgsConstructor
2728
@Slf4j
28-
public class BrokersController extends AbstractController implements BrokersApi {
29+
public class BrokersController extends AbstractController implements BrokersApi, McpTool {
2930
private static final String BROKER_ID = "brokerId";
3031

3132
private final BrokerService brokerService;

api/src/main/java/io/kafbat/ui/controller/ClientQuotasController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.kafbat.ui.model.ClientQuotasDTO;
77
import io.kafbat.ui.model.rbac.AccessContext;
88
import io.kafbat.ui.model.rbac.permission.ClientQuotaAction;
9+
import io.kafbat.ui.service.mcp.McpTool;
910
import io.kafbat.ui.service.quota.ClientQuotaRecord;
1011
import io.kafbat.ui.service.quota.ClientQuotaService;
1112
import java.math.BigDecimal;
@@ -22,7 +23,7 @@
2223

2324
@RestController
2425
@RequiredArgsConstructor
25-
public class ClientQuotasController extends AbstractController implements ClientQuotasApi {
26+
public class ClientQuotasController extends AbstractController implements ClientQuotasApi, McpTool {
2627

2728
private static final Comparator<ClientQuotaRecord> QUOTA_RECORDS_COMPARATOR =
2829
Comparator.nullsLast(Comparator.comparing(ClientQuotaRecord::user))

api/src/main/java/io/kafbat/ui/controller/ClustersController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.kafbat.ui.model.ClusterStatsDTO;
77
import io.kafbat.ui.model.rbac.AccessContext;
88
import io.kafbat.ui.service.ClusterService;
9+
import io.kafbat.ui.service.mcp.McpTool;
910
import lombok.RequiredArgsConstructor;
1011
import lombok.extern.slf4j.Slf4j;
1112
import org.springframework.http.ResponseEntity;
@@ -17,7 +18,7 @@
1718
@RestController
1819
@RequiredArgsConstructor
1920
@Slf4j
20-
public class ClustersController extends AbstractController implements ClustersApi {
21+
public class ClustersController extends AbstractController implements ClustersApi, McpTool {
2122
private final ClusterService clusterService;
2223

2324
@Override

api/src/main/java/io/kafbat/ui/controller/ConsumerGroupsController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.kafbat.ui.model.rbac.permission.TopicAction;
2020
import io.kafbat.ui.service.ConsumerGroupService;
2121
import io.kafbat.ui.service.OffsetsResetService;
22+
import io.kafbat.ui.service.mcp.McpTool;
2223
import java.util.Map;
2324
import java.util.Optional;
2425
import java.util.function.Supplier;
@@ -35,7 +36,7 @@
3536
@RestController
3637
@RequiredArgsConstructor
3738
@Slf4j
38-
public class ConsumerGroupsController extends AbstractController implements ConsumerGroupsApi {
39+
public class ConsumerGroupsController extends AbstractController implements ConsumerGroupsApi, McpTool {
3940

4041
private final ConsumerGroupService consumerGroupService;
4142
private final OffsetsResetService offsetsResetService;

api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.kafbat.ui.model.rbac.AccessContext;
2121
import io.kafbat.ui.model.rbac.permission.ConnectAction;
2222
import io.kafbat.ui.service.KafkaConnectService;
23+
import io.kafbat.ui.service.mcp.McpTool;
2324
import java.util.Comparator;
2425
import java.util.Map;
2526
import java.util.Set;
@@ -35,7 +36,7 @@
3536
@RestController
3637
@RequiredArgsConstructor
3738
@Slf4j
38-
public class KafkaConnectController extends AbstractController implements KafkaConnectApi {
39+
public class KafkaConnectController extends AbstractController implements KafkaConnectApi, McpTool {
3940
private static final Set<ConnectorActionDTO> RESTART_ACTIONS
4041
= Set.of(RESTART, RESTART_FAILED_TASKS, RESTART_ALL_TASKS);
4142
private static final String CONNECTOR_NAME = "connectorName";

0 commit comments

Comments
 (0)