Skip to content
nauro

Guides

Connect your agent

The default path is the local stdio MCP server: nauro serve --stdio resolves the project store from the repo's .nauro/config.json. nauro setup and nauro adopt write that stdio entry directly into each surface's config file. A separate, optional cloud path reaches the project from surfaces without a local copy via nauro auth login, nauro link --cloud, nauro sync, and the remote connector at https://mcp.nauro.ai/mcp.

Two connection planes: local stdio vs cloud connector

Nauro has two distinct transports for the same store and payloads:

  • Local. The stdio MCP server, spawned per-session by the agent client. It is canonical for any machine with a working copy.
  • Cloud. The hosted HTTP connector at https://mcp.nauro.ai/mcp, used from surfaces without a local checkout (claude.ai web) or from another machine.

stdio is the sole supported local transport; the former local FastAPI HTTP transport was retired (per the serve.py docstring). The cloud connector is HTTP and lives in a separate, private hosted server.

The stdio server resolves project context from the current directory's .nauro/config.json; there is no --project flag on serve (one source of truth, per the serve.py docstring).

Commit .nauro/config.json so the wiring is reproducible.

Same store and payloads on both planes: the stdio server delegates to the transport-agnostic implementations in nauro.mcp.tools, which call into nauro_core.operations.

Local plane Cloud plane
Transport stdio (spawned per session) HTTP connector
Endpoint nauro serve --stdio https://mcp.nauro.ai/mcp
When to use Any machine with a working copy (canonical) Surfaces with no local checkout, or another machine
Project resolution From the cwd's .nauro/config.json From the authenticated cloud project

The local stdio server registers 10 MCP tools (7 read, 3 write). The shared nauro_core.mcp_tools.ALL_TOOLS registry has 11; list_projects is remote-only because local installs auto-resolve to the single project store.

The README phrases the total as "11 tools total (8 read, 3 write)", counting list_projects among reads; the local-only subset is 7 read + 3 write = 10.

text · local stdio tools
get_context, get_raw_file, list_decisions, get_decision,
diff_since_last_session, search_decisions, check_decision,
propose_decision, flag_question, update_state

# list_projects is remote-only (11th tool in nauro_core.mcp_tools.ALL_TOOLS)

Source: serve.py; stdio_server.py (module docstring; run_stdio); tools.py (module docstring); README.md (MCP tools section).

The local stdio MCP server (nauro serve --stdio)

nauro serve --stdio calls run_stdio(), which sets the telemetry transport to "stdio", runs a best-effort session-start pull (_pull_on_startup()), then mcp.run(transport="stdio"). The MCP client (Claude Code, Cursor, Codex) spawns this process and talks over stdin/stdout.

The --stdio flag is hidden and a no-op: it is accepted only for backward compatibility with installed client configs that spawn nauro serve --stdio. stdio is now the only transport (the serve Typer option is hidden=True with default True).

On startup, _pull_on_startup() resolves the project key from the current directory (via .nauro/config.json, then a v2 registry path match resolve_v2_from_path, then the legacy v1 name resolve_project).

If the project is cloud-mode with a stored token, it pulls the latest from remote (pull_before_session) before accepting tool calls. It never raises: on failure the server logs a warning and starts with local state.

Store resolution precedence inside tools is: an explicit project_id argument, else the cwd's .nauro/config.json, else the registry. When unresolved, tools return a guidance envelope (NOT_A_NAURO_REPO / WELCOME_NO_PROJECT) rather than crashing.

The FastMCP server is constructed as FastMCP("nauro", instructions=MCP_INSTRUCTIONS, log_level="WARNING").

The "nauro" name pairs with the stdio entry's command; behavioral guidance ships via MCP server instructions, not an injected CLAUDE.md block (the legacy CLAUDE.md block is removed by setup).

python · stdio_server.py
def run_stdio() -> None:
    """Run the MCP server over stdio (called by `nauro serve --stdio`)."""
    from nauro.telemetry.transport import set_transport

    set_transport("stdio")
    _pull_on_startup()
    mcp.run(transport="stdio")

