Skip to content

GEML — a plain-text document format built to be edited in place, one section at a time

by geml-spec · remote · local (npm) · no sign-in to list tools · v1.12.6 · published by GitHub user geml-spec

One section in, one section out — on Markdown/GEML files, bad writes refused.

Deletes · 2Final · 1Changes · 2

9 tools: 2 can delete or cancel things, 1 do something that can't be undone, 2 change things and 4 only read.

Scanned Oct 21, 2026

2 checks matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http geml https://geml-mcp.supermarket.workers.dev/mcp

Findings

Matches of fixed checks, with the text they matched.

2 warn
  • The description contains "every other tool", wording about other tools or servers.

    Matched text

    description: "...all this FIRST: what it returns is what every other tool here addresses, and it is cheaper and m..."

    What this check looks for
  • The server marks "geml_to" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"openWorldHint":false}; The description's first verb is "convert": a word for a change.

    What this check looks for

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

9 tools

Deletes or cancels 2Claude Code: deny
  • geml_deleteRemove one or more blocks, each named by an id or any address geml_list prints; a filter (`=== type`, `{key=value}`) removes every block it matches.No findings

    Why it is in this group

    • The name "geml_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Remove one or more blocks, each named by an id or any address geml_list prints; a filter (`=== type`, `{key=value}`) removes every block it matches. Each block takes the blank line that separated it from its neighbours, so deleting what geml_add inserted leaves the file as it was. To undo a deletion, geml_revert the removed id; to change a block rather than remove it, use geml_set. References left pointing at a removed block come back as diagnostics but do NOT block the deletion — read them, then repair the references or revert. A selector that names nothing is skipped, so repeating a call changes nothing. Returns `{ok, file, diagnostics, revision}`, with `notes` when the write did something to say out loud (a block it dropped, an address it changed); a refusal is `ok: false` with a `hint`, and the file is unchanged. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Delete blocks
    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • geml_setReplace ONE block and leave every other byte untouched — prefer this to rewriting a file.No findings

    Why it is in this group

    • The name "geml_set" says "set": a word for a change.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Replace ONE block and leave every other byte untouched — prefer this to rewriting a file. For content that does not exist yet use geml_add; to remove a block, geml_delete. The replacement is validated before it is written: if it would break the document, nothing is written and the diagnostics come back — fix the body rather than resending it. Removing content is not refused: if the replacement drops blocks, the write goes through and the result names each one, so check it after shortening a section; geml_revert puts one back. `part` replaces the whole block (default), its head line, a section's `intro`, or its body. In a Markdown file a heading's anchor is its text, as on GitHub: new heading text gives the heading a new address, and the document's links to the old one follow in the same write. GEML content written over a Markdown heading or prose is converted to Markdown, as in geml_add; a GEML block already in the file stays GEML. An `id` that matches no block, or several, is refused. Returns `{ok, file, diagnostics, revision}`, with `notes` when the write did something to say out loud (a block it dropped, an address it changed); a refusal is `ok: false` with a `hint`, and the file is unchanged. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Replace one block
    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
Can't be undone 1Claude Code: ask
  • geml_toConvert a WHOLE document and get the result back as text — the read half of the CLI's `geml <file> --to <fmt>`.1 finding

    Why it is in this group

    • The description's first verb is "convert": a word for a change.
    • The input addresses people (input.to): what it sends cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "geml_to" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"openWorldHint":false}; The description's first verb is "convert": a word for a change.

      What this check looks for
    Description, as the server wrote it
    Convert a WHOLE document and get the result back as text — the read half of the CLI's `geml <file> --to <fmt>`. `to: "geml"` on a Markdown file is the importer, the one thing the block tools cannot do; `to: "md"` projects a GEML document out (lossy); `to: "json"` returns the full document model, for when geml_list plus geml_get is not enough. Nothing is written — pass the result to geml_add or geml_set to land it. `to: "html"` also works but returns a whole self-contained page, usually tens of kilobytes this server cannot save: prefer the CLI (`geml <file> --to html -o out.html`) unless you want the markup in the conversation. A document with errors returns its diagnostics instead of a conversion, and a file that is not under the server root is an error. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Convert a whole document
    • readOnlyHint: true
    • openWorldHint: false
