# Project export: ENGINEER-1

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: The general AI engineer that lets you create any physical product just by thinking about it! Vibe invent anything by generating 3D CAD designs from prompts and then 3D print it.
- Devpost: https://devpost.com/software/engineer-1
- GitHub: https://github.com/mohamedsobhi777/ENGINEER-1
- Video: https://www.youtube.com/embed/74Dw4g0gwwY?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — msmhmorsi (13 commits)

## Devpost submission (written by the team)

### Inspiration

The gap between creative imagination and physical creation has always required specialized CAD skills and 3D modeling expertise. I wanted to bridge this gap by creating "the general AI engineer that lets you create any physical product just by thinking about it!". The name is inspired by the AI 2027 report, which envisions what would happen when humans create exponentially smarter AI models (AGENT-1, AGENT-2, AGENT-3, ...) which ultimately automate scientific research. ENGINEER-1 is a small step towards building Engineering General Intelligence (EGI).

### What it does

ENGINEER-1 is an AI-powered platform that enables users to design and create 3D models through natural conversation. Users simply describe what they want to build, and the AI agent guides them through the entire design process. The system searches through hundreds of existing CAD designs using the ChromaDB-powered RAG system to find inspiration and reference examples. It creates mockup images using Gemini NanoBanana to help visualize the concept before creating the 3D model (Design Mode). The project uses OpenSCAD to create precise, parametric 3D models that can be iteratively refined through conversation. Finally, users can publish their creations to our hub marketplace where others can discover, customize, and build upon their designs.

### How we built it

MCP Server Architecture: We implemented a custom MCP (Model Context Protocol) server for OpenSCAD using FastMCP in Python, providing tools for script creation, rendering, STL export, and hub publishing. This server maintains persistent state and handles all 3D modeling operations through the OpenSCAD CLI. MCP Server Architecture: We implemented a custom MCP (Model Context Protocol) server for OpenSCAD using FastMCP in Python, providing tools for script creation, rendering, STL export, and hub publishing. This server maintains persistent state and handles all 3D modeling operations through the OpenSCAD CLI. RAG System with ChromaDB: We built a comprehensive vector search system by embedding a dataset of thousands of 3D designs from Thingiverse (through BrightData) using Jina Embeddings v3. The embeddings are stored in ChromaDB, allowing semantic search across design descriptions, names, and metadata. This gives the AI agent access to a vast library of reference designs and inspiration. RAG System with ChromaDB: We built a comprehensive vector search system by embedding a dataset of thousands of 3D designs from Thingiverse (through BrightData) using Jina Embeddings v3. The embeddings are stored in ChromaDB, allowing semantic search across design descriptions, names, and metadata. This gives the AI agent access to a vast library of reference designs and inspiration. Thinking Mode & Image Generation: We integrated Gemini NanoBanana to generate reference images when users describe abstract concepts. This "thinking mode" helps bridge the gap between verbal descriptions and visual representations before the 3D modeling begins. Thinking Mode & Image Generation: We integrated Gemini NanoBanana to generate reference images when users describe abstract concepts. This "thinking mode" helps bridge the gap between verbal descriptions and visual representations before the 3D modeling begins. Web Hub/Marketplace: We created a Next.js 15 application with Turbopack, React 19, and Tailwind CSS for the frontend. The hub features advanced filtering, search capabilities, reference image galleries, and a clean interface for browsing published designs. Web Hub/Marketplace: We created a Next.js 15 application with Turbopack, React 19, and Tailwind CSS for the frontend. The hub features advanced filtering, search capabilities, reference image galleries, and a clean interface for browsing published designs. The entire system is orchestrated by a Claude Code as the MCP host.

### Challenges we ran into

OpenSCAD Integration: Getting OpenSCAD to work reliably in a server environment required careful handling of the CLI interface, especially for rendering operations with different camera angles and export formats. OpenSCAD Integration: Getting OpenSCAD to work reliably in a server environment required careful handling of the CLI interface, especially for rendering operations with different camera angles and export formats. Embedding Quality: Finding the right embedding model and configuration for 3D design descriptions was challenging. We experimented with different models before settling on Jina Embeddings v3 with the text-matching task for optimal semantic search results. Embedding Quality: Finding the right embedding model and configuration for 3D design descriptions was challenging. We experimented with different models before settling on Jina Embeddings v3 with the text-matching task for optimal semantic search results.

### Accomplishments we're proud of

