openscad-mcp-server 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openscad_mcp_server-0.8.0/.github/workflows/auto-tag.yml +55 -0
- openscad_mcp_server-0.8.0/.github/workflows/publish.yml +43 -0
- openscad_mcp_server-0.8.0/.gitignore +9 -0
- openscad_mcp_server-0.8.0/.kiro/specs/openscad-mcp-server/tasks.md +188 -0
- openscad_mcp_server-0.8.0/Dockerfile.build +11 -0
- openscad_mcp_server-0.8.0/Dockerfile.render +13 -0
- openscad_mcp_server-0.8.0/LICENSE +21 -0
- openscad_mcp_server-0.8.0/PKG-INFO +219 -0
- openscad_mcp_server-0.8.0/README.md +203 -0
- openscad_mcp_server-0.8.0/pyproject.toml +33 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/__init__.py +5 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/__main__.py +14 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/models.py +101 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/prompts/__init__.py +1 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/prompts/workflow.py +334 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/__init__.py +1 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/library_ref.py +80 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/openscad_syntax.py +192 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/pitfalls.py +161 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/server.py +400 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/__init__.py +1 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/container.py +140 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/feedback_service.py +173 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/file_manager.py +132 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/library_service.py +274 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/session.py +28 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/__init__.py +1 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/build_stl.py +86 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/feedback_tools.py +69 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/finalize.py +39 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/init_tool.py +73 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/library_tools.py +110 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/render_images.py +148 -0
- openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/save_code.py +72 -0
- openscad_mcp_server-0.8.0/tests/__init__.py +1 -0
- openscad_mcp_server-0.8.0/tests/test_confidence.py +38 -0
- openscad_mcp_server-0.8.0/tests/test_container.py +158 -0
- openscad_mcp_server-0.8.0/tests/test_feedback.py +166 -0
- openscad_mcp_server-0.8.0/tests/test_file_manager.py +170 -0
- openscad_mcp_server-0.8.0/tests/test_init_tool.py +102 -0
- openscad_mcp_server-0.8.0/tests/test_library_service.py +292 -0
- openscad_mcp_server-0.8.0/tests/test_render.py +293 -0
- openscad_mcp_server-0.8.0/tests/test_save_code.py +156 -0
- openscad_mcp_server-0.8.0/tests/test_server.py +80 -0
- openscad_mcp_server-0.8.0/tests/test_session.py +49 -0
- openscad_mcp_server-0.8.0/tests/test_version_tag.py +51 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Auto Tag and Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
auto-tag:
|
|
10
|
+
if: github.event.pull_request.merged == true
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Read version from pyproject.toml
|
|
19
|
+
id: version
|
|
20
|
+
run: |
|
|
21
|
+
VERSION=$(python3 -c "
|
|
22
|
+
import re, pathlib
|
|
23
|
+
text = pathlib.Path('pyproject.toml').read_text()
|
|
24
|
+
match = re.search(r'^version\s*=\s*\"([^\"]+)\"', text, re.MULTILINE)
|
|
25
|
+
print(match.group(1))
|
|
26
|
+
")
|
|
27
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
28
|
+
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
29
|
+
|
|
30
|
+
- name: Check if tag exists
|
|
31
|
+
id: check_tag
|
|
32
|
+
run: |
|
|
33
|
+
if git ls-remote --tags origin "refs/tags/${{ steps.version.outputs.tag }}" | grep -q .; then
|
|
34
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
35
|
+
echo "Tag ${{ steps.version.outputs.tag }} already exists, skipping."
|
|
36
|
+
else
|
|
37
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
- name: Create tag and release
|
|
41
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
42
|
+
env:
|
|
43
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
+
run: |
|
|
45
|
+
git tag "${{ steps.version.outputs.tag }}"
|
|
46
|
+
git push origin "${{ steps.version.outputs.tag }}"
|
|
47
|
+
|
|
48
|
+
- name: Create GitHub release
|
|
49
|
+
if: steps.check_tag.outputs.exists == 'false'
|
|
50
|
+
env:
|
|
51
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
52
|
+
run: |
|
|
53
|
+
gh release create "${{ steps.version.outputs.tag }}" \
|
|
54
|
+
--title "${{ steps.version.outputs.tag }}" \
|
|
55
|
+
--generate-notes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.11"
|
|
20
|
+
|
|
21
|
+
- name: Install build tools
|
|
22
|
+
run: pip install build
|
|
23
|
+
|
|
24
|
+
- name: Verify version matches tag
|
|
25
|
+
run: |
|
|
26
|
+
TAG="${GITHUB_REF#refs/tags/v}"
|
|
27
|
+
VERSION=$(python3 -c "
|
|
28
|
+
import re, pathlib
|
|
29
|
+
text = pathlib.Path('pyproject.toml').read_text()
|
|
30
|
+
match = re.search(r'^version\s*=\s*\"([^\"]+)\"', text, re.MULTILINE)
|
|
31
|
+
print(match.group(1))
|
|
32
|
+
")
|
|
33
|
+
if [ "$TAG" != "$VERSION" ]; then
|
|
34
|
+
echo "::error::Tag v$TAG does not match package version $VERSION"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
echo "Version $VERSION matches tag."
|
|
38
|
+
|
|
39
|
+
- name: Build package
|
|
40
|
+
run: python -m build
|
|
41
|
+
|
|
42
|
+
- name: Publish to PyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Implementation Plan: OpenSCAD MCP Server
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Incremental implementation of the OpenSCAD MCP server in Python, building from core data models and services up through tool handlers, resources, prompts, container images, packaging, and CI/CD. Each task builds on previous work so there is no orphaned code.
|
|
6
|
+
|
|
7
|
+
NOTE: After completing each task, always create a feature branch, commit, push, merge to main, and push main. Never leave work unmerged.
|
|
8
|
+
|
|
9
|
+
## Tasks
|
|
10
|
+
|
|
11
|
+
- [x] 1. Project scaffolding and package configuration
|
|
12
|
+
- [x] 1.1 Create `pyproject.toml` with PEP 621 metadata, dependencies (`mcp`, `aiohttp`, `beautifulsoup4`), test extras (`pytest`, `pytest-asyncio`, `hypothesis`), console script entry point `openscad-mcp-server`, and build backend
|
|
13
|
+
- _Requirements: 13.1, 13.2, 13.4, 13.5_
|
|
14
|
+
- [x] 1.2 Create directory structure: `src/openscad_mcp_server/` with `__init__.py`, `__main__.py`, `tools/__init__.py`, `services/__init__.py`, `resources/__init__.py`, `prompts/__init__.py`, and `tests/__init__.py`
|
|
15
|
+
- _Requirements: 13.3_
|
|
16
|
+
- [x] 1.3 Implement `__main__.py` entry point that calls `main()` from `server.py`
|
|
17
|
+
- _Requirements: 13.3_
|
|
18
|
+
|
|
19
|
+
- [x] 2. Data models and session state
|
|
20
|
+
- [x] 2.1 Create `src/openscad_mcp_server/models.py` with dataclasses: `ContainerResult`, `LibraryCatalogEntry`, `ModuleSignature`, `LibrarySource`, `CameraAngle`, `InspectionImage`, `FeedbackRecord`, `FeedbackIndexEntry`, and the `CAMERA_ANGLES` constant (8 predefined angles)
|
|
21
|
+
- _Requirements: 6.2, 7.2, 12.5_
|
|
22
|
+
- [x] 2.2 Create `src/openscad_mcp_server/services/session.py` with `SessionState` class: `reviewed_libraries` set, `latest_confidence_score`, `container_runtime`, `container_executable`, `working_dir`, and methods `mark_library_reviewed`, `is_library_reviewed`, `set_confidence`
|
|
23
|
+
- _Requirements: 17.6, 7.5_
|
|
24
|
+
- [x] 2.3 Write property test for session state (Property 25: Reviewed libraries tracking round trip)
|
|
25
|
+
- **Property 25: Reviewed libraries tracking round trip**
|
|
26
|
+
- **Validates: Requirements 17.6**
|
|
27
|
+
- [x] 2.4 Write property test for confidence score computation (Property 22: Overall confidence is minimum of per-angle scores)
|
|
28
|
+
- **Property 22: Overall confidence is minimum of per-angle scores**
|
|
29
|
+
- **Validates: Requirements 16.8**
|
|
30
|
+
|
|
31
|
+
- [x] 3. Container manager service
|
|
32
|
+
- [x] 3.1 Create `src/openscad_mcp_server/services/container.py` with `ContainerManager` class: `__init__(runtime, executable)`, `detect()` static method probing Docker then Finch, `run(image, command, mounts, timeout)` executing container via `asyncio.create_subprocess_exec`, `image_exists(image)`, `build_image(dockerfile, tag)`
|
|
33
|
+
- _Requirements: 5.1, 5.2, 6.3, 8.3, 10.1_
|
|
34
|
+
- [x] 3.2 Write property test for container command generation (Property 3: Runtime-agnostic commands)
|
|
35
|
+
- **Property 3: Container command generation is runtime-agnostic**
|
|
36
|
+
- **Validates: Requirements 5.2, 6.3**
|
|
37
|
+
- [x] 3.3 Write property test for container mount correctness (Property 4: Container mount correctness)
|
|
38
|
+
- **Property 4: Container mount correctness**
|
|
39
|
+
- **Validates: Requirements 5.5, 9.6**
|
|
40
|
+
- [x] 3.4 Write property test for build error propagation (Property 5: Build error propagation)
|
|
41
|
+
- **Property 5: Build error propagation**
|
|
42
|
+
- **Validates: Requirements 5.4**
|
|
43
|
+
- [x] 3.5 Write property test for container start failure diagnostics (Property 6: Container start failure diagnostics)
|
|
44
|
+
- **Property 6: Container start failure diagnostics**
|
|
45
|
+
- **Validates: Requirements 5.6, 8.4**
|
|
46
|
+
|
|
47
|
+
- [x] 4. File manager service
|
|
48
|
+
- [x] 4.1 Create `src/openscad_mcp_server/services/file_manager.py` with `FileManager` class: manages working area (`working/`), final output (`output/`), libraries (`libraries/`), feedback (`feedback/`) directories. Methods: `save_code(code, filename)` with `.scad` extension normalization, `save_stl(data, filename)`, `save_renders(images)`, `clear_renders()`, `finalize()` copying working area to output, `ensure_dirs()` for auto-creation
|
|
49
|
+
- _Requirements: 4.1, 4.3, 4.4, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6_
|
|
50
|
+
- [x] 4.2 Write property test for save-code round trip (Property 1: Save-code round trip)
|
|
51
|
+
- **Property 1: Save-code round trip**
|
|
52
|
+
- **Validates: Requirements 4.1, 4.4**
|
|
53
|
+
- [x] 4.3 Write property test for filename extension normalization (Property 2: Filename extension normalization)
|
|
54
|
+
- **Property 2: Filename extension normalization**
|
|
55
|
+
- **Validates: Requirements 4.3**
|
|
56
|
+
- [x] 4.4 Write property test for working area overwrite invariant (Property 16: Working area overwrite invariant)
|
|
57
|
+
- **Property 16: Working area overwrite invariant**
|
|
58
|
+
- **Validates: Requirements 11.1, 11.2, 11.3, 11.4**
|
|
59
|
+
- [x] 4.5 Write property test for finalize copies all artifacts (Property 17: Finalize copies all working area artifacts)
|
|
60
|
+
- **Property 17: Finalize copies all working area artifacts**
|
|
61
|
+
- **Validates: Requirements 11.5**
|
|
62
|
+
|
|
63
|
+
- [x] 5. Checkpoint - Core services
|
|
64
|
+
- Ensure all tests pass, ask the user if questions arise.
|
|
65
|
+
|
|
66
|
+
- [x] 6. Library service
|
|
67
|
+
- [x] 6.1 Create `src/openscad_mcp_server/services/library_service.py` with `LibraryService` class: `browse_catalog(force_refresh)` fetching and parsing `openscad.org/libraries.html` with BeautifulSoup, `fetch_library(name, source_url, force_refresh)` cloning/downloading from source repo, `read_source(name)` reading `.scad` files and extracting module signatures. Includes session-level catalog cache and library cache with force-refresh support
|
|
68
|
+
- _Requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.7, 9.8, 17.1, 17.4_
|
|
69
|
+
- [x] 6.2 Write property test for catalog parser (Property 11: Catalog parser extracts structured entries)
|
|
70
|
+
- **Property 11: Catalog parser extracts structured entries**
|
|
71
|
+
- **Validates: Requirements 9.1**
|
|
72
|
+
- [x] 6.3 Write property test for library cache hit (Property 12: Library cache hit avoids re-download)
|
|
73
|
+
- **Property 12: Library cache hit avoids re-download**
|
|
74
|
+
- **Validates: Requirements 9.5**
|
|
75
|
+
- [x] 6.4 Write property test for fetch success (Property 13: Fetch-library success returns valid path)
|
|
76
|
+
- **Property 13: Fetch-library success returns valid path**
|
|
77
|
+
- **Validates: Requirements 9.7**
|
|
78
|
+
- [x] 6.5 Write property test for fetch failure (Property 14: Fetch-library failure includes source URL)
|
|
79
|
+
- **Property 14: Fetch-library failure includes source URL**
|
|
80
|
+
- **Validates: Requirements 9.8**
|
|
81
|
+
- [x] 6.6 Write property test for read-library-source (Property 23: Read-library-source returns source and summary)
|
|
82
|
+
- **Property 23: Read-library-source returns source and summary**
|
|
83
|
+
- **Validates: Requirements 2.2, 2.4, 17.1, 17.4**
|
|
84
|
+
|
|
85
|
+
- [x] 7. Feedback service
|
|
86
|
+
- [x] 7.1 Create `src/openscad_mcp_server/services/feedback_service.py` with `FeedbackService` class: `submit(critique, root_cause, working_area, confidence_score)` creating timestamped feedback record with artifact copies and index update, `list_records()` returning all feedback summaries. Feedback index stored as `feedback-index.json`
|
|
87
|
+
- _Requirements: 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7, 12.8, 12.9, 12.10_
|
|
88
|
+
- [x] 7.2 Write property test for feedback record completeness (Property 18: Feedback record completeness)
|
|
89
|
+
- **Property 18: Feedback record completeness**
|
|
90
|
+
- **Validates: Requirements 12.2, 12.3, 12.4, 12.5, 12.8**
|
|
91
|
+
- [x] 7.3 Write property test for feedback index round trip (Property 19: Feedback index round trip)
|
|
92
|
+
- **Property 19: Feedback index round trip**
|
|
93
|
+
- **Validates: Requirements 12.6, 12.7, 12.10**
|
|
94
|
+
- [x] 7.4 Write property test for confidence disagreement flag (Property 20: Confidence disagreement flag logic)
|
|
95
|
+
- **Property 20: Confidence disagreement flag logic**
|
|
96
|
+
- **Validates: Requirements 12.9**
|
|
97
|
+
|
|
98
|
+
- [x] 8. Checkpoint - All services complete
|
|
99
|
+
- Ensure all tests pass, ask the user if questions arise.
|
|
100
|
+
|
|
101
|
+
- [x] 9. MCP tool handlers - init and save-code
|
|
102
|
+
- [x] 9.1 Create `src/openscad_mcp_server/tools/init_tool.py` with `init` tool: probes Docker then Finch via `ContainerManager.detect()`, runs test container, returns runtime info and persistence content for LLM memory
|
|
103
|
+
- _Requirements: 10.1, 10.5, 10.6_
|
|
104
|
+
- [x] 9.2 Write property test for init runtime detection (Property 15: Init tool runtime detection)
|
|
105
|
+
- **Property 15: Init tool runtime detection**
|
|
106
|
+
- **Validates: Requirements 10.1, 10.5**
|
|
107
|
+
- [x] 9.3 Create `src/openscad_mcp_server/tools/save_code.py` with `save-code` tool: validates filename, parses `include`/`use` statements, checks session for library review status, delegates to `FileManager.save_code()`. Rejects save if referenced libraries not reviewed
|
|
108
|
+
- _Requirements: 4.1, 4.2, 4.3, 4.4, 17.2, 17.3_
|
|
109
|
+
- [x] 9.4 Write property test for library review enforcement (Property 24: Library review enforcement on save)
|
|
110
|
+
- **Property 24: Library review enforcement on save**
|
|
111
|
+
- **Validates: Requirements 17.2, 17.3**
|
|
112
|
+
|
|
113
|
+
- [x] 10. MCP tool handlers - build and render
|
|
114
|
+
- [x] 10.1 Create `src/openscad_mcp_server/tools/build_stl.py` with `build-stl` tool: launches build container via `ContainerManager.run()` with working directory and library mounts, runs `openscad -o output.stl <file>`, returns STL path or full error output
|
|
115
|
+
- _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6_
|
|
116
|
+
- [x] 10.2 Create `src/openscad_mcp_server/tools/render_images.py` with `render-images` tool: launches render container for each of 8 camera angles, generates 1024x1024 PNG images, reads PNGs from disk, returns list of MCP `TextContent` (camera metadata) and `ImageContent` blocks (base64 PNG). Handles partial failures by reporting failed angles
|
|
117
|
+
- _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 7.1, 7.2_
|
|
118
|
+
- [x] 10.3 Write property test for render produces 8 images (Property 7: Render produces exactly 8 images with correct angles)
|
|
119
|
+
- **Property 7: Render produces exactly 8 images with correct angles**
|
|
120
|
+
- **Validates: Requirements 6.1, 6.2, 6.4**
|
|
121
|
+
- [x] 10.4 Write property test for render command spec (Property 8: Render command specifies PNG at 1024x1024)
|
|
122
|
+
- **Property 8: Render command specifies PNG at 1024x1024**
|
|
123
|
+
- **Validates: Requirements 6.5**
|
|
124
|
+
- [x] 10.5 Write property test for partial render failure (Property 9: Partial render failure reports failed angles)
|
|
125
|
+
- **Property 9: Partial render failure reports failed angles**
|
|
126
|
+
- **Validates: Requirements 6.6**
|
|
127
|
+
- [x] 10.6 Write property test for MCP ImageContent blocks (Property 10: Render tool returns MCP ImageContent blocks)
|
|
128
|
+
- **Property 10: Render tool returns MCP ImageContent blocks**
|
|
129
|
+
- **Validates: Requirements 7.1, 7.2**
|
|
130
|
+
|
|
131
|
+
- [ ] 11. MCP tool handlers - library and feedback tools
|
|
132
|
+
- [ ] 11.1 Create `src/openscad_mcp_server/tools/library_tools.py` with four tools: `browse-library-catalog` (delegates to `LibraryService.browse_catalog`), `fetch-library` (delegates to `LibraryService.fetch_library`), `read-library-source` (delegates to `LibraryService.read_source`, marks library reviewed in session), `list-reviewed-libraries` (reads from session state)
|
|
133
|
+
- _Requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 17.1, 17.4, 17.5, 17.6_
|
|
134
|
+
- [ ] 11.2 Create `src/openscad_mcp_server/tools/feedback_tools.py` with two tools: `submit-feedback` (delegates to `FeedbackService.submit`, records confidence score and disagreement flag), `list-feedback` (delegates to `FeedbackService.list_records`)
|
|
135
|
+
- _Requirements: 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7, 12.8, 12.9, 12.10_
|
|
136
|
+
- [ ] 11.3 Create `src/openscad_mcp_server/tools/finalize.py` with `finalize` tool: delegates to `FileManager.finalize()`, returns final output directory path and file list
|
|
137
|
+
- _Requirements: 11.5, 11.6_
|
|
138
|
+
|
|
139
|
+
- [ ] 12. Checkpoint - All tools implemented
|
|
140
|
+
- Ensure all tests pass, ask the user if questions arise.
|
|
141
|
+
|
|
142
|
+
- [ ] 13. MCP resources
|
|
143
|
+
- [ ] 13.1 Create `src/openscad_mcp_server/resources/openscad_syntax.py` with `openscad://syntax-reference` resource: returns OpenSCAD language reference covering primitives, transformations, boolean operations, module definitions, variable scoping
|
|
144
|
+
- _Requirements: 2.1_
|
|
145
|
+
- [ ] 13.2 Create `src/openscad_mcp_server/resources/library_ref.py` with `openscad://library-reference/{library_name}` dynamic resource: generates reference from fetched library source including module signatures, parameter types/defaults, coordinate system conventions, usage examples
|
|
146
|
+
- _Requirements: 2.2, 2.3, 2.4_
|
|
147
|
+
- [ ] 13.3 Create `src/openscad_mcp_server/resources/pitfalls.py` with `openscad://pitfalls` resource: returns common pitfalls (manifold errors, z-fighting, boolean order, coordinate mismatches, missing `$fn`)
|
|
148
|
+
- _Requirements: 2.5_
|
|
149
|
+
|
|
150
|
+
- [ ] 14. MCP workflow prompt
|
|
151
|
+
- [ ] 14.1 Create `src/openscad_mcp_server/prompts/workflow.py` with `openscad-workflow` prompt: full step-by-step instructions covering init/settings check, library discovery/fetch/review, code generation, build, render, systematic per-angle inspection with checklist, per-angle confidence scoring, overall confidence as min of per-angle scores, iteration on low confidence, finalize, and feedback handling. Includes prohibitions on skipping angles, declaring complete below 0.5 confidence, and guessing at library APIs
|
|
152
|
+
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 7.4, 7.5, 7.6, 7.7, 9.9, 10.2, 10.3, 10.4, 16.1, 16.2, 16.3, 16.4, 16.5, 16.6, 16.7, 16.8, 16.9, 17.5_
|
|
153
|
+
|
|
154
|
+
- [ ] 15. MCP server wiring
|
|
155
|
+
- [ ] 15.1 Create `src/openscad_mcp_server/server.py`: instantiate `Server("openscad-mcp-server")`, create shared `SessionState`, register all 11 tools via `@app.tool()` decorators, register 3 resources via `@app.resource()`, register workflow prompt via `@app.prompt()`, implement `main()` async function running stdio transport
|
|
156
|
+
- _Requirements: 1.1, 1.2, 1.3_
|
|
157
|
+
- [ ] 15.2 Write unit tests for server initialization verifying tool/resource/prompt registration returns expected names
|
|
158
|
+
- _Requirements: 1.1, 1.2_
|
|
159
|
+
|
|
160
|
+
- [ ] 16. Checkpoint - Server fully wired
|
|
161
|
+
- Ensure all tests pass, ask the user if questions arise.
|
|
162
|
+
|
|
163
|
+
- [ ] 17. Container images
|
|
164
|
+
- [ ] 17.1 Create `Dockerfile.build` based on `ubuntu:24.04`, installing `openscad` package, with entrypoint `openscad`, working directory mount at `/work`, and `OPENSCADPATH` set to `/work/libraries`
|
|
165
|
+
- _Requirements: 8.1, 5.5, 9.6_
|
|
166
|
+
- [ ] 17.2 Create `Dockerfile.render` based on same base image, configured for OpenSCAD PNG export with `--camera` and `--imgsize` parameters
|
|
167
|
+
- _Requirements: 8.2_
|
|
168
|
+
|
|
169
|
+
- [ ] 18. GitHub Actions CI/CD
|
|
170
|
+
- [ ] 18.1 Create `.github/workflows/auto-tag.yml`: triggers on PR merge to main, reads version from `pyproject.toml`, creates git tag `v{version}`, creates GitHub release with auto-generated notes. Skips if tag already exists. Uses `GITHUB_TOKEN`
|
|
171
|
+
- _Requirements: 15.1, 15.2, 15.3, 15.4, 15.5_
|
|
172
|
+
- [ ] 18.2 Create `.github/workflows/publish.yml`: triggers on `v*` tag push, builds Python package, publishes to PyPI using trusted publishing (OIDC). Verifies package version matches tag. Reports errors on build or publish failure
|
|
173
|
+
- _Requirements: 14.1, 14.2, 14.3, 14.4, 14.5_
|
|
174
|
+
- [ ] 18.3 Write property test for version-tag matching (Property 21: Version-tag matching)
|
|
175
|
+
- **Property 21: Version-tag matching**
|
|
176
|
+
- **Validates: Requirements 14.5**
|
|
177
|
+
|
|
178
|
+
- [ ] 19. Final checkpoint - Full integration
|
|
179
|
+
- Ensure all tests pass, ask the user if questions arise.
|
|
180
|
+
|
|
181
|
+
## Notes
|
|
182
|
+
|
|
183
|
+
- All tasks are required — no optional tasks
|
|
184
|
+
- Each task references specific requirements for traceability
|
|
185
|
+
- Checkpoints ensure incremental validation
|
|
186
|
+
- Property tests validate universal correctness properties from the design document (25 properties)
|
|
187
|
+
- The design uses Python throughout — all code examples and tests use Python with Hypothesis for PBT
|
|
188
|
+
- Container images may share a single base image since OpenSCAD handles both STL compilation and PNG export
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM ubuntu:24.04
|
|
2
|
+
|
|
3
|
+
RUN apt-get update \
|
|
4
|
+
&& apt-get install -y --no-install-recommends openscad xvfb \
|
|
5
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
6
|
+
|
|
7
|
+
ENV OPENSCADPATH=/work/libraries
|
|
8
|
+
|
|
9
|
+
WORKDIR /work
|
|
10
|
+
|
|
11
|
+
# Render entrypoint: OpenSCAD with xvfb for headless PNG export.
|
|
12
|
+
# Callers supply --camera, --imgsize, --export-format, -o, and input file.
|
|
13
|
+
ENTRYPOINT ["xvfb-run", "--auto-servernum", "openscad"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Lenk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openscad-mcp-server
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: MCP server for OpenSCAD 3D model generation, compilation, and visual inspection
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: aiohttp>=3.9
|
|
9
|
+
Requires-Dist: beautifulsoup4>=4.12
|
|
10
|
+
Requires-Dist: mcp>=1.0
|
|
11
|
+
Provides-Extra: test
|
|
12
|
+
Requires-Dist: hypothesis>=6.100; extra == 'test'
|
|
13
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
|
|
14
|
+
Requires-Dist: pytest>=8.0; extra == 'test'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# openscad-mcp-server
|
|
18
|
+
|
|
19
|
+
An MCP server that gives LLMs the ability to design, build, and visually verify 3D models using [OpenSCAD](https://openscad.org) — all through natural conversation.
|
|
20
|
+
|
|
21
|
+
The server handles the entire workflow: discovering and downloading OpenSCAD libraries on demand, saving code, compiling STL files in Docker/Finch containers, rendering 8-angle inspection images, and returning those images directly to the LLM's vision model as inline base64 PNGs. A built-in workflow prompt guides the LLM through systematic per-angle inspection with confidence scoring, so it catches defects before declaring a model complete.
|
|
22
|
+
|
|
23
|
+
## Why this exists
|
|
24
|
+
|
|
25
|
+
LLMs can write OpenSCAD code, but they can't run it, see the result, or iterate on mistakes. This server closes that loop. The LLM writes code, the server compiles and renders it in a container, and the LLM sees the rendered model from 8 angles — then decides whether to fix issues or finalize.
|
|
26
|
+
|
|
27
|
+
Key design choices:
|
|
28
|
+
- **Vision-native**: Rendered images come back as MCP `ImageContent` blocks, not file paths. The LLM sees the model directly.
|
|
29
|
+
- **Zero config**: The `init` tool auto-detects Docker or Finch and persists settings via the LLM's memory mechanism. No env vars or config files needed.
|
|
30
|
+
- **Library-aware**: Libraries are discovered from the [official OpenSCAD catalog](https://openscad.org/libraries.html) and downloaded on demand. The server enforces that the LLM reads library source code before using it — no guessing at APIs.
|
|
31
|
+
- **Correctness-first**: The workflow prompt requires per-angle confidence scoring (overall = min of all angles) and forbids declaring a model complete below 0.5 confidence.
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
### Prerequisites
|
|
36
|
+
|
|
37
|
+
- [Docker](https://docs.docker.com/get-docker/) or [Finch](https://runfinch.com/) installed and running
|
|
38
|
+
- Python 3.11+ (for development) or [uv](https://docs.astral.sh/uv/) (for running directly)
|
|
39
|
+
|
|
40
|
+
### Run with uvx (no install needed)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uvx openscad-mcp-server
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Or install and run
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install openscad-mcp-server
|
|
50
|
+
openscad-mcp-server
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The server communicates over stdio, so it's meant to be launched by an MCP client (Claude Desktop, Kiro, etc.), not run interactively.
|
|
54
|
+
|
|
55
|
+
### Build the container images
|
|
56
|
+
|
|
57
|
+
The server needs two container images for compiling and rendering. Build them from the included Dockerfiles:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
docker build -f Dockerfile.build -t openscad-build .
|
|
61
|
+
docker build -f Dockerfile.render -t openscad-render .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or with Finch:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
finch build -f Dockerfile.build -t openscad-build .
|
|
68
|
+
finch build -f Dockerfile.render -t openscad-render .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## MCP client configuration
|
|
72
|
+
|
|
73
|
+
### Kiro
|
|
74
|
+
|
|
75
|
+
Add to `.kiro/settings/mcp.json`:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"openscad": {
|
|
81
|
+
"command": "uvx",
|
|
82
|
+
"args": ["openscad-mcp-server"]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Claude Desktop
|
|
89
|
+
|
|
90
|
+
Add to your Claude Desktop config:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mcpServers": {
|
|
95
|
+
"openscad": {
|
|
96
|
+
"command": "uvx",
|
|
97
|
+
"args": ["openscad-mcp-server"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Tools
|
|
105
|
+
|
|
106
|
+
The server exposes 11 tools:
|
|
107
|
+
|
|
108
|
+
| Tool | Description |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `init` | Auto-detect Docker/Finch, configure working directory, return persistence content |
|
|
111
|
+
| `save-code` | Save OpenSCAD code to the working area (enforces library review) |
|
|
112
|
+
| `build-stl` | Compile `.scad` → `.stl` in a container |
|
|
113
|
+
| `render-images` | Render STL from 8 camera angles, return inline base64 PNG images |
|
|
114
|
+
| `browse-library-catalog` | Fetch the official OpenSCAD library listing |
|
|
115
|
+
| `fetch-library` | Download a library from its source repository |
|
|
116
|
+
| `read-library-source` | Read library `.scad` files and extract module signatures |
|
|
117
|
+
| `list-reviewed-libraries` | Show which libraries have been reviewed this session |
|
|
118
|
+
| `submit-feedback` | Record user feedback with artifact snapshots and confidence data |
|
|
119
|
+
| `list-feedback` | List all feedback records for analysis |
|
|
120
|
+
| `finalize` | Copy working area artifacts to the final output directory |
|
|
121
|
+
|
|
122
|
+
## Resources
|
|
123
|
+
|
|
124
|
+
| Resource | URI | Description |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| Syntax Reference | `openscad://syntax-reference` | OpenSCAD language reference (primitives, transforms, booleans, modules) |
|
|
127
|
+
| Library Reference | `openscad://library-reference/{name}` | Dynamic reference generated from fetched library source |
|
|
128
|
+
| Common Pitfalls | `openscad://pitfalls` | Manifold errors, z-fighting, boolean order, missing `$fn`, etc. |
|
|
129
|
+
|
|
130
|
+
## Prompts
|
|
131
|
+
|
|
132
|
+
| Prompt | Description |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `openscad-workflow` | Full step-by-step workflow: init → library discovery → code → build → render → inspect → iterate → finalize |
|
|
135
|
+
|
|
136
|
+
## How the workflow works
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
User: "Make me a phone stand with a 75° angle"
|
|
140
|
+
│
|
|
141
|
+
▼
|
|
142
|
+
┌─────────────────────────────────────────────┐
|
|
143
|
+
│ 1. init → detect Docker/Finch │
|
|
144
|
+
│ 2. browse-catalog → find relevant libraries │
|
|
145
|
+
│ 3. fetch-library → download BOSL2 │
|
|
146
|
+
│ 4. read-source → understand the API │
|
|
147
|
+
│ 5. save-code → write OpenSCAD code │
|
|
148
|
+
│ 6. build-stl → compile to STL │
|
|
149
|
+
│ 7. render-images → 8 angles, inline PNGs │
|
|
150
|
+
│ 8. inspect → per-angle confidence │
|
|
151
|
+
│ 9. iterate → fix issues, re-render │
|
|
152
|
+
│ 10. finalize → copy to output dir │
|
|
153
|
+
└─────────────────────────────────────────────┘
|
|
154
|
+
│
|
|
155
|
+
▼
|
|
156
|
+
Output: model.stl + model.scad + 8 inspection images
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The LLM inspects each rendered angle individually, assigns per-angle confidence scores, and computes the overall confidence as the minimum across all angles. If any angle scores below 0.5, the LLM must iterate before finalizing.
|
|
160
|
+
|
|
161
|
+
## Artifact layout
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
{working_dir}/
|
|
165
|
+
├── working/ # Latest iteration (overwritten each cycle)
|
|
166
|
+
│ ├── model.scad
|
|
167
|
+
│ ├── model.stl
|
|
168
|
+
│ └── renders/
|
|
169
|
+
│ ├── front.png
|
|
170
|
+
│ ├── back.png
|
|
171
|
+
│ ├── left.png
|
|
172
|
+
│ ├── right.png
|
|
173
|
+
│ ├── top.png
|
|
174
|
+
│ ├── bottom.png
|
|
175
|
+
│ ├── front-right-top-iso.png
|
|
176
|
+
│ └── back-left-top-iso.png
|
|
177
|
+
├── output/ # Final output (populated on finalize)
|
|
178
|
+
├── libraries/ # Downloaded OpenSCAD libraries
|
|
179
|
+
└── feedback/ # User feedback records with artifact snapshots
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Clone and install with test dependencies
|
|
186
|
+
git clone https://github.com/your-org/openscad-mcp-server.git
|
|
187
|
+
cd openscad-mcp-server
|
|
188
|
+
uv sync --extra test
|
|
189
|
+
|
|
190
|
+
# Run tests
|
|
191
|
+
uv run pytest tests/ -v
|
|
192
|
+
|
|
193
|
+
# Run with Hypothesis verbose output
|
|
194
|
+
uv run pytest tests/ -v --hypothesis-show-statistics
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Test suite
|
|
198
|
+
|
|
199
|
+
The test suite includes 51 tests covering all 25 correctness properties from the design spec, validated with [Hypothesis](https://hypothesis.readthedocs.io/) property-based testing:
|
|
200
|
+
|
|
201
|
+
- Save-code round trip and filename normalization
|
|
202
|
+
- Container command generation (runtime-agnostic)
|
|
203
|
+
- Mount correctness and error propagation
|
|
204
|
+
- Render output (8 angles, PNG format, MCP ImageContent blocks)
|
|
205
|
+
- Library catalog parsing, caching, and fetch error handling
|
|
206
|
+
- Feedback record completeness, index round trip, confidence disagreement
|
|
207
|
+
- Working area overwrite invariant and finalize correctness
|
|
208
|
+
- Session state tracking and confidence score computation
|
|
209
|
+
- Version-tag matching for release pipeline
|
|
210
|
+
- Server registration (tools, resources, prompts)
|
|
211
|
+
|
|
212
|
+
## CI/CD
|
|
213
|
+
|
|
214
|
+
- **PR merge → main**: Automatically creates a git tag and GitHub release from the version in `pyproject.toml`
|
|
215
|
+
- **Tag push (`v*`)**: Builds and publishes to PyPI using trusted publishing (OIDC)
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|