Skip to main content
Glama
README.md
# vibe3d

<p align="center">
  <a href="./README.md">English</a> |
  <a href="./README_CN.md">简体中文</a>
</p>

vibe3d is an MCP server for building and rendering 3D scenes. One command starts the protocol server, which launches the local daemon for you.

Requires **Node.js >= 20** (same as the [MCP TypeScript SDK v2](https://github.com/modelcontextprotocol/typescript-sdk)). Licensed under [Apache-2.0](./LICENSE).

<p align="center">
  <img src="./docs/wechat-core-group.jpg" alt="vibe3D and Blender discussion group" width="252"><br>
  <strong>vibe3D + Blender discussion group</strong>
</p>

## Identity

| Surface | Value |
| --- | --- |
| GitHub | [github.com/vibebytes/vibe3d](https://github.com/vibebytes/vibe3d) |
| npm | `@vibebyte/vibe3d-mcp` (and other `@vibebyte/vibe3d-*` packages) |
| MCP Registry | `io.github.vibebytes/vibe3d` |

## Install

```bash
npx -y @vibebyte/vibe3d-mcp@latest
```

The daemon listens on **loopback only** (`127.0.0.1` / `localhost`).

Client config:

```json
{
  "mcpServers": {
    "vibe3d": {
      "command": "npx",
      "args": ["-y", "@vibebyte/vibe3d-mcp@latest"]
    }
  }
}
```

GUI apps (Cursor, Claude Desktop) often do **not** load interactive shell config (`~/.zshrc`, nvm hooks, etc.). A wrapper like `bash -lc "nvm use …"` usually **fails** on macOS: login `bash` does not load zsh’s nvm. Do not put version-manager wrappers in MCP config.

Keep `@latest` in the config: without a version tag `npx` reuses its cache and never picks up new releases, even after you reload the MCP server. A newer MCP also restarts an older local daemon on start.

Make the **default** `node` / `npx` on the PATH those apps actually use **>= 20**, then use the JSON above:

1. Install Node.js 20+ with the [official installer](https://nodejs.org/) or Homebrew so the system `node` is already 20+; or
2. If you use a version manager, set its **default** to 20+ **and** ensure its shims are on the non-interactive PATH the GUI inherits (for example Volta’s `~/.volta/bin`). `nvm alias default 20` alone does not fix Cursor if nvm is only sourced in zsh.

### Cursor

Use the default JSON above in `.cursor/mcp.json` or the user MCP config. If status stays Error, open MCP Output — a Node < 20 message means fix the default Node on the GUI PATH (options above), not the MCP JSON shape.

### Claude Desktop

Same JSON in `claude_desktop_config.json`.

### Claude Code

```bash
claude mcp add vibe3d -- npx -y @vibebyte/vibe3d-mcp@latest
```

### VS Code

```json
{
  "servers": {
    "vibe3d": {
      "command": "npx",
      "args": ["-y", "@vibebyte/vibe3d-mcp@latest"]
    }
  }
}
```

### MCP Inspector

```bash
npx @modelcontextprotocol/inspector --cli npx -y @vibebyte/vibe3d-mcp@latest --method tools/list
```

## Environment

| Variable | Purpose |
| --- | --- |
| `VIBE3D_HOME` | Daemon data directory |
| `VIBE3D_CHROME` | Chrome or Chromium executable used for rendering |
| `VIBE3D_ROOM` | Room bound when the process starts |
| `VIBE3D_MCP_LOG` | Set to `0` to disable `mcp.log` |
| `VIBE3D_RESTART_DAEMON` | Set to `1` (or pass `--restart-daemon`) to restart the local daemon every time the MCP process starts |

When the MCP process starts and finds a local daemon of a different version, it stops that daemon (SIGTERM, data is flushed) and starts a matching one. Restarting the MCP therefore picks up an upgraded daemon. Rooms live only in daemon memory unless it runs with `--persist` (`VIBE3D_PERSIST=1`), so restarting a non-persistent daemon discards its rooms. `--url` remote daemons are never touched.

Rendering uses a system Chrome, Chromium, or Edge. The package does not download a browser. Without one, scene editing still works and `vibe3d_status` reports that rendering is unavailable.

## Support

| Platform | Status |
| --- | --- |
| macOS | Verified |
| Linux | Expected when Chrome or Chromium is on the default path. Not re-checked here |
| Windows | Not verified. Set `VIBE3D_CHROME` if you try it |

## CLI parameters

The `vibe3d` command line and MCP share one capability spec. Field names are camelCase. The CLI shows them in kebab-case, so `rotateStep` is `--rotate-step` and `withinBBox` is `--within-bbox`.

Comma-separated text splits into the field's array or tuple: `--tags a,b`, `--offset 1,0,0`. A command that has both `width` and `height` also accepts `--size 1280x720`. Objects take JSON, or `@file`.

```bash
vibe3d node create r1 --shape box --size 1,2,3
vibe3d scene query r1 --material m_aaaaaaaa --parent root
vibe3d anim gait r1 @runner --style run
```

`vibe3d help <command>` prints the same descriptions as `vibe3d_help`. An unknown flag is an error.

## Tools

The server exposes a small set of grouped tools. Each call passes `op`, for example `vibe3d_room` with `op: "create"`. This keeps `tools/list` small. Unknown tool names come back as tool errors (`isError`) so the model can correct itself. Long renders and exports are jobs: poll `vibe3d_job`.

## Multiple rooms and chats

One MCP process keeps one default room. While it has seen a single room, room tools may omit `room_id`; results marked "implicit" used that default. Once the process has seen a second room (or several chats share it), calls without `room_id` are rejected and list the candidates. Pass the `room_id` returned by your own `create` call on every call. An explicit `room_id` routes only that call and never changes the default. `vibe3d_room {op:"use"}` pins the default room when you know only one chat is active.

## Diagnostics

```bash
npx -y @vibebyte/vibe3d-mcp@latest --doctor --json
```

This prints the Node version, daemon health, Chrome discovery, schema file, and version match. It does not start an MCP session.

## Develop

See [CONTRIBUTING.md](./CONTRIBUTING.md). From this repository, with Node 20+:

```bash
CI=true pnpm install
pnpm --filter @vibebyte/vibe3d-editor build
pnpm --filter @vibebyte/vibe3d-daemon build
pnpm --filter @vibebyte/vibe3d-mcp build
```

`pnpm install` may ask to reinstall `node_modules` from scratch. `CI=true` accepts that prompt.

Security reports: [SECURITY.md](./SECURITY.md).