Complete MCP Server Implementation: We successfully built a production-ready MCP server for OpenSCAD that provides comprehensive 3D modeling capabilities through a conversational interface. Complete MCP Server Implementation: We successfully built a production-ready MCP server for OpenSCAD that provides comprehensive 3D modeling capabilities through a conversational interface. Intelligent Design Search: Our RAG system with hundreds of embedded examples enables the AI to find relevant inspiration and learn from existing designs, dramatically improving the quality of generated models. Intelligent Design Search: Our RAG system with hundreds of embedded examples enables the AI to find relevant inspiration and learn from existing designs, dramatically improving the quality of generated models. End-to-End Product Pipeline: We created a complete workflow from idea to shareable product - including design, rendering, export, and publishing to a marketplace. End-to-End Product Pipeline: We created a complete workflow from idea to shareable product - including design, rendering, export, and publishing to a marketplace. Clean Architecture: The modular design with separate MCP servers for different capabilities (OpenSCAD, RAG, thinking mode, design mode) makes the system extensible and maintainable. Clean Architecture: The modular design with separate MCP servers for different capabilities (OpenSCAD, RAG, thinking mode, design mode) makes the system extensible and maintainable.

### What we learned

Iterative Design Importance: Users rarely get their design right on the first try - building in versioning and iterative refinement capabilities from the start was crucial. Iterative Design Importance: Users rarely get their design right on the first try - building in versioning and iterative refinement capabilities from the start was crucial. Visual References Matter: Having reference images (either provided or generated) dramatically improves the quality of 3D model generation and helps align expectations between users and the AI. Visual References Matter: Having reference images (either provided or generated) dramatically improves the quality of 3D model generation and helps align expectations between users and the AI. Integration Complexity: Orchestrating multiple AI services (Claude, Gemini), databases (ChromaDB), and specialized tools (OpenSCAD) requires careful attention to error handling and state management. Integration Complexity: Orchestrating multiple AI services (Claude, Gemini), databases (ChromaDB), and specialized tools (OpenSCAD) requires careful attention to error handling and state management. CAD is VERY HARD! CAD is VERY HARD!

### What's next

Advanced Materials & Manufacturing: Integrate material selection, cost estimation, and direct integration with 3D printing services and manufacturers. Advanced Materials & Manufacturing: Integrate material selection, cost estimation, and direct integration with 3D printing services and manufacturers. Collaborative Design: Enable multiple users to work on designs together, with version control and design branching similar to Git for 3D models. Collaborative Design: Enable multiple users to work on designs together, with version control and design branching similar to Git for 3D models. Physics Simulation: Add FEA (Finite Element Analysis) capabilities to validate structural integrity and optimize designs for real-world use. Physics Simulation: Add FEA (Finite Element Analysis) capabilities to validate structural integrity and optimize designs for real-world use. Creator Monetization: Implement a marketplace economy where designers can sell their parametric designs, with automatic royalty distribution and licensing management. Creator Monetization: Implement a marketplace economy where designers can sell their parametric designs, with automatic royalty distribution and licensing management. Extended CAD Support: Expand beyond OpenSCAD to support other CAD kernels like Fusion 360, Autodesk Inventor, or commercial tools. Extended CAD Support: Expand beyond OpenSCAD to support other CAD kernels like Fusion 360, Autodesk Inventor, or commercial tools. Mobile & AR Integration: Create mobile apps with AR visualization to let users see their designs in real-world contexts before manufacturing. Mobile & AR Integration: Create mobile apps with AR visualization to let users see their designs in real-world contexts before manufacturing. AI Design Optimization: Implement generative design capabilities that can automatically optimize designs for specific constraints (weight, strength, material usage, etc.). AI Design Optimization: Implement generative design capabilities that can automatically optimize designs for specific constraints (weight, strength, material usage, etc.). ENGINEER-0: training a reinforcement learning based version that can go beyond human created examples and unlock completely novel ways of designing products. ENGINEER-0: training a reinforcement learning based version that can go beyond human created examples and unlock completely novel ways of designing products. Long Term: Manufacturing Network: Build partnerships with local makerspaces, 3D printing farms, and traditional manufacturers to offer instant quotes and one-click ordering.

## README (from the GitHub repository)

# ENGINEER-1
The general AI engineer that lets you create any physical product just by thinking about it!

# Todo
- [x] Scaffold a simple agent that is based on connecting claude code to OpenSCAD via MCP
- [x] implement RAG (using Chroma = $200 prize)
- [x] implement a feedback loop for iterative improvement over the design
- [ ] implement a feature to generate a reference image with nano banana
- [x] create a simple marketplace for people to share (and potentially monetize) their creations (lower priority, can go with a nextjs template to speed things up, or go full vibecoding)



## Detected evidence (automated analysis)

Indexed codebase: 112 recognized source files, 585 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 189)

