Release prep 0.6.2 (#39)

* docs: note macro repo move

* chore: bump workbench to 0.6.2

* docs: update README.md

* docs: improve release notes

* fix: better icons and minor tweaks

* fix: Add reverse test
This commit is contained in:
Sean P. Kane
2026-01-18 21:48:41 -08:00
committed by GitHub
parent a797038f6f
commit 2bef958994
7 changed files with 184 additions and 142 deletions
@@ -22,6 +22,7 @@ import contextlib
import errno
import io
import json
import os
import queue
import sys
import threading
@@ -236,8 +237,9 @@ class FreecadMCPPlugin:
self._running = True
# Print instance ID to stderr for test automation to capture.
# Stdout may be reserved for JSON-RPC when running in stdio mode.
# Print instance ID to stderr only for test automation (when env var is set).
# This avoids red error text in FreeCAD's console during normal use.
if os.environ.get("FREECAD_MCP_TESTING"):
print(
f"FREECAD_MCP_BRIDGE_INSTANCE_ID={self._instance_id}",
file=sys.stderr,
@@ -1,54 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<!-- White background -->
<rect width="64" height="64" fill="#ffffff"/>
<!-- Transparent background (allows Qt pressed state to show through) -->
<!-- Bridge Structure -->
<!-- Bridge Structure with light outline for dark background contrast -->
<!-- Bridge deck -->
<rect x="4" y="40" width="56" height="5" rx="1" fill="#8b7355" stroke="#6b5344" stroke-width="1"/>
<rect x="4" y="40" width="56" height="5" rx="1" fill="#8b7355" stroke="#d4c4b0" stroke-width="1.5"/>
<!-- Bridge railings -->
<rect x="4" y="37" width="56" height="2" rx="0.5" fill="#a08060"/>
<rect x="4" y="37" width="56" height="2" rx="0.5" fill="#b89c7a" stroke="#d4c4b0" stroke-width="0.5"/>
<!-- Bridge supports/pillars -->
<rect x="8" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="50" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="8" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<rect x="50" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<!-- Bridge arch underneath -->
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#6b5344" stroke-width="2"/>
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#7a6450" stroke-width="2.5"/>
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#d4c4b0" stroke-width="1" opacity="0.6"/>
<!-- Robot on the bridge (facing RIGHT - same direction as play buttons) -->
<!-- Robot body -->
<rect x="24" y="18" width="16" height="14" rx="2" fill="#4a90d9" stroke="#2c5aa0" stroke-width="1.5"/>
<!-- Robot body with light outline -->
<rect x="24" y="18" width="16" height="14" rx="2" fill="#5a9ee8" stroke="#e0f0ff" stroke-width="1.5"/>
<!-- Robot head (facing right) -->
<rect x="26" y="8" width="12" height="10" rx="2" fill="#4a90d9" stroke="#2c5aa0" stroke-width="1.5"/>
<rect x="26" y="8" width="12" height="10" rx="2" fill="#5a9ee8" stroke="#e0f0ff" stroke-width="1.5"/>
<!-- Robot face plate (on right side) -->
<rect x="32" y="10" width="6" height="6" rx="1" fill="#e8f4fc"/>
<rect x="32" y="10" width="6" height="6" rx="1" fill="#f0f8ff" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot eyes (looking right) -->
<circle cx="36" cy="12" r="1.5" fill="#2c5aa0"/>
<circle cx="36" cy="15" r="1.5" fill="#2c5aa0"/>
<!-- Robot antenna -->
<line x1="32" y1="8" x2="32" y2="3" stroke="#2c5aa0" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="2" r="3" fill="#27ae60"/>
<line x1="32" y1="8" x2="32" y2="3" stroke="#3c6ab0" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="2" r="3" fill="#2ecc71" stroke="#e0ffe0" stroke-width="1"/>
<!-- Robot arms (reaching forward to right) -->
<rect x="18" y="22" width="6" height="4" rx="1.5" fill="#3a7bc8"/>
<rect x="40" y="20" width="8" height="4" rx="1.5" fill="#3a7bc8"/>
<rect x="18" y="22" width="6" height="4" rx="1.5" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="40" y="20" width="8" height="4" rx="1.5" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot legs (walking pose) -->
<rect x="24" y="32" width="4" height="8" rx="1" fill="#3a7bc8" transform="rotate(10, 26, 36)"/>
<rect x="36" y="32" width="4" height="8" rx="1" fill="#3a7bc8" transform="rotate(-10, 38, 36)"/>
<rect x="24" y="32" width="4" height="8" rx="1" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5" transform="rotate(10, 26, 36)"/>
<rect x="36" y="32" width="4" height="8" rx="1" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5" transform="rotate(-10, 38, 36)"/>
<!-- Bold Play Triangles in TOP corners -->
<polygon points="4,4 4,16 12,10" fill="#27ae60" stroke="#1e8449" stroke-width="1.5"/>
<polygon points="52,4 52,16 60,10" fill="#27ae60" stroke="#1e8449" stroke-width="1.5"/>
<!-- Bold Play Triangles in TOP corners with light outline -->
<polygon points="4,4 4,16 12,10" fill="#2ecc71" stroke="#c0ffc0" stroke-width="1.5"/>
<polygon points="52,4 52,16 60,10" fill="#2ecc71" stroke="#c0ffc0" stroke-width="1.5"/>
<!-- Green Data Flow (river under bridge - active) -->
<!-- Data stream background -->
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#27ae60" stroke-width="4" opacity="0.3"/>
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#2ecc71" stroke-width="4" opacity="0.4"/>
<!-- Left arrow -->
<path d="M 6 59 L 16 59" stroke="#27ae60" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="6,59 11,56 11,62" fill="#27ae60"/>
<path d="M 6 59 L 16 59" stroke="#2ecc71" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="6,59 11,56 11,62" fill="#2ecc71" stroke="#c0ffc0" stroke-width="0.5"/>
<!-- Right arrow -->
<path d="M 48 59 L 58 59" stroke="#27ae60" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="58,59 53,56 53,62" fill="#27ae60"/>
<path d="M 48 59 L 58 59" stroke="#2ecc71" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="58,59 53,56 53,62" fill="#2ecc71" stroke="#c0ffc0" stroke-width="0.5"/>
<!-- Center data dots (flowing) -->
<circle cx="26" cy="57" r="2" fill="#27ae60"/>
<circle cx="32" cy="56" r="2" fill="#27ae60"/>
<circle cx="38" cy="57" r="2" fill="#27ae60"/>
<circle cx="26" cy="57" r="2" fill="#2ecc71" stroke="#c0ffc0" stroke-width="0.5"/>
<circle cx="32" cy="56" r="2" fill="#2ecc71" stroke="#c0ffc0" stroke-width="0.5"/>
<circle cx="38" cy="57" r="2" fill="#2ecc71" stroke="#c0ffc0" stroke-width="0.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -1,57 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<!-- White background -->
<rect width="64" height="64" fill="#ffffff"/>
<!-- Transparent background (allows Qt pressed state to show through) -->
<!-- Bridge Structure -->
<!-- Bridge Structure with light outline -->
<!-- Bridge deck -->
<rect x="4" y="40" width="56" height="5" rx="1" fill="#8b7355" stroke="#6b5344" stroke-width="1"/>
<rect x="4" y="40" width="56" height="5" rx="1" fill="#8b7355" stroke="#d4c4b0" stroke-width="1.5"/>
<!-- Bridge railings -->
<rect x="4" y="37" width="56" height="2" rx="0.5" fill="#a08060"/>
<rect x="4" y="37" width="56" height="2" rx="0.5" fill="#b89c7a" stroke="#d4c4b0" stroke-width="0.5"/>
<!-- Bridge supports/pillars -->
<rect x="8" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="50" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="8" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<rect x="50" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<!-- Bridge arch underneath -->
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#6b5344" stroke-width="2"/>
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#7a6450" stroke-width="2.5"/>
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#d4c4b0" stroke-width="1" opacity="0.6"/>
<!-- Robot SITTING on bridge edge, feet dangling -->
<!-- Robot body (sitting position - shorter/compressed) -->
<rect x="24" y="24" width="16" height="10" rx="2" fill="#4a90d9" stroke="#2c5aa0" stroke-width="1.5"/>
<!-- Robot head -->
<rect x="26" y="14" width="12" height="10" rx="2" fill="#4a90d9" stroke="#2c5aa0" stroke-width="1.5"/>
<!-- Robot body (sitting position - shorter/compressed) with light outline -->
<rect x="24" y="24" width="16" height="10" rx="2" fill="#5a9ee8" stroke="#e0f0ff" stroke-width="1.5"/>
<!-- Robot head with light outline -->
<rect x="26" y="14" width="12" height="10" rx="2" fill="#5a9ee8" stroke="#e0f0ff" stroke-width="1.5"/>
<!-- Robot face plate -->
<rect x="28" y="16" width="8" height="6" rx="1" fill="#e8f4fc"/>
<rect x="28" y="16" width="8" height="6" rx="1" fill="#f0f8ff" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot eyes (looking curious) -->
<circle cx="30" cy="19" r="1.5" fill="#2c5aa0"/>
<circle cx="34" cy="19" r="1.5" fill="#2c5aa0"/>
<!-- Robot antenna with YELLOW ball (matches status color) -->
<line x1="32" y1="14" x2="32" y2="9" stroke="#2c5aa0" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="8" r="3" fill="#f1c40f"/>
<!-- Robot antenna with YELLOW ball (matches status color) with light outline -->
<line x1="32" y1="14" x2="32" y2="9" stroke="#3c6ab0" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="8" r="3" fill="#f1c40f" stroke="#fff8c0" stroke-width="1"/>
<!-- Robot arms (resting on bridge) -->
<rect x="18" y="28" width="6" height="4" rx="1.5" fill="#3a7bc8"/>
<rect x="40" y="28" width="6" height="4" rx="1.5" fill="#3a7bc8"/>
<rect x="18" y="28" width="6" height="4" rx="1.5" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="40" y="28" width="6" height="4" rx="1.5" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot legs (dangling over edge) -->
<rect x="26" y="34" width="4" height="12" rx="1" fill="#3a7bc8"/>
<rect x="34" y="34" width="4" height="12" rx="1" fill="#3a7bc8"/>
<rect x="26" y="34" width="4" height="12" rx="1" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="34" y="34" width="4" height="12" rx="1" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot feet -->
<rect x="25" y="45" width="6" height="3" rx="1" fill="#2c5aa0"/>
<rect x="33" y="45" width="6" height="3" rx="1" fill="#2c5aa0"/>
<rect x="25" y="45" width="6" height="3" rx="1" fill="#3c6ab0" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="33" y="45" width="6" height="3" rx="1" fill="#3c6ab0" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Bold Question marks in TOP corners -->
<text x="9" y="16" font-family="Arial, Helvetica, sans-serif" font-size="18" font-weight="900" fill="#f1c40f" stroke="#d4ac0d" stroke-width="1" text-anchor="middle">?</text>
<text x="55" y="16" font-family="Arial, Helvetica, sans-serif" font-size="18" font-weight="900" fill="#f1c40f" stroke="#d4ac0d" stroke-width="1" text-anchor="middle">?</text>
<!-- Bold Question marks in TOP corners with light stroke -->
<text x="9" y="16" font-family="Arial, Helvetica, sans-serif" font-size="18" font-weight="900" fill="#f1c40f" stroke="#fff8c0" stroke-width="1.5" text-anchor="middle">?</text>
<text x="55" y="16" font-family="Arial, Helvetica, sans-serif" font-size="18" font-weight="900" fill="#f1c40f" stroke="#fff8c0" stroke-width="1.5" text-anchor="middle">?</text>
<!-- Yellow Data Flow (river under bridge - status check) -->
<!-- Data stream background -->
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#f1c40f" stroke-width="4" opacity="0.3"/>
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#f1c40f" stroke-width="4" opacity="0.4"/>
<!-- Left arrow -->
<path d="M 6 59 L 16 59" stroke="#f1c40f" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="6,59 11,56 11,62" fill="#f1c40f"/>
<polygon points="6,59 11,56 11,62" fill="#f1c40f" stroke="#fff8c0" stroke-width="0.5"/>
<!-- Right arrow -->
<path d="M 48 59 L 58 59" stroke="#f1c40f" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="58,59 53,56 53,62" fill="#f1c40f"/>
<polygon points="58,59 53,56 53,62" fill="#f1c40f" stroke="#fff8c0" stroke-width="0.5"/>
<!-- Center data dots (checking) -->
<circle cx="26" cy="57" r="2" fill="#f1c40f"/>
<circle cx="32" cy="56" r="2" fill="#f1c40f"/>
<circle cx="38" cy="57" r="2" fill="#f1c40f"/>
<circle cx="26" cy="57" r="2" fill="#f1c40f" stroke="#fff8c0" stroke-width="0.5"/>
<circle cx="32" cy="56" r="2" fill="#f1c40f" stroke="#fff8c0" stroke-width="0.5"/>
<circle cx="38" cy="57" r="2" fill="#f1c40f" stroke="#fff8c0" stroke-width="0.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

+24 -21
View File
@@ -1,43 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<!-- White background -->
<rect width="64" height="64" fill="#ffffff"/>
<!-- Transparent background (allows Qt pressed state to show through) -->
<!-- Bold Stop Squares in TOP corners (not overlapping bridge) -->
<rect x="2" y="2" width="14" height="14" rx="2" fill="#e74c3c" stroke="#c0392b" stroke-width="2"/>
<rect x="48" y="2" width="14" height="14" rx="2" fill="#e74c3c" stroke="#c0392b" stroke-width="2"/>
<!-- Bold Stop Squares in TOP corners with light outline -->
<rect x="2" y="2" width="14" height="14" rx="2" fill="#e74c3c" stroke="#ffc0c0" stroke-width="1.5"/>
<rect x="48" y="2" width="14" height="14" rx="2" fill="#e74c3c" stroke="#ffc0c0" stroke-width="1.5"/>
<!-- DRAWBRIDGE OPEN - No robot -->
<!-- Left bridge section (raised) -->
<!-- Left bridge section (raised) with light outline -->
<g transform="rotate(-35, 4, 40)">
<rect x="4" y="40" width="26" height="5" rx="1" fill="#8b7355" stroke="#6b5344" stroke-width="1"/>
<rect x="4" y="37" width="26" height="2" rx="0.5" fill="#a08060"/>
<rect x="4" y="40" width="26" height="5" rx="1" fill="#8b7355" stroke="#d4c4b0" stroke-width="1.5"/>
<rect x="4" y="37" width="26" height="2" rx="0.5" fill="#b89c7a" stroke="#d4c4b0" stroke-width="0.5"/>
</g>
<!-- Right bridge section (raised) -->
<!-- Right bridge section (raised) with light outline -->
<g transform="rotate(35, 60, 40)">
<rect x="34" y="40" width="26" height="5" rx="1" fill="#8b7355" stroke="#6b5344" stroke-width="1"/>
<rect x="34" y="37" width="26" height="2" rx="0.5" fill="#a08060"/>
<rect x="34" y="40" width="26" height="5" rx="1" fill="#8b7355" stroke="#d4c4b0" stroke-width="1.5"/>
<rect x="34" y="37" width="26" height="2" rx="0.5" fill="#b89c7a" stroke="#d4c4b0" stroke-width="0.5"/>
</g>
<!-- Bridge supports/pillars -->
<rect x="8" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="50" y="45" width="6" height="8" fill="#6b5344"/>
<!-- Bridge supports/pillars with light outline -->
<rect x="8" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<rect x="50" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<!-- Bridge arch underneath (broken/gap) -->
<path d="M 14 53 Q 20 49 26 50" fill="none" stroke="#6b5344" stroke-width="2"/>
<path d="M 38 50 Q 44 49 50 53" fill="none" stroke="#6b5344" stroke-width="2"/>
<path d="M 14 53 Q 20 49 26 50" fill="none" stroke="#7a6450" stroke-width="2.5"/>
<path d="M 14 53 Q 20 49 26 50" fill="none" stroke="#d4c4b0" stroke-width="1" opacity="0.6"/>
<path d="M 38 50 Q 44 49 50 53" fill="none" stroke="#7a6450" stroke-width="2.5"/>
<path d="M 38 50 Q 44 49 50 53" fill="none" stroke="#d4c4b0" stroke-width="1" opacity="0.6"/>
<!-- Red Data Flow (river under bridge - stopped) -->
<!-- Data stream background -->
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#e74c3c" stroke-width="4" opacity="0.3"/>
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#e74c3c" stroke-width="4" opacity="0.4"/>
<!-- Left arrow (blocked) -->
<path d="M 6 59 L 16 59" stroke="#e74c3c" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="6,59 11,56 11,62" fill="#e74c3c"/>
<polygon points="6,59 11,56 11,62" fill="#e74c3c" stroke="#ffc0c0" stroke-width="0.5"/>
<!-- Right arrow (blocked) -->
<path d="M 48 59 L 58 59" stroke="#e74c3c" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="58,59 53,56 53,62" fill="#e74c3c"/>
<polygon points="58,59 53,56 53,62" fill="#e74c3c" stroke="#ffc0c0" stroke-width="0.5"/>
<!-- Center X (flow blocked) -->
<line x1="28" y1="55" x2="36" y2="61" stroke="#e74c3c" stroke-width="2.5" stroke-linecap="round"/>
<line x1="36" y1="55" x2="28" y2="61" stroke="#e74c3c" stroke-width="2.5" stroke-linecap="round"/>
<line x1="28" y1="55" x2="36" y2="61" stroke="#e74c3c" stroke-width="3" stroke-linecap="round"/>
<line x1="36" y1="55" x2="28" y2="61" stroke="#e74c3c" stroke-width="3" stroke-linecap="round"/>
<line x1="28" y1="55" x2="36" y2="61" stroke="#ffc0c0" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
<line x1="36" y1="55" x2="28" y2="61" stroke="#ffc0c0" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -1,84 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<!-- White background -->
<rect width="64" height="64" fill="#ffffff"/>
<!-- Transparent background (allows Qt pressed state to show through) -->
<!-- Bridge Structure -->
<!-- Bridge Structure with light outline -->
<!-- Bridge deck -->
<rect x="4" y="40" width="56" height="5" rx="1" fill="#8b7355" stroke="#6b5344" stroke-width="1"/>
<rect x="4" y="40" width="56" height="5" rx="1" fill="#8b7355" stroke="#d4c4b0" stroke-width="1.5"/>
<!-- Bridge railings -->
<rect x="4" y="37" width="56" height="2" rx="0.5" fill="#a08060"/>
<rect x="4" y="37" width="56" height="2" rx="0.5" fill="#b89c7a" stroke="#d4c4b0" stroke-width="0.5"/>
<!-- Bridge supports/pillars -->
<rect x="8" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="50" y="45" width="6" height="8" fill="#6b5344"/>
<rect x="8" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<rect x="50" y="45" width="6" height="8" fill="#7a6450" stroke="#d4c4b0" stroke-width="1"/>
<!-- Bridge arch underneath -->
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#6b5344" stroke-width="2"/>
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#7a6450" stroke-width="2.5"/>
<path d="M 14 53 Q 32 47 50 53" fill="none" stroke="#d4c4b0" stroke-width="1" opacity="0.6"/>
<!-- Robot SITTING on bridge edge, feet dangling -->
<!-- Robot SITTING on bridge edge, feet dangling with light outline -->
<!-- Robot body (sitting position - shorter/compressed) -->
<rect x="24" y="24" width="16" height="10" rx="2" fill="#4a90d9" stroke="#2c5aa0" stroke-width="1.5"/>
<rect x="24" y="24" width="16" height="10" rx="2" fill="#5a9ee8" stroke="#e0f0ff" stroke-width="1.5"/>
<!-- Robot head -->
<rect x="26" y="14" width="12" height="10" rx="2" fill="#4a90d9" stroke="#2c5aa0" stroke-width="1.5"/>
<rect x="26" y="14" width="12" height="10" rx="2" fill="#5a9ee8" stroke="#e0f0ff" stroke-width="1.5"/>
<!-- Robot face plate -->
<rect x="28" y="16" width="8" height="6" rx="1" fill="#e8f4fc"/>
<rect x="28" y="16" width="8" height="6" rx="1" fill="#f0f8ff" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot eyes -->
<circle cx="30" cy="19" r="1.5" fill="#2c5aa0"/>
<circle cx="34" cy="19" r="1.5" fill="#2c5aa0"/>
<!-- Robot antenna with GRAY ball (matches gear color) -->
<line x1="32" y1="14" x2="32" y2="9" stroke="#2c5aa0" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="8" r="3" fill="#95a5a6"/>
<!-- Robot antenna with GRAY ball (matches gear color) with light outline -->
<line x1="32" y1="14" x2="32" y2="9" stroke="#3c6ab0" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="8" r="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="1"/>
<!-- Robot arms (resting on bridge) -->
<rect x="18" y="28" width="6" height="4" rx="1.5" fill="#3a7bc8"/>
<rect x="40" y="28" width="6" height="4" rx="1.5" fill="#3a7bc8"/>
<rect x="18" y="28" width="6" height="4" rx="1.5" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="40" y="28" width="6" height="4" rx="1.5" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot legs (dangling over edge) -->
<rect x="26" y="34" width="4" height="12" rx="1" fill="#3a7bc8"/>
<rect x="34" y="34" width="4" height="12" rx="1" fill="#3a7bc8"/>
<rect x="26" y="34" width="4" height="12" rx="1" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="34" y="34" width="4" height="12" rx="1" fill="#4a8bd8" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Robot feet -->
<rect x="25" y="45" width="6" height="3" rx="1" fill="#2c5aa0"/>
<rect x="33" y="45" width="6" height="3" rx="1" fill="#2c5aa0"/>
<rect x="25" y="45" width="6" height="3" rx="1" fill="#3c6ab0" stroke="#c0e0ff" stroke-width="0.5"/>
<rect x="33" y="45" width="6" height="3" rx="1" fill="#3c6ab0" stroke="#c0e0ff" stroke-width="0.5"/>
<!-- Better Gear symbols in TOP corners (more teeth, more obvious) -->
<!-- Better Gear symbols in TOP corners with light outlines -->
<!-- Left gear -->
<circle cx="10" cy="10" r="6" fill="#95a5a6" stroke="#7f8c8d" stroke-width="1"/>
<circle cx="10" cy="10" r="6" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="1.5"/>
<!-- Gear teeth (8 teeth around the gear) -->
<rect x="8" y="2" width="4" height="3" fill="#95a5a6"/>
<rect x="8" y="15" width="4" height="3" fill="#95a5a6"/>
<rect x="2" y="8" width="3" height="4" fill="#95a5a6"/>
<rect x="15" y="8" width="3" height="4" fill="#95a5a6"/>
<rect x="8" y="2" width="4" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<rect x="8" y="15" width="4" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<rect x="2" y="8" width="3" height="4" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<rect x="15" y="8" width="3" height="4" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<!-- Diagonal teeth -->
<rect x="3" y="3" width="3" height="3" fill="#95a5a6" transform="rotate(45, 4.5, 4.5)"/>
<rect x="14" y="3" width="3" height="3" fill="#95a5a6" transform="rotate(-45, 15.5, 4.5)"/>
<rect x="3" y="14" width="3" height="3" fill="#95a5a6" transform="rotate(-45, 4.5, 15.5)"/>
<rect x="14" y="14" width="3" height="3" fill="#95a5a6" transform="rotate(45, 15.5, 15.5)"/>
<rect x="3" y="3" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(45, 4.5, 4.5)"/>
<rect x="14" y="3" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(-45, 15.5, 4.5)"/>
<rect x="3" y="14" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(-45, 4.5, 15.5)"/>
<rect x="14" y="14" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(45, 15.5, 15.5)"/>
<!-- Gear center hole -->
<circle cx="10" cy="10" r="2.5" fill="#6b5344"/>
<circle cx="10" cy="10" r="2.5" fill="#7a6450" stroke="#d4c4b0" stroke-width="0.5"/>
<!-- Right gear -->
<circle cx="54" cy="10" r="6" fill="#95a5a6" stroke="#7f8c8d" stroke-width="1"/>
<circle cx="54" cy="10" r="6" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="1.5"/>
<!-- Gear teeth (8 teeth around the gear) -->
<rect x="52" y="2" width="4" height="3" fill="#95a5a6"/>
<rect x="52" y="15" width="4" height="3" fill="#95a5a6"/>
<rect x="46" y="8" width="3" height="4" fill="#95a5a6"/>
<rect x="59" y="8" width="3" height="4" fill="#95a5a6"/>
<rect x="52" y="2" width="4" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<rect x="52" y="15" width="4" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<rect x="46" y="8" width="3" height="4" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<rect x="59" y="8" width="3" height="4" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<!-- Diagonal teeth -->
<rect x="47" y="3" width="3" height="3" fill="#95a5a6" transform="rotate(45, 48.5, 4.5)"/>
<rect x="58" y="3" width="3" height="3" fill="#95a5a6" transform="rotate(-45, 59.5, 4.5)"/>
<rect x="47" y="14" width="3" height="3" fill="#95a5a6" transform="rotate(-45, 48.5, 15.5)"/>
<rect x="58" y="14" width="3" height="3" fill="#95a5a6" transform="rotate(45, 59.5, 15.5)"/>
<rect x="47" y="3" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(45, 48.5, 4.5)"/>
<rect x="58" y="3" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(-45, 59.5, 4.5)"/>
<rect x="47" y="14" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(-45, 48.5, 15.5)"/>
<rect x="58" y="14" width="3" height="3" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5" transform="rotate(45, 59.5, 15.5)"/>
<!-- Gear center hole -->
<circle cx="54" cy="10" r="2.5" fill="#6b5344"/>
<circle cx="54" cy="10" r="2.5" fill="#7a6450" stroke="#d4c4b0" stroke-width="0.5"/>
<!-- Gray Data Flow (river under bridge - settings) -->
<!-- Data stream background -->
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#95a5a6" stroke-width="4" opacity="0.3"/>
<path d="M 0 60 Q 32 55 64 60" fill="none" stroke="#a8b8c8" stroke-width="4" opacity="0.4"/>
<!-- Left arrow -->
<path d="M 6 59 L 16 59" stroke="#95a5a6" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="6,59 11,56 11,62" fill="#95a5a6"/>
<path d="M 6 59 L 16 59" stroke="#a8b8c8" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="6,59 11,56 11,62" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<!-- Right arrow -->
<path d="M 48 59 L 58 59" stroke="#95a5a6" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="58,59 53,56 53,62" fill="#95a5a6"/>
<path d="M 48 59 L 58 59" stroke="#a8b8c8" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="58,59 53,56 53,62" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<!-- Center data dots -->
<circle cx="26" cy="57" r="2" fill="#95a5a6"/>
<circle cx="32" cy="56" r="2" fill="#95a5a6"/>
<circle cx="38" cy="57" r="2" fill="#95a5a6"/>
<circle cx="26" cy="57" r="2" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<circle cx="32" cy="56" r="2" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
<circle cx="38" cy="57" r="2" fill="#a8b8c8" stroke="#d0e0f0" stroke-width="0.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

+5 -2
View File
@@ -33,6 +33,7 @@ Example:
import argparse
import logging
import os
import sys
import uuid
from collections.abc import AsyncIterator
@@ -411,8 +412,10 @@ def main() -> None:
# Set up logging
logging.getLogger().setLevel(config.log_level)
# Print instance ID to stderr for test automation to capture
# Must use stderr because stdout is reserved for JSON-RPC in stdio mode
# Print instance ID to stderr only for test automation (when env var is set).
# This avoids unwanted output during normal use.
# Must use stderr because stdout is reserved for JSON-RPC in stdio mode.
if os.environ.get("FREECAD_MCP_TESTING"):
print(f"FREECAD_MCP_INSTANCE_ID={INSTANCE_ID}", file=sys.stderr, flush=True)
logger.info("Starting FreeCAD Robust MCP Server")
+35 -1
View File
@@ -1,5 +1,6 @@
"""Tests for the main server module."""
import os
import sys
from unittest.mock import AsyncMock, MagicMock, patch
@@ -226,7 +227,7 @@ class TestMain:
"""Tests for main function."""
def test_main_prints_instance_id(self):
"""Main should print instance ID on startup."""
"""Main should print instance ID on startup when FREECAD_MCP_TESTING is set."""
import freecad_mcp.server as server_module
from freecad_mcp.config import TransportType
@@ -240,6 +241,7 @@ class TestMain:
patch.object(server_module, "get_config", return_value=mock_config),
patch.object(server_module.mcp, "run") as mock_run,
patch("builtins.print") as mock_print,
patch.dict(os.environ, {"FREECAD_MCP_TESTING": "1"}),
):
# Mock run to exit immediately
mock_run.return_value = None
@@ -258,6 +260,37 @@ class TestMain:
)
assert instance_id_call.kwargs.get("file") == sys.stderr
def test_main_no_instance_id_without_testing_env(self):
"""Main should NOT print instance ID when FREECAD_MCP_TESTING is unset."""
import freecad_mcp.server as server_module
from freecad_mcp.config import TransportType
mock_config = MagicMock()
mock_config.log_level = "INFO"
mock_config.mode = FreecadMode.EMBEDDED
mock_config.transport = TransportType.STDIO
# Ensure FREECAD_MCP_TESTING is not set
env_without_testing = {
k: v for k, v in os.environ.items() if k != "FREECAD_MCP_TESTING"
}
with (
patch.object(sys, "argv", DEFAULT_ARGV),
patch.object(server_module, "get_config", return_value=mock_config),
patch.object(server_module.mcp, "run") as mock_run,
patch("builtins.print") as mock_print,
patch.dict(os.environ, env_without_testing, clear=True),
):
# Mock run to exit immediately
mock_run.return_value = None
server_module.main()
# Check that instance ID was NOT printed
print_calls = [str(call) for call in mock_print.call_args_list]
assert not any("FREECAD_MCP_INSTANCE_ID=" in call for call in print_calls)
def test_main_http_transport(self):
"""Main should start HTTP transport when configured."""
import freecad_mcp.server as server_module
@@ -438,6 +471,7 @@ class TestStdioProtocolCleanliness:
"FREECAD_MODE": "xmlrpc",
"FREECAD_XMLRPC_PORT": "59999",
"FREECAD_SOCKET_HOST": "localhost",
"FREECAD_MCP_TESTING": "1", # Enable stderr instance ID output
},
)