Spaces:
Paused
Paused
Download app.py from X-R-1-8/onshape-mcp: direct link, hf CLI and curl.
- Browser
- Download file 5.78 kB
-
https://huggingface.co/spaces/X-R-1-8/onshape-mcp/resolve/main/app.py
- Command line
-
hf download hf://spaces/X-R-1-8/onshape-mcp/app.py
-
curl -L -o app.py https://huggingface.co/spaces/X-R-1-8/onshape-mcp/resolve/main/app.py
5.78 kB
| """Hugging Face Space Application for Onshape MCP. | |
| Provides both: | |
| 1. An interactive Gradio web playground at `/` | |
| 2. A full Model Context Protocol (MCP) SSE endpoint at `/sse` for ChatGPT, Claude, and Cursor. | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import json | |
| import os | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| # Add src to sys.path | |
| sys.path.insert(0, str(Path(__file__).parent / "src")) | |
| import gradio as gr | |
| from fastapi import FastAPI | |
| from starlette.middleware.cors import CORSMiddleware | |
| # Background task to ensure Playwright chromium is downloaded without blocking startup | |
| import threading | |
| def _background_install() -> None: | |
| try: | |
| subprocess.run([sys.executable, "-m", "playwright", "install", "chromium"], check=False) | |
| except Exception: | |
| pass | |
| threading.Thread(target=_background_install, daemon=True).start() | |
| # ZeroGPU compatibility (no-op decorator so ZeroGPU hardware remains active without burning quota) | |
| try: | |
| import spaces | |
| def _gpu_keepalive() -> str: | |
| return "GPU active" | |
| except Exception: | |
| def _gpu_keepalive() -> str: | |
| return "CPU fallback" | |
| # Import Onshape MCP server | |
| from onshape_mcp.server import mcp | |
| from onshape_mcp.intent import parse as parse_intent | |
| from onshape_mcp.tools import ALL_TOOLS | |
| # 1. Build Gradio Interface | |
| def test_prompt(user_prompt: str) -> str: | |
| """Preview how Onshape MCP parses a prompt into CAD tools.""" | |
| if not user_prompt.strip(): | |
| return "Please enter a CAD prompt (e.g., 'Draw a 10cm by 5cm box on the Top plane')." | |
| try: | |
| plan = parse_intent(user_prompt) | |
| if plan: | |
| return json.dumps( | |
| { | |
| "status": "Recognized Deterministic CAD Actions", | |
| "action_count": len(plan), | |
| "actions": [ | |
| {"tool": tool, "args": args} for tool, args in plan | |
| ], | |
| }, | |
| indent=2, | |
| ) | |
| else: | |
| return json.dumps( | |
| { | |
| "status": "Complex Multi-Step Goal", | |
| "handler": "Autonomous Vision Loop (act)", | |
| "note": "Will be executed closed-loop in Onshape.", | |
| }, | |
| indent=2, | |
| ) | |
| except Exception as e: | |
| return f"Error parsing prompt: {e}" | |
| def list_tools() -> str: | |
| """Return available sketch tools catalog.""" | |
| tools_summary = [] | |
| for tool in ALL_TOOLS: | |
| tools_summary.append({ | |
| "name": tool.name, | |
| "purpose": tool.purpose, | |
| "requires": tool.requires, | |
| "produces": tool.produces, | |
| "status": tool.status, | |
| }) | |
| return json.dumps(tools_summary, indent=2) | |
| with gr.Blocks(title="Onshape MCP β Autonomous CAD AI", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown( | |
| """ | |
| # π Onshape MCP β Autonomous CAD Agent | |
| ### Control Onshape directly from ChatGPT, Claude, Cursor, and any MCP-compatible AI. | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown( | |
| """ | |
| ### π Connect This Space to Your AI Client | |
| **MCP SSE Endpoint:** | |
| ```text | |
| https://x-r-1-8-onshape-mcp.hf.space/sse | |
| ``` | |
| * **ChatGPT Web**: In Custom GPTs or MCP Settings, add the SSE URL above. | |
| * **Claude Desktop**: Add this block to `claude_desktop_config.json`: | |
| ```json | |
| { | |
| "mcpServers": { | |
| "onshape": { | |
| "url": "https://x-r-1-8-onshape-mcp.hf.space/sse" | |
| } | |
| } | |
| } | |
| ``` | |
| * **Cursor / LibreChat**: Add as a remote SSE MCP server. | |
| """ | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown( | |
| """ | |
| ### βοΈ Space Status | |
| * **Hardware**: ZeroGPU (RTX PRO 6000) | |
| * **Status**: π’ Server Live | |
| * **Sketch Tools**: 35+ Tools & 13 Constraints | |
| * **Protocol**: Model Context Protocol (2024-11-05) | |
| """ | |
| ) | |
| gr.Markdown("---") | |
| gr.Markdown("### π§ͺ Test CAD Natural Language Parser") | |
| with gr.Row(): | |
| with gr.Column(): | |
| prompt_input = gr.Textbox( | |
| label="CAD Prompt", | |
| placeholder="e.g. Draw a 10cm by 5cm box on the Top plane and extrude 20mm", | |
| lines=2, | |
| value="Draw a 10cm by 5cm box on the Top plane", | |
| ) | |
| btn_run = gr.Button("Parse Intent Plan", variant="primary") | |
| with gr.Column(): | |
| output_json = gr.Code(label="Parsed Deterministic Plan", language="json") | |
| btn_run.click(fn=test_prompt, inputs=[prompt_input], outputs=[output_json]) | |
| with gr.Accordion("π View Full CAD Tools Catalog (35+ Tools)", open=False): | |
| tools_viewer = gr.Code(value=list_tools(), language="json", lines=15) | |
| # 2. Create Parent FastAPI App | |
| app = FastAPI() | |
| # Enable CORS for web clients | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # 3. Mount FastMCP SSE App at / | |
| sse_app = mcp.sse_app() | |
| app.mount("/mcp", sse_app) | |
| # Also expose direct /sse and /messages routes at root | |
| for route in sse_app.routes: | |
| app.routes.append(route) | |
| # 4. Mount Gradio interface at root | |
| app = gr.mount_gradio_app(app, demo, path="/") | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.environ.get("PORT", "7860")) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |