Skip to main content
Glama

rayomd-mcp

Markdown to PDF for AI agents, over MCP, in about half a millisecond a call. No browser. No LaTeX. No Pandoc. No Chrome to download.

Quick start · Tools · Security · Performance · RayoMD

NOTE

Before the first release: rayomd-mcp is not on npm yet, and it needs a RayoMD with rayomd --serve --json (protocol 1), which comes after 5.2.5. Until both are released, build RayoMD from master and run the server from this repository (Build from source).

rayomd-mcp is an MCP server around RayoMD, the native Markdown-to-PDF converter: agents that write reports, summaries, release notes or documentation call markdown_to_pdf and get the PDF, its page count and what did not render. One warm rayomd process converts each document in a fraction of a millisecond; the server itself is one 460 KB JavaScript file on the official TypeScript SDK v2.

Highlights

Fast

A small note in 0.5 ms a call, end to end over stdio; ready 117 ms after start, the first call as fast as the rest.

Light

No headless browser, no TeX Live: one Node.js file and a 1.2 MB download of the static rayomd, fetched once and checked against its SHA-256.

Contained

Reads and writes only inside --root folders, resolved through links; URL images, themes and embedded sources off unless the server allows them.

Same PDF every run

Byte-identical to rayomd --export with the same flags, tested on six option sets, PDF/A and PDF/UA among them.

Everything RayoMD draws

TeX math, Mermaid diagrams, code coloured in 29 languages, tables of contents, PDF/A-3b and tagged PDF/UA-1.

Live preview

open_in_gui opens the note in RayoMD GUI, which follows each version the agent writes.

Related MCP server: Markdown2PDF MCP Server

Quick start

Always pass --root with the folders the agent may use: a desktop client starts servers in a working folder of its own choosing.

claude mcp add rayomd -- npx -y rayomd-mcp --root "$PWD"
{
  "mcpServers": {
    "rayomd": {
      "command": "npx",
      "args": ["-y", "rayomd-mcp", "--root", "/Users/me/Documents/reports"]
    }
  }
}

On Windows use "command": "cmd", "args": ["/c", "npx", "-y", "rayomd-mcp", "--root", "C:\\Users\\me\\Documents\\reports"].

{
  "servers": {
    "rayomd": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "rayomd-mcp", "--root", "${workspaceFolder}"]
    }
  }
}
npx @modelcontextprotocol/inspector npx -y rayomd-mcp --root .
npx @modelcontextprotocol/inspector --cli npx -y rayomd-mcp --root . --method tools/list

Node.js 20 or newer, and a rayomd with --serve --json (build RayoMD from master until its next release):

git clone https://github.com/Butterski/rayomd-mcp && cd rayomd-mcp
npm ci && npm run build
RAYOMD_PATH=/path/to/rayomd node dist/rayomd-mcp.mjs --root ~/Documents/reports

In a client's configuration: "command": "node", "args": ["/path/to/rayomd-mcp/dist/rayomd-mcp.mjs", "--root", "..."], and "env": {"RAYOMD_PATH": "/path/to/rayomd"}.

The server finds rayomd itself:

  1. RAYOMD_PATH, when set.

  2. Else rayomd on PATH (rayomd.exe on Windows).

  3. Else the RayoMD release pinned in this package, downloaded on the first call from GitHub releases (the static Linux x64 tarball, the Windows x64 zip), checked against the SHA-256 recorded in src/client/releases.ts and kept in ~/.cache/rayomd/bin/<version>/ ($XDG_CACHE_HOME/rayomd/bin, %LOCALAPPDATA%\RayoMD\cache\bin on Windows). A download whose hash differs is not used; after the first one the server works offline.

npx rayomd-mcp --install-binary does this at once and prints the binary's path, for machines that go offline later. macOS and Linux on ARM have no release binary: build rayomd from source and put it on PATH or in RAYOMD_PATH. A rayomd without --serve --json is reported as such in the tool's error.

Tools

