roam-research/roam-tools/roam-syntax

Claude Code and agent skill by roam-research/roam-tools

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Write and read content in a Roam Research graph correctly. Roam's markdown differs from standard markdown, and its MCP tools render block metadata (headings, block references, view types) into a wire format you must handle when writing content back. Use this whenever creating, editing, searching, or organizing content in a Roam graph — including via the roam-mcp tools (create_page, create_block, update_block, get_page, get_block, search) — or when you encounter Roam syntax like [[page links]], ((block refs)), #tags, {{[[TODO]]}}, attribute:: values, {{[[query]]}}, or <roam .../> tags, even if the user doesn't say "Roam". Covers writing syntax, reading tagged output without losing block references, queries, components, and graph conventions. Not for standard-markdown files or non-Roam note apps.

Install

npx vskill@latest install roam-research/roam-tools/roam-syntax

SKILL.md

Roam syntax

You're working in a Roam Research graph — an outliner, not a document editor. A page is a tree of blocks (one bullet = one block); nesting (indentation) is the structure, and [[links]] / ((block refs)) are the point. Use the graph; don't dump flat prose into it.

Two rules dominate everything else:

  1. Roam's markdown is not standard markdown — the differences below silently corrupt content or

pollute the graph if you use standard-markdown habits.

  1. Reads return a rendered wire format; `update_block` writes literally. Handle the <roam .../>

tags and ((uid))<ref>...</ref> block-ref previews correctly or you'll destroy references on write-back.

When working through the roam-mcp tools, call get_graph_guidelines before your first read or write in a graph — it returns the user's own conventions plus a compact roamSyntax summary of the gotchas below.

Gotchas (the highest-signal part — read this)