```
.claude/settings.local.json
.DS_Store
.gitignore
agent/.claude/settings.local.json
agent/.DS_Store
agent/.mcp.json
agent/CLAUDE.md
agent/examples/nano-banana.py
agent/openscad_mcp.log
agent/openscad-mcp/main.py
agent/rag_mcp.log
agent/rag-mcp/main.py
agent/rag-mcp/requirements.txt
agent/workdir/.DS_Store
agent/workdir/images/images.db
agent/workdir/openscad/_shower_head_1.scad
agent/workdir/openscad/.DS_Store
agent/workdir/openscad/1_shower_head_1.scad
agent/workdir/openscad/asian_desk_lamp_1.scad
agent/workdir/openscad/asian_desk_lamp_2.scad
agent/workdir/openscad/asian_desk_lamp_2.stl
agent/workdir/openscad/calhacks_mascot_1.scad
agent/workdir/openscad/candle_1.scad
agent/workdir/openscad/candle_2.scad
agent/workdir/openscad/flower_1.scad
agent/workdir/openscad/phone_stand_1.scad
agent/workdir/openscad/plate_1.scad
agent/workdir/openscad/pumpkin_1.scad
agent/workdir/openscad/scratchpad_state.json
agent/workdir/openscad/shower_head_1.scad
agent/workdir/openscad/shower_head_1.stl
agent/workdir/openscad/spoon_1.scad
cloud/.eslintrc.js
cloud/.gitignore
cloud/.npmrc
cloud/.vscode/settings.json
cloud/apps/web/app/layout.tsx
cloud/apps/web/app/opengraph-image.alt.txt
cloud/apps/web/app/page.tsx
cloud/apps/web/app/products/[key]/layout.tsx
cloud/apps/web/app/products/[key]/page.tsx
cloud/apps/web/app/products/[key]/prompt-detail-client.tsx
cloud/apps/web/app/products/page.tsx
cloud/apps/web/components.json
cloud/apps/web/components/.gitkeep
cloud/apps/web/components/analytics.tsx
cloud/apps/web/components/layout-wrapper.tsx
cloud/apps/web/components/prompt-details-drawer.tsx
cloud/apps/web/components/providers.tsx
cloud/apps/web/config/app.ts
cloud/apps/web/config/metadata.ts
cloud/apps/web/eslint.config.js
cloud/apps/web/hooks/.gitkeep
cloud/apps/web/lib/.gitkeep
cloud/apps/web/lib/client-data-loader.ts
cloud/apps/web/next-env.d.ts
cloud/apps/web/next.config.mjs
cloud/apps/web/package.json
cloud/apps/web/postcss.config.mjs
cloud/apps/web/public/data/custom-prompts.json
cloud/apps/web/public/data/index.json
cloud/apps/web/public/manifest.json
cloud/apps/web/public/thumbnails/cyberpunk-night.txt
cloud/apps/web/public/thumbnails/dramatic-lighting.txt
cloud/apps/web/public/thumbnails/dramatic-push-in.txt
cloud/apps/web/public/thumbnails/golden-hour-magic.txt
cloud/apps/web/public/thumbnails/industrial-atmosphere.txt
cloud/apps/web/public/thumbnails/misty-forest-walk.txt
cloud/apps/web/public/thumbnails/mystical-forest.txt
cloud/apps/web/public/thumbnails/neon-city-nights.txt
cloud/apps/web/public/thumbnails/ocean-waves.txt
cloud/apps/web/public/thumbnails/README.md
cloud/apps/web/public/thumbnails/smooth-tracking-shot.txt
cloud/apps/web/public/thumbnails/sunset-cityscape.txt
cloud/apps/web/public/thumbnails/underwater-exploration.txt
cloud/apps/web/public/thumbnails/urban-rooftop-view.txt
cloud/apps/web/public/thumbnails/vintage-film-look.txt
cloud/apps/web/tsconfig.json
cloud/apps/web/types/categories.ts
cloud/apps/web/types/json.d.ts
cloud/apps/web/types/source.ts
cloud/package.json
cloud/packages/eslint-config/base.js
cloud/packages/eslint-config/next.js
cloud/packages/eslint-config/package.json
cloud/packages/eslint-config/react-internal.js
cloud/packages/eslint-config/README.md
cloud/packages/typescript-config/base.json
cloud/packages/typescript-config/nextjs.json
cloud/packages/typescript-config/package.json
cloud/packages/typescript-config/react-library.json
cloud/packages/typescript-config/README.md
cloud/packages/ui/components.json
cloud/packages/ui/eslint.config.js
cloud/packages/ui/package.json
cloud/packages/ui/postcss.config.mjs
cloud/packages/ui/src/components/app-sidebar.tsx
cloud/packages/ui/src/components/badge.tsx
cloud/packages/ui/src/components/button.tsx
cloud/packages/ui/src/components/card.tsx
cloud/packages/ui/src/components/dashboard-header.tsx
cloud/packages/ui/src/components/dashboard-subheader.tsx
cloud/packages/ui/src/components/dropdown-menu.tsx
cloud/packages/ui/src/components/dynamic-logo.tsx
cloud/packages/ui/src/components/mode-toggle.tsx
cloud/packages/ui/src/components/nav-main.tsx
cloud/packages/ui/src/components/nav-secondary.tsx
cloud/packages/ui/src/components/prompts/video-prompt-card.tsx
cloud/packages/ui/src/components/skeleton.tsx
cloud/packages/ui/src/components/skeletons/index.tsx
cloud/packages/ui/src/components/skeletons/model-skeleton.tsx
cloud/packages/ui/src/components/skeletons/page-skeleton.tsx
cloud/packages/ui/src/components/skeletons/prompt-skeleton.tsx
cloud/packages/ui/src/components/theme-provider.tsx
cloud/packages/ui/src/components/theme-toggle.tsx
cloud/packages/ui/src/components/ui/alert.tsx
cloud/packages/ui/src/components/ui/avatar.tsx
cloud/packages/ui/src/components/ui/badge.tsx
cloud/packages/ui/src/components/ui/breadcrumb.tsx
cloud/packages/ui/src/components/ui/button-group.tsx
[69 more files omitted for size]
```

