rayomd-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@rayomd-mcpConvert report.md to PDF and tell me the page count"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
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/listNode.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/reportsIn 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:
RAYOMD_PATH, when set.Else
rayomdonPATH(rayomd.exeon Windows).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.tsand kept in~/.cache/rayomd/bin/<version>/($XDG_CACHE_HOME/rayomd/bin,%LOCALAPPDATA%\RayoMD\cache\binon 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 | |
| exactly one of them | |
| the PDF, ending in | |
| with | |
|
| |
|
| |
|
|
|
|
| |
|
| 1 to 6 levels |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
|
|
|
| only with the server's |
|
| only with the server's |
|
| only with |
|
| only with |
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
--rootfolders (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(.mdforrecover_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 --jsonchildren 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 | 182 ms | 117 ms |
First | 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.mjsis 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
rayomdand 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.tskeeps 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_paththat is the output's.Several children when calls overlap; one when they do not.
In rayomd: the request reader of
--serve --jsonreads 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 | --helpVariable | |
| the rayomd executable |
| the RayoMD GUI executable for |
| where the downloaded binary is kept |
| 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 defaultA 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 toolsbook_to_pdfBook to PDFAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| toc | No | One table of contents for the whole book | |
| pdfa | No | PDF/A-3b: every font embedded | |
| pdfua | No | PDF/UA-1, tagged for screen readers (formulas as TeX source) | |
| style | No | Look of the document (default elegant) | |
| inputs | Yes | One SUMMARY.md (mdBook, GitBook), one folder (its SUMMARY.md, or its .md files by name, README.md first), or Markdown files in order | |
| margin | No | compact, normal (default), wide, or margin=N in inches or margin=Npt: margin=0.75in, margin=54pt | |
| output | Yes | The PDF to write, ending in .pdf; an existing file is replaced | |
| dialect | No | github (default), or obsidian: callouts, ==highlights==, %%comments%%, [[wiki links]], ![[embeds]] | |
| diagrams | No | Draw ```mermaid flowcharts, sequence diagrams and pie charts (default true) | |
| footnotes | No | end (default): after the text; page: at the foot of the page of the first reference | |
| page_size | No | a4 (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_depth | No | Heading levels the table of contents lists (default 3) | |
| black_text | No | For black-and-white printers: text, links and code black, fills light grey | |
| page_numbers | No | "N / M" at the foot of every page | |
| highlight_code | No | Colour fenced code by language (default true) | |
| number_headings | No | Number the headings of this many levels: 1, 1.1, 1.1.1 | |
| page_break_before | No | A new page before each heading of level 1 to N (h1: chapters) |
Output Schema
| Name | Required | Description |
|---|---|---|
| ms | Yes | |
| code | Yes | |
| bytes | Yes | |
| pages | Yes | |
| inputs | Yes | |
| output | Yes | |
| status | Yes | |
| missing_characters | Yes |
TDQS
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.
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.
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.
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.
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.
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 PDFAIdempotent
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.
| Name | Required | Description | Default |
|---|---|---|---|
| toc | No | A table of contents first, or at a [TOC] paragraph | |
| path | No | A Markdown file to convert (give this or markdown) | |
| pdfa | No | PDF/A-3b: every font embedded | |
| pdfua | No | PDF/UA-1, tagged for screen readers (formulas as TeX source) | |
| style | No | Look of the document (default elegant) | |
| margin | No | compact, normal (default), wide, or margin=N in inches or margin=Npt: margin=0.75in, margin=54pt | |
| output | Yes | The PDF to write, ending in .pdf; an existing file is replaced | |
| dialect | No | github (default), or obsidian: callouts, ==highlights==, %%comments%%, [[wiki links]], ![[embeds]] | |
| diagrams | No | Draw ```mermaid flowcharts, sequence diagrams and pie charts (default true) | |
| markdown | No | The Markdown text (give this or path) | |
| footnotes | No | end (default): after the text; page: at the foot of the page of the first reference | |
| page_size | No | a4 (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_depth | No | Heading levels the table of contents lists (default 3) | |
| black_text | No | For black-and-white printers: text, links and code black, fills light grey | |
| source_path | No | With markdown: the .md file the text stands for; relative images resolve from its folder. Default: output with .md | |
| page_numbers | No | "N / M" at the foot of every page | |
| highlight_code | No | Colour fenced code by language (default true) | |
| number_headings | No | Number the headings of this many levels: 1, 1.1, 1.1.1 | |
| page_break_before | No | A new page before each heading of level 1 to N (h1: chapters) |
Output Schema
| Name | Required | Description |
|---|---|---|
| ms | Yes | Build time in milliseconds |
| code | Yes | rayomd exit code: 0 ok, 2 bad request, 3 input not readable, 10+N build error, 12 output not writable |
| bytes | Yes | |
| error | No | |
| input | Yes | The input path, or the source_path of Markdown text |
| pages | Yes | |
| output | Yes | The PDF written |
| status | Yes | |
| failed_images | Yes | Images that could not be loaded (their alt text is printed) |
| missing_characters | Yes | Characters without a glyph in the fonts found (drawn as a base letter or ?) |
TDQS
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.
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.
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.
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.
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.
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 GUIARead-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).
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | The .md file to open |
Output Schema
| Name | Required | Description |
|---|---|---|
| gui | Yes | The RayoMD GUI executable started |
| pid | No | |
| file | Yes |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| Yes | A PDF exported with the source embedded (rayomd --embed-source) | ||
| output | No | A new .md file to write the source to (never replaces a file); left out, the Markdown is returned as text |
Output Schema
| Name | Required | Description |
|---|---|---|
| bytes | Yes | Bytes of Markdown recovered |
| input | Yes | The PDF read |
| output | No | The Markdown file written, when output was given |
| markdown | No | The recovered Markdown, when no output was given |
TDQS
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.
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.
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.
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.
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.
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.
4 tool updates
v0.1.0- First observed
book_to_pdf - First observed
markdown_to_pdf - First observed
open_in_gui - First observed
recover_markdown
TDQS
Scored across 4 tools
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.
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.
Four tools are well-scoped for a Markdown-to-PDF conversion server. Each tool covers a distinct workflow without unnecessary surface area.
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
Related MCP Connectors
Render Markdown and LLM output into accessible PDF/UA-1 PDFs. No headless Chromium.
High-fidelity PDF to structured Markdown conversion and document field extraction.
Convert PDFs, Office docs, websites and audio into AI-ready Markdown, then search and ask your docs.
Markdown in, any format out. PDFs merged, split, watermarked. Runs on our own doc engines.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to generate professional PDF documents from markdown content with advanced typography, syntax highlighting, math equations, dark mode, and customizable styling options.MIT
- AlicenseAqualityDmaintenanceConverts Markdown documents to PDF files with support for syntax highlighting, custom styling, Mermaid diagrams, optional page numbers, and configurable watermarks.153 npmMIT
- FlicenseNot gradedqualityDmaintenanceProvides tools for converting Markdown content and files into professional PDF documents with full support for Mermaid diagrams and LaTeX rendering. It allows for high-quality output customization, including paper size, table of contents, and syntax highlighting styles.-
- AlicenseNot gradedqualityDmaintenanceConverts Markdown files and raw content into professionally styled PDFs with full support for Mermaid diagrams and syntax highlighting. It offers customizable page formats, margins, and modern typography for high-quality document generation.11MIT