nodiomcloud

Documentation

How to connect an agent to Nodiom Cloud, authenticate, and address structure inside a Markdown document.

Quickstart

Nodiom Cloud speaks the Model Context Protocol over HTTP at https://api.nodiom.md/mcp. Apps that support MCP sign-in connect with that address alone: you sign in to Nodiom, choose a workspace, and the app is connected. No key to copy.

Claude (web and desktop)

Settings → Connectors → Add → Add custom connector. Enter the name (Nodiom) and the address as the MCP Server URL, then Continue and sign in. It carries over to Claude Code on the same account.

ChatGPT

Settings → Security and login, enable Developer mode. Then Plugins → Browse plugins → + → Add custom MCP server: enter the name, the address as the server URL and OAuth as the authentication, tick “I understand”, Create, and sign in.

Claude Code

claude mcp add --transport http nodiom https://api.nodiom.md/mcp
# then in Claude Code: /mcp → nodiom → Authenticate

Cursor and VS Code

Add an HTTP MCP server with the address; each signs you in the first time it connects. The one-click buttons on nodiom.md do this for you.

Your own agent (Node.js, MCP SDK)

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(
  new URL('https://api.nodiom.md/mcp'),
  { requestInit: { headers: { Authorization: `Bearer ${process.env.NODIOM_API_KEY}` } } },
);

const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(transport);

const result = await client.callTool({
  name: 'nodiom_append',
  arguments: {
    doc_id: 'project-wiki',
    selector: '## Tasks',
    new_content: '- [ ] Ship the docs page',
  },
});

console.log(result.content[0].text);

Authentication

There are two ways in, and both end up as a key on one workspace.

Sign in from an app (OAuth)

MCP clients that support sign-in find it on their own: a request without a key gets a 401 that points to Nodiom's sign-in (OAuth 2.1 with PKCE and dynamic client registration). You approve the app for one workspace, and it appears on that workspace's API keys page as a connected app, metered and attributed like any key. Revoke it there to disconnect the app.

API keys (scripts, services, your own agents)

Send a key in the Authorization header:

Authorization: Bearer nk_live_...

Create a key from Dashboard → API Keys. The raw key is shown once, at creation. Store it somewhere safe, since Nodiom Cloud only ever keeps a hash of it. Revoking a key takes effect immediately.

Selectors

A selector addresses a location inside a document's Markdown structure — the same syntax used by the open-source nodiom library. It's a " > "-separated path, each segment matching one node.

Heading segments

"# Project Alpha"              → the H1 "Project Alpha" and everything under it
"## Tasks"                     → any top-level H2 named "Tasks"
"# Project Alpha > ## Tasks"   → the H2 "Tasks" nested under H1 "Project Alpha"

The number of # characters must match the heading's actual depth in the document.

Element segments

"## Tasks > li[0]"   → first list item under ## Tasks
"## Tasks > li[-1]"  → last list item (negative indexing)
"## Notes > p[0]"    → first paragraph under ## Notes

Valid element types: p, li, code, blockquote, table, hr.

"Content under ## Tasks" means everything after that heading until the next heading of equal or lesser depth — the same way you'd read it.

Tool reference

19 tools, grouped by what they do. doc_id identifies a document within your account (see nodiom_list_docs); a fresh account starts with none — create one with nodiom_create_doc.

Each tool's exact parameters, as the server advertises them (exported 2026-10-18). A parameter marked optional can be left out. Writes save at once; there's no separate save step.

Documents

nodiom_list_docs

Returns all document IDs available in Nodiom Cloud storage.

No parameters.

nodiom_create_doc

Creates a new document in Nodiom Cloud storage. Does nothing if the document already exists.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
content
required
stringInitial Markdown content for the document.
nodiom_get_doc

Returns the full Markdown content of a document.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
nodiom_delete_doc

Permanently deletes a document from Nodiom Cloud storage.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters

Reading structure

nodiom_tree

Returns the full structural outline of a document as a nested JSON tree. Use this first to understand a document's structure.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
nodiom_read

Returns the Markdown content at a structural location within a document.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
selector
required
stringStructural selector. Examples: "## Signups", "## Active > li[0]", "## Active > li[contains=\"id=c_7f3a\"]". Prefer the contains form when writing: an index is a position, not an identity, so if another agent appends to the same section between your read and your write, li[0] now points at a different node and your write lands on the wrong one. A contains selector must match exactly one node or it errors rather than guessing.
at most 2048 characters
nodiom_read_list

Returns all list items under a selector as a JSON array of strings.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
selector
required
stringStructural selector. Examples: "## Signups", "## Active > li[0]", "## Active > li[contains=\"id=c_7f3a\"]". Prefer the contains form when writing: an index is a position, not an identity, so if another agent appends to the same section between your read and your write, li[0] now points at a different node and your write lands on the wrong one. A contains selector must match exactly one node or it errors rather than guessing.
at most 2048 characters
nodiom_query