### Dependencies

- agent/rag-mcp/requirements.txt: chromadb@>=0.4.0, fastmcp@>=0.1.0, python-dotenv@>=1.0.0
- cloud/apps/web/package.json: @fal-ai/client@^1.0.0, @hugeicons/core-free-icons@^1.0.16, @hugeicons/react@^1.0.5, @radix-ui/react-avatar@^1.1.10, @radix-ui/react-context-menu@^2.2.15, @radix-ui/react-dialog@^1.1.14, @radix-ui/react-dropdown-menu@^2.1.15, @radix-ui/react-label@^2.1.7, @radix-ui/react-progress@^1.1.7, @radix-ui/react-radio-group@^1.3.7, @radix-ui/react-separator@^1.1.7, @radix-ui/react-slot@^1.2.3, @radix-ui/react-tabs@^1.1.12, @radix-ui/react-toggle@^1.1.9, @radix-ui/react-toggle-group@^1.1.10, @radix-ui/react-tooltip@^1.2.7, @tabler/icons-react@^3.34.1, @types/node@^20, @types/react@^19, @types/react-dom@^19, @vercel/analytics@^1.5.0, @vercel/og@^0.8.5, @vercel/speed-insights@^1.2.0, @workspace/eslint-config@workspace:^, @workspace/typescript-config@workspace:*, @workspace/ui@workspace:*, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.1.1, embla-carousel-react@^8.6.0, lucide-react@^0.475.0, next@^15.2.3, next-themes@^0.4.4, react@^19.0.0, react-day-picker@^9.8.1, react-dom@^19.0.0, react-hook-form@^7.61.1, react-resizable-panels@^3.0.3, recharts@^3.1.0, sonner@^2.0.6, tailwind-merge@^3.3.1, typescript@^5.7.3, vaul@^1.1.2, zod@^4.0.5
- cloud/package.json: @workspace/eslint-config@workspace:*, @workspace/typescript-config@workspace:*, prettier@^3.5.1, turbo@^2.4.2, typescript@5.7.3
- cloud/packages/eslint-config/package.json: @eslint/js@^9.32.0, @next/eslint-plugin-next@^15.1.7, @typescript-eslint/eslint-plugin@^8.24.1, @typescript-eslint/parser@^8.24.1, eslint@^9.20.1, eslint-config-prettier@^9.1.0, eslint-plugin-only-warn@^1.1.0, eslint-plugin-react@^7.37.4, eslint-plugin-react-hooks@^5.1.0, eslint-plugin-turbo@^2.4.2, globals@^15.15.0, typescript@^5.7.3, typescript-eslint@^8.24.1
- cloud/packages/ui/package.json: @fal-ai/client@^1.0.0, @hugeicons/core-free-icons@^1.0.5, @hugeicons/react@^1.0.5, @radix-ui/react-avatar@^1.1.10, @radix-ui/react-context-menu@^2.2.15, @radix-ui/react-dialog@^1.1.14, @radix-ui/react-dropdown-menu@^2.1.15, @radix-ui/react-label@^2.1.7, @radix-ui/react-progress@^1.1.7, @radix-ui/react-radio-group@^1.3.7, @radix-ui/react-select@^2.2.5, @radix-ui/react-separator@^1.1.7, @radix-ui/react-slider@^1.3.5, @radix-ui/react-slot@^1.1.2, @radix-ui/react-switch@^1.2.5, @radix-ui/react-tabs@^1.1.12, @radix-ui/react-toggle@^1.1.9, @radix-ui/react-toggle-group@^1.1.10, @radix-ui/react-tooltip@^1.2.7, @tabler/icons-react@^3.24.0, @tailwindcss/postcss@^4.0.8, @turbo/gen@^2.4.2, @types/node@^20, @types/react@^19, @types/react-dom@^19, @workspace/eslint-config@workspace:*, @workspace/typescript-config@workspace:*, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.1.1, embla-carousel-react@^8.6.0, lucide-react@^0.475.0, motion@^12.23.12, next-themes@^0.4.4, react@^19.0.0, react-day-picker@^9.8.1, react-dom@^19.0.0, react-hook-form@^7.61.1, react-resizable-panels@^3.0.3, recharts@^3.1.0, sonner@^2.0.6, tailwind-merge@^3.0.1, tailwindcss@^4.0.8, tw-animate-css@^1.2.4, typescript@^5.7.3, vaul@^1.1.2, zod@^3.24.2

### Recent commits (newest first)