Each tool answers with structuredContent (its outputSchema), a resource_link to the file written (a file:// URI) and one line of text. Failures are tool results with isError: true and a message the model can act on.

markdown_to_pdf

Markdown text (markdown) or a file (path) to the PDF output. Not read-only, not destructive, idempotent, not open-world.

{ "markdown": "# Q3 report\n\nRevenue grew $\\frac{3}{2}$ times.", "output": "q3.pdf", "toc": true, "page_numbers": true }

Argument

rayomd flag

markdown / path

exactly one of them

output

the PDF, ending in .pdf; an existing file is replaced

source_path

with markdown: the .md file the text stands for; relative images resolve from its folder. Default: output with .md

style

elegant modern tech

margin

compact normal wide margin=0.75in margin=54pt

page_size

--page-size=

a4 a3 a5 letter legal, -landscape, 210x297mm, 210mmxauto

toc, toc_depth

--toc, --toc-depth=N

number_headings

--number-headings=N

1 to 6 levels

page_numbers

--page-numbers

footnotes

--footnotes=end|page

dialect

--dialect=github|obsidian

pdfa, pdfua

--pdfa, --pdfua

black_text

--black-text

highlight_code: false

--no-highlight

diagrams: false

--no-diagrams

page_break_before

--page-break-before=hN

h1 to h6

allow_url_images

--allow-url-images

only with the server's --allow-url-images

allow_unsafe_local_images

--allow-unsafe-local-images

only with the server's --allow-unsafe-local-images

theme

--theme=FILE

only with --allow-theme; the file inside the roots

embed_source

--embed-source

only with --allow-embed-source

The answer is rayomd's --report record: input, output, status, code, pages, bytes, ms, missing_characters (characters no font had), failed_images (images printed as their alt text). The PDF is byte-identical to rayomd --export <file> <output> <flags> (for markdown, to --export of a file at source_path holding that text). The description carries a short form of the syntax reference.

book_to_pdf

inputs: one SUMMARY.md (mdBook, GitBook), one folder (its SUMMARY.md, else its Markdown files by name, README.md first) or Markdown files in order; output; the same options but embed_source. Runs rayomd --book <inputs> <output> <flags> once, without a shell; the files a SUMMARY.md lists or a folder holds must lie in that folder (rayomd checks). Answers inputs, output, pages, bytes, ms, missing_characters.

recover_markdown

pdf: a PDF exported with the source embedded (embed_source, so the server needs --allow-embed-source to make one, or one made by rayomd --embed-source). Without output the Markdown comes back as the result's text and in structuredContent.markdown; with output (a new .md file inside the roots) it is written there and never over an existing file. The PDF is not changed. It is not marked read-only because it can write that new file.

open_in_gui

path: a .md file inside the roots, opened in RayoMD GUI (RAYOMD_GUI_PATH, else rayomd-gui on PATH), started detached, without a shell. The GUI checks the file about once a second and reloads it when it changed on disk and its buffer has no unsaved edits, so the user sees each version the agent writes. Errors when the GUI is not installed, when there is no Wayland display on Linux, or when it exits at once.

Resource rayomd://syntax

What RayoMD renders (GitHub Markdown, alerts, footnotes, task lists, the TeX subset, Mermaid flowcharts, sequence diagrams and pie charts, code in 29 languages) and what degrades (HTML and CSS layout, LaTeX packages, other diagram types): the RayoMD skill's references/syntax.md, vendored in resources/syntax.md. Agents that read it write Markdown that renders instead of finding out from the PDF.

Security model

  • Roots. Files are read and written only inside the --root folders (repeatable; default the working folder). Relative paths resolve from the first root. Every path is resolved to its real location (.., symbolic links, junctions) before the check: an input must be a file whose real path lies in a root; an output's real folder must lie in a root, its name must end in .pdf (.md for recover_markdown), and an existing output that is a link, a folder or a file with other hard links is refused. MCP roots (deprecated in 2026-07-28, SEP-2577) are not used.

  • Images. rayomd loads local images only from the Markdown file's folder (source_path's for text), so they stay inside the roots too, and no URL images.

  • Off unless the server's command line turns them on: URL images (--allow-url-images), local images from anywhere (--allow-unsafe-local-images), theme files (--allow-theme, the theme file itself checked against the roots) and embedded sources (--allow-embed-source). Only then does the tool option exist. Tool calls never pass a raw flag to rayomd: every flag comes from a typed, validated option.

  • Processes. rayomd and the GUI are started with argument arrays, never through a shell. Warm rayomd --serve --json children serve the conversions, each one request at a time: the first starts once the client has initialized (when a rayomd binary is already here), a second only when a call finds the first busy, up to --workers (default one a CPU, at most 4); calls for one output file stay on one child, in order. A child that exits, breaks the protocol or does not answer within the timeout (60 s, --timeout) is stopped, that call fails, and the next call starts a new child. They end with the server.

  • Downloads. Only the pinned release asset, only when its SHA-256 matches.

