> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nora.my/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP on Nora Local

> Drive your self-hosted Nora from Claude Code, Codex CLI, or Cursor. Same tool surface as SaaS, pointed at your local server.

Every Nora Local deployment — desktop, compose, Kubernetes, air-gap — exposes the **same MCP server** as SaaS at `POST /api/v1/mcp` on the on-prem host. Point your coding agent at the local URL and you get the identical tool surface (Flow CRUD, block ops, wiring, variables, publish, traces).

## The URL

```text theme={null}
http://localhost:${NORA_HOST_PORT:-8090}/api/v1/mcp
```

* The **desktop app** and **compose** paths default to `http://localhost:8090/api/v1/mcp`. Change `NORA_HOST_PORT` in `.env.onprem` if that port is taken — the MCP URL moves with it.
* The **Kubernetes** path uses whatever hostname you set for `publicUrl` in the Helm chart (for example `https://nora.example.internal/api/v1/mcp`). See [Kubernetes (Helm)](/local-app/deploy/kubernetes).

## Connecting

<Tabs>
  <Tab title="Claude Code">
    ```bash theme={null}
    claude mcp add --transport http --scope user nora-local \
      http://localhost:8090/api/v1/mcp
    ```

    * `nora-local` is just the alias Claude Code shows in its tool list — pick a name that distinguishes it from any SaaS `nora` entry you already have.
    * `--scope user` writes it to `~/.claude.json` so every project on this laptop sees the local server. Drop the flag to register only in the current project.
    * The first tool call opens an OAuth login in your browser against **your own local server**. Sign in with a Nora Local account and grant `read` · `build` · `run`.

    Verify:

    ```bash theme={null}
    claude mcp list          # nora-local should appear
    claude mcp get nora-local
    ```
  </Tab>

  <Tab title="Codex CLI">
    ```bash theme={null}
    codex mcp add --transport http nora-local \
      http://localhost:8090/api/v1/mcp
    ```

    First tool call opens the browser OAuth flow. Verify with `codex mcp list`.
  </Tab>

  <Tab title="Cursor / other hosts">
    Add the entry to your host's MCP config.

    ```json theme={null}
    {
      "mcpServers": {
        "nora-local": {
          "type": "http",
          "url": "http://localhost:8090/api/v1/mcp"
        }
      }
    }
    ```
  </Tab>
</Tabs>

## OAuth requires NORA\_PUBLIC\_URL to match

The OAuth flow bounces the browser back to a callback URL the server advertises. That URL comes from `NORA_PUBLIC_URL` (and `NORA_OAUTH_REDIRECT_BASE`), so both need to match the URL your browser actually hits.

* **Localhost defaults** — `http://localhost:${NORA_HOST_PORT:-8090}` works out of the box for the desktop app and single-host compose.
* **Reverse proxy or ingress** — set `NORA_PUBLIC_URL=https://nora.example.internal` (or Helm's `publicUrl`) to the public URL. Otherwise the OAuth callback lands on `http://localhost:8090`, the browser can't reach it, and the login stalls.

## Alongside a SaaS entry

Register both with different aliases so tool names stay unambiguous.

```bash theme={null}
# SaaS
claude mcp add --transport http --scope user nora \
  https://platform.nora.my/api/v1/mcp

# Your local server
claude mcp add --transport http --scope user nora-local \
  http://localhost:8090/api/v1/mcp
```

Claude Code prefixes each tool with the server alias, so `nora_list_flows` and `nora-local_list_flows` are cleanly separated.

## What's exposed

The tool surface is identical to SaaS — see [MCP tool surface](/cli/mcp#what-tools-mcp-exposes) for the full list. Permissions mirror your Nora Local role: a Guest can only read; approval-gated actions still go through the approval flow when run over MCP.

## Air-gap notes

Nothing about MCP requires internet access — the whole flow (OAuth login, tool calls) stays on your local host.

* If `NORA_AIRGAP=1` is on, the server still serves MCP normally. The boot probe only checks the server can't reach the *public* internet; it doesn't restrict inbound traffic from the local browser or CLI.
* For headless server installs where the target host has no browser, run the coding agent on a laptop that can reach the server over the LAN, and set `NORA_PUBLIC_URL` to the LAN hostname (`http://nora.internal:8090`) so the OAuth callback lands somewhere your browser can hit.

## Troubleshooting

* **`connection refused` on `claude mcp add`.** The stack isn't up. Check `docker ps` or the desktop app tray. The URL must include the scheme (`http://`, not just `localhost:8090`).
* **OAuth login page 404s or hangs.** `NORA_PUBLIC_URL` doesn't match the URL your browser is on. Set it to the exact base URL (scheme + host + port) you're using and restart the stack.
* **Tool list is empty after login.** The account you signed in with has no workspace access on this server. Check permissions on the SPA (`Settings → Members`), then reconnect.
* **`nora-local` and `nora` collide.** They don't — Claude Code prefixes tool names per server. If tools appear duplicated in the picker, restart the host so the tool list refreshes.

## Related

<CardGroup cols={2}>
  <Card title="MCP (SaaS)" icon="plug" href="/cli/mcp">
    The same page for the hosted `platform.nora.my` server.
  </Card>

  <Card title="Settings — host port" icon="sliders" href="/local-app/desktop/settings">
    Change `NORA_HOST_PORT` when the default 8090 is taken.
  </Card>

  <Card title="Kubernetes (Helm)" icon="dharmachakra" href="/local-app/deploy/kubernetes">
    `publicUrl` chart value and ingress setup.
  </Card>
</CardGroup>