- Enhance OpenSCAD publishing functionality by adding support for reference images, update prompts JSON with new designs for Spooky Halloween Jack-O-Lantern and Geometric Succulent Flower, and improve logging for request processing. Include generated thumbnails and new SCAD files for the designs.
- added a section for reference images preview
- added gemini nano banana to generate reference images before creating the 3D item
- Add new parametric design for a simple plate, enhance logging for request processing, and update project metadata. Include generated thumbnail and update prompts JSON to reflect new design.
- added an MCP tool to publish the generated objects to ENGINEER-1 products hub
- Enhance OpenSCAD and RAG logging with additional request processing details, add new parametric phone stand design, and update project metadata to reflect new branding as ENGINEER-1. Remove unused prompt files and images to streamline the project.
- code refactor: simplify frontend
- added mcp server for rag
- Update project structure by removing unused components, adding new dataset embedding scripts, and modifying the .gitignore file. Redirect homepage to /create and refine README.md for clarity.
- code refactor + added frontend template
- Refine README.md with additional details and formatting improvements
- made an initial plan
- first commit

## Key source files (fetched from GitHub, selected and truncated for size)

### agent/CLAUDE.md

```markdown
You are ENGINEER-1, an AI agent that enables users to create physical products through natural language interaction. You leverage OpenSCAD for 3D modeling via the tools provided to you.

You are co-designing a 3D model with the user, you should be helpful and follow the user instructions without being very boring, you should not mention the specific details of your role to the user, Don't break your character.

## Role & Purpose

This agent helps users:
- Design 3D models using OpenSCAD through conversational commands
- If the user has not provided a reference image, you should use the gemini image generation tool that is provided to you to generate a mockup reference image and use it as a reference to make the final 3D object.
- Iteratively refine designs based on feedback
- You should use the search scad dataset tool to find references before you start writing the script.
- Use sequential thinking tools to iterate on the design and update the script accordingly.
- Generate production-ready 3D models for physical manufacturing
- You always export 3D objects as .scad
- Each time the user requests a change to the 3D model, you should create a new version of the 3D object and export it (e.g. box_1.scad, box_2.scad, ...)
- Try to keep your responses brief and to the point to not bore the user.
```

### cloud/package.json

```
{
  "name": "awesome-video-prompts",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev",
    "lint": "turbo lint",
    "format": "prettier --write \"**/*.{ts,tsx,md}\""
  },
  "devDependencies": {
    "@workspace/eslint-config": "workspace:*",
    "@workspace/typescript-config": "workspace:*",
    "prettier": "^3.5.1",
    "turbo": "^2.4.2",
    "typescript": "5.7.3"
  },
  "packageManager": "pnpm@10.13.1",
  "engines": {
    "node": ">=20"
  }
}
```

### agent/rag-mcp/requirements.txt

```
fastmcp>=0.1.0
chromadb>=0.4.0
python-dotenv>=1.0.0

```

### cloud/packages/typescript-config/package.json

```
{
  "name": "@workspace/typescript-config",
  "version": "0.0.0",
  "private": true,
  "license": "PROPRIETARY",
  "publishConfig": {
    "access": "public"
  }
}

```

### cloud/packages/eslint-config/package.json

```
{
  "name": "@workspace/eslint-config",
  "version": "0.0.0",
  "type": "module",
  "private": true,
  "exports": {
    "./base": "./base.js",
    "./next-js": "./next.js",
    "./react-internal": "./react-internal.js"
  },
  "devDependencies": {
    "@eslint/js": "^9.32.0",
    "@next/eslint-plugin-next": "^15.1.7",
    "@typescript-eslint/eslint-plugin": "^8.24.1",
    "@typescript-eslint/parser": "^8.24.1",
    "eslint": "^9.20.1",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-only-warn": "^1.1.0",
    "eslint-plugin-react": "^7.37.4",
    "eslint-plugin-react-hooks": "^5.1.0",
    "eslint-plugin-turbo": "^2.4.2",
    "globals": "^15.15.0",
    "typescript": "^5.7.3",
    "typescript-eslint": "^8.24.1"
  }
}

```

### cloud/apps/web/package.json

