{"name":"io.github.gchen19/ankusdrive","slug":"gchen19-ankusdrive","title":"AnkusDrive","description":"Drive FreeCAD from an agent: CAD modeling, drawings, FEM, CFD, EM, and multiphysics simulation.","url":"https://mcp.market/server/gchen19-ankusdrive","rating":null,"grade":"A","score":85,"certified":false,"status":"active","category":"productivity","tags":["productivity","ai"],"presence":{"score":25,"stars":5,"forks":0,"downloads_week":null,"last_push_at":"2026-09-19T19:20:39.000Z","license":"Apache-2.0"},"uptime":null,"claimed":false,"transport":"pypi","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/gchen19/AnkusDrive","website":"https://github.com/gchen19/AnkusDrive","version":"0.5.5","remotes":[],"packages":[{"registryType":"pypi","registryBaseUrl":"https://pypi.org","identifier":"ankusdrive","version":"0.5.5","runtimeHint":"uvx","transport":{"type":"stdio"},"packageArguments":[{"value":"mcp","type":"positional"}],"environmentVariables":[{"description":"Absolute path to FreeCAD 1.1's freecadcmd. Leave unset to auto-discover on PATH and standard install locations.","format":"filepath","name":"ANKUSDRIVE_FREECADCMD"}]}],"tools":[{"name":"add_bearing","description":"Add a deep-groove ball bearing as an assembly *envelope* solid: an annular ring (outer-diameter cylinder minus bore cylinder) of the given width, axis along +Z. Balls/races are not modeled — this is the fit envelope a coordinator needs to size the shaft, the housing bore, and the shoulder spacing.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_fastener","description":"Add a standard ISO metric fastener (screw / bolt / nut / washer) as a solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_gear","description":"Add an involute spur gear (FreeCAD's core involute generator), extruded to a solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_part","description":"Add a part to an assembly via App::Link.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_primitive","description":"Add a primitive to the active document.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_pulley","description":"Add a timing-belt (or V) pulley as a static solid. Axis is +Z; toothed belt face spans z in [0, width].","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_rack","description":"Add a linear gear rack (a spur gear's straight counterpart) as a solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_rib","description":"Add a reinforcing rib/web inside a PartDesign Body by thickening an OPEN sketch profile into a wall that fuses with the body's surrounding material.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_sketch_constraint","description":"Add a constraint to a sketch.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_sketch_external","description":"Project an external edge/face/vertex into a sketch as construction geometry.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_sketch_geometry","description":"Append geometric primitives to a sketch.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_spring","description":"Add a helical compression spring: a round wire swept along a cylindrical helix.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_sprocket","description":"Add a roller-chain sprocket (ISO 606 / ANSI), built as a static solid plate.","write_action":false,"price_micros":0,"input_schema":null},{"name":"add_thread","description":"Generate a REAL helical ISO-style 60-degree thread as a static solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"annotate_face","description":"Declare the semantic ROLE of a face — what it is FOR — so later edits can be checked against intent instead of re-derived from raw geometry. The role binds to the face's stable f_* tag and persists in the .FCStd as a JSON property bag (same mechanism as publish_interface); it survives save/reopen. Once declared, check_airtight_path accepts the role/name directly (e.g. inlet=\"inlet\").","write_action":false,"price_micros":0,"input_schema":null},{"name":"assembly_lock","description":"Write a lockfile recording each component's content hash, published- interface hash, and mate dependencies — the provenance baseline a coordinator uses to detect drift across a team. Call after a clean merge. lockfile defaults to <manifest>.lock.json. Returns {lockfile, components}.","write_action":true,"price_micros":0,"input_schema":null},{"name":"bom_extract","description":"Walk an assembly and return [{part, count, total_volume_mm3, total_mass_kg?}, ...] grouped by source (component file + object), NOT the bare object name, so two distinct components both named \"Box\" don't collapse into one row. density (kg/mm³) is optional.","write_action":false,"price_micros":0,"input_schema":null},{"name":"boolean_op","description":"Boolean operation on two existing objects, referenced by their handles.","write_action":false,"price_micros":0,"input_schema":null},{"name":"bounding_box","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"catalog_check","description":"Is this exact part something you can buy off the shelf?","write_action":false,"price_micros":0,"input_schema":null},{"name":"catalog_nearest","description":"Snap a desired standard part to the nearest one that actually exists.","write_action":false,"price_micros":0,"input_schema":null},{"name":"catalog_search","description":"Browse the off-the-shelf catalog: which standard components actually EXIST, in which sizes, in which stocked lengths. Call it while designing, before you commit geometry to a number — it is the difference between a design somebody can build and one that needs a special.","write_action":false,"price_micros":0,"input_schema":null},{"name":"chamfer_edges","description":"Chamfer (bevel) specific edges of a shaped Part object — the direct-shape counterpart to fillet_edges.","write_action":false,"price_micros":0,"input_schema":null},{"name":"check_airtight_path","description":"Functional check for an enclosed-flow part (a vacuum adapter, manifold, duct): is there a single connected void joining the inlet to the outlet, bounded by solid everywhere else? This catches what `check_shape` cannot — a watertight solid can still have a blocked flow path or a hidden leak. Inspection only: measures, returns no handle, mutates nothing.","write_action":false,"price_micros":0,"input_schema":null},{"name":"check_shape","description":"Check a shaped object's geometry validity and topology before you build on it. Inspection only — measures, returns no handle, mutates nothing, and does NOT auto-repair. Use it as a guard after booleans/sweeps/imports to confirm you have one clean watertight solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"classify_face_sides","description":"Inside-vs-outside topology: for every face, decide whether its outward side opens into an enclosed cavity (wetted) or ambient (exterior). Answers the \"which faces are inside the airflow path\" question from issue #19 and suggests a role per face. Inspection only; returns no handle, mutates nothing.","write_action":false,"price_micros":0,"input_schema":null},{"name":"close_document","description":"Close a document by name (or 'active' for the currently active one). Frees its objects and invalidates any handles into the closed doc. Returns {closed, invalidated_handles}.","write_action":false,"price_micros":0,"input_schema":null},{"name":"close_sketch","description":"Recompute and report DOF status. Returns {geometry_count, constraint_count, open_vertices, fully_constrained}.","write_action":false,"price_micros":0,"input_schema":null},{"name":"close_workspace","description":"Shut down a workspace's worker and free its pool slot. All of that workspace's documents, unsaved changes, and handles are lost. Closing the workspace you are currently in returns you to the \"default\" workspace. The default workspace can be closed too (its worker respawns clean on next use). Returns {closed: <bool>, workspace: <name>, current: <name>}.","write_action":false,"price_micros":0,"input_schema":null},{"name":"component_contract_check","description":"Builder-side contract gate for one component (issue #169) — the local half of the gate merge_assembly re-runs at fan-in. Any MCP host builds a component with the full AnkusDrive tool surface, then calls this on its part BEFORE saving, against its `builder brief` (a ankusdrive.builder_brief/1 slice), and repairs any failing check. Catching a violation here turns the expensive loop (build → merge →","write_action":false,"price_micros":0,"input_schema":null},{"name":"copy_shape","description":"Duplicate a shaped object as an INDEPENDENT static solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"declare_intent","description":"Record the functional invariants a part must keep satisfying, so they can be re-checked after every edit (see verify_intent). Persists in the .FCStd as a JSON property bag (AD_Intent); one contract per part — re-declaring replaces.","write_action":false,"price_micros":0,"input_schema":null},{"name":"designation_check","description":"Gate: every purchased part on this assembly must be orderable.","write_action":false,"price_micros":0,"input_schema":null},{"name":"draft","description":"Apply a draft angle to faces (for moldability).","write_action":false,"price_micros":0,"input_schema":null},{"name":"engrave_text","description":"Engrave (cut) or emboss (add) extruded text onto a planar face of a solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"envelope_check","description":"Keep-out gate: assert each named part's world bounding box stays inside its declared envelope. envelopes maps a part's link name (or label) to {\"min\": [x,y,z], \"max\": [x,y,z]} in the assembly frame. Returns violations [{part, axis, got, allowed}, ...]; empty means everything is within its box.","write_action":false,"price_micros":0,"input_schema":null},{"name":"export_shape","description":"Export a shape to STEP/IGES/BREP/STL. Format inferred from path extension.","write_action":false,"price_micros":0,"input_schema":null},{"name":"fillet_edges","description":"Fillet edges of a shaped Part object. `edges` accepts tags (e_... from list_edges, preferred), 'EdgeN' strings, or bare 1-based ints. `radius` is the blend radius in mm (> 0).","write_action":false,"price_micros":0,"input_schema":null},{"name":"get_object","description":"Dump a handle's properties + shape stats. Useful when no dedicated tool exposes what you need.","write_action":false,"price_micros":0,"input_schema":null},{"name":"helix","description":"Generate a Part::Helix curve. radius, pitch, height in mm. angle (deg) is the cone angle (0 = cylindrical helix, >0 = conical).","write_action":false,"price_micros":0,"input_schema":null},{"name":"hole","description":"Drill a parametric Hole from a sketch (one or more circles).","write_action":false,"price_micros":0,"input_schema":null},{"name":"interface_align_check","description":"Gate: verify declared interface pairs coincide in world space — the \"do the OTHER interfaces line up?\" check for multi-interface mates. After the primary mate seats a part, this confirms its secondary interfaces (a second bolt pattern, a bore axis) actually meet the parent's.","write_action":false,"price_micros":0,"input_schema":null},{"name":"interference_check","description":"Pairwise interference: compute volume of intersection between every pair of parts. Returns [{a, b, interference_mm3}, ...] descending by volume. Empty list = no interference.","write_action":false,"price_micros":0,"input_schema":null},{"name":"linear_pattern","description":"Repeat a PartDesign feature linearly along a direction.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_assembly_parts","description":"List parts of an assembly: name, type, linked-target name, position, volume.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_documents","description":"List all open documents: [{name, label, file_path, dirty, active, object_count}, ...].","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_edges","description":"List all edges of a shaped object with stable tags + descriptors.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_face_roles","description":"Read back the semantic face roles declared on a part (see annotate_face).","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_faces","description":"List all faces of a shaped object with stable tags + geometric descriptors.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_objects","description":"List objects in the active document. Returns [{name, type, label}, ...].","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_thread_options","description":"Discover the COUPLED ThreadType / ThreadSize enums on the hole tool.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_workspaces","description":"List the live workspaces (freecadcmd processes) and the pool limits. Each entry is {name, alive, idle_s, current}. Use to see who is holding a slot before claiming or closing one. Returns {current, max, idle_reap_s, workspaces: [...]}.","write_action":false,"price_micros":0,"input_schema":null},{"name":"loft","description":"Loft (additively) between two or more sketches.","write_action":false,"price_micros":0,"input_schema":null},{"name":"make_assembly","description":"Create an App::Part container to hold linked parts. Returns {handle, name}.","write_action":true,"price_micros":0,"input_schema":null},{"name":"make_body","description":"Create a PartDesign Body. Subsequent sketches/features go inside it.","write_action":true,"price_micros":0,"input_schema":null},{"name":"make_datum_plane","description":"Create a Datum Plane in a Body.","write_action":true,"price_micros":0,"input_schema":null},{"name":"make_sketch","description":"Create a sketch in a Body, attached to a plane.","write_action":true,"price_micros":0,"input_schema":null},{"name":"mass_properties","description":"Mass properties of a shaped object: volume (mm³), surface area (mm²), centroid, bounding box, inertia tensor. If density (kg/mm³) is given, also returns mass (kg). Steel = 7.9e-6, aluminum = 2.7e-6, ABS = 1.05e-6.","write_action":false,"price_micros":0,"input_schema":null},{"name":"measure_angle","description":"Angle (degrees) between two planar faces or two straight edges.","write_action":false,"price_micros":0,"input_schema":null},{"name":"measure_distance","description":"Minimum distance between two entities, in mm. The workhorse measurement tool: lets a blind agent verify gaps, clearances, and contact.","write_action":false,"price_micros":0,"input_schema":null},{"name":"merge_assembly","description":"Construct-up an assembly from a manifest JSON (the coordinator's one call): create the doc, link each component by file path, place it, recompute, and run the gates. Component files resolve relative to the manifest's directory; links auto-reload, so re-running picks up updated components (deterministic, idempotent).","write_action":false,"price_micros":0,"input_schema":null},{"name":"min_clearance","description":"Closest approach between two solids — the measured gap, richer than the binary interference_check. `a` and `b` are object handles. All lengths mm, volumes mm³.","write_action":false,"price_micros":0,"input_schema":null},{"name":"mirrored","description":"Mirror a PartDesign feature across a plane.","write_action":false,"price_micros":0,"input_schema":null},{"name":"new_document","description":"Create a new FreeCAD document and make it active. Returns {doc: <name>}.","write_action":true,"price_micros":0,"input_schema":null},{"name":"open_document","description":"Open an existing .FCStd file, make it active. Returns doc name and object list.","write_action":false,"price_micros":0,"input_schema":null},{"name":"oring_groove","description":"Compute a static O-ring gland (groove) and optionally cut it into a face.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pad","description":"Pad a sketch by `length` mm. symmetric=True extrudes both directions.","write_action":false,"price_micros":0,"input_schema":null},{"name":"partdesign_chamfer","description":"PartDesign Chamfer on edges of a feature in a Body. `edges` accepts e_* tags or 'EdgeN' index strings; `size` is the chamfer leg in mm (> 0).","write_action":false,"price_micros":0,"input_schema":null},{"name":"partdesign_fillet","description":"PartDesign Fillet on edges of a feature in a Body. `edges` accepts e_* tags or 'EdgeN' index strings; `radius` in mm (> 0).","write_action":false,"price_micros":0,"input_schema":null},{"name":"ping","description":"Check that the FreeCAD worker is alive. Returns 'pong' on success.","write_action":false,"price_micros":0,"input_schema":null},{"name":"pocket","description":"Subtract a pad of `length` mm from the body. through_all ignores length.","write_action":false,"price_micros":0,"input_schema":null},{"name":"polar_pattern","description":"Repeat a PartDesign feature around an axis.","write_action":false,"price_micros":0,"input_schema":null},{"name":"publish_interface","description":"Record a named interface frame on a component so other parts can mate to it — the published \"here is where you bolt to me, and how it's oriented\".","write_action":true,"price_micros":0,"input_schema":null},{"name":"query_faces","description":"Filter faces by a structured predicate. Returns matching descriptors.","write_action":false,"price_micros":0,"input_schema":null},{"name":"register_handle","description":"Register an existing FreeCAD object into the AnkusDrive handle table. Use after run_script (when auto_register=False) or after open_document to bring objects into the handle ecosystem so subsequent tool calls accept them via handle.","write_action":false,"price_micros":0,"input_schema":null},{"name":"resolve_edge","description":"Resolve an edge tag to the current EdgeN index. Errors on miss or ambiguity.","write_action":false,"price_micros":0,"input_schema":null},{"name":"resolve_face","description":"Resolve a face tag to the current FaceN index. Errors on miss or ambiguity.","write_action":false,"price_micros":0,"input_schema":null},{"name":"restart_worker","description":"Kill the current workspace's FreeCAD worker process and spawn a fresh one. Use when the worker is wedged (e.g. App.ActiveDocument desynced from internal state). All open documents, unsaved changes, and handles in THIS workspace are lost — save first if needed. Other workspaces are untouched. Returns {restarted: True, workspace: <name>, freecad: [...]}.","write_action":true,"price_micros":0,"input_schema":null},{"name":"revolve","description":"Revolve a sketch around a body origin axis ('X'|'Y'|'Z') by `angle` deg.","write_action":false,"price_micros":0,"input_schema":null},{"name":"run_script","description":"Escape hatch: execute Python in the worker with App/Part/ObjectsFem in scope.","write_action":true,"price_micros":0,"input_schema":null},{"name":"save_document","description":"Save the active document to the given .FCStd path.","write_action":false,"price_micros":0,"input_schema":null},{"name":"scale_shape","description":"Scale a shape uniformly or per-axis, baking a fresh static solid.","write_action":false,"price_micros":0,"input_schema":null},{"name":"section_view","description":"Cut a solid with a plane and return the cross-section it exposes. This is the best way to \"see inside\" a part blind: it measures the cut area and its extent, and can optionally emit the section outline as a new object for rendering/export. Units: mm (lengths), mm^2 (areas).","write_action":false,"price_micros":0,"input_schema":null},{"name":"set_active_document","description":"Switch the active document by name (the value returned from new_document/open_document).","write_action":false,"price_micros":0,"input_schema":null},{"name":"set_property","description":"Set a single property by name on an object. Coerces lists → Vector for Vector properties; other values pass through.","write_action":false,"price_micros":0,"input_schema":null},{"name":"set_visibility","description":"Override an object's persistent Visibility flag. By default save_document auto-hides producer-inputs (the Base/Tool of a Cut, features inside a Body) so the re-opened doc shows just the final composition. Use this to override — e.g. to keep a reference primitive visible next to a derived part. Note that the next save_document with visibility_hygiene=True (the default) may re-hide it; pass visibili","write_action":false,"price_micros":0,"input_schema":null},{"name":"shell_solid","description":"Hollow a raw Part solid into a thin-walled shell (the direct-shape counterpart to `thickness`, which only works on PartDesign bodies).","write_action":false,"price_micros":0,"input_schema":null},{"name":"standard_part_designate","description":"The canonical, orderable designation of a purchased standard part — the string a buyer can actually quote against: \"ISO 4762 M4×12 A2\", \"608-2RS\", \"AS568-214 NBR70\". A BOM row that reads \"SocketHeadCapScrew\" is not a buyable line; this is what turns it into one.","write_action":false,"price_micros":0,"input_schema":null},{"name":"sweep","description":"Sweep a profile sketch along a spine sketch (additive pipe).","write_action":false,"price_micros":0,"input_schema":null},{"name":"thickness","description":"Hollow out a solid into a shell.","write_action":false,"price_micros":0,"input_schema":null},{"name":"transaction_abort","description":"Roll back the most recent open transaction. Implementation note: FreeCAD 1.1 headless `abortTransaction` is unreliable, so the worker commits then undoes — net effect is a clean rollback.","write_action":false,"price_micros":0,"input_schema":null},{"name":"transaction_commit","description":"Commit the most recent open transaction; changes are kept.","write_action":false,"price_micros":0,"input_schema":null},{"name":"transaction_open","description":"Begin an undoable transaction on the active document. Pair with transaction_commit (keep the changes) or transaction_abort (roll back). Transactions nest — the most-recent open is committed/aborted first.","write_action":false,"price_micros":0,"input_schema":null},{"name":"transform","description":"Move and/or rotate an existing object in place — first-class replacement for hand-poking an object's Placement via set_property.","write_action":false,"price_micros":0,"input_schema":null},{"name":"use_workspace","description":"Claim (creating if needed) an isolated workspace and make it the target of subsequent tool calls. Each workspace is its own freecadcmd process with its own ActiveDocument and handle registry — handles do NOT cross workspaces. This is how concurrent agents share one MCP server without clobbering each other's documents: each agent calls use_workspace with a unique name once, up front.","write_action":false,"price_micros":0,"input_schema":null},{"name":"validate_manifest","description":"Validate a manifest WITHOUT building it (the cheap front door, RFC §11.7). Structural + cross-reference checks a JSON shape can't enforce: every component has exactly one of file/manifest/library; a library carries a tool; every instance references a known component; every mate/check references a known instance; a present `schema` is the known version (\"ankusdrive.manifest/1\").","write_action":false,"price_micros":0,"input_schema":null},{"name":"verify_contract","description":"Build-time self-check of a component against its manifest slice (RFC §11.3): a builder calls this on its OWN part before save, so a contract violation is caught locally and cheaply instead of after a fan-in merge (build → merge → gate-fail → rebuild becomes build → self-check → fix). Never raises on a failing check (a failure is a passed=False row), so it is safe to call in a loop. Inspection only","write_action":false,"price_micros":0,"input_schema":null},{"name":"verify_feature","description":"Compare a feature's actual volume change against an expected signed delta. Run after each subtractive/additive operation to catch silent failures — Pocket on a curved surface that under-cut, Hole that drilled outside the body, Cut whose Tool didn't intersect the Base.","write_action":false,"price_micros":0,"input_schema":null},{"name":"verify_intent","description":"Re-run every invariant declared with declare_intent — the regression gate to run after each edit. Composes check_shape / check_airtight_path / face-role resolution; never raises on a failing invariant (a failure is a passed=False row), so it is safe to call in a loop. Inspection only; mutates nothing.","write_action":false,"price_micros":0,"input_schema":null},{"name":"version","description":"Return FreeCAD and bundled Python versions from the worker.","write_action":false,"price_micros":0,"input_schema":null}],"scan":{"score":85,"grade":"A","scanned_at":"2026-09-19T20:03:46.094Z","report":{"scannerVersion":"0.1.5","scannedAt":"2026-09-19T20:03:46.075Z","components":{"code":{"score":20,"max":25,"notes":["104 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":12,"max":15,"notes":["local package, no credentials required"]},"maintenance":{"score":15,"max":15,"notes":["last push 0 days ago"]},"identity":{"score":8,"max":10,"notes":["registry namespace matches repository owner","GitHub account older than a year"]}},"findings":[{"id":"exec.eval","severity":"medium","component":"code","title":"eval / new Function used","evidence":"ankusdrive-0.5.5/ankusdrive/experiment.py: …led agent round on it? The multi-agent eval (tests/test_multiagent_m2.py) bills real …"}],"inputs":{"packages":[{"registryType":"pypi","identifier":"ankusdrive","version":"0.5.5","found":true,"license":"Apache-2.0","dependencyCount":19,"publishedAt":"2026-09-14T05:24:58.699108Z","repositoryUrl":"https://github.com/gchen19/AnkusDrive"}],"repo":{"found":true,"owner":"gchen19","repo":"AnkusDrive","archived":false,"pushedAt":"2026-09-19T19:20:39Z","stars":5,"forks":0,"openIssues":11,"ownerType":"User","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/4622700?v=4","ownerCreatedAt":"2013-06-05T16:33:15Z","license":"Apache-2.0"},"icon":{"url":"https://avatars.githubusercontent.com/u/4622700?v=4&s=128","source":"registry"},"presence":{"stars":5,"forks":0,"downloadsWeek":null,"license":"Apache-2.0","lastPushAt":"2026-09-19T19:20:39.000Z","score":25}}}},"grade_history":[],"reviews":[]}