# MCP on a self-hosted install

> Enable the MCP server of a self-hosted Nodaro so Claude, Cursor, ChatGPT and other MCP clients can use your install over OAuth, from its own MCP hostname.

Source: https://nodaro.ai/docs/self-hosting/mcp

The Nodaro **MCP server** lets AI clients such as Claude, Cursor, Cline, Continue, Goose and ChatGPT use the tools of your self-hosted install on a user's behalf. It is off by default. To enable it, you give MCP its own hostname, route that hostname to the API, and set two variables. Users then sign in to your install with OAuth from their client.

## How MCP is served

- **The API serves MCP** at the path `/mcp`, on port `9000` inside the `nodaro` container.
- **The web server on port `3000` refuses MCP requests.** A request to `/mcp` on the app's main address answers `405` with the error `wrong_mcp_host` and the address of the real endpoint.
- **So MCP needs its own hostname**, such as `mcp.nodaro.example.com`, whose traffic reaches the API directly.
- **Sign-in stays on your main domain.** The OAuth discovery document `/.well-known/oauth-authorization-server` and the consent screen are served on `PUBLIC_URL`.

## Enable MCP

### Route an MCP hostname to the API

Create a DNS record for `mcp.<your-domain>` that resolves to your server. Do not proxy it through a CDN: proxies break the long-lived Server-Sent Events connections that MCP uses.

**Docker Compose**

The compose file does not publish the API port. Publish it on the loopback interface, under the `nodaro` service:

```yaml
nodaro:
ports:
      - "3000:3000"
      - "127.0.0.1:9000:9000"
```

Then give your reverse proxy a second site for the MCP host that forwards to `127.0.0.1:9000` instead of `127.0.0.1:3000`. Keep response buffering off, as in the [nginx example](https://nodaro.ai/docs/self-hosting/reverse-proxy#option-a-nginx).

**Railway**

Add the MCP hostname as a custom domain of the backend service:

```bash
railway domain add mcp.your-domain.com --service backend
```

Or, in the Railway dashboard: **Project › backend service › Settings › Domains › Add custom domain**. Then add the CNAME record at your DNS provider.

### Set the variables

```bash
MCP_ENABLED=true                              # required; the default is off
MCP_PUBLIC_URL=https://mcp.your-domain.com    # the hostname from step 1
```

On the compose stack, add both under `environment:` of the `nodaro` service: the compose file does not pass them from `.env`. Then run `docker compose -f docker-compose.community.yml up -d`.

`MCP_PUBLIC_URL` is the identity that the discovery endpoints advertise for the protected resource (RFC 9728), and the base of the upload links that tools return. Without it, your install advertises the Nodaro Cloud MCP host. If you serve MCP on your main domain instead of a subdomain, set it to the same value as `PUBLIC_URL`. Requests to `/mcp` must then still reach the API port directly.

### Check the discovery endpoints

```bash
curl https://mcp.your-domain.com/.well-known/oauth-protected-resource
curl https://your-domain.com/.well-known/oauth-authorization-server
```

Both must return JSON with status `200`.

### Add the connector in your client

The MCP endpoint is `https://mcp.your-domain.com/mcp`. In Claude, open **Settings › Connectors › Add custom connector** and enter that URL. For other clients, see [Connect a client](https://nodaro.ai/docs/mcp/connect) and use your own MCP URL instead of Nodaro Cloud's.

## Which clients can register

MCP clients sign in with OAuth. Supported clients register themselves with your install through dynamic client registration (RFC 7591). Two variables control it, and their defaults suit most installs:

| Variable | Default | What it does |
| --- | --- | --- |
| `MCP_DYNAMIC_REGISTRATION` | `allowlist` | `allowlist` accepts only the client names in `MCP_DCR_ALLOWLIST`. `open` accepts any client. `off` disables dynamic registration. |
| `MCP_DCR_ALLOWLIST` | 14 known clients | The client names accepted in `allowlist` mode, comma-separated. |

The default allowlist is: Claude, Claude Code, Cursor, Cline, Continue, Goose, ChatGPT, OpenAI, Lovable, Gemini, Gemini CLI, Codex, MCP Inspector and mcp-inspector. To add your own client, set the full list:

```bash
MCP_DCR_ALLOWLIST=Claude,Cursor,Cline,Continue,Goose,YourCustomClient
```

`allowlist` is the recommended mode.

## What the clients can do

The MCP server covers the generation tools for image, video, audio, characters, locations and objects, and tools for the gallery, workflows, apps, saved components and models. Each user acts as their own account on your install. See the [MCP tools](https://nodaro.ai/docs/mcp/tools) reference and [MCP troubleshooting](https://nodaro.ai/docs/mcp/troubleshooting).

## Frequently asked questions

### Is MCP on by default on a self-hosted Nodaro?

No. Set MCP_ENABLED=true and MCP_PUBLIC_URL, route an MCP hostname to the API, and restart. On the compose stack, add both variables under the nodaro service's environment block, because the compose file does not pass them from .env.

### Why does my MCP client get 405 wrong_mcp_host?

The client uses the app's main address, where the web server refuses MCP requests. Point the client at the MCP host instead, https://your-mcp-host/mcp, which must reach the API on port 9000 directly.

### Which MCP clients can register with my install?

By default, 14 known clients, including Claude, Claude Code, Cursor, Cline, Continue, Goose, ChatGPT and Codex. Add names to MCP_DCR_ALLOWLIST, or set MCP_DYNAMIC_REGISTRATION=open to accept any client.

### Can I put a CDN in front of the MCP host?

Not one that proxies traffic. MCP keeps long-lived Server-Sent Events connections open, and a proxying CDN breaks them. Point DNS straight at your server.
