MCP server for rendering HTML and Markdown to PDF, and manipulating PDF files
foliopdf-mcp has well-structured input schemas with proper JSON types and descriptions for all parameters. Tool names are clear action verbs (render_html, render_markdown, manipulate_pdf, extract_pdf). Descriptions are present and moderately detailed (100-200 chars). However, output schemas are not documented, LLMs cannot see what fields to expect from responses. Error handling is minimal; no recovery guidance or actionable error messages visible in the code. Parameter validation is thorough (enums, ranges, path traversal checks) but not reflected in schema constraints. Missing tool annotations (readOnlyHint, destructiveHint) despite clear risk levels (WRITE vs READ_ONLY).
Extract text and metadata from a PDF.
Perform merge, split, or rotate operations on PDFs.
Render HTML to PDF. Supports full CSS, Tailwind v3 (if enabled), and Go template syntax like {{.name}}.
Render Markdown to PDF. Supports code blocks with syntax highlighting.
Output schemas not documented. LLMs cannot infer what fields render_html, render_markdown, manipulate_pdf, and extract_pdf return. This forces agents to guess at response structure and breaks downstream tool chaining.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Tools are marked WRITE or READ_ONLY in metadata but not in schema. LLMs cannot determine safety/idempotency from tool definitions alone.
Error handling lacks recovery guidance. Code validates inputs (validateFormat, validatePageRange) but error messages are generic. No guidance on what to do next (e.g., 'Invalid format, use A4, Letter, Legal, A3, A5, or Tabloid').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 58 | 2026-07-28+ | v2 |
Parameter constraints not expressed in JSON Schema. Enums (format, theme, operation, watermark_scope) are validated in code but not declared as enum in schema. LLMs cannot see valid options without reading descriptions.
manipulate_pdf combines three distinct operations (merge, split, rotate) in one tool. The 'operation' parameter determines which other params apply (pages for split, degrees for rotate). This violates single-responsibility and forces LLMs to reason about conditional parameter validity.