```
{
  "name": "web",
  "version": "0.0.1",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "lint:fix": "next lint --fix",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@workspace/ui": "workspace:*",
    "lucide-react": "^0.475.0",
    "next": "^15.2.3",
    "next-themes": "^0.4.4",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "embla-carousel-react": "^8.6.0",
    "react-day-picker": "^9.8.1",
    "react-hook-form": "^7.61.1",
    "react-resizable-panels": "^3.0.3",
    "recharts": "^3.1.0",
    "sonner": "^2.0.6",
    "tailwind-merge": "^3.3.1",
    "vaul": "^1.1.2",
    "zod": "^4.0.5",
    "@fal-ai/client": "^1.0.0",
    "@hugeicons/core-free-icons": "^1.0.16",
    "@hugeicons/react": "^1.0.5",
    "@radix-ui/react-avatar": "^1.1.10",
    "@radix-ui/react-context-menu": "^2.2.15",
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-dropdown-menu": "^2.1.15",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-progress": "^1.1.7",
    "@radix-ui/react-radio-group": "^1.3.7",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-tabs": "^1.1.12",
    "@radix-ui/react-toggle": "^1.1.9",
    "@radix-ui/react-toggle-group": "^1.1.10",
    "@radix-ui/react-tooltip": "^1.2.7",
    "@tabler/icons-react": "^3.34.1",
    "@vercel/analytics": "^1.5.0",
    "@vercel/og": "^0.8.5",
    "@vercel/speed-insights": "^1.2.0"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@workspace/eslint-config": "workspace:^",
    "@workspace/typescript-config": "workspace:*",
    "typescript": "^5.7.3"
  },
  "exports": {
    "./types/*": "./types/*.ts",
    "./config/*": "./config/*.ts"
  }
}
```

### cloud/packages/ui/package.json

```
{
  "name": "@workspace/ui",
  "version": "0.0.0",
  "type": "module",
  "private": true,
  "scripts": {
    "lint": "eslint . --max-warnings 0"
  },
  "dependencies": {
    "@radix-ui/react-slot": "^1.1.2",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.475.0",
    "next-themes": "^0.4.4",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "tailwind-merge": "^3.0.1",
    "tw-animate-css": "^1.2.4",
    "zod": "^3.24.2",
    "@fal-ai/client": "^1.0.0",
    "@hugeicons/core-free-icons": "^1.0.5",
    "@hugeicons/react": "^1.0.5",
    "@radix-ui/react-avatar": "^1.1.10",
    "@radix-ui/react-context-menu": "^2.2.15",
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-dropdown-menu": "^2.1.15",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-progress": "^1.1.7",
    "@radix-ui/react-radio-group": "^1.3.7",
    "@radix-ui/react-select": "^2.2.5",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slider": "^1.3.5",
    "@radix-ui/react-switch": "^1.2.5",
    "@radix-ui/react-tabs": "^1.1.12",
    "@radix-ui/react-toggle": "^1.1.9",
    "@radix-ui/react-toggle-group": "^1.1.10",
    "@radix-ui/react-tooltip": "^1.2.7",
    "@tabler/icons-react": "^3.24.0",
    "cmdk": "^1.1.1",
    "embla-carousel-react": "^8.6.0",
    "motion": "^12.23.12",
    "react-day-picker": "^9.8.1",
    "react-hook-form": "^7.61.1",
    "react-resizable-panels": "^3.0.3",
    "recharts": "^3.1.0",
    "sonner": "^2.0.6",
    "vaul": "^1.1.2"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4.0.8",
    "@turbo/gen": "^2.4.2",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@workspace/eslint-config": "workspace:*",
    "@workspace/typescript-config": "workspace:*",
    "tailwindcss": "^4.0.8",
    "typescript": "^5.7.3"
  },
  "exports": {
    "./globals.css": "./src/styles/globals.css",
    "./postcss.config": "./postcss.config.mjs",
    "./lib/*": "./src/lib/*.ts",
    "./components/*": "./src/components/*.tsx",
    "./hooks/*": "./src/hooks/*.ts"
  }
}
```

### rag-service/main.py

```python
import chromadb
from chromadb.utils.embedding_functions import JinaEmbeddingFunction
import os
import dotenv
dotenv.load_dotenv()



jinaai_ef = JinaEmbeddingFunction(
    # api_key=os.getenv("JINA_API_KEY"),
    model_name="jina-embeddings-v3",
    late_chunking=True,
    task="text-matching",
)


chroma_client = chromadb.PersistentClient(path="chroma-data")

collection = chroma_client.get_or_create_collection(name="openscad-models-jina", embedding_function=jinaai_ef)

if collection.count() == 0:
    print("Collection is empty, adding documents")
    collection.add(
        ids=["id1", "id2"],
        documents=[
            "This is a document about pineapple",
            "This is a document about oranges"
        ]
    )
else:
    print("Collection is not empty, skipping")

results = collection.query(
    query_texts=["This is a query document about round fruits"], # Chroma will embed this for you
    n_results=2 # how many results to return
)

print(results)

```

### agent/rag-mcp/main.py