Returns structural metadata about a location: exists, type, depth, childCount.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
selector
required
stringStructural selector. Examples: "## Signups", "## Active > li[0]", "## Active > li[contains=\"id=c_7f3a\"]". Prefer the contains form when writing: an index is a position, not an identity, so if another agent appends to the same section between your read and your write, li[0] now points at a different node and your write lands on the wrong one. A contains selector must match exactly one node or it errors rather than guessing.
at most 2048 characters

Writing structure

nodiom_write

Replaces the content at a selector. The heading is preserved. Changes are saved automatically.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
selector
required
stringStructural selector. Examples: "## Signups", "## Active > li[0]", "## Active > li[contains=\"id=c_7f3a\"]". Prefer the contains form when writing: an index is a position, not an identity, so if another agent appends to the same section between your read and your write, li[0] now points at a different node and your write lands on the wrong one. A contains selector must match exactly one node or it errors rather than guessing.
at most 2048 characters
new_content
optional
stringNew Markdown content to place at this location.
content
optional
stringSame as new_content — the name the open-source nodiom-mcp server uses.
nodiom_append

Appends new content after the last item in a section. Changes are saved automatically.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
selector
required
stringStructural selector. Examples: "## Signups", "## Active > li[0]", "## Active > li[contains=\"id=c_7f3a\"]". Prefer the contains form when writing: an index is a position, not an identity, so if another agent appends to the same section between your read and your write, li[0] now points at a different node and your write lands on the wrong one. A contains selector must match exactly one node or it errors rather than guessing.
at most 2048 characters
new_content
optional
stringMarkdown content to append.
content
optional
stringSame as new_content — the name the open-source nodiom-mcp server uses.
nodiom_delete

Removes the node or section matched by the selector. Changes are saved automatically.

doc_id
required
stringDocument ID. Examples: "waitlist", "content-calendar", "metrics".
at most 255 characters
selector
required
stringStructural selector. Examples: "## Signups", "## Active > li[0]", "## Active > li[contains=\"id=c_7f3a\"]". Prefer the contains form when writing: an index is a position, not an identity, so if another agent appends to the same section between your read and your write, li[0] now points at a different node and your write lands on the wrong one. A contains selector must match exactly one node or it errors rather than guessing.
at most 2048 characters

Boards

A board is an ordinary document: columns are ## headings and cards are checklist items. These tools let an agent work on one without learning that convention.

board_create

Creates a board: a shared Markdown document whose columns are ## headings and whose cards are checklist items. Returns its doc_id. Columns default to Backlog, In Progress, Needs Review, Done. For work handed from one agent to another, use Received, Doing, Done. The last column is where finished work goes; review_column is where a person signs off before it does.

title
required
stringThe board's name. Its doc_id is derived from it.
at least 1 characters · at most 80 characters
columns
optional
string[]Column names, in order.
at least 2 items · at most 8 items
review_column
optional
string | nullThe column where a person approves work before it moves to the last column: any column but the first and the last. null for no review step. Left out, a column whose name mentions review or approval is used if there is one.
board_read

Returns a board's columns and cards as JSON, including each card's id, owner and acceptance criteria. Read the board before you pick up, add or move work. Cards are addressed by id, never by position. A card a person has approved or sent back carries review_notes, oldest first: when a card comes back to you, its last note says what to change.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
board_add_card

Adds a card to a board, with a permanent id. Returns the card's id. Goes into the first column unless you name another. Use this to hand work to another agent or to a person.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
title
required
stringOne line describing the work.
at least 1 characters · at most 200 characters
column
optional
stringWhich column. Defaults to the first.
brief
optional
stringA short paragraph of context.
at most 2000 characters
criteria
optional
string[]Acceptance criteria, each a checkbox.
at most 20 items · each at most 300 characters
agent
optional
stringWho is adding it. Defaults to this API key's name.
at most 40 characters
board_move_card

Moves a card to another column in one atomic step — it never exists twice or not at all, even while other agents are writing. Move a card when you pick it up, as you work it, and when it's finished. Moving to the last column ticks it done. If the board has a review column, tick the criteria you met (board_update_card), then move finished work there and stop: only a person, or a key the owner allowed to review, can approve it into the last column or send it back, and moves that would skip review are refused. When you pick up a card that was sent back, the reply includes what the reviewer said.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
card_id
required
stringThe card's id, e.g. c_7f3a. See board_read.
matches ^c_[0-9a-z]+$
to
required
stringThe destination column's name, exactly as board_read shows it.
note
optional
stringOnly when a reviewer key approves or sends back a card: what to tell the agent. Written into the card as a review note.
at most 1000 characters
agent
optional
stringWho is moving it. Defaults to this API key's name.
at most 40 characters
board_update_card

