solidcraft --control PORT writes a random per-launch token to a user-only file (<data>/control/PORT.token; 0700/0600 on Unix, icacls on Windows; SOLIDCRAFT_CONTROL_TOKEN overrides) and refuses any connection whose first request lacks it. mcp --connect, the demo and packaging smoke clients send it. MCP execute/batch/open/save/export/screenshot are annotated destructive and open-world, new_design destructive. Docs: control-protocol.md, mcp.md, AGENTS.md, pending release note in releasing.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d9237d004e
commit
fe3717f2e4
19 files changed
+465
-53
No files matched your search
+37
-30
@@ -23,13 +23,33 @@ fn obj(props: Value, required: &[&str]) -> Value {
|
||||
o
|
||||
}
|
||||
|
||||
fn tool(name: &str, title: &str, desc: &str, schema: Value, read_only: bool, destructive: bool) -> Value {
|
||||
/// What a tool may do, for the client's approval prompts (MCP tool annotations). Clients that
|
||||
/// auto-approve on these hints must not be told less than the truth (#205).
|
||||
#[derive(Clone, Copy)]
|
||||
enum Effect {
|
||||
/// Only looks.
|
||||
ReadOnly,
|
||||
/// Changes the design in memory, undoably.
|
||||
Edits,
|
||||
/// Replaces the design in memory (unsaved changes are lost).
|
||||
Replaces,
|
||||
/// May read, write, overwrite or delete files on disk (any command, a file path).
|
||||
Files,
|
||||
}
|
||||
|
||||
fn tool(name: &str, title: &str, desc: &str, schema: Value, effect: Effect) -> Value {
|
||||
let (read_only, destructive, open_world) = match effect {
|
||||
Effect::ReadOnly => (true, false, false),
|
||||
Effect::Edits => (false, false, false),
|
||||
Effect::Replaces => (false, true, false),
|
||||
Effect::Files => (false, true, true),
|
||||
};
|
||||
json!({
|
||||
"name": name,
|
||||
"title": title,
|
||||
"description": desc,
|
||||
"inputSchema": schema,
|
||||
"annotations": {"title": title, "readOnlyHint": read_only, "destructiveHint": destructive, "idempotentHint": read_only, "openWorldHint": false},
|
||||
"annotations": {"title": title, "readOnlyHint": read_only, "destructiveHint": destructive, "idempotentHint": read_only, "openWorldHint": open_world},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -49,8 +69,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
substring `filter` over id, label, tab and panel (not the parameter docs, so a command id \
|
||||
finds that command rather than every command whose docs mention it).",
|
||||
obj(json!({"filter": string("Substring to match, e.g. \"sketch\", \"fillet\", \"pattern\"")}), &[]),
|
||||
true,
|
||||
false,
|
||||
Effect::ReadOnly,
|
||||
),
|
||||
tool(
|
||||
"execute",
|
||||
@@ -73,8 +92,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&["command"],
|
||||
),
|
||||
false,
|
||||
false,
|
||||
Effect::Files,
|
||||
),
|
||||
tool(
|
||||
"batch",
|
||||
@@ -95,8 +113,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&["commands"],
|
||||
),
|
||||
false,
|
||||
false,
|
||||
Effect::Files,
|
||||
),
|
||||
tool(
|
||||
"inspect_design",
|
||||
@@ -112,8 +129,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&[],
|
||||
),
|
||||
true,
|
||||
false,
|
||||
Effect::ReadOnly,
|
||||
),
|
||||
tool(
|
||||
"measure",
|
||||
@@ -121,8 +137,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
"Volume (mm³), surface area (mm²), centre of mass, bounding box and face/edge/vertex counts of one body or all \
|
||||
bodies, with totals.",
|
||||
obj(json!({"body": string("Body name (default: all bodies)")}), &[]),
|
||||
true,
|
||||
false,
|
||||
Effect::ReadOnly,
|
||||
),
|
||||
tool(
|
||||
"body_topology",
|
||||
@@ -130,8 +145,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
"The edges (index, midpoint, length, endpoints) and faces (index, area, centroid, plane normal) of a body. Use \
|
||||
an edge midpoint as the [x,y,z] point that picks the edge for fillet and chamfer.",
|
||||
obj(json!({"body": string("Body name, e.g. \"Body1\"")}), &["body"]),
|
||||
true,
|
||||
false,
|
||||
Effect::ReadOnly,
|
||||
),
|
||||
tool(
|
||||
"set_parameter",
|
||||
@@ -148,8 +162,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&["name", "value"],
|
||||
),
|
||||
false,
|
||||
false,
|
||||
Effect::Edits,
|
||||
),
|
||||
tool(
|
||||
"screenshot",
|
||||
@@ -171,8 +184,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&[],
|
||||
),
|
||||
true,
|
||||
false,
|
||||
Effect::Files,
|
||||
),
|
||||
tool(
|
||||
"export",
|
||||
@@ -187,8 +199,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&["path"],
|
||||
),
|
||||
false,
|
||||
false,
|
||||
Effect::Files,
|
||||
),
|
||||
tool(
|
||||
"crash_reports",
|
||||
@@ -205,18 +216,16 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
}),
|
||||
&[],
|
||||
),
|
||||
true,
|
||||
false,
|
||||
Effect::ReadOnly,
|
||||
),
|
||||
tool("undo", "Undo", "Undo the last change to the design.", obj(json!({}), &[]), false, false),
|
||||
tool("redo", "Redo", "Redo the last undone change.", obj(json!({}), &[]), false, false),
|
||||
tool("undo", "Undo", "Undo the last change to the design.", obj(json!({}), &[]), Effect::Edits),
|
||||
tool("redo", "Redo", "Redo the last undone change.", obj(json!({}), &[]), Effect::Edits),
|
||||
tool(
|
||||
"new_design",
|
||||
"New design",
|
||||
"Start a new, empty design (unsaved changes to the current one are discarded).",
|
||||
obj(json!({"name": string("Design name (default Untitled)")}), &[]),
|
||||
false,
|
||||
true,
|
||||
Effect::Replaces,
|
||||
),
|
||||
tool(
|
||||
"open",
|
||||
@@ -225,8 +234,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
Import base feature) or a 3MF/STL mesh (.3mf/.stl: mesh bodies). Replaces the current design; insert into it \
|
||||
instead with the file.insert_step (STEP) or file.insert_mesh (3MF/STL) commands.",
|
||||
obj(json!({"path": string("Path to a .solidcraft, .step/.stp, .3mf or .stl file")}), &["path"]),
|
||||
false,
|
||||
true,
|
||||
Effect::Files,
|
||||
),
|
||||
tool(
|
||||
"save",
|
||||
@@ -236,8 +244,7 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
json!({"path": string("Path to save to (default: the current file)"), "format": {"type": "string", "enum": ["container", "json"], "description": "Design encoding (default: JSON for .json, container otherwise)"}}),
|
||||
&[],
|
||||
),
|
||||
false,
|
||||
false,
|
||||
Effect::Files,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user