Reference
MCP reference
Nauro exposes its decision and context store to agents over the Model Context Protocol. One shared tool registry (nauro_core.mcp_tools.ALL_TOOLS) holds 11 tools, 8 read and 3 write, consumed by two transports: a local stdio server and a hosted HTTP endpoint. Tool descriptions, titles, JSON input schemas, and behavioral annotations are centralized in mcp_tools.py so both transports stay in sync. As of 1.0, the stdio MCP tool contract — the tool names, JSON input schemas, and return shapes documented here — is stable and follows semantic versioning; the hosted HTTP endpoint is versioned and operated separately.
Transports: local stdio vs hosted endpoint
The local stdio server is run with nauro serve --stdio. Claude Code and other MCP clients spawn this process and talk over stdin and stdout. The --stdio flag is a hidden no-op kept for backward compatibility, since stdio is the only supported local transport (the former local FastAPI HTTP transport was retired).
There is no --project flag on the local server. It resolves the project from the cwd's .nauro/config.json.
On startup the stdio server pulls latest from the remote (best-effort) before accepting tool calls, then runs mcp.run(transport="stdio"). Pull failures are logged and the server starts with local state.
The server is a FastMCP("nauro", instructions=MCP_INSTRUCTIONS, log_level="WARNING") instance. Read tools that have a renderer in nauro_core.renderers.RENDERERS return a CallToolResult with a single TextContent block carrying the renderer output. Write tools, get_raw_file, and diff_since_last_session keep a single-block shape.
The hosted endpoint is https://mcp.nauro.ai/mcp. Add it as an MCP connector in your tool's settings to reach a cloud-synced project from surfaces without a local copy (for example claude.ai web) or from another machine.
The hosted path requires nauro auth login, then a one-time nauro link --cloud to promote the local project, then nauro sync. The hosted server implementation lives in a private repository but consumes the same ALL_TOOLS registry and operations kernel.
The local FastMCP server and the remote HTTP server read tool metadata from the same nauro_core.mcp_tools registry. The local server derives input schemas from Python type hints and consumes title, description, and annotations; the remote (JSON-RPC) server consumes the whole ToolSpec entry directly.
# Local stdio server (spawned by the MCP client; runnable directly)
$ nauro serve --stdio
Source: packages/nauro/src/nauro/cli/commands/serve.py; packages/nauro/src/nauro/mcp/stdio_server.py; packages/nauro/README.md; README.md.
Read/write annotation model
Every tool is closed-world (openWorldHint: False). It operates only on the local or remote Nauro store.
Writes are non-destructive (destructiveHint: False). The source comment states writes are additive and nothing is ever deleted.
Read tools carry _READ_ANNOTATIONS = {readOnlyHint: True, openWorldHint: False} plus idempotentHint: True. Write tools carry _WRITE_ANNOTATIONS = {readOnlyHint: False, destructiveHint: False, openWorldHint: False} plus idempotentHint: False.
_READ_ANNOTATIONS = {"readOnlyHint": True, "openWorldHint": False}
_WRITE_ANNOTATIONS = {"readOnlyHint": False, "destructiveHint": False, "openWorldHint": False}
Source: packages/nauro-core/src/nauro_core/mcp_tools.py.
Connector config (mcp.json)
nauro adopt (or nauro setup claude-code) writes the entry into the repo's project-scope .mcp.json under mcpServers["nauro"]. The entry shape is {"command": "<validated absolute path to nauro>", "args": ["serve", "--stdio"]} when a durable install is available. Setup probes candidates, prefers durable absolute installs, warns before recording a fragile working path, and uses bare nauro only as the warned terminal fallback.
Cursor uses the same shape in <repo>/.cursor/mcp.json. Codex uses ~/.codex/config.toml.
The local stdio connector below applies to both Claude Code (.mcp.json) and Cursor (.cursor/mcp.json).
{
"mcpServers": {
"nauro": {
"command": "<validated absolute path to nauro>",
"args": ["serve", "--stdio"]
}
}
}
The hosted remote connector is registered under a name distinct from the local nauro stdio server, as in the Codex example below.
Hosted failure modes split by cause. Exhausting the monthly free-tier allowance (5,000 tools/call per account) is a tool-level result, not a transport error: the call returns HTTP 200 with isError and a message carrying the reset date, so the model can relay it in conversation, and successful calls near the threshold carry an appended remaining-allowance note. The per-user burst rate limiter is separate and does return HTTP 429 (transient; retry). Details under plans and limits.
$ codex mcp add nauro-cloud --url https://mcp.nauro.ai/mcp
# pin the OAuth callback port in ~/.codex/config.toml (Codex 0.131.0+)
mcp_oauth_callback_port = 8765
Source: packages/nauro/src/nauro/cli/commands/setup.py; README.md.
Tool reference (11 tools)
Each entry below gives the tool name, READ or WRITE kind, a one-line purpose, input parameters (type, required or optional plus default), and return shape. Schemas are from mcp_tools.py (input_schema); return shapes from the kernel *Result Pydantic models.
Every tool except list_projects accepts an optional project_id (string), used by the remote surface to disambiguate when a user has multiple projects; local installs auto-resolve from cwd. list_projects takes an empty object schema.
The local stdio surface also accepts an internal cwd (string) parameter for store resolution; it is not part of the published input schema, and MCP clients never need to pass it.
| Tool | Kind | Purpose |
|---|---|---|
get_context | read | Return project context at the requested detail level. |
get_raw_file | read | Return the raw markdown of any store file. |
list_decisions | read | Browse the full decision history. |
get_decision | read | Return a specific decision by number. |
diff_since_last_session | read | Show what changed since the last snapshot. |
search_decisions | read | Search decisions by BM25 relevance. |
check_decision | read | Check overlap with existing decisions without writing. |
propose_decision | write | Record an architectural decision. |
flag_question | write | Flag or resolve an open question. |
update_state | write | Update current state with a progress delta. |
list_projects | read, remote-only | Return the projects this user can access. |
get_context (READ)
Returns project context at the requested detail level (L0 concise, L1 working set, L2 full dump).
L0 is the concise orientation payload: project summary, current state, top open questions, and the last 10 decisions. L1 is a bounded working set, and L2 is the full dump. On mature stores, L2 can reach hundreds of thousands of tokens, so agents should start with L0 and pull exact decisions on demand.
Inputs:
level(string enumL0|L1|L2, optional, defaultL0). Detail level. The kernel also accepts int0|1|2.project_id(string, optional).
Returns GetContextResult: content (markdown payload string) on success, or error (kind="rejected", invalid level) otherwise. L0 appends a *Last synced: ...* trailer when present; L2 appends a ## Snapshot Diff (...) block; empty or no-decision stores get a NO_CONTEXT_YET body. The envelope adds store and project identity.
get_raw_file (READ)
Returns the raw markdown of any file in the project store (a low-level escape hatch).
Inputs:
path(string, required). Path relative to store root, e.g.project.md,decisions/001-initial-architecture.md.project_id(string, optional).
Returns GetRawFileResult: content (file text) on success, or error (kind="error") on a miss. The adapter rejects path traversal with Invalid path: <path>, and on a miss adds an available_files list (capped at 20, excludes snapshots/).
list_decisions (READ)
Browse the full decision history (beyond the last 10 in get_context, or with the include-superseded filter).
Inputs:
limit(integer, optional, default20). Max decisions returned.include_superseded(boolean, optional, defaultfalse).project_id(string, optional).
Returns ListDecisionsResult: decisions, a list of DecisionSummary rows (number, title, date?, status, type?, confidence), sorted by number descending and truncated to limit.
get_decision (READ)
Returns a specific decision by its number, as a header triage projection or the full body.
Inputs:
number(integer, required). Decision number, e.g.23.mode(string enumheader|full, optional, defaultfull).headeris frontmatter plus title plus short lede;fullis the complete decision markdown.project_id(string, optional).
Returns GetDecisionResult: content (decision markdown body) on success, or error (kind="error") on a miss.
diff_since_last_session (READ)
Shows what changed in the project context since the last snapshot, or since N days ago.
Inputs:
days(integer, optional). Look back N days; finds the nearest snapshot to N days ago and diffs against the latest. Omit to diff the two most recent snapshots (session-scoped).project_id(string, optional).
Returns DiffSinceLastSessionResult: diff (human-readable diff body) and optional cutoff_date_used (echoes the baseline timestamp for time-based lookups). Sentinel diff strings cover "not enough snapshots (need at least 2)" and "one snapshot covers the requested range" rather than erroring.
search_decisions (READ)
Searches all decisions by BM25 relevance over titles and rationale.
Inputs:
query(string, required, non-empty). Search text.limit(integer, optional, default10). Max results.include_superseded(boolean, optional, defaultfalse).project_id(string, optional).
Returns SearchDecisionsResult: results, a list of SearchHit rows (number, title, date?, status, relevance_snippet?, score), sorted by BM25 score descending and truncated to limit; error (kind="rejected") on an empty or whitespace query.
check_decision (READ)
Checks whether a proposed approach overlaps existing decisions without writing anything; returns related decisions and a deterministic assessment. It is advisory and does not judge whether the proposal should proceed.
Inputs:
proposed_approach(string, required). The approach you are considering.context(string, optional). Extra context on why.project_id(string, optional).
Returns CheckDecisionResult: related_decisions, a list of RelatedDecision (id, title, score, status, date, rationale_preview) via Tier 1 plus Tier 2 BM25 retrieval; and assessment (a deterministic summary string).
The tool does not judge conflicts. The agent reads each related decision via get_decision before proposing.
propose_decision (WRITE)
Records an architectural decision; commits on Tier 1 structural validation (single-call), with Tier 2 BM25 similarity advisory (non-blocking). An agent must obtain explicit human approval for every add, update, or supersede before calling this tool.
Inputs:
title(string). The only schema-required field isrationale: a title must be present foraddandsupersede(Tier 1 rejects a blank one) and must be omitted onupdate.rationale(string, required). Why, including constraints and tradeoffs.operation(string enumadd|update|supersede, optional, defaultadd).affected_decision_id(string, optional). Required when operation isupdateorsupersede(e.g.decision-042).rejected(array of{alternative, reason}objects, optional). Alternatives considered.confidence(string enumhigh|medium|low, optional, default effectivelymediumfor add/supersede).decision_type(string enumarchitecture|api_design|infrastructure|pattern|refactor|data_model, optional).reversibility(string enumeasy|moderate|hard, optional).files_affected(array of strings, optional). Repo-relative paths.resolves_questions(array of strings, optional). Open-question ids to mark resolved.project_id(string, optional).
operation="update" is rationale-only: the server consumes rationale and affected_decision_id and rejects title, rejected, files_affected, decision_type, reversibility, and confidence at the boundary. Use supersede to change any of those. An update bumps the decision's version and appends a dated paragraph to its rationale.
Returns ProposeDecisionResult: status (confirmed|rejected), tier (int), operation (add|update|supersede|reject), similar_decisions (advisory RelatedDecision hits), assessment, decision_id (on-disk file stem on success), touched_decisions (files rewritten), resolved_questions (ids moved under ## Resolved), and optional error. The confirmed status means the write succeeded; it is not a second approval prompt.
After a successful write the adapter captures a snapshot, refreshes a Nauro-managed AGENTS.md, and pushes to cloud. This incidental path preserves an unmanaged AGENTS.md with a warning. Adopt and setup do the same. nauro sync is the sole explicit overwrite path, and a # Manual section survives replacement.
flag_question (WRITE)
Flags an unresolved question for human review, or resolves existing questions against a decision (writes open-questions.md). Pass exactly one of question or resolved_by.
Inputs:
question(string, optional). The question to flag.context(string, optional). Why it matters.targets(array of strings, optional). Question ids; on flag, duplicate candidates that short-circuit if already resolved; on resolve, the entries to stamp (every id must exist).resolved_by(string, optional). Decision id that resolvestargets; when set, the call resolves instead of appending.project_id(string, optional).
Returns FlagQuestionResult: status (ok|rejected), num (minted id on the ok-flag path; unset when rejected), and error. On flag, the adapter may add a BM25 similarity hint pointing to an addressing decision (the question is still logged). On success it captures a snapshot and pushes.
The local stdio surface does not surface the structured envelope; it returns a short string ("Question flagged." / "Question(s) resolved." / a hint plus "The question has still been logged." / a rejection reason).
update_state (WRITE)
Replaces the project's current state body (state_current.md) with the delta, archiving the prior body to state_history.md, and triggers a snapshot. Each call replaces the whole body rather than appending, so compose one delta per session; deltas over 5,000 characters (MAX_DELTA_LENGTH) are rejected at the boundary.
Inputs:
delta(string, required). What changed, e.g."Deployed v0.2.0 to staging".project_id(string, optional).
Returns UpdateStateResult: status (ok wrote a new state body; noop means no existing state file to update), optional warning (keyword-overlap caution; the update is still applied), and optional error. On a non-noop the adapter captures a snapshot and pushes.
The local stdio surface returns a short string ("State updated." optionally with the warning appended).
list_projects (READ, remote-only)
Returns the projects this user has access to (the discovery entry point for picking a project_id).
Inputs: none (empty object schema).
Returns the projects accessible to the current user. It is not registered on the local stdio server, because local installs auto-resolve to the single project store, so the discovery tool is unnecessary; only the remote or hosted surface registers it. The return-shape detail is not defined in nauro_core.operations (it is handled by the private hosted server).
The hosted result also carries a usage line (used count, monthly allowance, remaining, reset date), so list_projects doubles as the self-serve usage check.
Source: packages/nauro-core/src/nauro_core/mcp_tools.py; packages/nauro/src/nauro/mcp/tools.py; packages/nauro-core/src/nauro_core/operations/results.py; packages/nauro/src/nauro/mcp/stdio_server.py.
Example tool calls (MCP JSON-RPC arguments)
The arguments below are passed in MCP tools/call requests, one example per common workflow.
// get_context at session start
{ "name": "get_context", "arguments": { "level": "L0" } }
// search the decision log
{ "name": "search_decisions", "arguments": { "query": "authentication", "limit": 10 } }
// check before adopting an approach (read-only, no write)
{ "name": "check_decision",
"arguments": { "proposed_approach": "Use Redis as the hot-read cache" } }
// record a decision (write)
{ "name": "propose_decision",
"arguments": {
"title": "Adopt Redis for hot-read cache",
"rationale": "In-memory cache for hot read paths; lower p99 than DB round-trips.",
"rejected": [{ "alternative": "Memcached", "reason": "Less feature-rich" }],
"files_affected": ["src/cache.py", "src/api.py"],
"confidence": "medium",
"decision_type": "infrastructure"
} }
Source: packages/nauro-core/src/nauro_core/mcp_tools.py (input schemas); README.md (CLI mirror of propose-decision).
Key facts
- One shared registry
nauro_core.mcp_tools.ALL_TOOLSholds 11 tools: 8 read, 3 write. Order: 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. - Read tools (8): get_context, get_raw_file, list_decisions, get_decision, diff_since_last_session, search_decisions, check_decision, list_projects. Write tools (3): propose_decision, flag_question, update_state.
- The local stdio server registers 10 (7 read, 3 write);
list_projectsis remote-only, since local installs auto-resolve the single project store. - Local transport:
nauro serve --stdiorunsnauro.mcp.stdio_server.run_stdio(), aFastMCP("nauro")server over stdin and stdout.--stdiois a hidden no-op kept for back-compat; stdio is the only local transport. - Hosted endpoint:
https://mcp.nauro.ai/mcp(private repo); requiresnauro auth loginplusnauro link --cloudplusnauro sync. Enter the URL with no trailing slash. - All tools are
openWorldHint: False(closed-world); writes aredestructiveHint: False(additive, nothing deleted). Reads addreadOnlyHint: TrueplusidempotentHint: True; writes addreadOnlyHint: FalseplusidempotentHint: False. .mcp.jsonconnector entry shape (written bynauro adopt/nauro setup claude-code):mcpServers.nauro = {"command": "<validated absolute path to nauro>", "args": ["serve", "--stdio"]}when a durable install is available. Cursor uses.cursor/mcp.jsonwith the same shape; Codex uses~/.codex/config.toml. Fragile and unresolved fallback paths produce warnings.- Every tool except
list_projectsaccepts optionalproject_id(string);list_projectstakes an empty object schema.propose_decisionrequiresaffected_decision_idwhenoperationisupdateorsupersede;flag_questiontakes exactly one ofquestionorresolved_by. - Return shapes are typed Pydantic
*Resultmodels innauro_core.operations.results; each addsstore(and the local surface addsprojectidentity) at serialization time.
Scope of this reference
This page documents the shared tool contract from the public nauro_core registry and operations layer, which both the local stdio server and the hosted server implement. The hosted server's own internals (JSON-RPC framing, auth flow, response envelope) belong to the hosted service and are not part of this contract.
list_projects is the one tool defined only by the hosted server: its inputs (none) and purpose are documented above, but its exact return fields are hosted-side and may evolve independently of the stdio contract.
On the counts: 11 tools total, 8 read of which list_projects is remote-only, so the local stdio server registers 10 (7 read, 3 write).