Performance

Measured on 2026-11-04 on four pinned cores of an AMD Ryzen 5 PRO 4650GE in node:22-alpine (Node.js 22.23), with the static rayomd and a raw JSON-RPC client over stdio, as a desktop client talks to the server: the median of each run, the mean of two to four runs. Before is the first commit of this repository with RayoMD 426e4ed, the first --serve --json; after is this one with RayoMD b71ec98, its vectorized request reader.

Call

Before

After

Start until initialize is answered

182 ms

117 ms

First markdown_to_pdf (tester.md), 300 ms after initialize

18.2 ms

3.8 ms

Small Markdown text

0.69 ms

0.52 ms

Small Markdown file

0.53 ms

0.44 ms

1.1 MB Markdown text

30.5 ms

21.5 ms

7.9 MB Markdown text

606 ms

142 ms

1.1 MB Markdown files, 8 calls in flight

296 calls/s

545 calls/s

Of a small call, rayomd converts for 0.05 ms; the rest is the SDK's handling of the messages and the round trips through the pipes. What made the difference:

  • One file to load. dist/rayomd-mcp.mjs is one minified bundle of the server, the SDK and zod: Node.js reads, resolves and compiles one module instead of some 150. Most of what remains is Node.js's own start (54 ms) and the SDK building its schemas.

  • A warm first call. Once the client has initialized, the server starts the first rayomd and makes one call through a server of its own, in memory, into a temporary folder it deletes: the code of a call has run, its schemas are compiled and rayomd's fonts are loaded before the model's first call arrives.

  • Linear reads of large messages. The SDK's stdio reader copies everything it holds for each 64 KiB chunk and searches it from the start, and stops at 10 MB; src/stdio.ts keeps a message's chunks and joins them once, up to rayomd's 32 MiB request line.

  • System calls instead of thread-pool trips for the root checks, and no second check of a source_path that is the output's.

  • Several children when calls overlap; one when they do not.

  • In rayomd: the request reader of --serve --json reads strings 16 bytes a step (SSE2) and decodes escapes in blocks: a 1 MB request of Markdown parses in 0.3 ms instead of 2.75.

A 1 MB request still spends about 5 ms in JSON.stringify of the Markdown on its way to rayomd: a raw-bytes frame would save it, at the cost of a stream that loses its place after a malformed line, so the protocol stays JSON.

Command line and environment

rayomd-mcp [--root DIR]... [--allow-url-images] [--allow-unsafe-local-images]
           [--allow-theme] [--allow-embed-source] [--timeout SECONDS] [--workers N]
rayomd-mcp --install-binary | --version | --help

Variable

RAYOMD_PATH

the rayomd executable

RAYOMD_GUI_PATH

the RayoMD GUI executable for open_in_gui

XDG_CACHE_HOME, LOCALAPPDATA

where the downloaded binary is kept

RAYOMD_FONT, RAYOMD_FALLBACK_FONT

read by rayomd itself: fonts for missing characters and PDF/A

The client library

rayomd-mcp/client is the binary resolver and --serve --json client on their own, for other programs (the RayoMD VS Code extension):

import { exportArgs, RayomdPool, resolveRayomd } from 'rayomd-mcp/client';

const rayomd = new RayomdPool({ command: async () => ({ command: (await resolveRayomd()).path }) });
const record = await rayomd.request({ input: 'notes.md', output: 'notes.pdf', args: exportArgs({ toc: true }) });
console.log(record.status, record.pages);
await rayomd.close();

RayomdServe is one child on its own; the module also exports runRayomd (one command, no shell), downloadRelease, findOnPath, RELEASE_SHA256 and the archive readers.

Development

npm ci
npm test                                        # build, unit tests against test/fake-rayomd.mjs
RAYOMD_TEST_BINARY=/path/to/rayomd npm test     # and the real-binary suite (byte-identical PDFs)
RAYOMD_PATH=/path/to/rayomd npm run smoke       # raw JSON-RPC over stdio against dist/rayomd-mcp.mjs
npm run sync-syntax -- ../rayomd/skills/rayomd/references/syntax.md
npm run pin-release -- 5.3.0                    # pin a release's asset hashes and download it by default

