mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 14:32:36 +00:00
MCP bridge: expose all 16 methods live + fix optional-param required-ness
- snaporca_mcp_bridge.py: _param_schema maps array/object types + carries per-param description; _FALLBACK_TOOLS expanded slice-1 -> full 16-method surface (socket-down at client startup still shows the whole toolset) - describe_tools: give genuinely-optional params a default (body -1, profile [], shell.face -1, extrude.distance2 0 / up_to_face -1) so the bridge no longer marks them required (draft.face/edge/path/a/b/reference stay required) Product code byte-identical to snaporca (md5 match). orca_cad slicer builds clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b93b9d558c
commit
f341ee2677
@@ -104,11 +104,11 @@ json describe_tools()
|
||||
json{{"name", "height"}, {"type", "number"}, {"unit", "mm"}, {"default", 20}, {"min", 0.01}},
|
||||
json{{"name", "distance"}, {"type", "number"}, {"unit", "mm"}, {"default", 10}, {"min", 0.01}},
|
||||
json{{"name", "plane"}, {"type", "string"}, {"enum", json::array({"XY", "XZ", "YZ"})}, {"default", "XY"}},
|
||||
json{{"name", "profile"}, {"type", "array"}, {"description", "optional closed contour [[x,y],...] in plane mm; overrides width/height"}},
|
||||
json{{"name", "profile"}, {"type", "array"}, {"default", json::array()}, {"description", "optional closed contour [[x,y],...] in plane mm; overrides width/height"}},
|
||||
json{{"name", "boolean"}, {"type", "string"}, {"enum", json::array({"new", "union", "subtract", "intersect"})}, {"default", "new"}},
|
||||
json{{"name", "end"}, {"type", "string"}, {"enum", json::array({"blind", "symmetric", "two_sided", "through_all", "up_to_face"})}, {"default", "blind"}},
|
||||
json{{"name", "distance2"},{"type", "number"}, {"unit", "mm"}, {"description", "second-side depth when end=two_sided"}},
|
||||
json{{"name", "up_to_face"},{"type", "integer"}, {"description", "target face id (query_topology on the last body) when end=up_to_face"}},
|
||||
json{{"name", "distance2"},{"type", "number"}, {"unit", "mm"}, {"default", 0}, {"description", "second-side depth when end=two_sided (else falls back to distance)"}},
|
||||
json{{"name", "up_to_face"},{"type", "integer"}, {"default", -1}, {"description", "target face id (query_topology on the last body) when end=up_to_face"}},
|
||||
json{{"name", "taper"}, {"type", "number"}, {"unit", "deg"}, {"default", 0}, {"description", "draft/taper of the side wall"}},
|
||||
json{{"name", "flip"}, {"type", "boolean"}, {"default", false}},
|
||||
})}},
|
||||
@@ -120,20 +120,20 @@ json describe_tools()
|
||||
json{{"name", "axis"}, {"type", "integer"}, {"enum", json::array({0, 1})}, {"default", 0}},
|
||||
json{{"name", "flip"}, {"type", "boolean"}, {"default", false}},
|
||||
json{{"name", "plane"}, {"type", "string"}, {"enum", json::array({"XY", "XZ", "YZ"})}, {"default", "XY"}},
|
||||
json{{"name", "profile"}, {"type", "array"}, {"description", "optional closed contour [[x,y],...] in plane mm; overrides width/height"}},
|
||||
json{{"name", "profile"}, {"type", "array"}, {"default", json::array()}, {"description", "optional closed contour [[x,y],...] in plane mm; overrides width/height"}},
|
||||
json{{"name", "boolean"}, {"type", "string"}, {"enum", json::array({"new", "union", "subtract", "intersect"})}, {"default", "new"}},
|
||||
})}},
|
||||
json{{"name", "fillet"}, {"summary", "Round a measured edge of a body (edge id from query_topology on that body)."},
|
||||
{"params", json::array({
|
||||
json{{"name", "edge"}, {"type", "integer"}},
|
||||
json{{"name", "radius"}, {"type", "number"}, {"unit", "mm"}, {"default", 1}, {"min", 0.01}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"description", "target body; omit for the last body. edge id is resolved against THIS body."}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"default", -1}, {"description", "target body; omit for the last body. edge id is resolved against THIS body."}},
|
||||
})}},
|
||||
json{{"name", "chamfer"}, {"summary", "Chamfer a measured edge of a body (edge id from query_topology on that body)."},
|
||||
{"params", json::array({
|
||||
json{{"name", "edge"}, {"type", "integer"}},
|
||||
json{{"name", "distance"}, {"type", "number"}, {"unit", "mm"}, {"default", 1}, {"min", 0.01}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"description", "target body; omit for the last body. edge id is resolved against THIS body."}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"default", -1}, {"description", "target body; omit for the last body. edge id is resolved against THIS body."}},
|
||||
})}},
|
||||
json{{"name", "hole"}, {"summary", "Drill a circular hole into the current body at (x,y) on a plane."},
|
||||
{"params", json::array({
|
||||
@@ -160,19 +160,19 @@ json describe_tools()
|
||||
json{{"name", "dir"}, {"type", "integer"}, {"enum", json::array({0, 1})}, {"default", 0}, {"description", "linear axis: 0=plane X, 1=plane Y"}},
|
||||
json{{"name", "angle"}, {"type", "number"}, {"unit", "deg"}, {"default", 360}, {"description", "circular total sweep"}},
|
||||
json{{"name", "plane"}, {"type", "string"}, {"enum", json::array({"XY", "XZ", "YZ"})}, {"default", "XY"}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"description", "target body; omit for the last body"}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"default", -1}, {"description", "target body; omit for the last body"}},
|
||||
})}},
|
||||
json{{"name", "shell"}, {"summary", "Hollow a body to a wall thickness (inward); optionally leave one face open."},
|
||||
{"params", json::array({
|
||||
json{{"name", "thickness"}, {"type", "number"}, {"unit", "mm"}, {"default", 1}, {"min", 0.01}},
|
||||
json{{"name", "face"}, {"type", "integer"}, {"description", "face id to leave open (query_topology); omit for a closed hollow"}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"description", "target body; omit for the last body"}},
|
||||
json{{"name", "face"}, {"type", "integer"}, {"default", -1}, {"description", "face id to leave open (query_topology); omit for a closed hollow"}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"default", -1}, {"description", "target body; omit for the last body"}},
|
||||
})}},
|
||||
json{{"name", "draft"}, {"summary", "Taper a body face by an angle about its base (pull direction +Z)."},
|
||||
{"params", json::array({
|
||||
json{{"name", "face"}, {"type", "integer"}, {"description", "face id to draft (query_topology)"}},
|
||||
json{{"name", "angle"}, {"type", "number"}, {"unit", "deg"}, {"default", 5}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"description", "target body; omit for the last body"}},
|
||||
json{{"name", "body"}, {"type", "integer"}, {"default", -1}, {"description", "target body; omit for the last body"}},
|
||||
})}},
|
||||
json{{"name", "query_topology"}, {"summary", "Measured faces (centroid/normal/cylinder) and edges (length/circle) of a body."},
|
||||
{"params", json::array({
|
||||
|
||||
@@ -39,26 +39,75 @@ def app_call(method, params=None):
|
||||
return json.loads(buf.decode())
|
||||
|
||||
# --- describe_tools -> MCP tool schemas ---------------------------------------
|
||||
_TYPE_MAP = {"number": "number", "integer": "integer", "string": "string", "boolean": "boolean"}
|
||||
_TYPE_MAP = {"number": "number", "integer": "integer", "string": "string",
|
||||
"boolean": "boolean", "array": "array", "object": "object"}
|
||||
|
||||
def _param_schema(p):
|
||||
sch = {"type": _TYPE_MAP.get(p.get("type", "string"), "string")}
|
||||
if "unit" in p: sch["description"] = f"in {p['unit']}"
|
||||
desc = p.get("description")
|
||||
if "unit" in p: desc = (desc + " " if desc else "") + f"in {p['unit']}"
|
||||
if desc: sch["description"] = desc
|
||||
if "enum" in p: sch["enum"] = p["enum"]
|
||||
if "min" in p: sch["minimum"] = p["min"]
|
||||
if "max" in p: sch["maximum"] = p["max"]
|
||||
if "default" in p: sch["default"] = p["default"]
|
||||
return sch
|
||||
|
||||
# Slice-1 fallback when the app socket is unreachable at list time.
|
||||
# Fallback when the app socket is unreachable at list time (e.g. the GUI isn't up yet when the
|
||||
# MCP client lists tools at session start). The LIVE describe_tools reply is authoritative — this
|
||||
# mirrors its 16-method surface so the agent still sees the full toolset; params without a
|
||||
# `default` are surfaced as required. Keep in sync with McpControl.cpp describe_tools().
|
||||
def _p(name, typ="number", **kw): return dict(name=name, type=typ, **kw)
|
||||
_PLANE = _p("plane", "string", enum=["XY", "XZ", "YZ"], default="XY")
|
||||
_FALLBACK_TOOLS = {"tools": [
|
||||
{"name": "describe_tools", "summary": "List callable tools and their parameters.", "params": []},
|
||||
{"name": "describe_scene", "summary": "Feature tree + per-body bounding boxes.", "params": []},
|
||||
{"name": "extrude", "summary": "Create a solid: a centred rectangle sketch extruded to a depth.",
|
||||
"params": [{"name": "width", "type": "number", "unit": "mm", "default": 20},
|
||||
{"name": "height", "type": "number", "unit": "mm", "default": 20},
|
||||
{"name": "distance", "type": "number", "unit": "mm", "default": 10},
|
||||
{"name": "plane", "type": "string", "enum": ["XY", "XZ", "YZ"], "default": "XY"}]},
|
||||
{"name": "extrude", "summary": "Extrude a profile (or width x height rectangle) to a depth; Onshape end conditions.",
|
||||
"params": [_p("width", default=20), _p("height", default=20), _p("distance", default=10), _PLANE,
|
||||
_p("profile", "array", default=[], description="closed [[x,y],...] overrides width/height"),
|
||||
_p("boolean", "string", enum=["new", "union", "subtract", "intersect"], default="new"),
|
||||
_p("end", "string", enum=["blind", "symmetric", "two_sided", "through_all", "up_to_face"], default="blind"),
|
||||
_p("distance2", default=0, description="second side when end=two_sided"),
|
||||
_p("up_to_face", "integer", default=-1, description="target face id when end=up_to_face"),
|
||||
_p("taper", default=0), _p("flip", "boolean", default=False)]},
|
||||
{"name": "revolve", "summary": "Revolve a profile about a plane axis.",
|
||||
"params": [_p("width", default=20), _p("height", default=10), _p("angle", default=360),
|
||||
_p("axis", "integer", enum=[0, 1], default=0), _p("flip", "boolean", default=False), _PLANE,
|
||||
_p("profile", "array", default=[], description="closed [[x,y],...] overrides width/height"),
|
||||
_p("boolean", "string", enum=["new", "union", "subtract", "intersect"], default="new")]},
|
||||
{"name": "fillet", "summary": "Round a measured edge of a body.",
|
||||
"params": [_p("edge", "integer"), _p("radius", default=1),
|
||||
_p("body", "integer", default=-1, description="target body; omit for last")]},
|
||||
{"name": "chamfer", "summary": "Chamfer a measured edge of a body.",
|
||||
"params": [_p("edge", "integer"), _p("distance", default=1),
|
||||
_p("body", "integer", default=-1, description="target body; omit for last")]},
|
||||
{"name": "hole", "summary": "Drill a circular hole at (x,y) on a plane.",
|
||||
"params": [_p("diameter", default=5), _p("depth", default=10), _p("through", "boolean", default=False),
|
||||
_p("x", default=0), _p("y", default=0), _PLANE]},
|
||||
{"name": "boolean", "summary": "Combine two bodies: union | subtract | intersect.",
|
||||
"params": [_p("op", "string", enum=["union", "subtract", "intersect"], default="subtract"),
|
||||
_p("target", "integer", default=0), _p("tool", "integer", default=1),
|
||||
_p("keep_tool", "boolean", default=False), _p("tolerance", default=0)]},
|
||||
{"name": "pattern", "summary": "Replicate a body: linear or circular.",
|
||||
"params": [_p("circular", "boolean", default=False), _p("count", "integer", default=3),
|
||||
_p("spacing", default=10), _p("dir", "integer", enum=[0, 1], default=0),
|
||||
_p("angle", default=360), _PLANE, _p("body", "integer", default=-1, description="target body; omit for last")]},
|
||||
{"name": "shell", "summary": "Hollow a body to a wall thickness; optionally open one face.",
|
||||
"params": [_p("thickness", default=1), _p("face", "integer", default=-1, description="face id to leave open; omit for closed"),
|
||||
_p("body", "integer", default=-1, description="target body; omit for last")]},
|
||||
{"name": "draft", "summary": "Taper a body face by an angle (pull +Z).",
|
||||
"params": [_p("face", "integer"), _p("angle", default=5),
|
||||
_p("body", "integer", default=-1, description="target body; omit for last")]},
|
||||
{"name": "query_topology", "summary": "Measured faces and edges of a body.",
|
||||
"params": [_p("body", "integer", default=0)]},
|
||||
{"name": "measure", "summary": "Distance/angle between two refs {face|edge|point} on a body.",
|
||||
"params": [_p("body", "integer", default=0), _p("a", "object"), _p("b", "object")]},
|
||||
{"name": "slice_body", "summary": "Cross-section of a body; ordered closed/open contours.",
|
||||
"params": [_p("body", "integer", default=0), _PLANE, _p("offset", default=0)]},
|
||||
{"name": "import_step", "summary": "Import a STEP file as native B-rep bodies.",
|
||||
"params": [_p("path", "string")]},
|
||||
{"name": "validate_against", "summary": "Volume + bbox + surface deviation of a body vs a reference {step|body}.",
|
||||
"params": [_p("body", "integer", default=0), _p("reference", "object")]},
|
||||
]}
|
||||
|
||||
def list_tools():
|
||||
|
||||
Reference in New Issue
Block a user