Skip to content

Commit

Permalink
Initial version of support for stdio transport
Browse files Browse the repository at this point in the history
  • Loading branch information
mkouba committed Jan 7, 2025
1 parent 1ac8e87 commit f940632
Show file tree
Hide file tree
Showing 122 changed files with 1,603 additions and 355 deletions.
11 changes: 6 additions & 5 deletions deployment/pom.xml → core/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@

<parent>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server-parent</artifactId>
<artifactId>quarkus-mcp-server-core-parent</artifactId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>quarkus-mcp-server-deployment</artifactId>
<name>Quarkus MCP Server - Deployment</name>
<artifactId>quarkus-mcp-server-core-deployment</artifactId>
<name>Quarkus MCP Server Core - Deployment</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
<artifactId>quarkus-vertx-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server</artifactId>
<artifactId>quarkus-mcp-server-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,11 @@
import io.quarkiverse.mcp.server.runtime.ResourceManager;
import io.quarkiverse.mcp.server.runtime.ResultMappers;
import io.quarkiverse.mcp.server.runtime.ToolManager;
import io.quarkiverse.mcp.server.runtime.config.McpBuildTimeConfig;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AutoAddScopeBuildItem;
import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem;
import io.quarkus.arc.deployment.InvokerFactoryBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem;
import io.quarkus.arc.deployment.TransformedAnnotationsBuildItem;
import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem;
import io.quarkus.arc.processor.BeanInfo;
Expand All @@ -67,9 +65,7 @@
import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
Expand All @@ -82,18 +78,9 @@
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.vertx.http.deployment.BodyHandlerBuildItem;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.runtime.HandlerType;

class McpServerProcessor {

@BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem("mcp-server");
}

@BuildStep
void addBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf("io.quarkiverse.mcp.server.runtime.ConnectionManager"));
Expand All @@ -108,27 +95,6 @@ AutoAddScopeBuildItem autoAddScope() {
.build();
}

@Record(RUNTIME_INIT)
@Consume(SyntheticBeansRuntimeInitBuildItem.class)
@BuildStep
void registerEndpoints(McpBuildTimeConfig config, HttpRootPathBuildItem rootPath, McpServerRecorder recorder,
BodyHandlerBuildItem bodyHandler,
BuildProducer<RouteBuildItem> routes) {
String mcpPath = rootPath.relativePath(config.rootPath());

routes.produce(RouteBuildItem.builder()
.route(mcpPath + "/" + "sse")
.handlerType(HandlerType.NORMAL)
.handler(recorder.createSseEndpointHandler(mcpPath))
.build());

routes.produce(RouteBuildItem.builder()
.routeFunction(mcpPath + "/" + "messages/:id", recorder.addBodyHandler(bodyHandler.getHandler()))
.handlerType(HandlerType.NORMAL)
.handler(recorder.createMessagesEndpointHandler())
.build());
}

