package ai.dynamixs.mcp; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import jakarta.json.Json; import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; /** * Represents an MCP Tool based on Imixs-Workflow BPMN models. * * MCP Tools represent functions that can be called by the MCP client. Each tool * corresponds to a workflow task/event combination. */ public class MCPTool implements Serializable { private static final long serialVersionUID = 1L; // MCP Standard Fields private String name; // Tool name/identifier private String description; // Tool description private JsonObject inputSchema; // JSON Schema for input parameters // Imixs-specific fields private String modelVersion; private int taskId; private int eventId; private List tags; private List examples; // Constructors public MCPTool() { this.tags = new ArrayList<>(); this.examples = new ArrayList<>(); } public MCPTool(String modelVersion, int taskId, int eventId) { this(); this.modelVersion = modelVersion; this.taskId = taskId; this.eventId = eventId; this.name = createToolName(modelVersion, taskId, eventId); } /** * Creates a tool name from Imixs workflow components */ public static String createToolName(String modelVersion, int taskId, int eventId) { if (modelVersion == null || modelVersion.trim().isEmpty()) { throw new IllegalArgumentException("Model version cannot be null or empty"); } return String.format("%s_%d_%d", modelVersion.replace("-", "_"), taskId, eventId); } /** * Parses model version from tool name */ public String getModelVersionFromName() { if (name == null || !name.contains("_")) { throw new IllegalStateException("Invalid tool name format: " + name); } return name.substring(0, name.lastIndexOf('_', name.lastIndexOf('_') - 1)).replace("_", "-"); } /** * Parses task ID from tool name */ public int getTaskIdFromName() { if (name == null || !name.contains("_")) { throw new IllegalStateException("Invalid tool name format: " + name); } String[] parts = name.split("_"); if (parts.length < 3) { throw new IllegalStateException("Invalid tool name format: " + name); } try { return Integer.parseInt(parts[parts.length - 2]); } catch (NumberFormatException e) { throw new IllegalStateException("Invalid task ID in tool name: " + name, e); } } /** * Parses event ID from tool name */ public int getEventIdFromName() { if (name == null || !name.contains("_")) { throw new IllegalStateException("Invalid tool name format: " + name); } String[] parts = name.split("_"); if (parts.length < 3) { throw new IllegalStateException("Invalid tool name format: " + name); } try { return Integer.parseInt(parts[parts.length - 1]); } catch (NumberFormatException e) { throw new IllegalStateException("Invalid event ID in tool name: " + name, e); } } /** * Creates a default input schema for workflow tools */ public JsonObject createDefaultInputSchema() { return Json.createObjectBuilder() .add("type", "object") .add("properties", Json.createObjectBuilder() .add("workitem", Json.createObjectBuilder() .add("type", "object") .add("description", "Workflow item data") .add("properties", Json.createObjectBuilder() .add("$modelversion", Json.createObjectBuilder() .add("type", "string") .add("description", "Workflow model version") .build()) .add("$taskid", Json.createObjectBuilder() .add("type", "integer") .add("description", "Task ID") .build()) .add("$eventid", Json.createObjectBuilder() .add("type", "integer") .add("description", "Event ID") .build()) .build()) .add("required", Json.createArrayBuilder() .add("$modelversion") .add("$taskid") .add("$eventid") .build()) .build()) .build()) .add("required", Json.createArrayBuilder() .add("workitem") .build()) .build(); } /** * Converts this tool to MCP JSON format */ public JsonObject toJsonObject() { JsonObjectBuilder builder = Json.createObjectBuilder() .add("name", name != null ? name : "") .add("description", description != null ? description : ""); // Input schema if (inputSchema != null) { builder.add("inputSchema", inputSchema); } else { builder.add("inputSchema", createDefaultInputSchema()); } return builder.build(); } /** * Creates an MCPTool from a JsonObject */ public static MCPTool fromJsonObject(JsonObject jsonObject) { MCPTool tool = new MCPTool(); tool.setName(jsonObject.getString("name", "")); tool.setDescription(jsonObject.getString("description", "")); if (jsonObject.containsKey("inputSchema")) { tool.setInputSchema(jsonObject.getJsonObject("inputSchema")); } return tool; } /** * Validates this tool for MCP compliance */ public boolean isValid() { if (name == null || name.trim().isEmpty()) { return false; } if (description == null || description.trim().isEmpty()) { return false; } // Validate tool name format try { getModelVersionFromName(); getTaskIdFromName(); getEventIdFromName(); } catch (Exception e) { return false; } return true; } // Getters and Setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public JsonObject getInputSchema() { return inputSchema; } public void setInputSchema(JsonObject inputSchema) { this.inputSchema = inputSchema; } public String getModelVersion() { return modelVersion; } public void setModelVersion(String modelVersion) { this.modelVersion = modelVersion; } public int getTaskId() { return taskId; } public void setTaskId(int taskId) { this.taskId = taskId; } public int getEventId() { return eventId; } public void setEventId(int eventId) { this.eventId = eventId; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags != null ? tags : new ArrayList<>(); } public List getExamples() { return examples; } public void setExamples(List examples) { this.examples = examples != null ? examples : new ArrayList<>(); } @Override public String toString() { return String.format("MCPTool{name='%s', description='%s', modelVersion='%s', taskId=%d, eventId=%d}", name, description, modelVersion, taskId, eventId); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; MCPTool that = (MCPTool) obj; return name != null ? name.equals(that.name) : that.name == null; } @Override public int hashCode() { return name != null ? name.hashCode() : 0; } }