A change to the call path comes with before and after numbers, measured as above. A release: bump version in package.json and both versions in server.json, tag vX.Y.Z; release.yml publishes to npm with provenance and to the MCP Registry.

License

Apache-2.0, as RayoMD. See LICENSE.

Available Tools

4 tools
book_to_pdfBook to PDFA
Idempotent

Makes one PDF of several Markdown files: each from a new page, links between them kept, one outline (and with toc one table of contents). The files a SUMMARY.md lists or a folder holds must lie in that folder.

NameRequiredDescriptionDefault
tocNoOne table of contents for the whole book
pdfaNoPDF/A-3b: every font embedded
pdfuaNoPDF/UA-1, tagged for screen readers (formulas as TeX source)
styleNoLook of the document (default elegant)
inputsYesOne SUMMARY.md (mdBook, GitBook), one folder (its SUMMARY.md, or its .md files by name, README.md first), or Markdown files in order
marginNocompact, normal (default), wide, or margin=N in inches or margin=Npt: margin=0.75in, margin=54pt
outputYesThe PDF to write, ending in .pdf; an existing file is replaced
dialectNogithub (default), or obsidian: callouts, ==highlights==, %%comments%%, [[wiki links]], ![[embeds]]
diagramsNoDraw ```mermaid flowcharts, sequence diagrams and pie charts (default true)
footnotesNoend (default): after the text; page: at the foot of the page of the first reference
page_sizeNoa4 (default), a3, a5, letter or legal, each also with -landscape; WIDTHxHEIGHT in mm, cm, in or pt (210x297mm); or WIDTHxauto (210mmxauto: one long page)
toc_depthNoHeading levels the table of contents lists (default 3)
black_textNoFor black-and-white printers: text, links and code black, fills light grey
page_numbersNo"N / M" at the foot of every page
highlight_codeNoColour fenced code by language (default true)
number_headingsNoNumber the headings of this many levels: 1, 1.1, 1.1.1
page_break_beforeNoA new page before each heading of level 1 to N (h1: chapters)

Output Schema

NameRequiredDescription
msYes
codeYes
bytesYes
pagesYes
inputsYes
outputYes
statusYes
missing_charactersYes

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare non-readonly, non-destructive and idempotent, so the safety profile is covered. The description adds real behavior beyond them: each file starts a new page, inter-file links are preserved, a single outline is produced, and optionally a table of contents. It does not say what happens if a listed file is missing or whether the source files are modified.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the core action, then the input constraint. The parenthetical '(and with toc one table of contents)' is a bit cramped but each element still earns its place. No filler or restatement of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 17-parameter rendering tool with an output schema and full schema coverage, the description supplies the essential mental model: many Markdown files in, one structured PDF out, with inputs constrained to a single folder. Remaining gaps, such as missing-file handling and whether rendering requires external tooling, are minor given the rich schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 17 parameters thoroughly, including the SUMMARY.md/folder/file-list forms of inputs. The description's one added constraint, that listed or folder-held files must lie in that folder, adds genuine meaning for the inputs parameter but is terse and slightly ambiguous. Baseline 3 is appropriate when the schema carries nearly all semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb and resource: 'Makes one PDF of several Markdown files', with the multi-file scope, page-break, link-preservation and outline behavior spelled out. It implicitly separates itself from markdown_to_pdf (singular) by emphasizing 'several' files, but never names that sibling, so the differentiation is left for the agent to infer.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the tool is for compiling a book of Markdown files into one PDF. There is no explicit statement of when to prefer this over markdown_to_pdf, and no mention of prerequisites or failure modes. The final sentence reads as an input constraint rather than routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

markdown_to_pdfMarkdown to PDFA
Idempotent

Converts Markdown (text or a file) to a PDF with RayoMD and returns the PDF's page count and warnings. Renders: GitHub Markdown (tables, task lists, footnotes, alerts > [!NOTE]), PNG/JPEG/SVG images with alt text (relative paths inside the Markdown file's folder), TeX math ($...$, $$...$$ on own lines, math), mermaid flowchart/graph, sequenceDiagram and pie, code colouring in 29 languages, page breaks (\pagebreak, ), [TOC], YAML front matter (title, author, lang) as PDF metadata. Degrades: CSS/HTML layout (text kept), LaTeX packages and macros, other Mermaid types (printed as code), SVGs needing scripts. Full list: resource rayomd://syntax.

NameRequiredDescriptionDefault
tocNoA table of contents first, or at a [TOC] paragraph
pathNoA Markdown file to convert (give this or markdown)
pdfaNoPDF/A-3b: every font embedded
pdfuaNoPDF/UA-1, tagged for screen readers (formulas as TeX source)
styleNoLook of the document (default elegant)
marginNocompact, normal (default), wide, or margin=N in inches or margin=Npt: margin=0.75in, margin=54pt
outputYesThe PDF to write, ending in .pdf; an existing file is replaced
dialectNogithub (default), or obsidian: callouts, ==highlights==, %%comments%%, [[wiki links]], ![[embeds]]
diagramsNoDraw ```mermaid flowcharts, sequence diagrams and pie charts (default true)
markdownNoThe Markdown text (give this or path)
footnotesNoend (default): after the text; page: at the foot of the page of the first reference
page_sizeNoa4 (default), a3, a5, letter or legal, each also with -landscape; WIDTHxHEIGHT in mm, cm, in or pt (210x297mm); or WIDTHxauto (210mmxauto: one long page)
toc_depthNoHeading levels the table of contents lists (default 3)
black_textNoFor black-and-white printers: text, links and code black, fills light grey
source_pathNoWith markdown: the .md file the text stands for; relative images resolve from its folder. Default: output with .md
page_numbersNo"N / M" at the foot of every page
highlight_codeNoColour fenced code by language (default true)
number_headingsNoNumber the headings of this many levels: 1, 1.1, 1.1.1
page_break_beforeNoA new page before each heading of level 1 to N (h1: chapters)

