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:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-12 17:17:01 -04:00
1 parent 27415e4dd7
commit 296683229d
464 files changed
+3408 -2956

No files matched your search

+6 -6
View File
@@ -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 -1
View File
@@ -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`,
+8 -6
View File
@@ -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
View File
@@ -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"));
}
+4 -4
View File
@@ -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,