Skip to main content
Glama
README.md
# Hello3DMCP Server - MCP Server for 3D Model Control

MCP server that provides Model Context Protocol (MCP) tools for controlling a 3D model visualization application via WebSocket. This server bridges MCP clients (like ChatGPT, Claude Desktop, Cursor) with the frontend 3D application.

## Features

- **MCP Protocol Support**: Works with any MCP-compatible client (ChatGPT, Claude Desktop, Cursor, etc.)
- **Dual Transport Modes**: Supports both STDIO (subprocess) and HTTP/SSE (remote) modes
- **WebSocket Bridge**: Real-time bidirectional communication with frontend applications
- **Session Management**: Multi-user support with isolated sessions
- **State Management**: Query and cache application state for accurate relative operations

## Quick Start

### Prerequisites

- Node.js (v18 or higher)
- npm
- Frontend application running (see [hello3dmcp-frontend](https://github.com/aidenlab/hello3dmcp-frontend))

### Installation

1. **Install dependencies:**
   ```bash
   npm install
   ```

2. **Configure environment (optional):**
   ```bash
   cp .env.example .env
   # Edit .env with your settings
   ```

3. **Start the server:**
   ```bash
   npm start
   ```
   
   Or:
   ```bash
   node server.js
   ```

The server starts on:
- **MCP endpoint**: `http://localhost:3000/mcp` (HTTP mode)
- **WebSocket server**: `ws://localhost:3001`

## Configuration

### Environment Variables

Create a `.env` file (or use environment variables):

```bash
# MCP server port (default: 3000)
MCP_PORT=3000

# WebSocket server port (default: 3001)
WS_PORT=3001

# Browser URL for the 3D app frontend
# Used when generating connection URLs for MCP clients
# Default: http://localhost:5173
BROWSER_URL=http://localhost:5173
```

### Command-Line Arguments

You can override environment variables via command-line:

```bash
# Set browser URL
node server.js --browser-url https://your-app.netlify.app

# Or using short form
node server.js -u https://your-app.netlify.app

# Show help
node server.js --help
```

**Configuration Priority:**
1. Command-line argument (`--browser-url` or `-u`) - highest priority
2. Environment variable (`BROWSER_URL`)
3. `.env` file (`BROWSER_URL`)
4. Default (`http://localhost:5173`) - lowest priority

## Connecting MCP Clients

### Quick Comparison: MCP Client Options

| Client | Cost | Works with Localhost | Requires Public URL | Best For |
|--------|------|---------------------|---------------------|----------|
| **MCP Inspector** | Free | ✅ Yes | ❌ No | Testing & debugging tools |
| **Cursor** | Free | ✅ Yes | ❌ No | Full IDE with AI assistant |
| **VS Code + MCP** | Free | ✅ Yes | ❌ No | VS Code users |
| **Claude Code** | Free | ✅ Yes | ❌ No | CLI-based testing |
| **Continue.dev** | Free | ✅ Yes | ❌ No | VS Code extension users |
| **Claude Desktop** | Free | ✅ Yes | ❌ No | Desktop app with Claude |
| **ChatGPT** | Paid (Plus) | ❌ No | ✅ Yes (tunnel needed) | OpenAI integration |

### Claude Desktop Installation

Install the MCP package (`.mcpb`) file:

1. **Build the package:**
   ```bash
   npm run build
   ```
   This creates `hello3dmcp-server.mcpb` in your project root.

2. **Install in Claude Desktop:**
   - Open Claude Desktop → Settings → Extensions → Advanced Settings
   - Click "Install Extension"
   - Select the `hello3dmcp-server.mcpb` file
   - Restart Claude Desktop

3. **Get connection URL:**
   - Ask Claude: "How do I connect to the 3D app?" or "Get browser URL"
   - Claude will provide a URL with your unique session ID
   - Open that URL in your browser

**Benefits:** No manual configuration needed, self-contained package, easy updates.

### ChatGPT Setup

ChatGPT requires a publicly accessible server.

1. **Start server:**
   ```bash
   node server.js --browser-url https://your-frontend.netlify.app
   ```

2. **Create tunnel:**
   ```bash
   ngrok http 3000
   # or
   lt --port 3000 --subdomain hello3dmcp-server
   ```

3. **Configure ChatGPT:**
   - Open ChatGPT → Settings → Personalization → Model Context Protocol
   - Add server:
     - **Name**: `hello3dmcp-server`
     - **URL**: `https://your-tunnel-url/mcp` ⚠️ **Include `/mcp` at the end!**
     - **Transport**: HTTP or Streamable HTTP

### Cursor

**Option 1: Deeplink (macOS)**
```bash
open 'cursor://anysphere.cursor-deeplink/mcp/install?name=hello3dmcp-server&config=eyJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvbWNwIn0='
```

**Option 2: Manual Configuration**
1. Open Cursor Settings → Features → Model Context Protocol
2. Add server:
   ```json
   {
     "mcpServers": {
       "hello3dmcp-server": {
         "url": "http://localhost:3000/mcp"
       }
     }
   }
   ```

### MCP Inspector

The MCP Inspector is a developer tool for testing and debugging MCP servers. It provides an interactive web interface to explore server capabilities, test tools, and view resources.

![MCP Inspector Interface](docs/mcp-inspector-screenshot.png)

*Example: MCP Inspector connected to the server, showing tools being tested.*

**Testing Options:**

You can test either the source code (development) or the bundled version (production):

**Option 1: Test Source Code (Development)**

1. **Start the server from source:**
   ```bash
   npm start
   # or
   node server.js
   ```
   Server should be running on `http://localhost:3000/mcp`

2. **Start the MCP Inspector:**
   ```bash
   npx @modelcontextprotocol/inspector http://localhost:3000/mcp
   ```

**Option 2: Test Bundled Version (Production)**

1. **Build the bundle:**
   ```bash
   npm run build:bundle
   ```
   This creates `dist/hello3dmcp-server.js`

2. **Start the bundled server:**
   ```bash
   npm run start:prod
   # or
   node dist/hello3dmcp-server.js
   ```
   Server should be running on `http://localhost:3000/mcp`

3. **Start the MCP Inspector:**
   ```bash
   npx @modelcontextprotocol/inspector http://localhost:3000/mcp
   ```

**Using the Inspector:**

3. **Open the Inspector UI:**
   - The inspector will start a web interface (usually on `http://localhost:5173`)
   - Open your browser and navigate to the URL shown in the terminal

4. **Configure the connection:**
   - **Transport Type:** Select "Streamable HTTP" (this matches your server's transport)
   - **URL:** Enter `http://localhost:3000/mcp` (note: port 3000, not 3001)
   - **Connection Type:** Select "Direct"
   - Click the **"Connect"** button
   - You should see a green dot and "Connected" status when successful

5. **Browse and test tools:**
   - Click on the **"Tools"** tab in the top navigation bar
   - You'll see a list of all available tools in the middle pane:
     - `change_model_color` - Change the color of the 3D model
     - `change_model_size` - Change the uniform size of the model
     - `scale_model` - Scale the model independently in each dimension
     - `change_background_color` - Change the background color of the scene
     - `set_key_light_intensity` - Set the intensity of the key light
     - `set_key_light_position` - Set the position of the key light
     - And more...

6. **Call a tool:**
   - Click on any tool name in the tools list to select it
   - The right pane will show the tool's description and parameters
   - Enter the required parameter value(s) in the input field(s):
     - For `change_background_color`: Enter a color name (e.g., `"tin"`) or hex code (e.g., `"#878687"`)
     - For `change_model_size`: Enter a number (e.g., `2.5`)
     - For `scale_model`: Enter values for x, y, z axes
   - Click the **"Run Tool"** button (paper airplane icon)
   - The result will appear below, showing "Success" and the response message
   - If your 3D app is running and connected, you'll see the changes reflected immediately

7. **View history:**
   - The bottom-left "History" pane shows all your previous tool calls
   - Click on any history entry to see its details
   - Use "Clear" to remove history entries

**Example:** To change the background color to tin:
1. Select `change_background_color` from the tools list
2. Enter `tin` in the "color" parameter field
3. Click "Run Tool"
4. You'll see: `"Background color changed to tin (#878687)"`
5. The background in your 3D app will update to the new color

**Note:** The Inspector connects directly to your MCP HTTP endpoint. Make sure your server is running before starting the Inspector. If you're using a tunneled server (for remote access), you can also connect to the tunneled URL:
```bash
npx @modelcontextprotocol/inspector https://your-tunnel-url.ngrok-free.app/mcp
```

## Available MCP Tools

The server provides extensive tools for controlling the 3D model:

### Model Control
- `change_model_color` - Change model color (hex or Apple crayon color name)
- `change_model_size` - Change uniform model size
- `scale_model` - Scale model independently in x, y, z dimensions
- `set_model_rotation` - Set model rotation (Euler angles)
- `rotate_model_clockwise` - Rotate model clockwise (relative)
- `rotate_model_counterclockwise` - Rotate model counterclockwise (relative)
- `nudge_model_pitch_up` - Adjust model pitch up (relative)
- `nudge_model_pitch_down` - Adjust model pitch down (relative)
- `nudge_model_roll` - Adjust model roll (relative)
- `get_model_color` - Get current model color
- `get_model_scale` - Get current model scale
- `get_model_rotation` - Get current model rotation

### Lighting Control
- `set_key_light_intensity` - Set key light intensity
- `set_key_light_color` - Set key light color
- `set_key_light_position_spherical` - Set key light position (spherical coordinates)
- `set_key_light_distance` - Set key light distance
- `swing_key_light_up/down/left/right` - Swing key light in directions
- `walk_key_light_in/out` - Move key light closer/farther
- `rotate_key_light_clockwise/counterclockwise` - Rotate key light
- `nudge_key_light_elevation_up/down` - Adjust key light elevation
- `move_key_light_toward_direction` - Move key light toward direction
- Similar tools for fill light
- `get_key_light_*` / `get_fill_light_*` - Query light properties

### Camera Control
- `dolly_camera` - Set camera distance
- `dolly_camera_in/out` - Move camera closer/farther
- `set_camera_fov` - Set camera field of view
- `increase_camera_fov` / `decrease_camera_fov` - Adjust FOV
- `get_camera_distance` - Get camera distance
- `get_camera_fov` - Get camera FOV

### Scene Control
- `change_background_color` - Change scene background color
- `get_background_color` - Get background color

### Connection
- `get_browser_connection_url` - Get URL to connect browser to 3D app

## Architecture

The server supports **two transport modes**:

### STDIO Mode (Subprocess - Claude Desktop)
```
┌─────────────────┐         ┌──────────────┐         ┌─────────────┐
│ Claude Desktop  │──stdin▶│  MCP Server   │────────▶│  WebSocket  │
│  (Subprocess)   │◀─stdout│  (server.js)  │         │   Server    │
└─────────────────┘         └──────────────┘         └─────────────┘
                                      │                       │
                                      │              ┌────────▼────────┐
                                      └──────────────▶│  Frontend App   │
                                                     │  (WebSocket)     │
                                                     └─────────────────┘
```

### HTTP/SSE Mode (ChatGPT, Manual)
```
┌─────────────────┐         ┌──────────────┐         ┌─────────────┐
│   MCP Client    │──HTTP──▶│  MCP Server   │────────▶│  WebSocket  │
│  (AI Assistant) │──SSE───▶│  (server.js)  │         │   Server    │
└─────────────────┘         └──────────────┘         └─────────────┘
                                      │                       │
                                      │              ┌────────▼────────┐
                                      └──────────────▶│  Frontend App   │
                                                     │  (WebSocket)     │
                                                     └─────────────────┘
```

**How it works:**
1. **MCP Client** sends tool call requests to the MCP Server (via STDIO or HTTP/SSE)
2. **MCP Server** auto-detects the transport mode and processes requests accordingly
3. **MCP Server** routes commands via WebSocket to connected browser clients (by session ID)
4. **Frontend App** receives WebSocket messages and updates the 3D model
5. Changes are immediately visible in the browser

**Transport Detection:**
- **STDIO Mode**: Automatically detected when `stdin` is not a TTY (subprocess)
- **HTTP Mode**: Automatically detected when `stdin` is a TTY (manual execution)

## WebSocket Protocol

The server communicates with frontend applications via WebSocket:

### Session Registration

Frontend sends on connection:
```json
{
  "type": "registerSession",
  "sessionId": "<session-id>"
}
```

### Sending Commands

Server sends commands to frontend:
```json
{
  "type": "changeColor",
  "color": "#ff0000"
}
```

### State Queries

Server can request current state:
```json
{
  "type": "requestState",
  "requestId": "<unique-id>",
  "forceRefresh": false
}
```

Frontend responds:
```json
{
  "type": "stateResponse",
  "requestId": "<unique-id>",
  "state": { /* current state object */ }
}
```

## Deployment

### Railway

1. **Connect repository** to Railway
2. **Set environment variables:**
   - `MCP_PORT`: 3000 (or Railway's assigned port)
   - `WS_PORT`: 3001 (or use same port as MCP_PORT)
   - `BROWSER_URL`: Your frontend URL
3. **Deploy**

### Render

1. **Create new Web Service**
2. **Set environment variables:**
   - `MCP_PORT`: 3000
   - `WS_PORT`: 3001
   - `BROWSER_URL`: Your frontend URL
3. **Deploy**

### Fly.io

1. **Create `fly.toml`** configuration
2. **Set environment variables** via `fly secrets`
3. **Deploy**: `fly deploy`

### Important Notes

- **WebSocket Support**: Ensure your hosting platform supports WebSocket connections
- **Port Configuration**: Some platforms assign a single port - you may need to use the same port for both MCP and WebSocket
- **HTTPS/WSS**: Use `wss://` (secure WebSocket) for production deployments

## Development

### Adding New Tools

1. **Register tool in `server.js`:**
   ```javascript
   mcpServer.registerTool(
     'your_tool_name',
     {
       title: 'Your Tool Title',
       description: 'Description',
       inputSchema: {
         param: z.string().describe('Parameter')
       }
     },
     async ({ param }) => {
       routeToCurrentSession({
         type: 'yourCommandType',
         param: param
       });
       return {
         content: [{ type: 'text', text: 'Success' }]
       };
     }
   );
   ```

2. **Frontend handles command** in `Application.js` WebSocket message handler

3. **Update documentation** in README.md

## Troubleshooting

### Port Already in Use

```bash
# Check what's using the ports
lsof -i :3000 -i :3001

# Kill processes
lsof -ti :3000 -ti :3001 | xargs kill -9
```

### WebSocket Connection Issues

- Verify WebSocket server is running on port 3001
- Check firewall/security groups allow WebSocket connections
- Ensure frontend is connecting with correct session ID

### MCP Client Can't Connect

- Verify MCP endpoint is accessible: `http://localhost:3000/mcp`
- Check CORS settings (server allows all origins by default)
- For remote clients, ensure tunnel is running and URL is correct

### Tools Not Appearing

- Restart MCP client after server changes
- Check server logs for errors
- Verify server started successfully

## Related Projects

- [hello3dmcp-frontend](https://github.com/aidenlab/hello3dmcp-frontend) - 3D visualization frontend application

## License

MIT

## Contributing

Contributions welcome! Please feel free to submit a Pull Request.

TDQS

B3.4/5.0

Scored across 61 tools

Disambiguation3/5

The tools are mostly distinct by target (model, key light, fill light, camera, background), but there is significant overlap in light manipulation: swing_*, walk_*, move_*_toward_direction, set_*_position_spherical, and set_*_distance all adjust light position in different but confusingly similar ways. The swing/walk/move/nudge/rotate families for lights can be hard to distinguish without deep reading.

Naming Consistency3/5

The naming is mostly verb_noun (change_model_color, set_key_light_intensity, get_camera_fov), but there is inconsistency: some use 'change_', some 'set_', some 'get_', some 'swing_', 'walk_', 'nudge_', 'rotate_', 'dolly_', 'increase_', 'decrease_'. The verb families are semantically different but the pattern is not uniform, and 'dolly_camera' vs 'dolly_camera_in/out' is slightly redundant.

Tool Count2/5

61 tools is far too many for a 3D scene manipulation server. The surface is heavily fragmented: light controls alone account for dozens of tools, with separate tools for every direction and relative/absolute variants. This could be consolidated into parameterized tools (e.g., set_light_position with direction and amount).

Completeness4/5

The domain is 3D scene manipulation (model, lights, camera, background), and the coverage is quite thorough: model color/size/rotation/scale, key and fill light color/intensity/position/size, camera dolly/FOV, background color, and connection URL. Minor gaps: no model loading/selection, no camera rotation/orbit, no rim light or environment controls, but for the apparent scope it's fairly complete.

Maintenance

ActivityInactive
ResponsivenessNo issues