Output Schema

NameRequiredDescription
msYesBuild time in milliseconds
codeYesrayomd exit code: 0 ok, 2 bad request, 3 input not readable, 10+N build error, 12 output not writable
bytesYes
errorNo
inputYesThe input path, or the source_path of Markdown text
pagesYes
outputYesThe PDF written
statusYes
failed_imagesYesImages that could not be loaded (their alt text is printed)
missing_charactersYesCharacters without a glyph in the fonts found (drawn as a base letter or ?)

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare non-read-only, idempotent, non-destructive, closed-world, so safety is covered. The description adds real behavioral value beyond that: it discloses the rendering support matrix and, more importantly, an explicit degradation list (CSS/HTML layout, LaTeX packages, non-flowchart Mermaid, script-dependent SVGs) plus the return values, so an agent can predict silent quality loss. It does not mention performance, file-size limits, or failure modes, which keeps it below 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first sentence is well front-loaded, but the remainder is one long run-on catalogue of syntax features with semicolon-and-comma chaining that is hard to scan. Much of that catalogue is duplicated by the referenced resource rayomd://syntax, so the listing is informative but not tightly curated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 19-parameter converter with an output schema and full schema coverage, the description covers what matters: purpose, capability envelope, degradation, and a pointer to a resource for the exhaustive syntax list. Only the missing sibling routing and parameter-interaction notes keep it from being complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3, and the description meaningfully exceeds it: it explains that relative image paths resolve from the Markdown file's folder (clarifying how path vs markdown vs source_path interact), and it ties dialect to concrete syntax sets (obsidian callouts, ==highlights==, [[wiki links]]) and diagrams to the Mermaid types actually drawn. It still does not explain interaction between toc, toc_depth and a [TOC] paragraph, or defaults for the boolean flags.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Converts Markdown ... to a PDF with RayoMD') plus the return payload (page count and warnings). The scope (text or file, single document) is distinguishable from siblings like book_to_pdf and recover_markdown, and the supported/dialect surface tells an agent exactly what class of document this handles.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the purpose but never stated: there is no guidance on when to pick this over book_to_pdf, or when recover_markdown should come first. The closest thing to routing guidance is the parameter-level hint 'give this or path' / 'give this or markdown', which helps invocation but not tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

open_in_guiOpen in RayoMD GUIA
Read-only

Opens a Markdown file in RayoMD GUI, the desktop editor with a live PDF preview, for the user to read or edit. The GUI checks the file about once a second, focused or not, and reloads it when it changed on disk and its buffer has no unsaved edits, so a file the agent rewrites shows the new version. Needs RayoMD GUI installed (rayomd-gui on PATH or RAYOMD_GUI_PATH) and a desktop session (Wayland on Linux).

NameRequiredDescriptionDefault
pathYesThe .md file to open

