MCP integration
Nottario speaks MCP over streamable HTTP from the same binary that serves the web UI. One container, two interfaces.
Wire an agent in one command
From your project's Settings → Tokens tab in the web UI, issue an API token. The page shows a copy-paste line that looks like:
claude mcp add nottario --transport http \
--header "Authorization: Bearer <your-token>" \
-- https://your-host/mcp
That installs the MCP server into your local Claude Code config, scoped to one project (the project the token was issued under). An agent using that token can only read or write data in that project — admin tokens are not exempt.
Narrow the tool catalogue
Every tool definition a client loads stays in the agent's context for the whole session. Two optional parameters on the MCP URL trim the catalogue for one connection:
?tools=tasks,docskeeps only those domains (arch,cycles,docs,projects,search,skill,tasks);whoamialways stays.?shell=trueis for agents that can runcurl: it hidesdocs.readanddocs.write, because such an agent moves documents throughdocs.download_urlanddocs.upload_urlinstead.
For example, a coding agent that never touches the architecture
graph: https://your-host/mcp?tools=tasks,docs,projects,search,skill&shell=true.
Without parameters the connection sees every tool.
Tools
The MCP server exposes one tool per natural verb. Highlights:
nottario.tasks.list— list tasks, with filters by state, type, assignee, role, parent, cycle.nottario.tasks.claim_next— atomic "pick the next eligible task and assign it to me", in one locked statement.nottario.tasks.claim— atomic claim by id.nottario.tasks.create— files a task;claim: truecreates it already assigned to the caller and indoing, for work the agent is about to start.nottario.tasks.set_state— transitions with precondition checks. Moving a task out oftodoassigns it to the caller when it has no assignee, so nothing sits indoingor reachesdoneunowned.nottario.tasks.add_comment,nottario.tasks.link_commit— keep the audit trail honest.nottario.tasks.add_steps,nottario.tasks.update_step,nottario.tasks.remove_step,nottario.tasks.move_step— keep a task's checklist of short steps. Closing a task with steps undone is allowed; the response lists them underpending_stepswith anoticeasking the agent to tell the human.nottario.docs.read,nottario.docs.write— versioned markdown documents with optimistic concurrency viaexpected_version.nottario.docs.move— rename a document, keeping its history and its architecture links.nottario.docs.stat,nottario.docs.append— the cheap pair: fingerprint a document without its body to decide whether a write is needed, and add to the end of one without resending it.nottario.docs.upload_url,nottario.docs.download_url— signed, five-minute URLs to upload a whole document from a file on disk (single use) and to download one to a file, so document bodies do not pass through the agent's context.nottario.arch.upsert_node,nottario.arch.upsert_edge— maintain the architecture graph.nottario.search— full-text across tasks, docs and arch nodes.nottario.skill.install— returns a signed URL + install plan for the skill bundle zip (see below).
nottario.tasks.next exists as a read-only preview but does not
claim — prefer claim_next to avoid races.
Response shape
The tools return slim payloads by default. A mutation like
tasks.create, tasks.set_state or tasks.add_comment returns only
the fields an agent needs to chain the next call — id, title,
state, priority, updated_at, role/assignee — and does not
echo back the description or comment body the agent just sent.
tasks.list rows follow the same rule. docs.write and
docs.append answer with {path, current_version, updated_at} — the
markdown you just sent is not read back to you.
The same instinct applies to reads. docs.stat exists so an agent can
check whether a document changed without paying for its body, and
docs.read { head_only: true } returns frontmatter plus the first 400
characters when you only need to confirm you have the right file.
When the full object is genuinely needed (e.g. an agent reading a
task it didn't create), pass verbose: true on the call to opt
back into every field.
tasks.list defaults to open tasks only (state='todo' or
'doing'). Closed rows accumulate forever and would dominate every
walk in a long-lived project; pass include_closed: true when you
genuinely need them, or set an explicit state filter (e.g.
state='done') to scope to a closed bucket.
tasks.get returns the base task in full (descriptions are the
reason you called get in the first place) together with related:
the parent feature, the subtasks, the tasks it depends on and the tasks
it blocks, each as {id, title, state, type}, and steps, the open
items of the task's checklist in order, each {id, text, done}
(include_done_steps: true lists the done ones too). The step tools
answer with the step they touched and the progress, not the whole
list. List rows and slim shapes carry only the progress,
steps: {done, total}. Commits and comments are
omitted unless you ask for them:
nottario.tasks.get {
project_id, task_id,
include_commits: true,
include_comments: true,
}
Each flag defaults to false. The motivation is plain: comment lists and per-row descriptions are the largest payloads in the API, and an agent that doesn't need them shouldn't pay tokens for them. A typical "claim → comment → done" loop now sits in ~600 B of MCP traffic instead of ~5 KB.
Skills: zip-and-install
Nottario ships a skill bundle aimed at agents (operating rules, task discipline, sqlc conventions). Installation is a single tool call:
nottario.skill.install returns a small JSON descriptor with a
short-lived signed URL for the bundle zip, the install plan, and a
bundle_version: the SHA-256 of the SHA256SUMS file inside the zip,
which the agent compares against its installed copy before
downloading anything. The bundle content
does not flow through the response — the agent fetches the URL
out of band and unzips into the local skill directory using whatever
HTTP and unzip tools its host exposes. Prefer
<repo>/.claude/skills/nottario/ (workspace-scoped, committed
alongside the code); fall back to ~/.claude/skills/nottario/ when
you work across unrelated checkouts.
Restart the host application (Claude Code or whichever agent client you use) after a sync — most clients load the bundle once at session start. See Agent skills for the response shape and copy-pasteable fetch/extract commands per platform.
Authentication
Every MCP request must carry a Bearer token. Tokens are issued per-project from the Settings tab and stored hashed. Revoking from the UI invalidates immediately.
The token is configured once, in the MCP client, by the person
setting up the agent. Agents are instructed never to read it, search
for it or reuse it against the HTTP API: everything they need is an
MCP tool, and for whole files nottario.docs.upload_url and
nottario.docs.download_url issue short-lived signed URLs instead of
exposing a credential. A signed upload URL stops working after five
minutes, after one successful upload, or as soon as the token that
requested it is revoked; a download URL names one version of one
document and stops working after five minutes or on revocation.