feat: add singleton enforcement, extended MCP tools, and drawing improvements
Prevent multiple instances via fs2 file locking and improve agent server port-in-use detection. Add polygon shapes, vertex editing, grouping, alignment, z-ordering, duplication, SVG export, screenshot capture, app state introspection, and extended shape properties (opacity, rotation, corner radius, font family, max width) across both WebSocket and MCP APIs.
This commit is contained in:
@@ -105,6 +105,10 @@ pub struct CreateDrawingElementParam {
|
||||
pub x2: Option<f32>,
|
||||
#[schemars(description = "End Y (Line/Arrow)")]
|
||||
pub y2: Option<f32>,
|
||||
#[schemars(description = "Arrow control offset X from midpoint (optional)")]
|
||||
pub control_offset_x: Option<f32>,
|
||||
#[schemars(description = "Arrow control offset Y from midpoint (optional)")]
|
||||
pub control_offset_y: Option<f32>,
|
||||
#[schemars(description = "Text content (Text shape only)")]
|
||||
pub text: Option<String>,
|
||||
#[schemars(description = "Font size in pixels (Text shape, default 20)")]
|
||||
@@ -115,6 +119,24 @@ pub struct CreateDrawingElementParam {
|
||||
pub stroke_color: Option<String>,
|
||||
#[schemars(description = "Stroke width in pixels (default 2.0)")]
|
||||
pub stroke_width: Option<f32>,
|
||||
#[schemars(description = "Opacity from 0.0 to 1.0 (default 1.0)")]
|
||||
pub opacity: Option<f32>,
|
||||
#[schemars(description = "Rotation in degrees (default 0)")]
|
||||
pub rotation: Option<f32>,
|
||||
#[schemars(description = "Corner radius for rectangles (default 0)")]
|
||||
pub corner_radius: Option<f32>,
|
||||
#[schemars(description = "Font family for text: 'monospace' or omit for default")]
|
||||
pub font_family: Option<String>,
|
||||
#[schemars(description = "Number of sides for Polygon (default 6)")]
|
||||
pub sides: Option<u32>,
|
||||
#[schemars(description = "Inner radius ratio for star polygon (0.0-1.0)")]
|
||||
pub star_inner_ratio: Option<f32>,
|
||||
#[schemars(
|
||||
description = "Maximum text width for wrapping (Text shape only). Text will wrap at this width."
|
||||
)]
|
||||
pub max_width: Option<f32>,
|
||||
#[schemars(description = "Group ID to assign to the element")]
|
||||
pub group_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
@@ -149,6 +171,10 @@ pub struct UpdateDrawingElementParam {
|
||||
pub x2: Option<f32>,
|
||||
#[schemars(description = "End Y (Line/Arrow)")]
|
||||
pub y2: Option<f32>,
|
||||
#[schemars(description = "Arrow control offset X from midpoint")]
|
||||
pub control_offset_x: Option<f32>,
|
||||
#[schemars(description = "Arrow control offset Y from midpoint")]
|
||||
pub control_offset_y: Option<f32>,
|
||||
#[schemars(description = "Text content (Text shape)")]
|
||||
pub text: Option<String>,
|
||||
#[schemars(description = "Font size (Text shape)")]
|
||||
@@ -159,6 +185,144 @@ pub struct UpdateDrawingElementParam {
|
||||
pub stroke_color: Option<String>,
|
||||
#[schemars(description = "Stroke width in pixels")]
|
||||
pub stroke_width: Option<f32>,
|
||||
#[schemars(description = "Opacity from 0.0 to 1.0")]
|
||||
pub opacity: Option<f32>,
|
||||
#[schemars(description = "Rotation in degrees")]
|
||||
pub rotation: Option<f32>,
|
||||
#[schemars(description = "Corner radius for rectangles")]
|
||||
pub corner_radius: Option<f32>,
|
||||
#[schemars(description = "Font family for text: 'monospace' or omit for default")]
|
||||
pub font_family: Option<String>,
|
||||
#[schemars(description = "Number of sides for Polygon")]
|
||||
pub sides: Option<u32>,
|
||||
#[schemars(description = "Inner radius ratio for star polygon (0.0-1.0)")]
|
||||
pub star_inner_ratio: Option<f32>,
|
||||
#[schemars(
|
||||
description = "Maximum text width for wrapping (Text shape only). Text will wrap at this width."
|
||||
)]
|
||||
pub max_width: Option<f32>,
|
||||
#[schemars(description = "Group ID to assign to the element")]
|
||||
pub group_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GroupElementsParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "IDs of drawing elements to group")]
|
||||
pub element_ids: Vec<String>,
|
||||
#[schemars(description = "Optional group ID. If omitted, agcanvas auto-generates one")]
|
||||
pub group_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct UngroupElementsParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "IDs of drawing elements to ungroup")]
|
||||
pub element_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct AlignElementsParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "IDs of drawing elements to align")]
|
||||
pub element_ids: Vec<String>,
|
||||
#[schemars(
|
||||
description = "Alignment operation: left, right, top, bottom, center_h, center_v, distribute_h, distribute_v"
|
||||
)]
|
||||
pub operation: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct ExportSvgParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "File path to save the SVG export to")]
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct ConvertToPathParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "IDs of drawing elements to convert to editable path shapes")]
|
||||
pub element_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct MoveVertexParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "ID of the Path element")]
|
||||
pub element_id: String,
|
||||
#[schemars(description = "Index of the polygon within the Path (usually 0)")]
|
||||
pub polygon_idx: usize,
|
||||
#[schemars(description = "Index of the vertex to move")]
|
||||
pub vertex_idx: usize,
|
||||
#[schemars(description = "If true, targets hole ring instead of exterior (default false)")]
|
||||
pub is_hole: Option<bool>,
|
||||
#[schemars(description = "New X position for the vertex")]
|
||||
pub x: f32,
|
||||
#[schemars(description = "New Y position for the vertex")]
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct AddVertexParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "ID of the Path element")]
|
||||
pub element_id: String,
|
||||
#[schemars(description = "Index of the polygon within the Path (usually 0)")]
|
||||
pub polygon_idx: usize,
|
||||
#[schemars(description = "Insert after this vertex index")]
|
||||
pub after_vertex_idx: usize,
|
||||
#[schemars(description = "If true, targets hole ring instead of exterior (default false)")]
|
||||
pub is_hole: Option<bool>,
|
||||
#[schemars(description = "X position for the new vertex")]
|
||||
pub x: f32,
|
||||
#[schemars(description = "Y position for the new vertex")]
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct DeleteVertexParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "ID of the Path element")]
|
||||
pub element_id: String,
|
||||
#[schemars(description = "Index of the polygon within the Path (usually 0)")]
|
||||
pub polygon_idx: usize,
|
||||
#[schemars(description = "Index of the vertex to delete")]
|
||||
pub vertex_idx: usize,
|
||||
#[schemars(description = "If true, targets hole ring instead of exterior (default false)")]
|
||||
pub is_hole: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct ReorderElementParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "IDs of drawing elements to reorder")]
|
||||
pub element_ids: Vec<String>,
|
||||
#[schemars(
|
||||
description = "Reorder operation: bring_forward, send_backward, bring_to_front, send_to_back"
|
||||
)]
|
||||
pub operation: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct DuplicateElementsParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "IDs of drawing elements to duplicate")]
|
||||
pub element_ids: Vec<String>,
|
||||
#[schemars(description = "X offset for duplicated elements (default 20)")]
|
||||
pub offset_x: Option<f32>,
|
||||
#[schemars(description = "Y offset for duplicated elements (default 20)")]
|
||||
pub offset_y: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
@@ -215,6 +379,20 @@ pub struct ExportCanvasParam {
|
||||
pub background: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CaptureScreenshotParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
#[schemars(description = "File path to save the screenshot PNG to")]
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GetAppStateParam {
|
||||
#[schemars(description = "Session ID to target. If omitted, uses the active session.")]
|
||||
pub session_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct BatchParam {
|
||||
#[schemars(
|
||||
@@ -386,7 +564,7 @@ impl AgCanvasServer {
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Create a drawing element on the canvas. Shape types: Rectangle (x,y,width,height), Ellipse (center_x,center_y,radius_x,radius_y), Line (x1,y1,x2,y2), Arrow (x1,y1,x2,y2), Text (x,y,text,font_size). Colors are hex strings like '#ff0000'. Returns the created element with its auto-generated ID."
|
||||
description = "Create a drawing element on the canvas. Shape types: Rectangle (x,y,width,height), Ellipse (center_x,center_y,radius_x,radius_y), Line (x1,y1,x2,y2), Arrow (x1,y1,x2,y2), Polygon (center_x,center_y,radius_x,sides,star_inner_ratio), Text (x,y,text,font_size). Colors are hex strings like '#ff0000'. Returns the created element with its auto-generated ID."
|
||||
)]
|
||||
async fn create_drawing_element(
|
||||
&self,
|
||||
@@ -436,6 +614,12 @@ impl AgCanvasServer {
|
||||
if let Some(v) = params.y2 {
|
||||
obj.insert("y2".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.control_offset_x {
|
||||
obj.insert("control_offset_x".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.control_offset_y {
|
||||
obj.insert("control_offset_y".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.text {
|
||||
obj.insert("text".into(), v.into());
|
||||
}
|
||||
@@ -451,6 +635,30 @@ impl AgCanvasServer {
|
||||
if let Some(v) = params.stroke_width {
|
||||
obj.insert("stroke_width".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.opacity {
|
||||
obj.insert("opacity".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.rotation {
|
||||
obj.insert("rotation".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.corner_radius {
|
||||
obj.insert("corner_radius".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.font_family {
|
||||
obj.insert("font_family".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.sides {
|
||||
obj.insert("sides".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.star_inner_ratio {
|
||||
obj.insert("star_inner_ratio".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.max_width {
|
||||
obj.insert("max_width".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.group_id {
|
||||
obj.insert("group_id".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
@@ -508,6 +716,12 @@ impl AgCanvasServer {
|
||||
if let Some(v) = params.y2 {
|
||||
obj.insert("y2".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.control_offset_x {
|
||||
obj.insert("control_offset_x".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.control_offset_y {
|
||||
obj.insert("control_offset_y".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.text {
|
||||
obj.insert("text".into(), v.into());
|
||||
}
|
||||
@@ -523,6 +737,229 @@ impl AgCanvasServer {
|
||||
if let Some(v) = params.stroke_width {
|
||||
obj.insert("stroke_width".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.opacity {
|
||||
obj.insert("opacity".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.rotation {
|
||||
obj.insert("rotation".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.corner_radius {
|
||||
obj.insert("corner_radius".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.font_family {
|
||||
obj.insert("font_family".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.sides {
|
||||
obj.insert("sides".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.star_inner_ratio {
|
||||
obj.insert("star_inner_ratio".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.max_width {
|
||||
obj.insert("max_width".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.group_id {
|
||||
obj.insert("group_id".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(description = "Group drawing elements together so they move/select as one unit")]
|
||||
async fn group_elements(
|
||||
&self,
|
||||
Parameters(params): Parameters<GroupElementsParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "GroupElements",
|
||||
"element_ids": params.element_ids,
|
||||
});
|
||||
let obj = request.as_object_mut().unwrap();
|
||||
if let Some(v) = params.session_id {
|
||||
obj.insert("session_id".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.group_id {
|
||||
obj.insert("group_id".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(description = "Ungroup drawing elements (remove group association)")]
|
||||
async fn ungroup_elements(
|
||||
&self,
|
||||
Parameters(params): Parameters<UngroupElementsParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "UngroupElements",
|
||||
"element_ids": params.element_ids,
|
||||
});
|
||||
if let Some(v) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(v);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Align selected drawing elements (left, right, top, bottom, center_h, center_v, distribute_h, distribute_v)"
|
||||
)]
|
||||
async fn align_elements(
|
||||
&self,
|
||||
Parameters(params): Parameters<AlignElementsParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "AlignElements",
|
||||
"element_ids": params.element_ids,
|
||||
"operation": params.operation,
|
||||
});
|
||||
if let Some(v) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(v);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(description = "Export drawing elements as SVG vector file")]
|
||||
async fn export_svg(
|
||||
&self,
|
||||
Parameters(params): Parameters<ExportSvgParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "ExportSvg",
|
||||
"path": params.path,
|
||||
});
|
||||
if let Some(v) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(v);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Reorder drawing elements in the z-stack. Operations: bring_forward (up one), send_backward (down one), bring_to_front (topmost), send_to_back (bottommost)."
|
||||
)]
|
||||
async fn reorder_element(
|
||||
&self,
|
||||
Parameters(params): Parameters<ReorderElementParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "ReorderElement",
|
||||
"element_ids": params.element_ids,
|
||||
"operation": params.operation,
|
||||
});
|
||||
if let Some(v) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(v);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Duplicate drawing elements. Creates copies offset from the originals. Returns the new elements with their auto-generated IDs."
|
||||
)]
|
||||
async fn duplicate_elements(
|
||||
&self,
|
||||
Parameters(params): Parameters<DuplicateElementsParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "DuplicateElements",
|
||||
"element_ids": params.element_ids,
|
||||
});
|
||||
let obj = request.as_object_mut().unwrap();
|
||||
if let Some(v) = params.session_id {
|
||||
obj.insert("session_id".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.offset_x {
|
||||
obj.insert("offset_x".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.offset_y {
|
||||
obj.insert("offset_y".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Convert drawing elements to editable Path shapes with explicit vertices. Works on Rectangle, Ellipse, Polygon, Line, Arrow. Text and Path shapes are skipped. After conversion, use move_vertex/add_vertex/delete_vertex to edit individual points."
|
||||
)]
|
||||
async fn convert_to_path(
|
||||
&self,
|
||||
Parameters(params): Parameters<ConvertToPathParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "ConvertToPath",
|
||||
"element_ids": params.element_ids,
|
||||
});
|
||||
if let Some(v) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(v);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Move a vertex on a Path element to a new position. The element must be a Path shape (use convert_to_path first if needed)."
|
||||
)]
|
||||
async fn move_vertex(
|
||||
&self,
|
||||
Parameters(params): Parameters<MoveVertexParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "MoveVertex",
|
||||
"element_id": params.element_id,
|
||||
"polygon_idx": params.polygon_idx,
|
||||
"vertex_idx": params.vertex_idx,
|
||||
"x": params.x,
|
||||
"y": params.y,
|
||||
});
|
||||
let obj = request.as_object_mut().unwrap();
|
||||
if let Some(v) = params.session_id {
|
||||
obj.insert("session_id".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.is_hole {
|
||||
obj.insert("is_hole".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Insert a new vertex on a Path element after the specified vertex index. The element must be a Path shape."
|
||||
)]
|
||||
async fn add_vertex(
|
||||
&self,
|
||||
Parameters(params): Parameters<AddVertexParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "AddVertex",
|
||||
"element_id": params.element_id,
|
||||
"polygon_idx": params.polygon_idx,
|
||||
"after_vertex_idx": params.after_vertex_idx,
|
||||
"x": params.x,
|
||||
"y": params.y,
|
||||
});
|
||||
let obj = request.as_object_mut().unwrap();
|
||||
if let Some(v) = params.session_id {
|
||||
obj.insert("session_id".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.is_hole {
|
||||
obj.insert("is_hole".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Delete a vertex from a Path element. Exterior ring must keep at least 3 vertices. The element must be a Path shape."
|
||||
)]
|
||||
async fn delete_vertex(
|
||||
&self,
|
||||
Parameters(params): Parameters<DeleteVertexParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "DeleteVertex",
|
||||
"element_id": params.element_id,
|
||||
"polygon_idx": params.polygon_idx,
|
||||
"vertex_idx": params.vertex_idx,
|
||||
});
|
||||
let obj = request.as_object_mut().unwrap();
|
||||
if let Some(v) = params.session_id {
|
||||
obj.insert("session_id".into(), v.into());
|
||||
}
|
||||
if let Some(v) = params.is_hole {
|
||||
obj.insert("is_hole".into(), v.into());
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
@@ -600,6 +1037,37 @@ impl AgCanvasServer {
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Capture a pixel-perfect screenshot of the entire Augmented Canvas application window (including UI chrome, toolbar, panels). Saves as PNG. The screenshot is taken asynchronously and may take 1-2 frames."
|
||||
)]
|
||||
async fn capture_screenshot(
|
||||
&self,
|
||||
Parameters(params): Parameters<CaptureScreenshotParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({
|
||||
"type": "CaptureScreenshot",
|
||||
"path": params.path,
|
||||
});
|
||||
if let Some(sid) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(sid);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Get the current application state including active tool, selected elements, zoom level, pan offset, theme, panel visibility, session info, and canvas dimensions. Useful for debugging and understanding the current UI state."
|
||||
)]
|
||||
async fn get_app_state(
|
||||
&self,
|
||||
Parameters(params): Parameters<GetAppStateParam>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let mut request = serde_json::json!({"type": "GetAppState"});
|
||||
if let Some(sid) = params.session_id {
|
||||
request["session_id"] = serde_json::Value::String(sid);
|
||||
}
|
||||
self.call_agcanvas(&request).await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Send multiple Augmented Canvas operations in one request. Accepts a JSON array of request objects and returns one response per operation in a BatchResult."
|
||||
)]
|
||||
|
||||
Reference in New Issue
Block a user