Files
figcraft/crates/mcp/src/resources.rs
T
Brandon ThomasandClaude Opus 5.5 b36fd771ab M0.9: MCP server + figcraft-cli, PDF export, web app, release/CI packaging, vector edit mode, workspace lint cleanup
- crates/mcp: JSON-RPC MCP server (18 tools, resources, prompts), headless and attached backends, end-to-end agent tests
- apps/figcraft-cli: info/export/convert/render/run/commands/mcp/perf
- crates/pdf: vector PDF export via krilla with embedded text; file.exportFramesPdf
- apps/figcraft-web + packaging/{macos,windows,linux,freebsd,web} + release/ci workflows
- engine vectoredit: edit points/handles/bend/add/delete-heal; canvas overlay and edit strip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:31:53 -04:00

56 lines
2.6 KiB
Rust

//! MCP resources: the document, one layer, one command.
use serde_json::{Value, json};
use crate::backend::Backend;
pub const DOC_URI: &str = "figcraft://document";
pub const COMMANDS_URI: &str = "figcraft://commands";
pub const NODE_TEMPLATE: &str = "figcraft://node/{id}";
pub const COMMAND_TEMPLATE: &str = "figcraft://command/{id}";
pub enum ReadError {
NotFound(String),
Backend(String),
}
pub fn list() -> Value {
json!({"resources": [
{"uri": DOC_URI, "name": "document", "title": "Active document", "description": "The active document as JSON (current page's layer tree, pages, styles, selection, history), like inspect_document.", "mimeType": "application/json"},
{"uri": COMMANDS_URI, "name": "commands", "title": "Command catalog", "description": "Every FigCraft command with label, menu, shortcut and parameter docs.", "mimeType": "application/json"},
]})
}
pub fn templates() -> Value {
json!({"resourceTemplates": [
{"uriTemplate": NODE_TEMPLATE, "name": "node", "title": "Layer", "description": "One layer and its children (depth 2) as JSON.", "mimeType": "application/json"},
{"uriTemplate": COMMAND_TEMPLATE, "name": "command", "title": "Command", "description": "One command's label, menu path, shortcut and parameter docs, e.g. figcraft://command/layout.add.", "mimeType": "application/json"},
]})
}
pub fn read(b: &mut dyn Backend, uri: &str) -> Result<Value, ReadError> {
if uri == DOC_URI {
return b.call("document.inspect", json!({"depth": 8})).map_err(ReadError::Backend);
}
if uri == COMMANDS_URI {
return b.call("engine.commands", json!({})).map_err(ReadError::Backend);
}
if let Some(id) = uri.strip_prefix("figcraft://node/") {
let Some(id) = figcraft_engine::params::id_of(&Value::String(id.to_string())) else { return Err(ReadError::NotFound(uri.into())) };
return match b.call("node.get", json!({"id": id.0, "depth": 2})) {
Ok(Value::Null) => Err(ReadError::NotFound(uri.into())),
Ok(v) => Ok(v),
Err(e) if e.contains("no such node") => Err(ReadError::NotFound(uri.into())),
Err(e) => Err(ReadError::Backend(e)),
};
}
if let Some(id) = uri.strip_prefix("figcraft://command/") {
let all = b.call("engine.commands", json!({})).map_err(ReadError::Backend)?;
return all
.as_array()
.and_then(|a| a.iter().find(|c| c.get("id").and_then(Value::as_str) == Some(id)).cloned())
.ok_or_else(|| ReadError::NotFound(uri.into()));
}
Err(ReadError::NotFound(uri.into()))
}