Source: serve.py; stdio_server.py (run_stdio, _pull_on_startup, FastMCP construction).

nauro setup [claude-code|cursor|codex|all]: what it writes

nauro setup wires the local stdio server into each surface's config by writing the file directly, not by shelling out to the client's own mcp add.

All surfaces use the same key name nauro and the same entry shape: {"command": <abs path to nauro or "nauro">, "args": ["serve", "--stdio"]}. The command path is resolved via shutil.which("nauro"), falling back to the literal "nauro" (_find_nauro_command).

Subcommand Writes Key / scope Flags
setup claude-code <repo>/.mcp.json per registered repo path mcpServers.nauro (project scope) --project, --remove, --with-hooks
setup cursor <repo>/.cursor/mcp.json per repo mcpServers.nauro --project, --remove
setup codex ~/.codex/config.toml [mcp_servers.nauro] (user-global) --remove
setup all Claude Code + Cursor + Codex in one call; regenerates AGENTS.md all of the above --project, --remove, --with-subagents, --force-overwrite, --with-skills, --with-hooks

setup claude-code also removes any legacy injected CLAUDE.md block (_remove_claude_md) and prunes a redundant user-scope HTTP nauro entry from ~/.claude.json (_prune_redundant_user_scope_mcp). Only the HTTP-transport / url entry is pruned; a user-defined stdio entry is left alone.

For setup codex, teardown of the user-global entry is gated on the registry (_user_scope_safe_to_clear): only the last registered project's --remove clears it. setup all continues across per-handler errors so partial coverage still reports progress.

--with-subagents (on setup all / adopt) installs the bundled @nauro-planner, @nauro-executor, @nauro-reviewer, and @nauro-tech-lead (AGENT_NAMES) into ~/.claude/agents/; it is off by default to avoid overwriting customized files.

A differing existing nauro-*.md is refreshed and the prior content stashed to <name>.md.bak; --force-overwrite skips the .bak and overwrites in place. Currently only the Claude Code surface is implemented; Cursor and Codex emit a "not yet implemented" skip line.

When subagents are installed, setup prints SUBAGENTS_CONNECTOR_NAME_NOTICE: name the remote MCP connector exactly Nauro so the bundled mcp__claude_ai_Nauro__* tools resolve.

--with-hooks (Claude Code only) wires an advisory UserPromptSubmit hook into <repo>/.claude/settings.json that runs nauro hook user-prompt-submit (timeout 10s; HOOK_COMMAND, HOOK_TIMEOUT_SECONDS). It surfaces related decisions as context each turn (BM25 retrieval) and never blocks.

The add path is idempotent (it skips if a nauro hook is already present); remove strips only the nauro-authored entry. Cursor and Codex have no comparable per-turn event.

json · .mcp.json (Claude Code / Cursor)
{
  "mcpServers": {
    "nauro": {
      "command": "nauro",
      "args": ["serve", "--stdio"]
    }
  }
}
toml · ~/.codex/config.toml
[mcp_servers.nauro]
command = "nauro"
args = ["serve", "--stdio"]
shell
$ nauro setup claude-code      # writes <repo>/.mcp.json per repo
$ nauro setup all              # claude-code + cursor + codex in one call
$ nauro setup all --with-subagents --with-hooks

Source: setup.py (_configure_json_mcp, _configure_mcp, _configure_cursor_for_repo, _configure_codex, _find_nauro_command, _prune_redundant_user_scope_mcp, claude_code, cursor, codex, all_, setup_all_surfaces, materialize_agents, materialize_hooks_claude_code, HOOK_COMMAND, HOOK_TIMEOUT_SECONDS, SUBAGENTS_CONNECTOR_NAME_NOTICE).

nauro adopt: register and wire in one shot (existing repo)

nauro adopt bootstraps a project from an existing repo. It detects the repo root (or --repo), refuses non-git directories (_is_git_repo), guards against re-adopting (an existing .nauro/config.json), and runs a same-name collision pre-check against the local v2 registry (_check_collision via find_projects_by_name_v2).

