Feature/mcp (#846)
* Added MCP support using SSE on http://localhost:8080/sse * Reverted change that IntelliJ complains about * Pre-rework * Cleaned up the code a fair bit * Renamed * Renamed * Running spotless * Reuse FhirContext in result serialization to make MCP server work with R5 * Added support for transactions * PoC tool for CDS Hooks * some cleanup * Upgrade of model protocol * Added comments * Removed field injection ... CDS to be changed to AutoConfig eventually * Adjusted to new builder pattern * Update src/main/java/ca/uhn/fhir/rest/server/MCPBridge.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * A bit of restructuring * More rework * Removing (suspected unnecessary) formatting * Add more example doc * Added a smoke- / passthrough-test * Applied spotless * Update src/main/java/ca/uhn/fhir/jpa/starter/mcp/RequestBuilder.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/java/ca/uhn/fhir/jpa/starter/mcp/RequestBuilder.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/java/ca/uhn/fhir/jpa/starter/mcp/ToolFactory.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/java/ca/uhn/fhir/rest/server/McpCdsBridge.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/java/ca/uhn/fhir/rest/server/McpCdsBridge.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Formatting * Added some documentation * spotless cares about MD? * Reverting back to default values * minor refinements * Fixed CDS hooks configuration * Fixed some wirings * Revert "Fixed some wirings" This reverts commit c9d3bc0b3b6756d7b15f5d2cf6100c99784fb868. * Revert "Fixed CDS hooks configuration" This reverts commit 67c4279100bf14432c164906235ea6348ee8af22. --------- Co-authored-by: Ádám Z. Kövér <adamzkover@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5585170c7d
commit
680255ff62
101
src/main/java/ca/uhn/fhir/rest/server/McpFhirBridge.java
Normal file
101
src/main/java/ca/uhn/fhir/rest/server/McpFhirBridge.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.starter.mcp.CallToolResultFactory;
|
||||
import ca.uhn.fhir.jpa.starter.mcp.Interaction;
|
||||
import ca.uhn.fhir.jpa.starter.mcp.RequestBuilder;
|
||||
import ca.uhn.fhir.jpa.starter.mcp.ToolFactory;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.modelcontextprotocol.server.McpServerFeatures;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class McpFhirBridge implements McpBridge {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(McpFhirBridge.class);
|
||||
|
||||
private final RestfulServer restfulServer;
|
||||
private final FhirContext fhirContext;
|
||||
|
||||
public McpFhirBridge(RestfulServer restfulServer) {
|
||||
this.restfulServer = restfulServer;
|
||||
this.fhirContext = restfulServer.getFhirContext();
|
||||
}
|
||||
|
||||
public List<McpServerFeatures.SyncToolSpecification> generateTools() {
|
||||
|
||||
try {
|
||||
return List.of(
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.createFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.CREATE))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.readFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.READ))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.updateFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.UPDATE))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.deleteFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.DELETE))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.conditionalPatchFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.PATCH))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.searchFhirResources())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.SEARCH))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.conditionalUpdateFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.UPDATE))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.patchFhirResource())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.PATCH))
|
||||
.build(),
|
||||
new McpServerFeatures.SyncToolSpecification.Builder()
|
||||
.tool(ToolFactory.createFhirTransaction())
|
||||
.callHandler((exchange, request) -> getToolResult(request, Interaction.TRANSACTION))
|
||||
.build());
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private McpSchema.CallToolResult getToolResult(McpSchema.CallToolRequest contextMap, Interaction interaction) {
|
||||
|
||||
var response = new MockHttpServletResponse();
|
||||
var request = new RequestBuilder(fhirContext, contextMap.arguments(), interaction).buildRequest();
|
||||
|
||||
try {
|
||||
restfulServer.handleRequest(interaction.asRequestType(), request, response);
|
||||
var status = response.getStatus();
|
||||
var body = response.getContentAsString();
|
||||
|
||||
if (status >= 200 && status < 300) {
|
||||
if (body.isBlank()) {
|
||||
return CallToolResultFactory.failure("Empty successful response for " + interaction);
|
||||
}
|
||||
|
||||
return CallToolResultFactory.success(
|
||||
contextMap.arguments().get("resourceType").toString(), interaction, body, status);
|
||||
} else {
|
||||
return CallToolResultFactory.failure(String.format("FHIR server error %d: %s", status, body));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return CallToolResultFactory.failure("Unexpected error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user