get_version
Check server version and Python-Ruby compatibility to verify runtime health. Returns version details and compatibility status even when other tools fail.
Instructions
Return the server version and Python↔Ruby compatibility verdict.
Useful as a runtime sanity probe — always returns a payload, even when the connection or other tools surface errors. The result is a JSON string with fields: python_version, ruby_version, min_compatible_ruby, max_compatible_ruby, ruby_min_compatible_python, ruby_max_compatible_python, compatible (bool), error (string | null).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/sketchup_mcp/tools.py:453-534 (handler)The actual tool handler function decorated with @mcp.tool() — calls the Ruby backend via _raw_call, performs two-way version compatibility checks, and always returns a diagnostic JSON payload.
@mcp.tool() async def get_version(ctx: Context) -> str: """Return the server version and Python↔Ruby compatibility verdict. Useful as a runtime sanity probe — always returns a payload, even when the connection or other tools surface errors. The result is a JSON string with fields: python_version, ruby_version, min_compatible_ruby, max_compatible_ruby, ruby_min_compatible_python, ruby_max_compatible_python, compatible (bool), error (string | null). """ def _payload(ruby_version, ruby_min, ruby_max, compatible, error_msg): return json.dumps({ "python_version": compat.CLIENT_VERSION, "ruby_version": ruby_version, "min_compatible_ruby": compat.MIN_RUBY, "max_compatible_ruby": compat.MAX_RUBY, "ruby_min_compatible_python": ruby_min, "ruby_max_compatible_python": ruby_max, "compatible": compatible, "error": error_msg, }) try: raw = await _raw_call(ctx, "get_version") except ConnectionError as e: return _payload(None, None, None, False, f"SketchUp not running or extension not started: {e}") except SketchUpError as e: # Covers old Ruby returning -32601 "unknown tool: get_version" # and any other JSON-RPC error envelope. Version compatibility is # validated once at connect-time in ``_handshake``; once a # connection survives that, tool-level errors here come from the # Ruby handler itself (not from per-request version checks). return _payload(None, None, None, False, str(e)) # Defensive parse: any unexpected shape (missing keys, non-list content, # non-string text, invalid JSON, non-dict payload) must STILL produce a # payload — the tool's contract is "always returns a payload even on # mismatch / error", so a KeyError/IndexError/TypeError/JSONDecodeError # escaping here would violate it. try: ruby_payload = json.loads(raw["content"][0]["text"]) if not isinstance(ruby_payload, dict): raise TypeError( f"ruby payload is {type(ruby_payload).__name__}, expected dict" ) except (KeyError, IndexError, TypeError, json.JSONDecodeError) as e: return _payload(None, None, None, False, f"unexpected get_version response shape: {e}") ruby_version = ruby_payload.get("ruby_version") ruby_min = ruby_payload.get("min_compatible_python") ruby_max = ruby_payload.get("max_compatible_python") # Two-way compatibility: BOTH sides' tables must accept the counterpart. try: compat.check_ruby_version(ruby_version) python_accepts_ruby, py_error = True, None except IncompatibleVersionError as e: python_accepts_ruby, py_error = False, str(e) try: ruby_accepts_python = bool( ruby_min and ruby_max and compat.parse(ruby_min) <= compat.parse(compat.CLIENT_VERSION) <= compat.parse(ruby_max) ) except ValueError: ruby_accepts_python = False compatible = python_accepts_ruby and ruby_accepts_python if py_error: error_msg = py_error elif not ruby_accepts_python: error_msg = ( f"SketchUp plugin advertises Python compatibility " f"{ruby_min}..{ruby_max}, which excludes v{compat.CLIENT_VERSION}." ) else: error_msg = None return _payload(ruby_version, ruby_min, ruby_max, compatible, error_msg) - src/sketchup_mcp/tools.py:453-462 (schema)Docstring serves as the schema definition — describes the input (none) and the output shape (8 JSON fields). No Pydantic model since the tool takes no arguments.
@mcp.tool() async def get_version(ctx: Context) -> str: """Return the server version and Python↔Ruby compatibility verdict. Useful as a runtime sanity probe — always returns a payload, even when the connection or other tools surface errors. The result is a JSON string with fields: python_version, ruby_version, min_compatible_ruby, max_compatible_ruby, ruby_min_compatible_python, ruby_max_compatible_python, compatible (bool), error (string | null). """ - src/sketchup_mcp/tools.py:453-534 (registration)Registered via the @mcp.tool() decorator on the FastMCP instance 'mcp' imported from sketchup_mcp.app. The side-effect import in app.py (import sketchup_mcp.tools) triggers registration.
@mcp.tool() async def get_version(ctx: Context) -> str: """Return the server version and Python↔Ruby compatibility verdict. Useful as a runtime sanity probe — always returns a payload, even when the connection or other tools surface errors. The result is a JSON string with fields: python_version, ruby_version, min_compatible_ruby, max_compatible_ruby, ruby_min_compatible_python, ruby_max_compatible_python, compatible (bool), error (string | null). """ def _payload(ruby_version, ruby_min, ruby_max, compatible, error_msg): return json.dumps({ "python_version": compat.CLIENT_VERSION, "ruby_version": ruby_version, "min_compatible_ruby": compat.MIN_RUBY, "max_compatible_ruby": compat.MAX_RUBY, "ruby_min_compatible_python": ruby_min, "ruby_max_compatible_python": ruby_max, "compatible": compatible, "error": error_msg, }) try: raw = await _raw_call(ctx, "get_version") except ConnectionError as e: return _payload(None, None, None, False, f"SketchUp not running or extension not started: {e}") except SketchUpError as e: # Covers old Ruby returning -32601 "unknown tool: get_version" # and any other JSON-RPC error envelope. Version compatibility is # validated once at connect-time in ``_handshake``; once a # connection survives that, tool-level errors here come from the # Ruby handler itself (not from per-request version checks). return _payload(None, None, None, False, str(e)) # Defensive parse: any unexpected shape (missing keys, non-list content, # non-string text, invalid JSON, non-dict payload) must STILL produce a # payload — the tool's contract is "always returns a payload even on # mismatch / error", so a KeyError/IndexError/TypeError/JSONDecodeError # escaping here would violate it. try: ruby_payload = json.loads(raw["content"][0]["text"]) if not isinstance(ruby_payload, dict): raise TypeError( f"ruby payload is {type(ruby_payload).__name__}, expected dict" ) except (KeyError, IndexError, TypeError, json.JSONDecodeError) as e: return _payload(None, None, None, False, f"unexpected get_version response shape: {e}") ruby_version = ruby_payload.get("ruby_version") ruby_min = ruby_payload.get("min_compatible_python") ruby_max = ruby_payload.get("max_compatible_python") # Two-way compatibility: BOTH sides' tables must accept the counterpart. try: compat.check_ruby_version(ruby_version) python_accepts_ruby, py_error = True, None except IncompatibleVersionError as e: python_accepts_ruby, py_error = False, str(e) try: ruby_accepts_python = bool( ruby_min and ruby_max and compat.parse(ruby_min) <= compat.parse(compat.CLIENT_VERSION) <= compat.parse(ruby_max) ) except ValueError: ruby_accepts_python = False compatible = python_accepts_ruby and ruby_accepts_python if py_error: error_msg = py_error elif not ruby_accepts_python: error_msg = ( f"SketchUp plugin advertises Python compatibility " f"{ruby_min}..{ruby_max}, which excludes v{compat.CLIENT_VERSION}." ) else: error_msg = None return _payload(ruby_version, ruby_min, ruby_max, compatible, error_msg) - src/sketchup_mcp/connection.py:43-53 (helper)get_version is listed in _RETRY_SAFE_TOOLS frozenset, marking it as a read-only diagnostic tool eligible for stale-socket auto-retry.
_RETRY_SAFE_TOOLS: frozenset[str] = frozenset( { "get_model_info", "list_components", "get_component_info", "find_components", "list_layers", "get_selection", "get_viewport_screenshot", # read-only viewport capture; idempotent in # both restore_view modes (no document state changes) "get_version", # read-only diagnostic; no side effects