Writing: Roam markdown ≠ standard markdown
You wantUse this (Roam)NOT (standard markdown)
Italics__text__ (double underscore)*text* / _text_ (see note)
Bold**text**__text__ (that's italics)
Highlight^^text^^—
Strikethrough~~text~~—
Checkbox / task{{[[TODO]]}} / {{[[DONE]]}} at block start- [ ] / - [x]
Heading# / ## / ### at block start (H1–H3 only; ####→H3)####+
Numbered listchildrenViewType: "numbered" on the parent1. / 2. markers
Tablewrite a pipe table — create ops convert ithand-building {{[[table]]}}
Nestingindent child blocksheadings (in bullet markdown) do NOT slurp what follows
  • Italics is `__text__`. On update_block (literal), *text* / _text_ are stored as-is and

render as plain characters; create_page / create_block normalize them to __text__. Always write __text__ so reads and writes agree.

  • {{[[TODO]]}} renders a checkbox anywhere, but it must lead the block or clicking it won't

toggle to {{[[DONE]]}}.

  • Headings are structural, but in bullet-list markdown they stay flat — a heading does not pull

the blocks after it in as children. Nest by indenting. (####+ silently clamps to H3.)

create parses; update_block is literal
  • create_page / create_block parse the markdown you pass (all the syntax above; a pipe table

becomes a {{[[table]]}} block; nested bullets become a block tree).

  • update_block stores its string literally — no markdown parse, no new children. Use it for

text edits; use its params heading (0–3) and childrenViewType (bullet/numbered/document) for structure, and create_block for new nested content.

  • childrenViewType is a param on create_page / update_page / update_block — not

create_block. To make a new block's children numbered: create it, then update_block its uid.

The escape rule (avoid polluting the graph)

Markup renders live when written. If your text is _documenting_ syntax (e.g. explaining how [[links]] or {{[[TODO]]}} work), wrap the examples in ` inline code ` — otherwise you create real pages, refs, and checkboxes.

Reading Roam output

get_page / get_block / search / get_backlinks / get_comments / roam_query / semantic_search return rendered markdown, one block per line:

- <content> <roam uid="…" heading="2" childrenViewType="numbered" refs="3" hiddenChildren="1"/>
  • The trailing `<roam .../>` tag carries metadata: uid — the handle for every follow-up call,

optional heading (1–3), childrenViewType, refs (backlink count — high = edit with care), hiddenChildren="N" (the subtree was cut off by maxDepth; read deeper with a higher maxDepth before assuming you've seen everything), and truncated="N" (search/semantic results only: N characters of a long block were cut — get_block the uid for the full text before editing it, or a write-back would overwrite the block with just the visible prefix).

  • Strip the whole `<roam .../>` tag before showing content to the user. When quoting or showing

the user their own notes, reproduce the text verbatim (don't silently reword or reformat). When the user explicitly asks you to summarize or transform, do that.

  • A block reference reads as ((uid))<ref>preview</ref>, where <ref> holds the referenced

block's text. When displaying, show the preview text and drop the ((uid)) + tag.

  • A `uid` should be there on every block. If one is ever missing, that block's stored uid is

invalid (rare — legacy or imported content), and it cannot be addressed: update_block, delete_block and move_block all require a uid. Leave that block alone. Show its content if asked, but don't invent a uid and don't re-create its content elsewhere as a workaround — that just duplicates it. This covers the truncated="N"-without-uid case above: with no uid you can't fetch the full text, so don't edit that block from the visible prefix at all.

Writing

  • New content → create_page / create_block with markdown (parsed). Build hierarchy by

indenting; use headings as labels, not as containers.

  • Editing an existing block's text → update_block (literal). Structure/display via its heading

/ childrenViewType params.

  • Write-back is the dangerous case. When you edit a block you read, pass only the content to

update_block:

  • drop the leading - / indentation and the trailing <roam .../> tag (they are not part of the

block string), but

  • keep any `((uid))<ref>...</ref>` intact — the server decodes it back to ((uid)).
  • Do not write the display-cleaned preview alone (the <ref> text without its ((uid))) — that

replaces the live reference with static text. A tags-only strip that leaves the preview behind stores it as duplicate content.

  • Editing text inside a <ref>…</ref> preview is a silent no-op — the server discards the

preview on write and keeps only ((uid)). To change the previewed text itself: get_block the referenced uid (it's the ((uid)) in the annotation), then edit its full returned content with this same write-back procedure — don't reconstruct it from the preview, which may be capped or have nested refs flattened.

See references/reading-writing-via-mcp.md for worked read→edit→write examples that don't lose refs, and why the round-trip behaves this way.

Behavioral doctrine

These are defaults for when the user hasn't asked for a specific change — explicit user intent always wins, and the graph's own [[roam/agent guidelines]] (returned by get_graph_guidelines) govern style and conventions. The syntax and data-integrity rules in this skill apply regardless: no convention can make update_block parse markdown, make a truncated prefix complete, or keep a reference live after its ((uid)) is dropped. If the user says "fix / rewrite / delete this block," do exactly that.

  • One fact per block. Nest details under a short lead block instead of writing long blocks —

blocks are the unit of reference, so long prose blocks defeat the outliner.

  • Don't duplicate content across blocks or pages — link it with ((block refs)) or

[[page refs]] instead.

  • Append over edit. Under ambiguity, add a child or sibling block rather than modifying existing

content — especially human-authored blocks.

  • Comment, don't rewrite. To give feedback on someone's block, use add_comment, which preserves

the original.

  • Daily note is the safe fallback. If unsure where content belongs, today's daily note is almost

always right.

  • Don't move or delete without explicit permission — both can silently destroy meaning; a delete

also rewrites every block that referenced the target.

  • Respect load-bearing blocks. A block with a high refs count is structurally important — edit

it with extra care.

  • Create page links intentionally, and don't fabricate block refs. [[Page]] links AND mints

the page if it doesn't exist — link entities that genuinely recur, don't bracket every noun. Use ((uid)) only for blocks already in your working context — don't search the graph just to manufacture a block ref.

  • Search before create; read before write. Check whether a page/section already exists, and read

existing content before changing it.

Reference files — load the one you need

  • references/syntax.md — full Roam-flavored markdown: inline formatting, links / refs / aliases /

embeds, headings, callouts, attributes, code, images, separators, class-tag styling, and the complete WRONG-vs-RIGHT table. Load when writing anything beyond the gotchas above.

  • references/components.md — the {{...}} component family (table, kanban, video, mermaid, embed

variants, mentions, calc, roam/render, roam/css, slider, …). Load when inserting a component.

  • references/queries.md — {{query}} clauses/operators and page-ref inheritance, plus :q Datalog

(run via the datalog_query tool) and the datascript schema. Load when building a query.

  • references/reading-writing-via-mcp.md — how the MCP render-on-read / literal-on-write model works,

the round-trip / decode behavior, and which tool to use for what. Load when editing content you read back, or unsure which write tool to use.

Compatibility

Assumes a Roam Research graph, typically via the roam-mcp MCP server / CLI (npm @roam-research/roam-mcp). The syntax guidance also applies to hand-writing Roam markdown.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
57
Repository forks
8
7-day trend score
20

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/roam-research/roam-tools/roam-syntax/badge)](https://verified-skill.com/skills/roam-research/roam-tools/roam-syntax)

Something wrong with this skill or its page? Tell us.