Changes things 2Claude Code: ask
  • geml_addInsert new content — one or more blocks, or prose — at the end of the document (`position: append`) or before/after the block named by `anchor`.No findings

    Why it is in this group

    • The name "geml_add" says "add": a word for a change.
    Description, as the server wrote it
    Insert new content — one or more blocks, or prose — at the end of the document (`position: append`) or before/after the block named by `anchor`. Use this for content that does not exist yet; to change a block that does, use geml_set. Ids inside the content are kept. In a `.md` file Markdown lands as written, and GEML content is converted to Markdown as `to: "md"` converts it, the result's `notes` saying so; content Markdown cannot hold, such as a view without its source, is refused. Like every write here it is validated first: a missing anchor, an id that clashes with an existing one, or content that would break the document is refused. Returns `{ok, file, diagnostics, revision}`, with `notes` when the write did something to say out loud (a block it dropped, an address it changed); a refusal is `ok: false` with a `hint`, and the file is unchanged. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Insert new content
    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • geml_renameRename a block id AND every reference to it in the same document, in one id-boundary-safe write.No findings

    Why it is in this group

    • The name "geml_rename" says "rename": a word for a change.
    Description, as the server wrote it
    Rename a block id AND every reference to it in the same document, in one id-boundary-safe write. Use this rather than geml_set or a text search-and-replace, which would also hit ids that merely share a prefix. A Markdown heading's anchor is its text, so it is renamed by changing the heading with geml_set (`part: head`), not here. An `old` id that does not exist, or a `new` one already taken, is refused. Returns `{ok, file, diagnostics, revision}`, with `notes` when the write did something to say out loud (a block it dropped, an address it changed); a refusal is `ok: false` with a `hint`, and the file is unchanged. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Rename a block id
    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
Only reads 4Claude Code: allow
  • geml_checkValidate a GEML document without changing it: returns every diagnostic with a stable `code`, a severity and a line, and an empty list means the document is valid.No findings

    Why it is in this group

    • The description's first verb is "validate": a word for reading.
    Description, as the server wrote it
    Validate a GEML document without changing it: returns every diagnostic with a stable `code`, a severity and a line, and an empty list means the document is valid. Use it to confirm a document is sound before reporting work as finished. Every write through this server runs the same check before it lands, so a refused write already carries this information. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Validate a document
    • readOnlyHint: true
    • openWorldHint: false
  • geml_findSearch block CONTENT in the document; each hit is one `<name>\t<address>` row.No findings

    Why it is in this group

    • The name "geml_find" says "find": a word for reading.
    Description, as the server wrote it
    Search block CONTENT in the document; each hit is one `<name>\t<address>` row. geml_list says what a document contains, this says which block holds the words — as an address that pastes straight into geml_get or geml_set, not a line number the next edit invalidates. The address is the innermost block holding the match, and a block matching on many lines is reported once. Substring, case-insensitive unless `case` is true. No match is an empty result, not an error. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Find the blocks that hold some text
    • readOnlyHint: true
    • openWorldHint: false
  • geml_getRead ONE block from a GEML document instead of the whole file: only that block comes back, typically a few percent of the document.No findings

    Why it is in this group

    • The name "geml_get" says "get": a word for reading.
    Description, as the server wrote it
    Read ONE block from a GEML document instead of the whole file: only that block comes back, typically a few percent of the document. Pass the `address` geml_list prints as `id` — it also reaches blocks with no `#id`; to locate a block by its words instead, use geml_find first. An `id` that matches nothing, or a file that is not under the server root, is an error naming it. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: Read one block
    • readOnlyHint: true
    • openWorldHint: false
  • geml_listList every addressable block in a GEML document — its address, kind and heading text — in one call, with no paging.1 finding

    Why it is in this group

    • The name "geml_list" says "list": a word for reading.

    Findings

    • Warn

      The description contains "every other tool", wording about other tools or servers.

      Matched text

      description: "...all this FIRST: what it returns is what every other tool here addresses, and it is cheaper and m..."

      What this check looks for
    Description, as the server wrote it
    List every addressable block in a GEML document — its address, kind and heading text — in one call, with no paging. Call this FIRST: what it returns is what every other tool here addresses, and it is cheaper and more reliable than reading the file to see what is in it. Rows marked `anon` have no `#id`; geml_get and geml_set take their `address` as `id`, while the other write tools need a real id, so give such a block one first. A file that is not under the server root is an error. This server is STATELESS: the document travels in `source` and a write comes back as `document` for you to save; it holds no other documents, so a cross-document reference such as `[[other.geml#id]]` cannot be resolved here and is reported as `unresolvable-document`.

    Annotations the server declares

    • title: List the blocks of a document
    • readOnlyHint: true
    • openWorldHint: false

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 1.12.6. Values in angle brackets are yours to fill in.

claude mcp add --transport http geml https://geml-mcp.supermarket.workers.dev/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__geml__geml_list",
      "mcp__geml__geml_find",
      "mcp__geml__geml_get",
      "mcp__geml__geml_check"
    ],
    "ask": [
      "mcp__geml__geml_to",
      "mcp__geml__geml_add",
      "mcp__geml__geml_rename"
    ],
    "deny": [
      "mcp__geml__geml_set",
      "mcp__geml__geml_delete"
    ]
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as geml. Review it before you use it.