Skip to main content
Glama
Reshvanth-Y

bottle-cap-mcp

by Reshvanth-Y
README.md
# bottle-cap-mcp

NitroStack MCP server exposing three tools for the bottle-cap-cad workflow.
Vision/dimension extraction from the source photo happens **client-side**
(in Claude, the MCP client) — none of these tools re-derive dimensions from
pixels; `generate_visual_mesh` is the one exception, since Meshy infers
geometry directly from the image itself.

## Tools

| Tool | Input | Backend | Network |
|---|---|---|---|
| `generate_precise_cap` | dimensions JSON | CadQuery (Python — local subprocess, or remote via `CAD_API_URL`) | none / HTTPS |
| `fill` | hole/cavity geometry JSON (circle/rectangle/polygon, uniform or tapered) | CadQuery (Python — local subprocess, or remote via `CAD_API_URL`) | none / HTTPS |
| `repair_mesh` | mesh URL or base64 | PyMeshLab (Python — local subprocess, or remote via `CAD_API_URL`) | none / HTTPS |
| `generate_visual_mesh` | raw image (base64) | Meshy API | HTTPS |

## Setup

```bash
npm install
pip install -r requirements.txt --break-system-packages   # or your own venv
cp .env.example .env   # set MESHY_API_KEY
npm run build
npm start
```

For local iteration without a build step: `npm run dev`.

## Deploying to NitroCloud (or any Node-only host)

NitroCloud's managed hosting runs this server in a Node-only container —
there's no `python3` there for `getPythonCommand()` to find, so
`fill`/`generate_precise_cap`/`repair_mesh` fail with "Could not find a
working Python interpreter" if deployed as-is.

The fix: run `api/server.py` (a small FastAPI wrapper around the same three
CAD scripts) somewhere that *does* have Python — your own machine, a VPS,
Render, Railway, Fly.io, etc. — and point the NitroCloud deployment at it.

**1. Run the API somewhere with Python:**

```bash
# locally, for testing (tunnel with ngrok/cloudflared if NitroCloud needs to reach it)
pip install -r requirements.txt --break-system-packages
export CAD_API_KEY=<pick-a-secret>
uvicorn api.server:app --host 0.0.0.0 --port 8787

# or as a container, anywhere that runs Docker:
docker build -f api/Dockerfile -t bottle-cap-cad-api .
docker run -d -p 8787:8787 -e CAD_API_KEY=<pick-a-secret> bottle-cap-cad-api
```

> `pymeshlab`'s mesh I/O plugins are Qt-based and need `libgl1`/`libglu1-mesa`
> present on the host even for fully headless use — the Dockerfile already
> includes them, but a bare VPS/Render/Railway box may need them installed
> separately (`apt-get install libgl1 libglu1-mesa`), or `/repair` calls will
> fail with `Unknown format for load: stl`.

**2. Point the NitroCloud deployment at it** — set these in NitroCloud's
environment variables dashboard (not just your local `.env`, since that
doesn't travel with the deploy):

```
CAD_API_URL=https://<wherever-you-ran-the-api-above>
CAD_API_KEY=<same secret as above>
```

That's it — `fill.tools.ts`/`cap.tools.ts`/`repair.tools.ts` check for
`CAD_API_URL` at call time and switch to HTTP automatically. Leave it unset
for local NitroStudio dev and they fall back to the original local-subprocess
behavior, unchanged.

## Structure

```
bottle-cap-mcp/
├── src/
│   ├── app.module.ts        # registers the tool providers
│   ├── main.ts               # bootstrap / entrypoint
│   └── tools/
│       ├── shapes.ts          # shared circle/rectangle/polygon zod schema
│       ├── cap.tools.ts       # generate_precise_cap
│       ├── fill.tools.ts      # fill
│       ├── repair.tools.ts    # repair_mesh
│       ├── mesh.tools.ts      # generate_visual_mesh (pure TS, calls Meshy)
│       ├── python-runtime.ts  # local python3/python/py auto-detection
│       └── cad-api-client.ts  # optional HTTP client for api/server.py (CAD_API_URL)
├── cad/
│   ├── precise_cap.py         # CadQuery threaded-cap builder
│   ├── fill.py                # CadQuery plug builder
│   └── repair_mesh.py         # PyMeshLab repair pipeline
├── api/
│   ├── server.py              # FastAPI wrapper exposing /fill /cap /repair over HTTP
│   └── Dockerfile              # standalone image for hosting api/server.py
├── package.json
├── tsconfig.json
└── requirements.txt
```

## Known gaps (carried over from the design discussion)

- ~~**Real threads**~~ — resolved: `precise_cap.py` now builds a solid,
  closed top and cuts a blind bore (not a through-hole), and unions one or
  more real helical thread ridges onto the skirt's inner wall via a
  triangular/trapezoidal profile swept along a `cq.Wire.makeHelix()` path
  (see `make_thread_solid`). `threadStarts` (1-4), `threadDepth`, and
  `topThickness` are new optional fields on `generate_precise_cap` —
  `threadPitch` still falls back to an approximate PCO 1810-style pitch
  (3.18mm, single start) when omitted. This is a parametric approximation
  tuned for FDM printing, not a certified thread-spec generator; swap in
  `cq_warehouse`'s generators if you need an exact standard spec (e.g. a
  certified PCO 1881 mating thread).
- **Polygon flush caps**: `fill`'s `capStyle: "flush"` is implemented for
  circle/rectangle top shapes only. A polygon top with `flush` is
  downgraded to `none` by `fill.tools.ts` (with a warning returned to the
  caller) rather than silently failing.
- **Loft corner fillets**: for tapered holes, rectangle corner radii are
  applied at the wire level before lofting (an approximation), since
  filleting a lofted solid's edges directly is more involved than
  filleting a simple extrusion's vertical edges.
- ~~**NitroCloud deployment target**~~ — resolved: NitroCloud's managed
  hosting is Node-only (no Python interpreter available for the `execFile`
  calls). CadQuery/PyMeshLab now run behind a separate `api/server.py`
  microservice, called over HTTP when `CAD_API_URL` is set — see "Deploying
  to NitroCloud" above. Local NitroStudio dev is unaffected (falls back to
  the original subprocess path when `CAD_API_URL` is unset).
- **`main.ts` bootstrap call**: `NitroFactory.createMcpServer(...).listen()`
  follows NitroStack's documented NestJS-style pattern, but the exact
  factory method wasn't pinned down in the source discussion — confirm
  against your installed NitroStack version.
- **Meshy polling**: `mesh.tools.ts` polls for task completion; swap for
  Meshy's webhook callback in production to avoid long-held connections.

TDQS

A4.1/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a unique purpose: precise cap generation, hole filling, visual mesh creation, and mesh repair. No overlap.

Naming Consistency4/5

Three tools use 'verb_noun' pattern (generate_precise_cap, generate_visual_mesh, repair_mesh) but 'fill' deviates by lacking a prefix.

Tool Count5/5

Four tools cover the core workflows without unnecessary bloat.

Completeness4/5

Covers generation, filling, visualization, and repair; missing explicit editing of existing caps.

Maintenance

ActivitySlowing
ResponsivenessNo issues