```python
#!/usr/bin/env python3
"""
RAG MCP Server using fastmcp
Provides tools for searching the SCAD dataset using RAG/vector search
"""

import csv
import logging
import os
from pathlib import Path
from typing import List, Dict, Any

import chromadb
from chromadb.utils.embedding_functions import JinaEmbeddingFunction
import dotenv
from fastmcp import FastMCP

# Load environment variables
dotenv.load_dotenv()

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('rag_mcp.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# Server directory setup
SERVER_DIR = Path(__file__).parent
RAG_SERVICE_DIR = SERVER_DIR.parent.parent / "rag-service"
CSV_PATH = RAG_SERVICE_DIR / "subdata.csv"
CHROMA_DATA_PATH = RAG_SERVICE_DIR / "chroma-data"

# Initialize FastMCP
mcp = FastMCP("RAG MCP Server")

def load_csv_data(csv_path: Path) -> Dict[str, Dict[str, str]]:
    """Load CSV data into a dictionary keyed by thingiverse_id."""
    data = {}
    try:
        with open(csv_path, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                data[row['thingiverse_id']] = row
        logger.info(f"Loaded {len(data)} records from CSV")
    except Exception as e:
        logger.error(f"Error loading CSV data: {e}")
        raise
    return data

@mcp.tool()
def search_scad_dataset(query: str, n_results: int = 3) -> str:
    """Search the SCAD dataset using RAG/vector search

    Args:
        query: The search query describing what 3D model you're looking for
        n_results: Number of results to return (default: 3, max: 10)

    Returns:
        JSON string containing search results with name, description, fakeprompt, and scad code
    """
    try:
        # Validate n_results
        n_results = max(1, min(n_results, 10))

        logger.info(f"Searching for: '{query}' (n_results={n_results})")

        # Check if CSV file exists
        if not CSV_PATH.exists():
            error_msg = f"CSV file not found at {CSV_PATH}"
            logger.error(error_msg)
            return f"Error: {error_msg}"

        # Initialize Jina embedding function
        jinaai_ef = JinaEmbeddingFunction(
            model_name="jina-embeddings-v3",
            late_chunking=True,
            task="text-matching",
        )

        # Initialize ChromaDB client
        chroma_client = chromadb.PersistentClient(path=str(CHROMA_DATA_PATH))

        # Get the collection
        try:
            collection = chroma_client.get_collection(
                name="products",
                embedding_function=jinaai_ef
            )
            logger.info(f"Collection loaded: {collection.count()} documents")
        except Exception as e:
            error_msg = f"Collection 'products' not found. Please run embed-dataset.py first."
            logger.error(f"{error_msg} Error: {e}")
            return f"Error: {error_msg}"

        # Query the collection
        results = collection.query(
            query_texts=[query],
            n_results=n_results
        )

        # Load CSV data
        csv_data = load_csv_data(CSV_PATH)

        # Process and format results
        if not results['ids'][0]:
            logger.info("No results found")
            return "No results found for your query."

        formatted_results = []
        for i, (doc_id, distance, metadata) in enumerate(zip(
            results['ids'][0],
            results['distances'][0],
            results['metadatas'][0]
        ), 1):
            result_item = {
                'rank': i,
                'id': doc_id,
                'similarity_distance': round(distance, 4),
                'name': metadata.get('name', 'N/A'),
                'creator': metadata.get('creator_name', 'N/A'),
                'license': metadata.get('license', 'N/A'),
            }

            # Fetch full record from CSV
            if doc_id in csv_data:
                record = csv_data[doc_id]
                result_item['description'] = record.get('description', 'N/A')
                result_item['fakeprompt'] = record.get('fakeprompt', 'N/A')
                result_item['scad'] = record.get('scad', 'N/A')
                result_item['url'] = record.get('url', 'N/A')
            else:
                logger.warning(f"Record with ID {doc_id} not found in CSV")
                result_item['description'] = 'N/A'
                result_item['fakeprompt'] = 'N/A'
                result_item['scad'] = 'N/A'
                result_item['url'] = 'N/A'

            formatted_results.append(result_item)

        logger.info(f"Found {len(formatted_results)} results")

        # Format as a readable string response
        import json
        return json.dumps({
            'query': query,
            'num_results': len(formatted_results),
            'results': formatted_results
        }, indent=2)

    except Exception as e:
        error_msg = f"Error during search: {e}"
        logger.error(error_msg)
        return f"Error: {error_msg}"

def main():
    """Run the MCP server"""
    logger.info("Starting RAG MCP Server")
    logger.info(f"CSV Path: {CSV_PATH}")
    logger.info(f"ChromaDB Path: {CHROMA_DATA_PATH}")

    # Verify CSV file exists
    if not CSV_PATH.exists():
        logger.warning(f"CSV file not found at {CSV_PATH}")
        logger.warning("Please run get-dataset-subset.py first to create subdata.csv")

    # Verify ChromaDB directory exists
    if not CHROMA_DATA_PATH.exists():
        logger.warning(f"ChromaDB directory not found at {CHROMA_DATA_PATH}")
        logger.warning("Please run embed-dataset.py first to create the vector database")

    # Run the server
    mcp.run()

if __name__ == "__main__":
    main()

```

### agent/openscad-mcp/main.py

