Rename SolveCraft to SolidCraft everywhere, with compatibility shims (#389)
* WIP deep rename (not for push) * WIP2 * WIP3 * WIP4 wxs fix * rustfmt * Rename the remaining identifiers in merged crash-report code Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RjGw4gA5ZvKrU8MQP6sBgT --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
27415e4dd7
commit
296683229d
464 files changed
+3408
-2956
No files matched your search
@@ -5,8 +5,8 @@ use std::net::{TcpStream, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solvecraft_engine::Session;
|
||||
use solvecraft_engine::render::{StandardView, render_png};
|
||||
use solidcraft_engine::Session;
|
||||
use solidcraft_engine::render::{StandardView, render_png};
|
||||
|
||||
/// Something that answers control-channel methods: `engine.execute`, `engine.commands`,
|
||||
/// `document.inspect`, `ui.view`, `ui.render`, `ui.screenshot`.
|
||||
@@ -20,8 +20,8 @@ pub trait Backend {
|
||||
/// Longest reply line accepted from the app.
|
||||
const MAX_REPLY: u64 = 64 << 20;
|
||||
|
||||
/// A running SolveCraft app reached through its loopback control port
|
||||
/// (`solvecraft --control PORT`).
|
||||
/// A running SolidCraft app reached through its loopback control port
|
||||
/// (`solidcraft --control PORT`).
|
||||
pub struct Remote {
|
||||
addr: String,
|
||||
conn: Option<(BufReader<TcpStream>, TcpStream)>,
|
||||
@@ -152,9 +152,9 @@ impl Backend for Headless {
|
||||
}
|
||||
"ui.render" => {
|
||||
let (w, h) = (dim(&p, "width", 1280.0), dim(&p, "height", 800.0));
|
||||
let mut cam = solvecraft_engine::view::home_camera(s);
|
||||
let mut cam = solidcraft_engine::view::home_camera(s);
|
||||
cam.set_view(self.view);
|
||||
let scene = solvecraft_engine::view::scene(s, &cam);
|
||||
let scene = solidcraft_engine::view::scene(s, &cam);
|
||||
let png = render_png(&scene, &cam, w, h).ok_or("render failed")?;
|
||||
let path = str_p("path").ok_or("missing `path`")?;
|
||||
std::fs::write(path, &png).map_err(|e| format!("{path}: {e}"))?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! SolveCraft MCP server.
|
||||
//! SolidCraft MCP server.
|
||||
//!
|
||||
//! JSON-RPC 2.0 over stdio (newline-delimited), implementing the MCP lifecycle, tools and
|
||||
//! resources. Tools map onto the control-channel methods (`engine.execute`, `document.inspect`,
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::io::{BufRead, Write};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
/// Longest accepted request line, shared with the control server.
|
||||
use solvecraft_engine::MAX_LINE;
|
||||
use solidcraft_engine::MAX_LINE;
|
||||
|
||||
use crate::backend::Backend;
|
||||
use crate::tools::{call_tool, check_arguments, tool_definitions};
|
||||
@@ -133,7 +133,7 @@ impl Server {
|
||||
Ok(json!({
|
||||
"protocolVersion": version,
|
||||
"capabilities": {"tools": {"listChanged": false}, "resources": {"listChanged": false}},
|
||||
"serverInfo": {"name": "solvecraft", "title": "SolidCraft", "version": env!("CARGO_PKG_VERSION")},
|
||||
"serverInfo": {"name": "solidcraft", "title": "SolidCraft", "version": env!("CARGO_PKG_VERSION")},
|
||||
"instructions": format!("{INSTRUCTIONS} Backend: {}.", self.backend.describe()),
|
||||
}))
|
||||
}
|
||||
@@ -149,16 +149,18 @@ impl Server {
|
||||
Ok(call_tool(self.backend.as_mut(), name, &args))
|
||||
}
|
||||
"resources/list" => Ok(json!({"resources": [
|
||||
{"uri": "solvecraft://design", "name": "Active design", "description": "document.inspect with measurements", "mimeType": "application/json"},
|
||||
{"uri": "solvecraft://commands", "name": "Command catalog", "description": "every command with its parameter docs", "mimeType": "application/json"},
|
||||
{"uri": "solidcraft://design", "name": "Active design", "description": "document.inspect with measurements", "mimeType": "application/json"},
|
||||
{"uri": "solidcraft://commands", "name": "Command catalog", "description": "every command with its parameter docs", "mimeType": "application/json"},
|
||||
]})),
|
||||
"resources/templates/list" => Ok(json!({"resourceTemplates": []})),
|
||||
"prompts/list" => Ok(json!({"prompts": []})),
|
||||
"resources/read" => {
|
||||
let uri = params.get("uri").and_then(Value::as_str).unwrap_or("");
|
||||
let v = match uri {
|
||||
"solvecraft://design" => self.backend.call("document.inspect", json!({"measure": true})),
|
||||
"solvecraft://commands" => self.backend.call("engine.commands", json!({})),
|
||||
// The solvecraft:// URIs are what earlier releases listed (compatibility shim,
|
||||
// docs/compatibility.md); clients that saved them still read the resource.
|
||||
"solidcraft://design" | "solvecraft://design" => self.backend.call("document.inspect", json!({"measure": true})),
|
||||
"solidcraft://commands" | "solvecraft://commands" => self.backend.call("engine.commands", json!({})),
|
||||
_ => return Err((RESOURCE_NOT_FOUND, format!("unknown resource `{uri}`"))),
|
||||
}
|
||||
.map_err(|e| (INTERNAL_ERROR, e))?;
|
||||
|
||||
+21
-5
@@ -55,7 +55,7 @@ fn handshake_and_tools_list() {
|
||||
let init =
|
||||
call(&mut s, 1, "initialize", json!({"protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": {"name": "t", "version": "0"}}));
|
||||
assert_eq!(init["result"]["protocolVersion"], "2025-03-26");
|
||||
assert_eq!(init["result"]["serverInfo"]["name"], "solvecraft");
|
||||
assert_eq!(init["result"]["serverInfo"]["name"], "solidcraft");
|
||||
assert!(init["result"]["capabilities"]["tools"].is_object());
|
||||
assert!(init["result"]["instructions"].as_str().unwrap().contains("headless"));
|
||||
// An unknown version gets ours.
|
||||
@@ -97,9 +97,9 @@ fn handshake_and_tools_list() {
|
||||
|
||||
let res = call(&mut s, 5, "resources/list", json!({}));
|
||||
assert_eq!(res["result"]["resources"].as_array().unwrap().len(), 2);
|
||||
let d = call(&mut s, 6, "resources/read", json!({"uri": "solvecraft://design"}));
|
||||
let d = call(&mut s, 6, "resources/read", json!({"uri": "solidcraft://design"}));
|
||||
assert!(d["result"]["contents"][0]["text"].as_str().unwrap().contains("timeline"));
|
||||
assert_eq!(call(&mut s, 7, "resources/read", json!({"uri": "solvecraft://nope"}))["error"]["code"], -32002);
|
||||
assert_eq!(call(&mut s, 7, "resources/read", json!({"uri": "solidcraft://nope"}))["error"]["code"], -32002);
|
||||
assert_eq!(call(&mut s, 8, "nope/nope", json!({}))["error"]["code"], -32601);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ fn agent_builds_box_fillet_hole() {
|
||||
assert!(img["data"].as_str().unwrap().starts_with("iVBORw0KGgo"), "not a PNG");
|
||||
|
||||
// Export, save, start over, open.
|
||||
let dir = std::env::temp_dir().join(format!("solvecraft-mcp-test-{}", std::process::id()));
|
||||
let dir = std::env::temp_dir().join(format!("solidcraft-mcp-test-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let step = dir.join("part.step");
|
||||
tool(&mut s, "export", json!({"path": step.to_string_lossy()}));
|
||||
@@ -165,7 +165,7 @@ fn agent_builds_box_fillet_hole() {
|
||||
let shot = dir.join("shot.png");
|
||||
tool(&mut s, "screenshot", json!({"path": shot.to_string_lossy(), "width": 64, "height": 64}));
|
||||
assert!(std::fs::metadata(&shot).unwrap().len() > 100);
|
||||
let design = dir.join("part.solvecraft");
|
||||
let design = dir.join("part.solidcraft");
|
||||
tool(&mut s, "save", json!({"path": design.to_string_lossy()}));
|
||||
tool(&mut s, "new_design", json!({"name": "Empty"}));
|
||||
assert_eq!(tool(&mut s, "measure", json!({}))["body_count"], 0);
|
||||
@@ -386,3 +386,19 @@ fn list_commands_filter_by_a_command_id_matches_ids() {
|
||||
assert!(ids.contains(&"sketch.delete"), "{ids:?}");
|
||||
assert!(ids.iter().all(|id| id.contains("sketch.delete")), "{} commands matched: {ids:?}", ids.len());
|
||||
}
|
||||
|
||||
/// Compatibility shims (docs/compatibility.md): a design saved before the rename (legacy
|
||||
/// extension and format tag) opens through MCP, and the legacy resource URIs still read.
|
||||
#[test]
|
||||
fn legacy_design_files_and_resource_uris() {
|
||||
let mut s = server();
|
||||
let saved = String::from_utf8(solidcraft_engine::io::write_design(&solidcraft_engine::doc::Document::default())).unwrap();
|
||||
assert!(saved.contains("\"solidcraft/"), "{saved}");
|
||||
let legacy = saved.replace("\"solidcraft/", "\"solvecraft/");
|
||||
let path = std::env::temp_dir().join(format!("solidcraft-mcp-legacy-{}.solvecraft", std::process::id()));
|
||||
std::fs::write(&path, legacy).unwrap();
|
||||
tool(&mut s, "open", json!({"path": path.to_string_lossy()}));
|
||||
let _ = std::fs::remove_file(&path);
|
||||
let d = call(&mut s, 1, "resources/read", json!({"uri": "solvecraft://design"}));
|
||||
assert!(d["result"]["contents"][0]["text"].as_str().unwrap().contains("timeline"));
|
||||
}
|
||||
@@ -214,17 +214,17 @@ pub fn tool_definitions() -> Vec<Value> {
|
||||
tool(
|
||||
"open",
|
||||
"Open a design",
|
||||
"Open a .solvecraft design file, a STEP file (.step/.stp: a new design whose bodies come from the file, as an \
|
||||
"Open a .solidcraft design file, a STEP file (.step/.stp: a new design whose bodies come from the file, as an \
|
||||
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 .solvecraft, .step/.stp, .3mf or .stl file")}), &["path"]),
|
||||
obj(json!({"path": string("Path to a .solidcraft, .step/.stp, .3mf or .stl file")}), &["path"]),
|
||||
false,
|
||||
true,
|
||||
),
|
||||
tool(
|
||||
"save",
|
||||
"Save the design",
|
||||
"Save the design as a .solvecraft file (to `path`, or to the file it came from).",
|
||||
"Save the design as a .solidcraft file (to `path`, or to the file it came from).",
|
||||
obj(json!({"path": string("Path to save to (default: the current file)")}), &[]),
|
||||
false,
|
||||
false,
|
||||
@@ -499,7 +499,7 @@ fn capture(b: &mut dyn Backend, a: &Map<String, Value>) -> Result<(Vec<u8>, Valu
|
||||
let keep = str_arg(a, "path").filter(|p| !p.trim().is_empty()).map(str::to_string);
|
||||
let path = keep.clone().unwrap_or_else(|| {
|
||||
let n = SHOT.fetch_add(1, Ordering::Relaxed);
|
||||
std::env::temp_dir().join(format!("solvecraft-mcp-{}-{n}.png", std::process::id())).to_string_lossy().to_string()
|
||||
std::env::temp_dir().join(format!("solidcraft-mcp-{}-{n}.png", std::process::id())).to_string_lossy().to_string()
|
||||
});
|
||||
let window = match str_arg(a, "source") {
|
||||
Some("model") => false,
|
||||
|
||||
Reference in new issue
Block a user