It then registers a v2 project (register_project_v2, mode=local), writes .nauro/config.json (save_repo_config), and scaffolds the store (scaffold_project_store), before wiring MCP and materializing skills across Claude Code, Cursor, and Codex via setup_all_surfaces.

After wiring, adopt smoke-tests the wired binary by booting <nauro> serve --stdio briefly (_smoke_test_wired_binary, default timeout 1.5s): a clean exit on stdin EOF (returncode 0) or a process still running at timeout is healthy; a non-zero exit before timeout (an import crash) prints a WARNING.

The closing message instructs the user to restart their agent and invoke /nauro-adopt.

Flags: --name, --repo, --print-prompt (mutually exclusive with all other flags; prints the canonical /nauro-adopt skill body to stdout via load_adopt_body() for pasting into chat surfaces), --no-setup-and-skills (skip MCP wiring and skill materialization), --with-subagents, --force-overwrite, --with-skills.

If the repo is already adopted and the caller passes --with-subagents / --with-skills / --force-overwrite, adopt installs just those artifacts onto the existing adoption (_install_into_adopted_repo via setup_all_surfaces, registration untouched) instead of dead-ending.

The always-installed skill set is SKILL_NAMES = ("nauro-adopt",); --with-skills additionally installs OPT_IN_SKILL_NAMES = ("nauro-ship-task", "nauro-handoff", "nauro-context"). nauro-ship-task references the @nauro-* subagents, so passing --with-skills without --with-subagents prints SHIP_TASK_NEEDS_SUBAGENTS_NOTICE; nauro-handoff and nauro-context compose only existing MCP tools and carry no such notice.

shell
$ cd /path/to/repo
$ nauro adopt                       # register + wire MCP + install /nauro-adopt skill
$ nauro adopt --with-subagents      # also install @nauro-* into ~/.claude/agents/
# then: restart your agent and run /nauro-adopt

Source: adopt.py (adopt, _is_git_repo, _smoke_test_wired_binary, _check_collision, _install_into_adopted_repo, --print-prompt, load_adopt_body); setup.py (SKILL_NAMES, OPT_IN_SKILL_NAMES).

Cloud path: auth login → link --cloud → sync (distinct steps)

The cloud path is opt-in and reaches a project from surfaces without a local copy or from another machine. It is three distinct steps, kept separate on purpose.

