NodaroAccount
Nodaro Docs
DocumentationNode ReferenceModelsTemplatesAI Agents (MCP)DevelopersSelf-hostingResearchCompare

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, on an MCP hostname or a path.

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. On the Business and Cloud editions, you can serve it by path on your own hosts instead.
  • 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.

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

  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.

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

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

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

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 and use your own MCP URL instead of Nodaro Cloud's.

MCP by path (no dedicated MCP host)

Business and Cloud editions only. It is a surface profile key, which Community ignores. A deployment with no hostname to spare for mcp.<your-domain>, or whose network allows only its own listed hosts, can serve the same MCP endpoint under a path:

NODARO_SURFACE_PROFILE={"mcp":{"byPath":true}}
  • Where it answers. At https://<host>/v1/mcp on each host the deployment declares as its own: PUBLIC_URL and every CORS_ORIGIN entry. mcp.path sets another path: /v1/mcp or a lower-case path under it, with no trailing slash and at most 64 characters. Any other path falls back to /v1/mcp, with a warning.
  • What it needs. MCP_ENABLED=true, and PUBLIC_URL set to the deployment's own address. Without PUBLIC_URL, the server logs a warning and keeps MCP on /mcp.
  • No proxy change. The bundled proxy already forwards /v1/* and /.well-known/oauth-* to the API. MCP_PUBLIC_URL is not needed either.
  • Every address follows the host. The 401 challenge, the OAuth discovery documents, the consent screen and the links the tools hand out all name the declared host the client connected through. People sign in on the site they are on. A request on a host the deployment did not declare gets the addresses of PUBLIC_URL.
  • One sign-in works on every host. A client signed in through one of the deployment's hosts may use the same token on another.
  • The path replaces /mcp. While the mode is on, /mcp and its /.well-known/…/mcp documents are not served. A client set up with an earlier /mcp address reconnects at the path.

List only origins this deployment serves in CORS_ORIGIN: each one counts as one of its own hosts while the mode is on. With the mode off, the default, every MCP answer is unchanged.

To check it:

curl https://your-domain.com/.well-known/oauth-protected-resource/v1/mcp

The resource must be https://your-domain.com/v1/mcp. Users then add https://your-domain.com/v1/mcp as the connector URL.

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:

VariableDefaultWhat it does
MCP_DYNAMIC_REGISTRATIONallowlistallowlist accepts only the client names in MCP_DCR_ALLOWLIST. open accepts any client. off disables dynamic registration.
MCP_DCR_ALLOWLIST14 known clientsThe 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:

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 reference and 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 serve MCP without a dedicated MCP hostname?

On the Business and Cloud editions, yes. Set mcp.byPath to true in the surface profile, and MCP answers at https://your-host/v1/mcp on the deployment's own hosts. No proxy change and no MCP_PUBLIC_URL are needed.

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.

Last updated on

On this page