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.
Files changed (46) hide show
  1. openscad_mcp_server-0.8.0/.github/workflows/auto-tag.yml +55 -0
  2. openscad_mcp_server-0.8.0/.github/workflows/publish.yml +43 -0
  3. openscad_mcp_server-0.8.0/.gitignore +9 -0
  4. openscad_mcp_server-0.8.0/.kiro/specs/openscad-mcp-server/tasks.md +188 -0
  5. openscad_mcp_server-0.8.0/Dockerfile.build +11 -0
  6. openscad_mcp_server-0.8.0/Dockerfile.render +13 -0
  7. openscad_mcp_server-0.8.0/LICENSE +21 -0
  8. openscad_mcp_server-0.8.0/PKG-INFO +219 -0
  9. openscad_mcp_server-0.8.0/README.md +203 -0
  10. openscad_mcp_server-0.8.0/pyproject.toml +33 -0
  11. openscad_mcp_server-0.8.0/src/openscad_mcp_server/__init__.py +5 -0
  12. openscad_mcp_server-0.8.0/src/openscad_mcp_server/__main__.py +14 -0
  13. openscad_mcp_server-0.8.0/src/openscad_mcp_server/models.py +101 -0
  14. openscad_mcp_server-0.8.0/src/openscad_mcp_server/prompts/__init__.py +1 -0
  15. openscad_mcp_server-0.8.0/src/openscad_mcp_server/prompts/workflow.py +334 -0
  16. openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/__init__.py +1 -0
  17. openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/library_ref.py +80 -0
  18. openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/openscad_syntax.py +192 -0
  19. openscad_mcp_server-0.8.0/src/openscad_mcp_server/resources/pitfalls.py +161 -0
  20. openscad_mcp_server-0.8.0/src/openscad_mcp_server/server.py +400 -0
  21. openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/__init__.py +1 -0
  22. openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/container.py +140 -0
  23. openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/feedback_service.py +173 -0
  24. openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/file_manager.py +132 -0
  25. openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/library_service.py +274 -0
  26. openscad_mcp_server-0.8.0/src/openscad_mcp_server/services/session.py +28 -0
  27. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/__init__.py +1 -0
  28. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/build_stl.py +86 -0
  29. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/feedback_tools.py +69 -0
  30. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/finalize.py +39 -0
  31. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/init_tool.py +73 -0
  32. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/library_tools.py +110 -0
  33. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/render_images.py +148 -0
  34. openscad_mcp_server-0.8.0/src/openscad_mcp_server/tools/save_code.py +72 -0
  35. openscad_mcp_server-0.8.0/tests/__init__.py +1 -0
  36. openscad_mcp_server-0.8.0/tests/test_confidence.py +38 -0
  37. openscad_mcp_server-0.8.0/tests/test_container.py +158 -0
  38. openscad_mcp_server-0.8.0/tests/test_feedback.py +166 -0
  39. openscad_mcp_server-0.8.0/tests/test_file_manager.py +170 -0
  40. openscad_mcp_server-0.8.0/tests/test_init_tool.py +102 -0
  41. openscad_mcp_server-0.8.0/tests/test_library_service.py +292 -0
  42. openscad_mcp_server-0.8.0/tests/test_render.py +293 -0
  43. openscad_mcp_server-0.8.0/tests/test_save_code.py +156 -0
  44. openscad_mcp_server-0.8.0/tests/test_server.py +80 -0
  45. openscad_mcp_server-0.8.0/tests/test_session.py +49 -0
  46. 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,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ *.egg
8
+ .pytest_cache/
9
+ .hypothesis/
@@ -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,11 @@
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
+ ENTRYPOINT ["xvfb-run", "--auto-servernum", "openscad"]
@@ -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