Step 1, nauro auth login. An Auth0 Authorization Code + PKCE (S256) flow. It opens a browser, runs a localhost callback server on REDIRECT_PORT = 18457 (redirect_uri http://localhost:18457/callback), waits up to 120s for the code, exchanges it, and stores tokens in ~/.nauro/config.json under the "auth" key (with sub, sanitized_sub, user_id, access_token, refresh_token).

The public OAuth identifiers (not secrets) are DEFAULT_API_URL=https://mcp.nauro.ai, DEFAULT_AUTH0_AUDIENCE=https://mcp.nauro.ai/mcp, and AUTH0_SCOPES="openid profile email offline_access read:context write:context".

The closing message tells cloud users to add https://mcp.nauro.ai/mcp as a connector and, for Codex, to set mcp_oauth_callback_port = 8765 at the top of ~/.codex/config.toml.

Step 2, nauro link --cloud. A one-time, one-way promotion of the current repo's local-only project to cloud. It reads the local-mode .nauro/config.json, requires auth (errors with "Run 'nauro auth login' first" otherwise), calls the remote POST /projects (create_project) to mint a cloud project_id, re-keys the local store directory and v2 registry entry under the new id (rename_project_id_v2, preserving repo_paths and store contents), and rewrites .nauro/config.json to mode=cloud with server_url=https://mcp.nauro.ai (DEFAULT_API_URL).

There is no inverse "unlink to local". The re-key is the irreversible step and persists before the push; the initial push (push_changed_files) is best-effort: a transient presign/S3 failure warns and exits 0, and nauro sync retries the upload.

Step 3, nauro sync. Captures a snapshot (capture_snapshot) and regenerates AGENTS.md in associated repos. With cloud sync configured it does a git-style pull-then-push: pull first (_pull_via_presign: GET /sync/manifestPOST /sync/presign → S3 GETs), then push (push_store_to_cloud).

For a local-only project it captures the snapshot and reports "local-only project; nothing to upload". Flags: --message/-m, --project, --status. The MCP update_state tool records what changed; sync does not touch state_current.md.

nauro attach <project_id> is the cloud equivalent of init --add-repo: associate the current repo with an existing cloud project (membership is verified against GET /projects via list_projects before any local write), writing a cloud-mode .nauro/config.json. For a fresh start, nauro init --cloud <name> creates a cloud-scoped project up front.

After the steps, add https://mcp.nauro.ai/mcp as an MCP connector in your tool's settings. Enter the URL exactly, with no trailing slash.

shell
$ nauro auth login
$ nauro link --cloud   # one-time: promote the local project to cloud
$ nauro sync
# then add https://mcp.nauro.ai/mcp as an MCP connector (no trailing slash)

Source: auth.py (login, DEFAULT_API_URL, DEFAULT_AUTH0_AUDIENCE, AUTH0_SCOPES, REDIRECT_PORT, REDIRECT_URI, closing message); link.py (link); sync.py (sync, _pull_via_presign, _pull_from_cloud); attach.py (attach); cloud_projects.py (create_project, list_projects); README.md (Cross-surface sync section).

Codex remote connector

  • Add the hosted connector under a name distinct from the local nauro stdio server: codex mcp add nauro-cloud --url https://mcp.nauro.ai/mcp.
  • Pin the OAuth callback port at the top of ~/.codex/config.toml: mcp_oauth_callback_port = 8765. Codex's callback uses a fixed, pre-registered port; without this line Codex picks a random port and login fails. The closing message from nauro auth login also surfaces this requirement.
  • Requires Codex 0.131.0 or newer. Enter the URL exactly as shown, with no trailing slash. If login reports a callback-port error, free port 8765.
  • This is separate from nauro setup codex, which wires the local stdio server under [mcp_servers.nauro] in the same ~/.codex/config.toml. The remote connector (nauro-cloud) and the local stdio server (nauro) coexist under different names.
shell
$ codex mcp add nauro-cloud --url https://mcp.nauro.ai/mcp
toml · top of ~/.codex/config.toml
mcp_oauth_callback_port = 8765

Source: README.md (Codex remote connector section); auth.py (login closing message); setup.py (_configure_codex, the local stdio entry under [mcp_servers.nauro]).

Chat surfaces (Claude.ai, ChatGPT, Perplexity)

Chat-paste mode does not create projects. First run nauro adopt --name <project> from a terminal: that registers the project, wires MCP across surfaces, and installs the skills.

Only then can the chat agent seed context.

After adoption, point the chat agent at docs/adopt-prompt.md. nauro adopt --print-prompt outputs the same canonical /nauro-adopt skill body to stdout (without the intro paragraph) for copy/paste.

Chat surfaces have no shell, so the skill operates only on content the user pastes (Step 3b) and only against an already-adopted project (verified in Step 2). The code-evidence path (Step 4) and the targeted probes (Step 6b) are unavailable; the skill skips from Step 3b directly to Step 5.

On chat surfaces the skill passes the project's id as the explicit project_id argument on every MCP call (propose_decision, update_state, flag_question, get_context, list_decisions, check_decision, get_decision).

Auto-resolve would otherwise route to the user's default project, not the one being seeded when local and cloud projects coexist.

Reaching a project from a chat surface without a local copy still requires the cloud connector (https://mcp.nauro.ai/mcp) configured in that tool's settings: the cloud path above must already be done.

shell
$ nauro adopt --name <your-project-name>   # register + wire MCP + install skills
# then in chat, follow docs/adopt-prompt.md (or: nauro adopt --print-prompt)

Source: docs/adopt-prompt.md (intro, Surface modes, Step 2, Step 3b); README.md (Chat surfaces line); adopt.py (--print-prompt, load_adopt_body).

Key facts and gotchas

  • The local stdio MCP entry (all surfaces) is keyed nauro with shape {"command": <nauro path>, "args": ["serve", "--stdio"]}; the command is resolved via shutil.which("nauro") with a fallback to the literal "nauro".
  • setup writes config files directly: claude-code → <repo>/.mcp.json (project scope, per repo); cursor → <repo>/.cursor/mcp.json (per repo); codex → ~/.codex/config.toml under [mcp_servers.nauro] (user-global).
  • The cloud connector URL is https://mcp.nauro.ai/mcp; enter it exactly, with no trailing slash.
  • Codex remote connector: codex mcp add nauro-cloud --url https://mcp.nauro.ai/mcp; requires mcp_oauth_callback_port = 8765 at the top of ~/.codex/config.toml; requires Codex 0.131.0 or newer.
  • auth.py constants: DEFAULT_API_URL=https://mcp.nauro.ai, DEFAULT_AUTH0_AUDIENCE=https://mcp.nauro.ai/mcp, REDIRECT_PORT=18457, REDIRECT_URI=http://localhost:18457/callback, AUTH0_SCOPES="openid profile email offline_access read:context write:context"; tokens are stored in ~/.nauro/config.json under "auth"; the callback timeout is 120s.
  • The cloud path is three distinct steps kept separate: nauro auth loginnauro link --cloud (one-time, one-way promotion; no inverse) → nauro sync.
  • The local stdio server registers 10 MCP tools (7 read, 3 write); nauro_core.mcp_tools.ALL_TOOLS has 11; list_projects is remote-only. The README states the total as "11 tools total (8 read, 3 write)".
  • stdio is the only supported local transport (the FastAPI HTTP transport was retired); --stdio is a hidden no-op kept for backward compatibility; there is no --project flag on serve.
  • nauro adopt registers a v2 project (mode=local), wires all surfaces via setup_all_surfaces, smoke-tests the binary, and installs the /nauro-adopt skill; --print-prompt emits the canonical adopt body; the closing message tells the user to restart their agent and run /nauro-adopt.
  • Always-installed skill: nauro-adopt. Opt-in (--with-skills): nauro-ship-task, nauro-handoff, nauro-context. --with-subagents installs @nauro-planner, @nauro-executor, @nauro-reviewer, @nauro-tech-lead into ~/.claude/agents/ (only the Claude Code surface is implemented).
  • When subagents are installed, name the remote connector exactly Nauro so the bundled mcp__claude_ai_Nauro__* tools resolve (SUBAGENTS_CONNECTOR_NAME_NOTICE).
  • --with-hooks (Claude Code only) writes an advisory UserPromptSubmit hook running nauro hook user-prompt-submit (timeout 10s) into <repo>/.claude/settings.json; it never blocks.
  • Chat surfaces: run nauro adopt in a terminal first (chat-paste mode does not create projects), then point the agent at docs/adopt-prompt.md; the skill passes an explicit project_id on every MCP call.
  • setup claude-code prunes a redundant user-scope HTTP nauro entry from ~/.claude.json because project-scope stdio is canonical and a same-name collision can resolve to the wrong store (only the HTTP/url entry is pruned; a user-defined stdio entry is left alone).

Gotchas to watch for:

  • The local stdio entry uses command/args (nauro serve --stdio), not a url; the url form (https://mcp.nauro.ai/mcp) is only for the hosted/remote connector. Do not present the cloud URL as the local .mcp.json value.
  • nauro setup codex (local stdio under [mcp_servers.nauro]) is a different thing from the Codex remote connector nauro-cloud added via codex mcp add --url. They coexist under different names in the same ~/.codex/config.toml.
  • link --cloud is one-way and irreversible: the re-key persists before the cloud push, and a failed push does not roll it back (it warns, exits 0, and you retry with nauro sync). There is no "unlink to local" command.
  • Codex's 8765 port is Codex-side and unrelated to nauro's own 18457 auth-login callback port.
  • Cloud-mode projects cannot be extended with init --add-repo; use nauro attach <project_id> instead.

The subagent doctrine-risk labels GREEN/AMBER/RED appear only in the @nauro-planner agent description, not in plan or pricing copy.