Output Schema

NameRequiredDescription
guiYesThe RayoMD GUI executable started
pidNo
fileYes

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations: it discloses the ~1s polling/reload cycle, the condition under which a rewrite is picked up (changed on disk and no unsaved buffer edits), and the hard prerequisites (rayomd-gui on PATH or RAYOMD_GUI_PATH, a desktop session, Wayland on Linux). The readOnlyHint=true is consistent, since the tool itself only opens a viewer even though the user may edit.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and resource, then prerequisites last, which is the right order. The middle sentence on reload semantics is long and comma-chained, but every sentence carries information an agent would otherwise have to guess.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Return values are covered by the output schema and prerequisites and side effects are well documented. The one real gap is whether the call blocks until the GUI is dismissed or returns immediately, which materially affects how an agent sequences subsequent calls.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With a single parameter at 100% schema coverage ("The .md file to open"), the schema already carries the semantics, so baseline 3 applies. The description adds no path constraints such as absolute vs relative paths or behavior for a nonexistent file.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Opens a Markdown file in RayoMD GUI") plus the user-facing purpose ("for the user to read or edit"), which cleanly separates it from the sibling converters markdown_to_pdf and book_to_pdf. An agent can pick this over the PDF tools without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The clause "for the user to read or edit" makes the intended situation clear: interactive desktop viewing/editing rather than conversion or recovery. It stops short of explicitly excluding alternatives (e.g. "use markdown_to_pdf to produce a PDF instead"), so routing is inferred rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

recover_markdownRecover Markdown from a PDFA

Gets back the exact Markdown source of a reversible PDF (one exported with the source embedded), checked against its digest. Returns the text, or writes it to a new .md file; the PDF is not changed.

NameRequiredDescriptionDefault
pdfYesA PDF exported with the source embedded (rayomd --embed-source)
outputNoA new .md file to write the source to (never replaces a file); left out, the Markdown is returned as text

Output Schema

NameRequiredDescription
bytesYesBytes of Markdown recovered
inputYesThe PDF read
outputNoThe Markdown file written, when output was given
markdownNoThe recovered Markdown, when no output was given

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds real context beyond the annotations: the result is digest-checked, the write target is always a new .md file and never overwrites, and the source PDF is left untouched — which reconciles the apparently write-capable readOnlyHint=false with non-destructive intent. It doesn't say what happens when the digest check fails (error vs. partial output), which is the remaining behavioral unknown.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One tight sentence front-loads the action, then the required input condition, then the output-mode distinction and the non-destructive guarantee. Every clause carries information, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter tool with a full output schema, the description covers the input precondition, output destination semantics, and side-effect profile. The only omission is failure behavior on a digest mismatch or a non-embedded PDF, which an agent would want to know before relying on it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameter descriptions already state the embedded-source requirement and the 'never replaces a file' / 'returned as text if omitted' behavior, so the description largely restates the schema. Baseline 3 applies; no extra syntax or format guidance is added.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource (recover the Markdown source) plus the scoping precondition — only reversible PDFs exported with the source embedded. That precondition implicitly distinguishes it from the inverse sibling markdown_to_pdf, so an agent can select it without reading the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'reversible PDF (one exported with the source embedded)' qualifier tells the agent exactly which inputs qualify, and the second clause contrasts returning text vs. writing a file. It stops short of naming what to do when the PDF lacks embedded source (e.g., use an extract/convert tool instead), so the exclusion is implied rather than routed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedbook_to_pdf
    • First observedmarkdown_to_pdf
    • First observedopen_in_gui
    • First observedrecover_markdown

TDQS

A4.2/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: single-file conversion, multi-file book conversion, source recovery, and GUI opening. The descriptions explicitly clarify boundaries, especially between markdown_to_pdf and book_to_pdf.

Naming Consistency4/5

Names are all snake_case and descriptive, but the convention mixes source_to_target names (markdown_to_pdf, book_to_pdf) with verb_object names (recover_markdown, open_in_gui). The pattern is still readable and logical.

Tool Count5/5

Four tools are well-scoped for a Markdown-to-PDF conversion server. Each tool covers a distinct workflow without unnecessary surface area.

Completeness5/5

The surface covers the core lifecycle: convert a single Markdown file, convert a multi-file book, recover embedded Markdown from reversible PDFs, and open files in the GUI for preview/editing. No obvious dead ends for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers