openvsp.inspect
Analyze OpenVSP geometry files to extract component IDs and structural information without altering the original design.
Instructions
Describe an OpenVSP geometry without modifying it. Returns component IDs and raw info.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| request | Yes |
Implementation Reference
- src/openvsp_mcp/tool.py:20-28 (handler)The handler function for the 'openvsp.inspect' tool, registered via FastMCP decorator. Extracts geometry_file from request and calls describe_geometry helper.@app.tool( name="openvsp.inspect", description=( "Describe an OpenVSP geometry without modifying it. Returns component IDs and raw info."), meta={"version": "0.1.0", "categories": ["geometry", "aero", "inspection"]}, ) def inspect(request: OpenVSPGeometryRequest) -> OpenVSPInspectResponse: return describe_geometry(request.geometry_file)
- src/openvsp_mcp/describe.py:14-62 (helper)Core helper function implementing the inspection logic: generates a no-op script, runs OpenVSP, checks exit code, parses .vsp3 XML to extract geometry IDs, wing names, and detailed component info.def describe_geometry(geometry_file: str) -> OpenVSPInspectResponse: """Run OpenVSP in describe mode and parse high-level geometry information.""" with tempfile.TemporaryDirectory(prefix="openvsp_mcp_describe_") as tmpdir: temp_request = OpenVSPRequest( geometry_file=geometry_file, set_commands=[], run_vspaero=False, case_name="describe", ) script_path = _write_script(temp_request, Path(tmpdir)) result = subprocess.run( [OPENVSP_BIN, "-script", str(script_path)], check=False, capture_output=True, ) if result.returncode not in _OK_EXIT_CODES: message = result.stderr.decode("utf-8", errors="ignore").strip() if not message: message = result.stdout.decode("utf-8", errors="ignore").strip() raise RuntimeError(message or "OpenVSP describe run failed") tree = ET.parse(geometry_file) root = tree.getroot() geom_ids: list[str] = [] wing_names: list[str] = [] info_lines: list[str] = [] for geom in root.findall(".//Geom"): name_elem = geom.find("ParmContainer/Name") geom_name = name_elem.text if name_elem is not None else "" id_elem = geom.find("ParmContainer/ID") geom_id = id_elem.text if id_elem is not None else "" type_elem = geom.find("GeomBase/TypeName") geom_type = type_elem.text if type_elem is not None else "" if geom_id: geom_ids.append(geom_id) if geom_type.lower() == "wing" and geom_name: wing_names.append(geom_name) info_lines.append(f"{geom_id or 'UNKNOWN'}:{geom_name or 'Unnamed'}:{geom_type or 'Unknown'}") return OpenVSPInspectResponse( geom_ids=geom_ids, wing_names=wing_names, info_log="\n".join(info_lines), )
- src/openvsp_mcp/models.py:14-16 (schema)Pydantic input schema for the openvsp.inspect tool request, requiring the path to the .vsp3 geometry file.class OpenVSPGeometryRequest(BaseModel): geometry_file: str = Field(..., description="Path to the .vsp3 file")
- src/openvsp_mcp/models.py:27-33 (schema)Pydantic output schema for the openvsp.inspect tool response, containing lists of geometry IDs and wing names, plus detailed info log.class OpenVSPInspectResponse(BaseModel): """High-level summary of a geometry.""" geom_ids: list[str] = Field(..., description="Top-level geometry IDs discovered") wing_names: list[str] = Field(default_factory=list, description="Detected wing geometry names") info_log: str = Field(..., description="Raw output from OpenVSP describe command")
- src/openvsp_mcp/__main__.py:74-75 (registration)Invocation of build_tool on the FastMCP server instance, which registers the openvsp.inspect tool (along with others).build_tool(app)