@BuildStep
void collectFeatureMethods(BeanDiscoveryFinishedBuildItem beanDiscovery, InvokerFactoryBuildItem invokerFactory,
BuildProducer<FeatureMethodBuildItem> features, BuildProducer<ValidationErrorBuildItem> errors) {
Expand Down Expand Up @@ -219,8 +185,7 @@ void collectFeatureMethods(BeanDiscoveryFinishedBuildItem beanDiscovery, Invoker
@Record(RUNTIME_INIT)
@BuildStep
void generateMetadata(McpServerRecorder recorder, RecorderContext recorderContext,
List<FeatureMethodBuildItem> featureMethods,
TransformedAnnotationsBuildItem transformedAnnotations,
List<FeatureMethodBuildItem> featureMethods, TransformedAnnotationsBuildItem transformedAnnotations,
BuildProducer<GeneratedClassBuildItem> generatedClasses, BuildProducer<SyntheticBeanBuildItem> syntheticBeans) {

// Note that the generated McpMetadata impl must be considered an application class
Expand Down
19 changes: 19 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server-parent</artifactId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>quarkus-mcp-server-core-parent</artifactId>
<packaging>pom</packaging>
<name>Quarkus MCP Server Core - Parent</name>

<modules>
<module>deployment</module>
<module>runtime</module>
</modules>
</project>
9 changes: 5 additions & 4 deletions runtime/pom.xml → core/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

<parent>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server-parent</artifactId>
<artifactId>quarkus-mcp-server-core-parent</artifactId>
<version>999-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>quarkus-mcp-server</artifactId>
<name>Quarkus MCP Server - Runtime</name>
<artifactId>quarkus-mcp-server-core</artifactId>
<name>Quarkus MCP Server Core - Runtime</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http</artifactId>
<artifactId>quarkus-vertx</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public interface McpConnection {

Status status();

boolean initialize(InitializeRequest request);

boolean setInitialized();

InitializeRequest initializeRequest();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
import jakarta.inject.Singleton;

@Singleton
class ConnectionManager {
public class ConnectionManager {

private ConcurrentMap<String, McpConnectionImpl> connections = new ConcurrentHashMap<>();
private ConcurrentMap<String, McpConnectionBase> connections = new ConcurrentHashMap<>();

McpConnectionImpl get(String id) {
public McpConnectionBase get(String id) {
return connections.get(id);
}

void add(McpConnectionImpl connection) {
public void add(McpConnectionBase connection) {
connections.put(connection.id(), connection);
}

boolean remove(String id) {
public boolean remove(String id) {
return connections.remove(id) != null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkiverse.mcp.server.runtime;

import io.vertx.core.json.JsonObject;

public class JsonRPC {

public static final String VERSION = "2.0";

public static final int RESOURCE_NOT_FOUND = -32002;

public static final int INTERNAL_ERROR = -32603;
public static final int INVALID_PARAMS = -32602;
public static final int METHOD_NOT_FOUND = -32601;
public static final int INVALID_REQUEST = -32600;
public static final int PARSE_ERROR = -32700;

public static boolean validate(JsonObject message, Responder responder) {
Object id = message.getValue("id");
String jsonrpc = message.getString("jsonrpc");
if (!VERSION.equals(jsonrpc)) {
responder.sendError(id, INVALID_REQUEST, "Invalid jsonrpc version: " + jsonrpc);
return false;
}
if (message.getString("method") == null) {
responder.sendError(id, METHOD_NOT_FOUND, "Method not set");
return false;
}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,18 @@

import io.quarkiverse.mcp.server.InitializeRequest;
import io.quarkiverse.mcp.server.McpConnection;
import io.vertx.core.http.HttpServerResponse;

class McpConnectionImpl implements McpConnection {
public abstract class McpConnectionBase implements McpConnection {

private final String id;

private final AtomicReference<Status> status;

private final HttpServerResponse response;

private final AtomicReference<InitializeRequest> initializeRequest;

McpConnectionImpl(String id, HttpServerResponse response) {
protected McpConnectionBase(String id) {
this.id = id;
this.status = new AtomicReference<>(Status.NEW);
this.response = response;
this.initializeRequest = new AtomicReference<>();
}

Expand All @@ -38,22 +34,16 @@ public InitializeRequest initializeRequest() {
return initializeRequest.get();
}

boolean initialize(InitializeRequest request) {
public boolean initialize(InitializeRequest request) {
if (status.compareAndSet(Status.NEW, Status.INITIALIZING)) {
initializeRequest.set(request);
return true;
}
return false;
}

boolean initialized() {
public boolean setInitialized() {
return status.compareAndSet(Status.INITIALIZING, Status.IN_OPERATION);
}

void sendEvent(String name, String data) {
response.write("event: " + name + "\n");
response.write("data: " + data + "\n");
response.write("\n\n");
}

}
Loading

0 comments on commit f940632

Please sign in to comment.