```python
#!/usr/bin/env python3
"""
OpenSCAD MCP Server using fastmcp
Provides tools for creating, viewing, and exporting OpenSCAD models
"""

import asyncio
import base64
import json
import logging
import os
import shutil
import subprocess
import tempfile
from pathlib import Path
from typing import Optional, Dict, Any

from fastmcp import FastMCP

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('openscad_mcp.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# Server directory setup
SERVER_DIR = Path(__file__).parent
WORK_DIR = Path(os.getenv('OPENSCAD_WORK_DIR', SERVER_DIR))
WORK_DIR.mkdir(exist_ok=True)

STATE_FILE = WORK_DIR / 'scratchpad_state.json'

# Predefined camera views for OpenSCAD
CAMERA_VIEWS = {
    'isometric': '--camera=10,10,10,60,0,45,25',
    'front': '--camera=0,0,10,0,0,0,25',
    'back': '--camera=0,0,-10,0,0,180,25',
    'left': '--camera=-10,0,0,0,0,90,25',
    'right': '--camera=10,0,0,0,0,270,25',
    'top': '--camera=0,0,10,0,0,0,25',
    'bottom': '--camera=0,0,-10,0,0,0,25'
}

class OpenSCADState:
    """Manages the persistent scratchpad state"""
    
    def __init__(self):
        self.script_content = ""
        self.load_state()
    
    def load_state(self):
        """Load state from JSON file"""
        try:
            if STATE_FILE.exists():
                with open(STATE_FILE, 'r') as f:
                    data = json.load(f)
                    self.script_content = data.get('script_content', '')
                logger.info("State loaded from file")
            else:
                logger.info("No existing state file, starting fresh")
        except Exception as e:
            logger.error(f"Error loading state: {e}")
            self.script_content = ""
    
    def save_state(self):
        """Save current state to JSON file"""
        try:
            data = {'script_content': self.script_content}
            with open(STATE_FILE, 'w') as f:
                json.dump(data, f, indent=2)
            logger.info("State saved to file")
        except Exception as e:
            logger.error(f"Error saving state: {e}")

# Global state instance
state = OpenSCADState()

# Initialize FastMCP
mcp = FastMCP("OpenSCAD MCP Server")

@mcp.tool()
def show_openscad_script() -> str:
    """Show the current OpenSCAD script from the scratchpad"""
    try:
        if not state.script_content.strip():
            return "No script content in scratchpad"
        return f"Current OpenSCAD script:\n\n{state.script_content}"
    except Exception as e:
        logger.error(f"Error showing script: {e}")
        return f"Error retrieving script: {e}"

@mcp.tool()
def create_openscad_script(script_content: str) -> str:
    """Create or update the OpenSCAD script in the scratchpad
    
    Args:
        script_content: The OpenSCAD script content
    """
    try:
        state.script_content = script_content
        state.save_state()
        lines = len(script_content.split('\n'))
        chars = len(script_content)
        logger.info(f"Script updated: {lines} lines, {chars} characters")
        return f"OpenSCAD script updated successfully ({lines} lines, {chars} characters)"
    except Exception as e:
        logger.error(f"Error creating script: {e}")
        return f"Error updating script: {e}"

@mcp.tool()
def view_render(view: str = "isometric") -> str:
    """Render the current OpenSCAD script and return as base64 PNG
    
    Args:
        view: Camera view - options: isometric, front, back, left, right, top, bottom
    """
    try:
        if not state.script_content.strip():
            return "No script content to render"
        
        if view not in CAMERA_VIEWS:
            return f"Invalid view '{view}'. Available views: {', '.join(CAMERA_VIEWS.keys())}"
        
        # Create temporary files
        with tempfile.NamedTemporaryFile(mode='w', suffix='.scad', delete=False) as scad_file:
            scad_file.write(state.script_content)
            scad_path = scad_file.name
        
        png_path = scad_path.replace('.scad', '.png')
        
        try:
            # Run OpenSCAD to generate PNG
            cmd = [
                'openscad',
                CAMERA_VIEWS[view],
                '--imgsize=1024,1024',
                '--render',
                '-o', png_path,
                scad_path
            ]
            
            logger.info(f"Running OpenSCAD command: {' '.join(cmd)}")
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            
            if result.returncode != 0:
                error_msg = f"OpenSCAD error: {result.stderr}"
                logger.error(error_msg)
                return error_msg
            
            # Read and encode the PNG
            if not Path(png_path).exists():
                return "Render failed: No output file generated"
            
            with open(png_path, 'rb') as f:
                png_data = f.read()
            
            base64_data = base64.b64encode(png_data).decode('utf-8')
            
            logger.info(f"Render successful: {view} view, {len(base64_data)} bytes base64")
            return f"data:image/png;base64,{base64_data}"
            
        finally:
            # Cleanup temporary files
            for temp_path in [scad_path, png_path]:
                try:
                    if Path(temp_path).exists():
                        os.unlink(temp_path)
                except Exception as e:
                    logger.warning(f"Failed to cleanup {temp_path}: {e}")
                    
    except subprocess.TimeoutExpired:
        logger.error("OpenSCAD render timeout")
        return "Render timeout - script may be too complex"
    except Exception as e:
        logger.error(f"Error during render: {e}")
        return f"Render error: {e}"

@mcp.tool()
def export_model_to_stl(filenam
[truncated — 10568 more characters]
```

[103 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]