Read, patch, and roll back one #id-addressed block of a document instead of rewriting the file.
io.github.geml-spec/geml Model Context Protocol (MCP) Server
The io.github.geml-spec/geml MCP server supports document edits at the block level. It can read, patch, and roll back a single #id-addressed block within a document, avoiding full-file rewrites.
π οΈ Key Features
Read a specific document block addressed by #id
Patch (modify) one #id-addressed block
Roll back changes to a single #id-addressed block
Operates on one block rather than rewriting the entire file
π Use Cases
Updating a targeted section of a document without rewriting the whole file
Applying incremental edits to #id-addressed content
One format, two readers.
People and AI agents now co-write the same document.
Legible for people; addressable, verifiable, and versioned for machines.
GEML is plain text β organized by one typed block for everything, remembered by a .gemlhistory sidecar.
βΆ Try GEML in the Playground β edit on the left, rendered on the right, and the build verdict flips the moment a reference breaks. No install.
GEML is a markup language for structured documents. A .geml file reads as plain text, so you never need a renderer to read it. And instead of a separate mini-syntax for each kind of content, GEML puts everything on one construct: the typed block.
code
=== code {#hello lang=python}
print("hi")
===
Code is a block. So are tables, diagrams, math, callouts, and document metadata. The shape is the same every time, which makes the format easy to learn and hard to get wrong.
Why a new format now
Markdown was designed for documents that people hand-write and people read. Today the same documents are also written, edited, reviewed, and queried by AI agents and CI pipelines β and that shift asks three things of a format that Markdown was never built to give:
Predictable structure, so a model emits valid output instead of guessing among a pile of per-feature special cases.
References that can be verified, so an automated edit that breaks a link fails loudly instead of rotting silently.
History that travels with the document, so a reader β human or agent β can see how and why it changed, offline and with no external service.
GEML is built around those three. The goal wasn't to bolt "AI features" onto a document format. It was to pick a format that is both simpler for people and more dependable for machines.
What's different about GEML
Plenty of formats do one or two of these. What's unusual about GEML is that one plain-text format does all three:
One primitive for every structured block. Code, tables, diagrams, math, callouts, metadata β all the same === type {β¦} typed block. One grammar to learn, one grammar to generate correctly: no per-feature syntax, no HTML fallback.
References checked at build time. Put an #id on any block and reference it anywhere; a dangling reference or a broken cross-document link is a build error, not a silent 404. Automated edits can't quietly rot.
Self-contained version history. A sibling .gemlhistory file reconstructs any past revision and rolls the document back β offline, with no git and no service β and it's plain text an agent can read to understand how the document evolved.
For a fuller side-by-side across Markdown, HTML, CommonMark, AsciiDoc, and Org-mode, see the format comparison.
The format in 5 minutes
Typed blocks
One shape, every type. A block is always === type {#id .class key=val} β¦ === β only the type (and how its body is read) changes:
code
=== code {lang=python}
print("hi")
===
=== note {.intro}
Parsed prose with *emphasis* and a [[#budget]] reference.
===
=== meta
title = "Budget plan"
===
A run of = (three or more) opens a block; an equal-length run closes it; longer fences nest inside shorter ones. A block that carries an #id can also close with the labeled fence=== #id β no fence-length counting, which makes long or nested blocks much harder to get wrong. The type decides how the body is read β raw (verbatim: code, diagram, math, table), flow (parsed prose with inline markup: note), or data (one key=val per line: meta) β and every block may carry an attribute object {#id .class key=val}, where a .class is a semantic label, never a styling hook. The full inline grammar (emphasis, links, [[#id]] auto-references, media, footnotes, inline $math$) is in the spec.
Both forms describe the same model. The FY column and Total row are computed at build time:
Segment
Q1
Q2
Q3
Q4
FY
Cloud
8
10
12
14
44.0
Platform
5
6
7
9
27.0
Services
3
4
4
5
16.0
Total
87.0
compute runs + - * / ( ) per row over columns; summary adds a foot row from the aggregates sum / avg / min / max / count (with arithmetic over them, e.g. weighted ratios); a trailing [printf] sets numeric display.
Tables can also pull their data from an external CSV via src="regions.csv".
xychart-beta
title "FY by segment"
x-axis [Cloud, Platform, Services]
y-axis "FY"
bar [44, 27, 16]
A gift for programmers β geml-code-graph
To really feel how powerful and flexible a single GEML primitive is, let's try it on a code graph β a familiar but demanding case for programmers:
your whole codebase's call graph, written as GEML.geml codemap build lays the call graph out as a tree of GEML documents β every method an #id block, with #calls / #called-by edges both ways. The downstream chain (what a method calls) for troubleshooting, the upstream chain (who calls it) for the blast radius β all visible in a second;
The method graph of geml-parser/render.ts: hovering RenderCtx.inline lights up its whole caller chain while everything else dims; clicking a node opens its source right beside the graph
sh
npm i -g @geml/geml # needs Node 22+
geml codemap build # --root defaults to . : detect languages -> index -> one merged graph in ./.geml-code-graph/
geml codemap serve # opens your browser on the graph
TS/JS β zero setup: build fetches the scip indexer by itself.
Java / C / Python / Go / Kotlin β one extra download, Joern: unzip its release package and pass that folder to build, e.g. --joern C:\joern\joern-cli (or put it on PATH and skip the flag).
Mixed front-end + back-end repo β everything merges into one graph.
geml-code-graph is itself a diagram format β one line embeds it in any GEML document (=== diagram {format=geml-code-graph src=.geml-code-graph/index.geml} ===), and every code change auto-triggers a rebuild, so the graph never drifts. Scale is no obstacle: the graph is plain-text data tables β tens of thousands of files and hundreds of thousands of edges stay instant to open and query (pan across the whole thing and its dense, web-like symmetry is genuinely striking), and you can grep any method name to trace its call chain.
Next β get hands-on
Install the browser extension, then open a raw .geml link and watch it render β the GEML spec itself (dogfood β the spec is a GEML document, rendered at scale), the showcase (a computed table, four charts, a Mermaid flow, and math), or playground/sample.geml for the interactive code-graph.
Or write your own right now in the βΆ Playground β no install.
Then read the full spec (EN / δΈζ) for the whole grammar.
Why this works for humans and AI
The same shape that makes GEML pleasant to read directly is what makes it reliable under automation:
Plain text, no rendering step. A model reads and writes .geml directly. What it sees is the document.
One uniform primitive. There's far less to get wrong when generating or parsing than with Markdown's special cases.
Build-time reference checking. A broken cross-reference is a hard error, so an automated edit either resolves its references or fails.
Structured content stays textual. Tables, math, diagrams, and metadata all live in plain text; an agent edits them right in the text, without writing HTML (guess how the logo at the top of this very README is embedded in its Markdown?).
Machine-readable feedback. The parser emits a document-model JSON with a diagnostics array, so agents and CI get a structured pass/fail signal.
Using GEML with an LLM
GEML is meant to be written and edited by models β precisely. To change one
thing, an agent needn't re-read and re-emit the whole document: it addresses a
single block by id, then validates. The CLI is designed against one bar β can a
single agent run a document's whole life from the shell? β so its verbs aim to be
complete (a verb for every step), ergonomic (few flags, pipeline-friendly
I/O), and consistent (name a target #id and the content adopts it; every
write is guarded).
sh
npm i -g @geml/geml # installs the `geml` command
geml doc.geml # document-model JSON (default --to json)
geml doc.geml --to md|html|geml # convert (geml notes.md -> GEML; -o writes a file)
geml get doc.geml ['#id'] # list every id, or print ONE block (a heading id = its section)
geml set doc.geml '#license' --in template.geml#mit # replace a block, forking another (id adopts #license)
geml add doc.geml --after '#intro' --in snippet.geml # insert a fragment (keeps its own ids)
geml delete doc.geml '#draft''#tmp'# remove one or more blocks
geml rename doc.geml '#old''#new'# rename an id + every reference to it
geml revert doc.geml '#plan' --rev -1 # roll ONE block back to an earlier revision
One entry, geml <file> [--to <fmt>], converts in either direction: the input
format is inferred (--from overrides > extension > GEML) and the target is
--to (default: a GEML input β JSON, a Markdown input β GEML). To mutate, four
verbs cover the whole block lifecycle: set replaces a block (forking content
from a file or stdin and adopting the target id), add inserts a fragment at a
position, delete removes one or more blocks, and rename rewrites an id and
every reference to it. Each mutation writes the whole updated document β in place
for a file, to stdout for -, -o to redirect β so edits pipe cleanly, and each
is guarded: re-parsed and refused if it would break the document. Read-and-patch
by id keeps every edit small and precise β a fraction of the tokens of shipping
the whole file.
Claude Code / Claude CLI. Install the package above, then copy the skills
in .claude/skills/ β geml/ for authoring,
geml-code-graph/ for the call
graph β into ~/.claude/skills/. Claude auto-loads them: it runs geml check
whenever it touches a .geml file, and builds/opens the code graph when you
ask "show me the code graph" or "who calls X" β no CLI or prompting needed.
ChatGPT, Gemini, or any model. Paste the primer below so the model emits
valid GEML, then run geml check on the output for a hard pass/fail.
GEML primer. Write the document as GEML. Every block is
=== type {#id .class key=val} β¦ ===; the closing fence is a run of = of
the exact opening length, and a longer fence nests a shorter one β or, when
the block has an #id, close it with the labeled fence === #id (no length
counting; prefer this for long or nested blocks). Block types:
code/diagram/math/table (verbatim body), note (prose with
inline markup), meta (one key=val per line). Headings are ATX # only β no
--- frontmatter (use === meta). Every #id is unique and every reference
([[#id]], [text](#id), [^id], chart data=#id) must resolve. No raw HTML.
Inline: *em*, **strong**, `code`, $math$, [text](url). The
normative spec is GEML-spec.md.
MCP Server
This package includes a standard Model Context Protocol (MCP) server that exposes GEML document CRUD operations. It runs locally and supports Windows, macOS, and Linux.
To connect it to an MCP-compatible client, provide the npx execution command and specify the --root argument (the directory containing your .geml files).
The geml CLI β one command for the whole document lifecycle (@geml/geml on npm; source: geml-parser/):
sh
npm i -g @geml/geml
geml check doc.geml # validate: broken refs are errors, non-zero exit β CI-ready
geml doc.geml --to html -o doc.html # one self-contained, interactive page
geml notes.md # come from Markdown; `--to md` goes back
Everything parses to a document-model JSON with a diagnostics array, so scripts and agents get a structured pass/fail β the block editor (get/set/add/delete/rename), versioning, formatter, and code graph below are all the same command.
Browser extension β integrations/geml-viewer/ renders .geml locally (file://) and on the web: tables with computed columns, geml-chart as inline SVG, Mermaid diagrams, KaTeX math, and the build-time diagnostics shown as a banner. Install: build it, then chrome://extensions β Load unpacked (steps). See it in one click: with the extension loaded, open a raw.geml URL (the raw file, not the GitHub blob page β that one is HTML) and it renders in place β try the showcase (a computed table, four charts, a Mermaid flow, and math) or the GEML spec itself, a full document rendered at scale. For the interactive geml-code-graph, download playground/sample.geml with its codemap/ folder and open it over file://.
Addressable blocks β geml get <file.geml> #id prints one block by id; set, add, delete, and rename mutate one block, a fragment, or an id at a time β each re-parsing and refusing any write that would break the document. A heading's #id addresses its whole section (through the next same-or-higher heading), so an agent edits one section β heading, prose, and nested blocks β without re-reading or re-emitting the whole file.
Versioned History β geml history <commit | verify | show | restore | log> <file.geml> over the self-contained .gemlhistory sidecar, plus geml revert <file.geml> #id [--rev -1] to roll a single block back to an earlier revision (by -N offset, 0 for the tip, or an id prefix; --rev changed skips to the last revision that actually changed that block). Addressable and versioned β the substrate for an agent that revises a document step by step and can rewind any one section. revert is the block-level undo: it splices changed content back, resurrects a deleted block, or removes one that did not exist at the target revision β the inverse of set/delete/add (and rename undoes itself with rename #new #old).
MCP server for document editing β geml mcp --root <dir> serves the block editor to an MCP client, so an assistant changes one block instead of rewriting a file (claude mcp add geml -- geml mcp --root /abs/path/to/docs). Nine tools: list/read/check/history, plus write/add/delete/rename/revert. A write is parsed before it reaches disk and refused with its diagnostics if it would break the document; every write first records a .gemlhistory revision, so geml_revert_block can undo a single block while the rest of the file stays byte-identical. Paths are confined to --root, which a client cannot widen. See docs/mcp-guide.md.
Canonical formatter β geml <file.geml> --to geml [-o out.geml] re-serializes the document model back to canonical GEML (the inverse of the parser). parse(serialize(parse(x))) is the same model β a round-trip property checked across the test suite β and the output is idempotent.
GEML β Markdown export β geml <file.geml> --to md [-o out.md] projects a document to GFM: frontmatter from meta, computed tables as GFM tables, note as blockquotes, footnotes, fenced code/mermaid, $$ math. Lossy by nature β Markdown has no typed-block primitive β so each unmappable construct (geml-chart, {hidden}, block ids) is reported as a note.
HTML render β geml <file.geml> --to html -o out.html turns a document into one self-contained, interactive HTML file: sortable/filterable tables, geml-chart as inline SVG drawn from its table, rendered diagrams, and the build-time checks carried through to a non-zero exit. See the showcase.geml source in docs/examples/.
Status, scope & contributing
GEML is 1.0 β stable, and used to write real documents (this repo's own spec is one).
Maturity signals. A complete core spec (Β§1βΒ§8) plus a history-extension spec, both EN / δΈζ; a working reference parser, renderer + CLI; a conformance suite (input β projected document model) that a second, independently-written parser must reproduce exactly β two separate implementations agreeing case-for-case is what keeps subtle rules like emphasis and lists from drifting β backed by 600+ unit and conformance checks (~99% line coverage; CI-gated at β₯95% lines / statements / functions / branches); and self-hosting β GEML-spec.geml is the specification written in GEML, parsed clean on every test run.
Design boundaries (non-goals). GEML stays small on purpose:
No raw-HTML escape hatch β semantics stay portable, tied to no backend or renderer.
Hosts external diagram DSLs (Mermaid, Graphviz, D2, β¦) rather than inventing one.
Tables compute, but aren't a spreadsheet engine β per-row formulas and summary aggregates, not cell addressing, lookups, or macros.
ATX headings only β no setext, no --- frontmatter, no thematic-break guesswork.
Contributing. Contributions of every kind are welcome β bug reports, tooling and integrations, broader conformance coverage, and the spec itself. GEML is 1.0, but the format can still evolve: substantive spec changes are discussed and land through a GEP, each with its conformance case. The reference parser's test suite is the contract, so code changes should keep npm test green and the dogfood spec parsing clean. The most valuable contribution is an independent parser in another language β a portable conformance suite makes it a weekend project; see docs/WRITING-A-PARSER.md.
spec/ Core spec + .gemlhistory extension (EN / δΈζ), the dogfood
GEML-spec.geml, the CC-BY spec license, and proposals/ (GEPs)
geml-parser/ Reference parser, renderer, CLI + codemap toolkit (TypeScript, Node 22)
integrations/ Everywhere GEML plugs in: geml-viewer (browser extension),
geml-check-action (CI), vscode, obsidian, tree-sitter (brief)
playground/ In-browser playground (+ a live geml-code-graph of this repo)
docs/ Guides, design notes, the format COMPARISON (EN / δΈζ),
assets, and an example .geml document to render
License & governance
Code (geml-parser/, integrations/geml-viewer/, integrations/geml-check-action/) is MIT (LICENSE). The specification documents are CC-BY-4.0 (LICENSE-spec.md) β a spec is not software, and anyone may build a conformant implementation. See GOVERNANCE.md for how decisions are made and CONTRIBUTING.md to get involved.