Chapters

Programs on 3DCraft

Build a program of your own on 3DCraft's engine: drive it from any language over serve or the window's control channel, give it to an AI agent over MCP, or link the Rust library.

Everything 3DCraft does is a command. So a gear generator, a furniture configurator or a CAD interface with buttons can each be a separate program that sends 3DCraft's commands. 3DCraft is open source under the MIT licence: you may build on it, also in a program you do not publish, as long as you keep its copyright and licence notice with it.

Before you write a program, see whether a script does what you need: a list of commands in a file, run by craft3d-cli run (The command line and scripts). And a design that others should set by its sizes may only need to be a Jig (Making Jigs).

Four ways in

Way For It speaks
craft3d-cli serve A program in any language, with no window JSON lines on standard input and output
The window's control channel A program that drives the window a person is watching JSON lines over a local network connection, with a token
craft3d-cli mcp An AI agent (Install › Connect an AI agent) MCP over standard input and output
The Rust library craft3d-engine A program written in Rust Function calls

All four run the same commands, with the same parameters and the same results. The full description of the protocols, every method and every limit is in control-protocol.md.

serve: JSON lines

craft3d-cli serve reads one request per line on its standard input and writes one reply per line on its standard output. Each request has an id, a method and its params; the reply carries the same id:

{"id": 1, "method": "engine.execute", "params": {"command": "file.new", "params": {"units": "mm"}}}
{"id": 1, "ok": true, "result": {"document": 0, "units": "mm"}, "ms": 0.1}
{"id": 2, "method": "engine.execute", "params": {"command": "feature.block", "params": {"size": ["40 mm", "20 mm", "10 mm"], "name": "Block"}}}
{"id": 2, "ok": true, "result": {"id": "F1", "name": "Block", "status": "ok"}, "ms": 0.3}
{"id": 3, "method": "engine.execute", "params": {"command": "feature.block", "params": {"sise": ["40 mm", "20 mm", "10 mm"]}}}
{"id": 3, "ok": false, "error": "feature.block: unknown parameter `sise`; its parameters are: size, position, axis, angle, name", "ms": 0.0}

The methods are few, because everything goes through engine.execute with a command:

Method Does
engine.execute Runs one command: {"command": "feature.block", "params": {...}}. The reply has result or error, and ms, how long it took
engine.commands The commands with their parameters and summaries (filter, id, brief as for command_list)
batch Runs up to 256 commands in order: {"steps": [{"command": …, "params": …}, …]}
methods The methods, their parameters and the limits

From a shell, three requests at once:

printf '%s\n' \
  '{"id":1,"method":"engine.execute","params":{"command":"file.new","params":{"units":"mm"}}}' \
  '{"id":2,"method":"batch","params":{"steps":[{"command":"feature.block","params":{"size":["40 mm","20 mm","10 mm"]}},{"command":"document.measure"}]}}' \
  '{"id":3,"method":"engine.execute","params":{"command":"file.export","params":{"path":"box.step"}}}' | \
  craft3d-cli serve --automation-write-root /work/project

A program may read and write files only beneath the folders serve was started with: --automation-read-root DIR and --automation-write-root DIR. Without them it reads and writes none. --recovery-dir DIR lets it autosave and recover documents; without it, serve keeps no autosave.

The window's control channel

A program can also drive the window a person is watching: open a file, set a Jig's parameter, fly the camera, take a picture of the window. It speaks the same JSON lines, over a connection to the window on the same computer, plus the window's own methods (ui.get, ui.set, ui.fit, ui.screenshot, …).

The connection is guarded:

--recovery-dir none keeps a window a script starts out of the person's recovery bar: what it leaves behind is not offered for recovery at the next start. A program that drives the person's own window should not need the token at all: craft3d-cli mcp --bridge finds the window and proves it holds the token for you. control-protocol.md › Authentication describes the token and the challenge that keeps it off the connection.

MCP

craft3d-cli mcp is the MCP server: an AI agent's tool starts it, and the agent gets the tools listed in Install › What the agent gets. With --bridge it passes every message to the window the person allowed agents in; without it, it keeps a document of its own (headless). Any program that speaks MCP can use it the same way an agent does.

The Rust library

The engine is the crate craft3d-engine. A Session holds the open documents and runs commands by id, with JSON parameters, and answers with JSON (here the session is called cad):

use craft3d_engine::{Session, Workspace};
use serde_json::json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut cad = Session::new(Workspace::unrestricted());
    cad.execute("file.new", &json!({"units": "mm"}))?;
    cad.execute("variable.set", &json!({"name": "width", "expr": "60 mm"}))?;
    cad.execute("feature.block", &json!({"size": ["width", "40 mm", "8 mm"], "name": "Plate"}))?;
    let measured = cad.execute("document.measure", &json!({"feature": "Plate"}))?;
    println!("volume: {} mm³", measured["volume"]);
    cad.execute("file.export", &json!({"path": "plate.step"}))?;
    Ok(())
}

Add it to your program's Cargo.toml from the source repository, at the version you build against:

[dependencies]
craft3d-engine = { git = "https://github.com/johancoppens/3dcraft", tag = "v0.1.1" }
serde_json = "1"

Text in a built-in font needs the font files, which are not compiled into the engine: point the environment variable CRAFT3D_FONTS at a folder that holds them. An installed 3DCraft has one:

In a checkout of the source, cargo xtask fonts fetches them into assets/fonts.

Workspace::unrestricted() lets commands read and write anywhere the program may. To keep them to one folder, give Workspace { read: Access::Root(dir.clone()), write: Access::Root(dir) } instead (Access is in craft3d_engine too). A command that cannot do what was asked returns Err with a message that names the command and the parameter; it never panics. Session::commands() lists every command with its parameters and summary.

The engine is the same code the window and craft3d-cli run, and so is every rule this guide describes. How it is built, and the rules a change to it must follow, are in the source repository's developer documentation, starting at docs/README.md.

What a command answers

Every command answers with JSON. These rules hold for all four ways in; where one way differs, the rule says so.

Next: Troubleshooting.

This page on GitHub