office-dynamixs-ai/dynamixs-ai-mcp/src/main/java/ai/dynamixs/mcp/MCPResource.java

315 lines
No EOL
9.5 KiB
Java

package ai.dynamixs.mcp;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonString;
/**
* Represents an MCP Resource based on Imixs-Workflow BPMN models.
*
* MCP Resources represent entities that can be read by the MCP client. Each
* resource corresponds to a workflow task/event combination.
*/
public class MCPResource implements Serializable {
private static final long serialVersionUID = 1L;
// MCP Standard Fields
private String uri; // Resource URI
private String name; // Human-readable name
private String description; // Resource description
private String mimeType; // MIME type of the resource
private Map<String, Object> annotations; // Additional metadata
// Imixs-specific fields
private String modelVersion;
private int taskId;
private int eventId;
// Constructors
public MCPResource() {
this.annotations = new HashMap<>();
}
public MCPResource(String modelVersion, int taskId, int eventId) {
this();
this.modelVersion = modelVersion;
this.taskId = taskId;
this.eventId = eventId;
this.uri = createResourceUri(modelVersion, taskId, eventId);
}
/**
* Creates a resource URI from Imixs workflow components
*/
public static String createResourceUri(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("imixs://workflow/%s/%d/%d", modelVersion, taskId, eventId);
}
/**
* Parses model version from URI
*/
public String getModelVersionFromUri() {
if (uri == null || !uri.startsWith("imixs://workflow/")) {
throw new IllegalStateException("Invalid resource URI format: " + uri);
}
String path = uri.substring("imixs://workflow/".length());
String[] parts = path.split("/");
if (parts.length < 3) {
throw new IllegalStateException("Invalid resource URI format: " + uri);
}
return parts[0];
}
/**
* Parses task ID from URI
*/
public int getTaskIdFromUri() {
if (uri == null || !uri.startsWith("imixs://workflow/")) {
throw new IllegalStateException("Invalid resource URI format: " + uri);
}
String path = uri.substring("imixs://workflow/".length());
String[] parts = path.split("/");
if (parts.length < 3) {
throw new IllegalStateException("Invalid resource URI format: " + uri);
}
try {
return Integer.parseInt(parts[1]);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid task ID in resource URI: " + uri, e);
}
}
/**
* Parses event ID from URI
*/
public int getEventIdFromUri() {
if (uri == null || !uri.startsWith("imixs://workflow/")) {
throw new IllegalStateException("Invalid resource URI format: " + uri);
}
String path = uri.substring("imixs://workflow/".length());
String[] parts = path.split("/");
if (parts.length < 3) {
throw new IllegalStateException("Invalid resource URI format: " + uri);
}
try {
return Integer.parseInt(parts[2]);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid event ID in resource URI: " + uri, e);
}
}
/**
* Converts this resource to MCP JSON format
*/
public JsonObject toJsonObject() {
JsonObjectBuilder builder = Json.createObjectBuilder()
.add("uri", uri != null ? uri : "")
.add("name", name != null ? name : "")
.add("description", description != null ? description : "");
if (mimeType != null) {
builder.add("mimeType", mimeType);
}
// Annotations object
if (annotations != null && !annotations.isEmpty()) {
JsonObjectBuilder annotationsBuilder = Json.createObjectBuilder();
annotations.forEach((key, value) -> {
if (value instanceof String) {
annotationsBuilder.add(key, (String) value);
} else if (value instanceof Number) {
annotationsBuilder.add(key, ((Number) value).doubleValue());
} else if (value instanceof Boolean) {
annotationsBuilder.add(key, (Boolean) value);
} else if (value != null) {
annotationsBuilder.add(key, value.toString());
}
});
builder.add("annotations", annotationsBuilder.build());
}
return builder.build();
}
/**
* Creates an MCPResource from a JsonObject
*/
public static MCPResource fromJsonObject(JsonObject jsonObject) {
MCPResource resource = new MCPResource();
resource.setUri(jsonObject.getString("uri", ""));
resource.setName(jsonObject.getString("name", ""));
resource.setDescription(jsonObject.getString("description", ""));
if (jsonObject.containsKey("mimeType")) {
resource.setMimeType(jsonObject.getString("mimeType"));
}
// Annotations
if (jsonObject.containsKey("annotations")) {
JsonObject annotationsObject = jsonObject.getJsonObject("annotations");
Map<String, Object> annotations = new HashMap<>();
annotationsObject.forEach((key, value) -> {
switch (value.getValueType()) {
case STRING:
annotations.put(key, ((JsonString) value).getString());
break;
case NUMBER:
annotations.put(key, ((jakarta.json.JsonNumber) value).doubleValue());
break;
case TRUE:
annotations.put(key, Boolean.TRUE);
break;
case FALSE:
annotations.put(key, Boolean.FALSE);
break;
default:
annotations.put(key, value.toString());
}
});
resource.setAnnotations(annotations);
}
return resource;
}
/**
* Validates this resource for MCP compliance
*/
public boolean isValid() {
if (uri == null || uri.trim().isEmpty()) {
return false;
}
if (name == null || name.trim().isEmpty()) {
return false;
}
// Validate URI format
try {
getModelVersionFromUri();
getTaskIdFromUri();
getEventIdFromUri();
} catch (Exception e) {
return false;
}
return true;
}
// Getters and Setters
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
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 String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
public Map<String, Object> getAnnotations() {
return annotations;
}
public void setAnnotations(Map<String, Object> annotations) {
this.annotations = annotations != null ? annotations : new HashMap<>();
}
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;
}
// Convenience methods for annotations
public void addAnnotation(String key, Object value) {
if (annotations == null) {
annotations = new HashMap<>();
}
annotations.put(key, value);
}
public String getAnnotationString(String key) {
Object value = annotations != null ? annotations.get(key) : null;
return value != null ? value.toString() : null;
}
public Boolean getAnnotationBoolean(String key) {
Object value = annotations != null ? annotations.get(key) : null;
if (value instanceof Boolean) {
return (Boolean) value;
}
return null;
}
@Override
public String toString() {
return String.format("MCPResource{uri='%s', name='%s', modelVersion='%s', taskId=%d, eventId=%d}",
uri, name, modelVersion, taskId, eventId);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
MCPResource that = (MCPResource) obj;
return uri != null ? uri.equals(that.uri) : that.uri == null;
}
@Override
public int hashCode() {
return uri != null ? uri.hashCode() : 0;
}
}