Changes a card's title, brief or acceptance criteria in place. It keeps its id, column, owner and review notes. Give only the parts to change; criteria replace the whole list, so send every criterion you want kept, ticking the ones that are met. On a board with a review step, cards waiting for review or already approved can't be edited.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
card_id
required
stringThe card's id, e.g. c_7f3a. See board_read.
matches ^c_[0-9a-z]+$
title
optional
stringA new one-line title.
at least 1 characters · at most 200 characters
brief
optional
stringA new brief. An empty string removes it.
at most 2000 characters
criteria
optional
string | object[]The full list of acceptance criteria: plain strings, or { text, done } to tick one.
at most 20 items
agent
optional
stringWho is editing it. Defaults to this API key's name.
at most 40 characters
board_delete_card

Removes a card from the board for good. On a board with a review step, a card waiting for review can't be deleted. To finish work, move it instead.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
card_id
required
stringThe card's id, e.g. c_7f3a. See board_read.
matches ^c_[0-9a-z]+$
agent
optional
stringWho is deleting it. Defaults to this API key's name.
at most 40 characters
board_reply

Answers the person who reviewed a card: your reply is written under their latest note, marked as yours, so they can see you understood ("Agreed, cutting it to four lines"). Use it after a card is sent back with a note, before you redo the work. It doesn't move the card or change its content, and it can never stand in for a review decision.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
card_id
required
stringThe card's id, e.g. c_7f3a. See board_read.
matches ^c_[0-9a-z]+$
text
required
stringYour reply, in a sentence or two.
at least 1 characters · at most 1000 characters
agent
optional
stringWho is replying. Defaults to this API key's name.
at most 40 characters
board_wait

Waits for a person to approve or send back the cards you handed to review on this board, and returns each decision with the reviewer's note. It returns as soon as there is a decision after `since`, or after timeout_seconds with none. Call it after moving work to review instead of polling board_read. Pass next_since from the previous reply to continue where you left off; timeout_seconds 0 checks without waiting. A card sent back to you is marked as picked up, so the reviewer sees you got it.

doc_id
required
stringThe board's document id, as returned by board_create or nodiom_list_docs.
at most 255 characters
since
optional
stringISO time to look after. Default: the last 24 hours.
at most 40 characters
timeout_seconds
optional
integerHow long to wait for a decision. Default 30, at most 45.
agent
optional
stringWhose hand-offs to watch. Defaults to this API key's name.
at most 40 characters

Plain HTTP, without an MCP client

Scripts and services can call the tools directly. The server keeps no session, so there's nothing to initialize: send one JSON-RPC POST per call, with both content types in Accept.

curl -s https://api.nodiom.md/mcp \
  -H "Authorization: Bearer $NODIOM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"board_read","arguments":{"doc_id":"launch-prep"}}}'

The reply may arrive as server-sent events: take the JSON after data:. The tool's output is in result.content[0].text, and result.isError is true when the call was refused.

// Node 18+: one call, with no MCP library
async function callTool(name, args) {
  const r = await fetch('https://api.nodiom.md/mcp', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.NODIOM_API_KEY}`,
      'Content-Type': 'application/json',
      Accept: 'application/json, text/event-stream',
    },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name, arguments: args } }),
  });
  const body = await r.text();
  const json = body.startsWith('{') ? body : body.split('\n').find((l) => l.startsWith('data:')).slice(5);
  const { result } = JSON.parse(json);
  if (result.isError) throw new Error(result.content[0].text);
  return result.content[0].text;
}

Modelling work on a board

  • One card per piece of work. A card moves left to right as it's picked up, worked and finished; it isn't copied into each column.
  • Columns are stages, in order. The last one is where finished work goes, and moving a card there ticks it done.
  • The review column is where agents hand work over and stop. Only a person in the app, or a key the owner marked “Can review”, can approve a card into the last column or send it back. Nothing skips it, raw edits included.
  • Review notes travel with the card. Approving or sending back can carry a note, written under the card with the reviewer's name and the time. board_read returns them as review_notes; when a card comes back, the last one says what to change. Agents can't write or alter them.
  • Hand off, then wait. After moving a card to review, an agent calls board_wait: it returns as soon as a person approves or sends back one of its cards, with the note. A sent-back card shows the person “Picked up by” that agent, and the agent can answer the note with board_reply before it redoes the work.
  • Claim before you work: move the card into your working column first. The move is one locked step, so two agents never take the same card.
  • Use a key per agent. Every write is attributed to its key's name, so the board shows who did what.

Writes & billing

Plans meter writes: successful calls that change a document — the document-management, writing and board tools above that create, edit, move or delete. Reads are always free and never blocked, even when a plan's writes run out. A write that changes nothing — a card moved to the column it's already in, a section rewritten with identical content — isn't counted, and neither are failed calls such as a bad selector or a missing document. See pricing for plan details, or manage billing if you're signed in.

Questions not covered here? dev@synexiomlabs.com.