Files
vectorcraft/crates/mcp/src/tools.rs
T
dotCooCoo 2c0d09e079 Scripts: File › Scripts › Other Script… runs JavaScript files
File › Scripts › Other Script… picks a .js or .jsx file, shows a warning
that can be turned off in Preferences › General, and runs the script
against the open documents through the `vc` object. The new
`script.run` and `script.stop` commands run and stop a script over MCP.

- A run is one transaction. Each document it changed gets one undo step
  named Run Script. An uncaught error, a limit or a stop puts every
  document, the paint, the clipboard, menu state and preferences back.
- A command that fails inside a run, or whose result is too large to
  reach the script, is undone and throws an error the script can catch.
- Files a command wrote stay on disk and the report names them, also
  when the run is stopped from the quit question.
- While a script runs, other commands that change anything fail with a
  busy error; MCP reports it as JSON-RPC error -32003.
- In the app the run advances one slice per frame with a Stop button in
  the status bar. A run that adds a great many objects asks whether to
  continue; Stop and Escape end it.
- Commands that change state outside the documents, such as prefs.*,
  plugin.* and file.place.queue, are refused to scripts.

Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
2026-11-09 18:35:41 -08:00

45 lines
1.5 KiB
Rust

//! MCP tool definitions (names, descriptions, JSON Schemas) and their implementations on top of a
//! [`Backend`].
use serde_json::{Map, Value, json};
use vectorcraft_engine::cmd::fileio::{ARTBOARD_PARAMS, FORMATS, OPEN_EXTS, SAVE_FORMATS, format, writable_format};
use crate::backend::Backend;
mod definitions;
mod dispatch;
mod handlers;
pub use definitions::tool_definitions;
#[cfg(test)]
pub(crate) use dispatch::MAX_COMMAND_BATCH_STEPS;
pub use dispatch::call_tool;
pub(crate) use dispatch::check_tool_arguments;
/// The result of `tools/call`: MCP content blocks plus the `isError` flag.
#[derive(Clone, Debug, PartialEq)]
pub struct ToolResult {
pub content: Vec<Value>,
pub is_error: bool,
}
impl ToolResult {
pub fn text(t: impl Into<String>) -> Self {
Self { content: vec![json!({"type": "text", "text": t.into()})], is_error: false }
}
pub fn json(v: &Value) -> Self {
Self::text(serde_json::to_string_pretty(v).unwrap_or_default())
}
pub fn error(t: impl Into<String>) -> Self {
Self { content: vec![json!({"type": "text", "text": t.into()})], is_error: true }
}
pub fn to_value(&self) -> Value {
json!({"content": self.content, "isError": self.is_error})
}
/// The error's text, when the engine refused the call because a script is running.
pub(crate) fn script_busy(&self) -> Option<String> {
let text = self.content.first()?.get("text")?.as_str()?;
(self.is_error && text.ends_with(vectorcraft_engine::SCRIPT_BUSY)).then(|| text.to_string())
}
}