Skip to main content
Glama

oxidize-pdf

Documentation · Context7 · Documentation publishing and indexing

Version 0.23.4 pins Rust core 5.5.6. See compatibility and validation.

PyPI version CI License: MIT Python Typed MCP

oxidize-python MCP server

Rust-powered PDF library for Python. Generate, parse, split, merge, and manipulate PDFs with native performance. Ships with a built-in MCP server so AI agents can work with PDFs out of the box.

No C dependencies. No Java. No subprocess calls.

Installation

pip install oxidize-pdf            # Core library
pip install "oxidize-pdf[mcp]"     # + MCP server for AI agents

Platforms: Linux (x86_64, aarch64) | macOS (x86_64, Apple Silicon) | Windows (x86_64) Requires: Python 3.10+

Version 0.23.4 pins Rust core 5.5.6, using the MIT-licensed oxidize-webpki RustCrypto provider for certificate verification. See the dependency policy for supported builds and limits. 0.23.4 release notes describe the signed-field permission fixes. New APIs include explicit text recovery, non-breaking-space normalization, build identification controls and incremental signature slots. See the API guide and validation.

The current develop branch contains post-release parity work. See the current capability inventory and Python/.NET comparison for the audited subset, missing bindings and inherited core limitations. Neither the core version nor the type stubs establish full functional parity; no global coverage percentage is claimed.

Related MCP server: PDFSizeAnalyzer-MCP

Why oxidize-pdf?

oxidize-pdf

Pure-Python libs

C/Java wrappers

Performance

Native (compiled Rust)

Interpreted

Native but heavy

Dependencies

Zero

Varies

Poppler, Java, Ghostscript

Memory safety

Rust ownership model

GC-dependent

Manual / GC

Type stubs

Public API inventory + strict mypy checks on develop

Varies

Varies

AI-ready (MCP)

Built-in

No

No


MCP Server

Expose the supported PDF tools to your AI agent:

oxidize-mcp

The built-in Model Context Protocol server exposes 12 tools, 5 static resources plus a session resource template, and 5 prompts over stdio. Install oxidize-pdf[mcp] to include its dependencies; the base library keeps MCP optional.

Version 0.20 uses FastMCP 4 and MCP Python SDK 2, with support for both modern and legacy stdio clients. See the migration guide for versions and installation routes.

If tools/list returns an empty array with an older installation (issue #198), upgrade to 0.20.0 or newer. These versions fix tool registration when starting server.py directly. On Windows, use the same Python environment for installation and startup:

python -m pip install --upgrade "oxidize-pdf[mcp]"
python -m oxidize_pdf.mcp.server

For a custom JSON-RPC client, send initialize, wait for its response, then send notifications/initialized before tools/list. Read newline-delimited JSON-RPC from stdout and keep stderr separate; the startup banner is written to stderr.

Claude Desktop integration

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "oxidize-pdf": {
      "command": "oxidize-mcp",
      "env": {
        "OXIDIZE_WORKSPACE": "/path/to/your/pdfs"
      }
    }
  }
}

GitHub Copilot (VS Code) integration

Copilot's agent mode speaks MCP. Add .vscode/mcp.json to your workspace:

{
  "servers": {
    "oxidize-pdf": {
      "command": "oxidize-mcp",
      "env": {
        "OXIDIZE_WORKSPACE": "/path/to/your/pdfs"
      }
    }
  }
}

Open the Chat view, switch to Agent mode, and the 12 PDF tools appear in the tool picker. (The same block also works under the mcp.servers key in your user settings.json if you prefer a global install.)

OpenAI Agents SDK integration

The OpenAI Agents SDK spawns the server over stdio and exposes its tools to an agent:

from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async with MCPServerStdio(
    params={"command": "oxidize-mcp", "env": {"OXIDIZE_WORKSPACE": "/path/to/your/pdfs"}},
    cache_tools_list=True,
) as server:
    agent = Agent(
        name="PDF assistant",
        instructions="Use the oxidize-pdf tools to inspect and manipulate PDFs.",
        mcp_servers=[server],
    )
    result = await Runner.run(agent, "How many pages does report.pdf have?")
    print(result.final_output)

A runnable version is in examples/openai_agents_quickstart.py.

Both integrations run the server locally over stdio, so its tools operate on PDFs in the configured workspace directory. This package does not expose a hosted HTTP endpoint. ChatGPT web needs a remote connection; developer-mode testing can also use Secure MCP Tunnel with a local stdio server. No ChatGPT plugin is published by this migration.

Available tools

Tool

What it does

read_pdf

Read metadata — page count, version, encryption status, title, author

extract_text

Extract text from all pages or a specific page

convert_pdf

Convert to markdown, chunks, or RAG-optimized format

create_pdf

Create a new PDF with optional metadata

save_pdf

Save a session to disk, with optional encryption

add_content

Add pages, text, and graphics to a session

annotate_pdf

Add text annotations and highlights

manipulate_pdf

Split, merge, rotate, extract pages, reverse, overlay

manage_forms

Create, fill, read, and validate form fields

secure_pdf

Encrypt, check permissions, verify signatures

extract_entities

Extract structured entities from pages

analyze_pdf

Validate structure, detect corruption, check PDF/A compliance

The server also exposes resources (session data, capabilities, version info) and prompts (guided workflows for summarization, data extraction, form filling, and more).

Configuration

OXIDIZE_WORKSPACE=/path/to/pdfs oxidize-mcp

The server is configured entirely through environment variables:

Variable

Default

Purpose

OXIDIZE_WORKSPACE

~/Documents/oxidize-mcp

Sandbox root; all paths must resolve inside it.

OXIDIZE_ALLOWED_PATHS

(none)

Comma-separated extra directories allowed outside the workspace.

OXIDIZE_MAX_FILE_SIZE_MB

100

Reject input PDFs larger than this on disk.

OXIDIZE_MAX_PAGES

10000

Reject documents with more pages than this before any extraction work.

OXIDIZE_MAX_OUTPUT_BYTES

10485760

Cap the serialized size of a tool's JSON response (10 MB).

OXIDIZE_MAX_SESSIONS

10

Maximum concurrent stateful PDF-creation sessions.

OXIDIZE_MAX_SESSION_BYTES

10485760

Cap the content a single session may accumulate (10 MB).

OXIDIZE_SESSION_TIMEOUT

3600

Session expiry, in seconds.

Resource caps (OXIDIZE_MAX_*) protect the server from a large or malicious PDF: oversized documents are rejected up front and tool responses are bounded rather than serialized unbounded. Exceeding a cap returns an error with code RESOURCE_LIMIT.

Or start programmatically:

from oxidize_pdf.mcp.server import run
run()

Python API

Create a PDF

from oxidize_pdf import Document, Page, Font, Color

doc = Document()
doc.set_title("My Document")
doc.set_author("Jane Doe")

page = Page.a4()
page.set_font(Font.HELVETICA, 24.0)
page.set_text_color(Color.black())
page.text_at(72.0, 750.0, "Hello from oxidize-pdf!")

page.set_font(Font.TIMES_ROMAN, 12.0)
page.text_at(72.0, 700.0, "Generated with Python + Rust.")

doc.add_page(page)
doc.save("output.pdf")

Parse an existing PDF

from oxidize_pdf import PdfReader

reader = PdfReader.open("document.pdf")
print(f"Pages: {reader.page_count}, Version: {reader.version}")

for i, text in enumerate(reader.extract_text()):
    print(f"--- Page {i + 1} ---")
    print(text)

Inspect tagged PDF structure

from pathlib import Path
from oxidize_pdf import TaggedPdfLimits, TaggedPdfValidationOptions, validate_tagged_pdf

report = validate_tagged_pdf(
    Path("input.pdf").read_bytes(),
    TaggedPdfValidationOptions(TaggedPdfLimits(max_depth=64)),
)
for finding in report.findings:
    print(finding.severity, finding.code, finding.path, finding.message)

The report includes structure elements, artifacts, role/class maps, the parent number tree and content MCIDs. Original dictionary values remain typed PdfValue objects, including unknown attributes and reference generations. Results are immutable snapshots; returned lists and dictionaries are owned copies.

valid means the core validator found no error-level findings. It is not a PDF/UA certificate: an untagged PDF can have tagged=False, valid=True and a MissingStructureTree warning. Malformed input raises PdfParseError, locked input raises PdfEncryptionError, and exceeded validation budgets raise PdfError.

TaggedPdfLimits bounds visited objects, traversal depth, entries and decoded content bytes (defaults: 100,000; 256; 1,000,000; 256 MiB). These are core validator budgets, not a process memory or time limit: input copying and initial PDF parsing occur before traversal. No PDF bytes are modified.

Operations

from oxidize_pdf import split_pdf, merge_pdfs, rotate_pdf, extract_pages

split_pdf("input.pdf", "output_dir/")                       # Split into individual pages
merge_pdfs(["part1.pdf", "part2.pdf"], "merged.pdf")         # Merge multiple PDFs
rotate_pdf("input.pdf", "rotated.pdf", 90)                   # Rotate all pages
extract_pages("input.pdf", "subset.pdf", [0, 2, 4])          # Extract specific pages

Graphics

from oxidize_pdf import Document, Page, Color

doc = Document()
page = Page.a4()

page.set_fill_color(Color.hex("#3498db"))
page.draw_rect(72.0, 700.0, 200.0, 100.0)
page.fill()

page.set_stroke_color(Color.red())
page.set_line_width(2.0)
page.draw_circle(300.0, 500.0, 50.0)
page.stroke()

doc.add_page(page)
doc.save("graphics.pdf")

Types

from oxidize_pdf import Color, Point, Rectangle, Margins, Font

# Colors
Color.rgb(1.0, 0.0, 0.0)          # RGB
Color.hex("#ff6600")               # Hex
Color.cmyk(0.0, 1.0, 1.0, 0.0)   # CMYK

# Geometry
Point(72.0, 720.0)
Rectangle.from_xywh(72.0, 72.0, 468.0, 648.0)
Margins.uniform(72.0)

# Fonts — all 14 standard PDF fonts
Font.HELVETICA    # Font.HELVETICA_BOLD
Font.TIMES_ROMAN  # Font.TIMES_BOLD
Font.COURIER      # Font.COURIER_BOLD

Error handling

from oxidize_pdf import PdfReader, PdfError, PdfIoError, PdfParseError

try:
    reader = PdfReader.open("missing.pdf")
except PdfIoError as e:
    print(f"I/O error: {e}")
except PdfParseError as e:
    print(f"Parse error: {e}")
except PdfError as e:
    print(f"PDF error: {e}")

Exception hierarchy: PdfError > PdfIoError, PdfParseError, PdfEncryptionError, PdfPermissionError

MCP Server

oxidize-pdf includes an MCP server that exposes PDF capabilities to AI assistants like Claude. Install with the mcp extra:

pip install oxidize-pdf[mcp]

Claude Desktop

Add this to your claude_desktop_config.json:

{
  "mcpServers": {
    "oxidize-pdf": {
      "command": "uvx",
      "args": ["--from", "oxidize-pdf[mcp]", "oxidize-mcp"]
    }
  }
}

Claude Code

claude mcp add oxidize-pdf -- uvx --from "oxidize-pdf[mcp]" oxidize-mcp

Available tools

Tool

Description

read_pdf

Open a PDF and get metadata (pages, version, encryption)

extract_text

Extract text content from PDF pages

convert_pdf

Convert between PDF versions

analyze_pdf

Analyze structure, fonts, images, and compliance

extract_entities

Extract images and digital signatures

manipulate_pdf

Split, merge, rotate, extract, and reorder pages

annotate_pdf

Add text annotations, highlights, and stamps

manage_forms

Create, fill, and read PDF form fields

secure_pdf

Encrypt, decrypt, and set document permissions

create_pdf

Create a new PDF document with pages

add_pdf_content

Add text, shapes, and images to pages

save_pdf

Save the document to file or bytes

Resources

  • oxidize://fonts — Available built-in PDF fonts

  • oxidize://page-sizes — Standard page sizes with dimensions

  • oxidize://capabilities — Server capabilities and tool listing

  • oxidize://version — Version information

  • oxidize://workspace — PDF files in the workspace directory

  • oxidize://session/{id} — Session data by ID

Known limitations

  • Encryption write support: Document.encrypt() configures encryption parameters but the underlying Rust library does not yet serialize the encryption dictionary to the PDF output. Reading encrypted PDFs works correctly.

  • Image extraction returns raw embedded streams: extract_images_from_pdf extracts each embedded image as-is (e.g. a DCTDecode JPEG is written byte-for-byte). Image preprocessing — auto rotation-correction, contrast enhancement, denoise, upscaling, force-grayscale — is not available, because the build excludes the upstream external-images feature (and its image-crate dependency). This keeps extraction faithful and lossless; it does not silently return empty or stub results.

  • CPython only: PyPy and GraalPy are not supported.

License

MIT — see LICENSE for details.

Semantic comparison with revision history

from oxidize_pdf import (
    compare_pdfs_semantically, SemanticComparisonLimits, SemanticComparisonOptions,
)

options = SemanticComparisonOptions(SemanticComparisonLimits(
    max_objects=100_000, max_depth=256,
    max_decoded_stream_bytes=256 * 1024 * 1024,
    max_canonical_bytes=256 * 1024 * 1024,
    max_extracted_text_bytes=64 * 1024 * 1024,
    max_unreachable_objects=10_000, max_revisions=1024,
))
result = compare_pdfs_semantically(original_bytes, revised_bytes, options)
for difference in result.differences:
    print(difference.path, difference.class_, difference.description)
for revision in result.right_revisions:
    print(revision.index, revision.xref_offset, revision.semantic_fingerprint.hex())
    for change in revision.object_changes:
        print(change.object_number, change.generation, change.kind)

The immutable result exposes both revision histories, unreachable object identities and pairs of semantically equivalent objects. Collection properties return independent snapshots. Revision fingerprints are 32-byte hashes, and revisions are ordered oldest first. class_ is the Python spelling of Rust's class field. Difference classes are Visual, Textual, Structural, Metadata, Security and SerializationOnly. semantically_equal may be true when the only difference is SerializationOnly; it includes supported historical semantics, so restoring the latest state does not erase an intervening semantic change.

Limits apply independently to each document. Decoded/canonical budgets count work across repeated traversals, not just unique input bytes. Initial input copies, parsing and some extraction allocations precede these checks; the options are not process-wide memory/time limits. Malformed input, exceeded budgets and unsupported decoding raise PDF errors; encrypted input raises PdfPermissionError and must be decrypted before comparison.

This API compares the core's supported object/text domains, not rendered pixels, and does not certify signatures or irreversible redaction. Core 5.4.2 includes physical ObjStm/XRef containers among objects unreachable from Root/Info. A qpdf conversion to object streams can therefore report Structural differences at /UnreachableObjects and /Revisions despite equal text and page content. The binding preserves that conservative result; it does not promise equivalence across every PDF layout. The existing compare_pdfs API remains available.

Images from pixel buffers

from oxidize_pdf import Image, ImageColorSpace

rgb = Image.from_raw_data(b'\xff\x00\x00', 1, 1, ImageColorSpace.DeviceRGB)
gray = Image.from_gray_data(b'\x80', 1, 1)
rgba = Image.from_rgba_data(b'\xff\x00\x00\x80', 1, 1)
assert rgba.alpha_data == b'\x80'
assert rgba.soft_mask.data == b'\x80'

from_raw_data(data, width, height, color_space, bits_per_component=8) accepts DeviceGray, DeviceRGB and DeviceCMYK. Components are packed in pixel order, most significant bits first, with each scanline padded to a whole byte. Supported component depths are 1, 2, 4, 8 and 16; 16-bit components use big-endian byte order. RGBA and grayscale convenience constructors use 8-bit components.

All three constructors require positive dimensions and an exact buffer length. Invalid sizes/depths raise ValueError; values outside unsigned argument ranges raise OverflowError. The RGBA/gray constructors reject products exceeding u32::MAX before calling the published core's dimension arithmetic. Validation happens before copying the pixel buffer.

data, format, bits_per_component, alpha_data and soft_mask expose owned snapshots. ImageFormat distinguishes Jpeg, Png, Tiff and Raw. Data is the core's image payload: JPEG remains encoded, while PNG/TIFF constructors expose decoded pixel data. RGBA separates RGB data from alpha; its soft mask can be inspected independently. These APIs use the existing core without external-images or new dependencies. Explicit mask creation/application and writer-object export remain outside this increment.

Marked content on new pages

Page.begin_marked_content(tag) and Page.begin_marked_content_with_actual_text(tag, actual_text) return a page-local MCID. Pair every begin with Page.end_marked_content(); next_mcid() and marked_content_depth() inspect the state. Tags are nonempty literal names (UTF-8 bytes are PDF-name escaped, so pass "Span", not "/Span"). Empty tags raise ValueError without changing state; ending an empty stack raises PdfError.

page = Page.a4()
page.set_font(Font.HELVETICA, 12)
mcid = page.begin_marked_content_with_actual_text("Span", "café 😀")
page.text_at(40, 700, "visible glyphs")
page.end_marked_content()

ActualText is a Unicode logical replacement; copying/extraction depends on the viewer honoring it. For tagged structure, attach the returned ID using StructureElement.add_mcid(page_index, mcid) and install the tree with Document.set_struct_tree(tree). These operations alone do not certify PDF/UA. The core writes these markers in the text context; they are intended for text operations, not as a general scope around separately buffered graphics.

Coordinate transformations

TransformMatrix(a, b, c, d, e, f) exposes immutable affine components, IDENTITY, translate, scale, rotate (radians), flip_y, multiply, transform_point(Point) and to_pdf_ctm(). Composition a.multiply(b) applies b first, then a. Values retain the core's floating-point semantics, including singular and nonfinite matrices; validate them before using the CTM in a PDF.

from oxidize_pdf import CoordinateSystem, Point, TransformMatrix

matrix = TransformMatrix.translate(10, 20).multiply(TransformMatrix.scale(2, 3))
point = matrix.transform_point(Point(4, 5))  # Point(18, 35)
custom = CoordinateSystem.custom(matrix)
pdf_point = custom.to_pdf_standard(Point(4, 5), 800)  # Point(18, 35)

CoordinateSystem supports point, Y-only and matrix conversions to PDF coordinates, plus grows_upward(). For custom systems, Y-only conversion uses x=0, orientation tests d > 0, and page height is ignored. Page.coordinate_system returns a snapshot of the stored system. In core 5.4.2, setting this property through set_coordinate_system does not automatically transform page content; convert positions explicitly. LayoutManager remains outside this binding increment.

RenderContext(coordinate_system, page_width, page_height) stores mutable coordinate_system, page_width, page_height and current_transform fields. Its to_pdf_standard(Point) and y_to_pdf(y) methods use the current system and height. The matrix is initialized from the constructor's system and height; later field assignments do not recalculate it, and conversions do not read it. This preserves core 5.4.2 behavior. Getters return independent value snapshots. Use context.current_transform.transform_point(point) when you explicitly want to apply the stored matrix. Numeric fields retain the core's f64 semantics.

Page.content_width and Page.content_height subtract the page margins. Page.content_area returns (left, bottom, right, top) bounds in unrotated PDF coordinates, rather than a position and size. These read-only properties reflect current margins and can be negative or inverted when margins exceed page size. Page.effective_width and Page.effective_height reflect rotation: 90°/270° swap width and height. They use the full page dimensions, without subtracting margins. Setting a coordinate system does not change these geometry properties.

Custom clipping paths

from oxidize_pdf import ClippingPath, WindingRule

clip = (ClippingPath().add_rect(10, 10, 80, 80)
        .add_polygon([(30, 30), (70, 30), (50, 70)])
        .with_winding_rule(WindingRule.EvenOdd))
page.set_clipping_path(clip)

move_to, line_to, curve_to, close_path and add_rect, add_circle, add_ellipse, add_rounded_rect, add_polygon mutate and return the same path. with_winding_rule returns an independent copy; the default is NonZero. clear() removes commands while preserving the rule. is_empty reports whether commands exist. Page.set_clipping_path retains a copy.

commands returns owned ClippingPathCommand snapshots with read-only kind and copied coordinates: move/line use [x, y], cubic curves use two control points and an endpoint [x1, y1, x2, y2, x3, y3], rectangles use [x, y, width, height], and close has no coordinates. Coordinates are PDF user space values; callers supply valid finite geometry. to_pdf_operations() emits the core's clipping operators, including W/W* and n. The existing rect, circle, and ellipse factories remain available. This API does not expose path intersection, text clipping, or a clipping stack.

Line dash patterns

LineDashPattern.dashed(dash_length, gap_length) and LineDashPattern.dotted(dot_size, gap_size) create two-length patterns with zero phase. Both use the same PDF dash representation; dots depend on length and line cap, and the factory does not select a round cap. Pass a pattern to page.set_dash_pattern(pattern); use solid() to reset to a continuous line.

array returns a copied list and phase is read-only. to_pdf_string() returns the core representation (without the d operator), rounded to two decimals. The core serializes non-finite components as zero and an empty array as [] 0, regardless of stored phase. Negative values remain unchanged; this API does not validate whether an arbitrary pattern is legal PDF. Use nonnegative lengths, with at least one positive length for a nonempty pattern, and a nonnegative phase.

Blend modes

All 16 modes from the published core 5.4.2 are available through BlendMode. Alongside the existing modes, use DIFFERENCE, EXCLUSION, HUE, SATURATION, COLOR, or LUMINOSITY with page.set_blend_mode(mode) before drawing. mode.pdf_name() returns the PDF name (for example, "Color"), and repr(mode) uses the Python constant name. These immutable values configure the page's extended graphics state; use BlendMode.NORMAL to reset blending.

Color conversion and inspection

color.to_rgb() and color.to_cmyk() return independent Color values. cmyk_components() returns four converted components, including for RGB/Gray inputs. Existing c, m, y, k properties still return zero for non-CMYK colors. is_rgb(), is_cmyk(), is_gray() inspect the stored variant, and color_space_name() returns DeviceRGB, DeviceCMYK, or DeviceGray. The existing color_space property keeps its shorter names.

Conversions use the published core 5.5.0's approximate formulas, without ICC profiles or calibrated color management. Gray becomes equal RGB components or CMYK with only black ink. RGB black converts to (0, 0, 0, 1). Constructors clamp finite out-of-range values and infinities; NaN may propagate through conversion. Callers should supply finite components for predictable colors.

Streaming and lazy document options

Core 5.5.0 exposes two separate consumers with immutable options and configuration snapshots. Existing calls such as LazyDocument.open(path) remain valid.

from oxidize_pdf import LazyDocument, MemoryOptions, StreamingDocument, StreamingOptions

options = StreamingOptions(page_cache_size=2, memory_limit=2 * 1024 * 1024,
                           max_content_stream_size=1024 * 1024)
with StreamingDocument.open("input.pdf", options) as document:
    configuration = document.configuration()
    while (page := document.next_page()) is not None:
        print(page.number, page.extract_text_streaming())
    print(document.cached_pages(), document.memory_usage())
    document.clear_cache()

with LazyDocument.open("input.pdf", MemoryOptions(cache_size=20)) as document:
    page = document.get_page(0)
    print(page.width, document.memory_stats().cached_objects)
    print(document.configuration().unsupported)

StreamingOptions accepts buffer_size, page_cache_size, max_content_stream_size, progressive_hints, and memory_limit as keyword arguments. Its minimal_memory() and fast_processing() presets and with_buffer_size(), with_page_cache_size(), and with_memory_limit() builders return independent values. MemoryOptions accepts lazy_loading, memory_mapping, cache_size, streaming, buffer_size, and mmap_threshold; use small_file()/large_file() or its with_lazy_loading(), with_memory_mapping(), with_cache_size(), and with_streaming() builders. All fields are readable properties.

Consumer

Effective contract

StreamingDocument

buffer_size configures input buffering. page_cache_size bounds retained pages; memory_limit bounds retained decoded-content plus UTF-8 text bytes. Either cache bound set to zero disables retention. memory_usage() reports this payload only. max_content_stream_size limits the combined decoded page content, including separators between streams, and raises a PDF error without advancing the cursor.

LazyDocument

lazy_loading=False eagerly reads indexed objects. cache_size bounds objects in the adapter LRU cache; zero disables it. get_object(number, generation=0), get_page(index), and preload_page(index) use the native consumer. buffer_size configures input buffering.

configuration() returns owned requested and effective snapshots. A zero buffer is normalized to one byte. Streaming progressive hints are reserved: effective.progressive_hints is false and progressive_hints_unsupported reports a requested hint. Lazy memory mapping and streaming are unsupported in this adapter: unsupported lists UnsupportedMemoryOption.MemoryMapping and/or .Streaming, effective flags are false, and the effective mapping threshold is the platform's maximum usize. These flags do not enable those modes.

These limits do not cap total RAM, parser caches, extraction working memory, caller-retained values, or peak allocations. Lazy memory_stats() reports adapter cache hits, misses, and retained object count; allocated_bytes, lazy_loads, and mapped_regions are uninstrumented counters, not measurements of process memory. clear_cache() clears adapter retention, not caller values.

Native StreamingDocument pages use unrotated MediaBox geometry. The existing PageStreamer retains its rotated geometry and seek behavior; its options belong to a different API. LazyDocument.get_page() returns ParsedPage with effective rotation. Pages and snapshots remain usable after closing either document; operations requiring the closed document raise ValueError. Both consumers reject encrypted documents; unlock through the reader API where needed.

Incremental tagged structure editing

IncrementalTaggedPdfEditor(pdf) owns an immutable byte snapshot. TaggedPdfMutation supports create_element, set_element_attribute (omit the value to remove it), reparent_element, associate_mcid, and set_parent_tree_entry. Inputs use TaggedPdfObjectRef (number and generation) and lossless PdfValue objects. plan(mutations) returns effective mutations and changed object identities; apply(mutations) returns pdf_bytes, the plan, and validation reports before and after the revision. Both evaluate mutations in order. A change followed by a restoration retains both effective operations.

from oxidize_pdf import IncrementalTaggedPdfEditor, TaggedPdfMutation, TaggedPdfObjectRef, PdfValue

editor = IncrementalTaggedPdfEditor(source_bytes)
mutation = TaggedPdfMutation.set_element_attribute(
    TaggedPdfObjectRef(6, 7), "T", PdfValue.string(b"New title"))
result = editor.apply([mutation])
assert result.pdf_bytes.startswith(source_bytes)

Object references must identify structure elements in the actual source PDF. Editors and returned snapshots are independent; a failed batch leaves the editor unchanged. Empty/no-op ordinary edits preserve source bytes exactly. Applied revisions preserve the exact source prefix, including for xref-stream sources.

with_validation_options() accepts TaggedPdfValidationOptions and its limits. preflight() performs unambiguous derived-index recovery for inspection; it does not prepare or mutate the editor. with_recovered_indexes() prepares an independent editor with recovered indexes/page keys. with_document_language() supplies a missing language; it rejects blank values and existing nonempty languages. Recovery does not invent accessibility descriptions or language.

Preparation builders are replayed in order for each operation. preparation_objects() inventories their changes, including xref-stream objects, separately from plan().changed_objects. Prepared editors return bytes only when the full result validates, including empty mutation batches. Missing descriptions must be supplied explicitly; preparation refuses to overwrite existing ones. Ordinary editors return diagnostic reports and may return structurally invalid results. Reports do not certify PDF/UA conformance.

Encrypted inputs and DocMDP P1/P2/P3 restrictions are enforced by the core in planning, application and preparation. Validation limits bound the validation work they describe, not all parsing, copying or preparation work. No signature verification, OCR recognition, or global memory cap is implied by this editor.

Incremental text notes and highlights

IncrementalTextNoteEditor(pdf) reads notes() and applies batches of TextNoteMutation.add(page_index, position, contents), .update(id, position, contents), and .remove(id). position is an (x, y) pair in PDF coordinates. New notes have a 20 × 20 point rectangle; updates preserve the existing rectangle's width, height and other dictionary keys. Contents must contain non-whitespace text and support Unicode. TextNoteUpdate.added_notes contains only newly added notes, in request order; reread the result to obtain the complete note set.

IncrementalHighlightEditor(pdf) reads highlights() and accepts HighlightMutation.add(page_index, quadrilaterals, color, opacity=None, contents=None) and .remove(id). HighlightUpdate.highlights contains the complete current set, ordered by page and identity. The native API has no highlight update operation.

from oxidize_pdf import (
    IncrementalTextNoteEditor, TextNoteMutation,
    IncrementalHighlightEditor, HighlightMutation,
    HighlightQuad, HighlightColor, HighlightOpacity,
)

notes = IncrementalTextNoteEditor(source_bytes).apply([
    TextNoteMutation.add(0, (40., 50.), "Revisar: café 世界"),
])
quad = HighlightQuad([(40., 90.), (100., 90.), (40., 70.), (100., 70.)])
result = IncrementalHighlightEditor(notes.pdf_bytes).apply([
    HighlightMutation.add(0, [quad], HighlightColor([1., 1., 0.]),
                          HighlightOpacity(.5), "Revisar"),
])
output_bytes = result.pdf_bytes

HighlightQuad takes exactly four (x, y) pairs in PDF QuadPoints order: start and end of one edge, followed by start and end of the opposite edge. The core rejects non-finite, repeated, crossed, concave and degenerate geometry. HighlightColor accepts exactly three finite DeviceRGB components in [0, 1]; HighlightOpacity accepts a finite value in [0, 1]. Their properties return owned values. A highlight requires at least one quad. Optional contents preserve None versus an empty string; opacity omission differs from explicit zero.

Page indices are zero-based. Coordinates must fit the page's effective CropBox (or MediaBox if absent) in unrotated PDF space; inherited bounds are respected. Identities are TextNoteId/HighlightId with object and generation numbers. Targeting another subtype, a nonexistent identity or one target twice in a batch raises a PDF error. Direct target annotation dictionaries and annotations shared across pages are unsupported because they lack an unambiguous stable identity.

Editors own immutable source snapshots. Results preserve the source as an exact byte prefix, with one appended revision for a successful nonempty batch; failed batches leave the source/editor unchanged. Empty batches preserve bytes exactly. Notes, highlights, mutations and result snapshots expose read-only properties. These APIs append revisions in memory; removal detaches references, so historical annotation bytes remain in the file. They do not provide irreversible redaction.

Core 5.5.0 enforces DocMDP for nonempty batches: P1/P2 reject edits and P3 permits these annotation operations. Reads and empty batches remain available on those certified inputs; encrypted inputs are rejected. This permission check does not verify signature authenticity. These editors preserve or emit native annotation dictionaries; they do not promise a generated appearance stream or identical annotation rendering in every viewer, nor impose a global memory limit.

Available Tools

12 tools
add_pdf_contentAdd content to a PDF sessionA

Append text or a new page to an open create_pdf session (step 2 of 3).

Mutates the in-memory session; nothing is written to disk until save_pdf. Returns JSON {status, session_id, page_count} on success, or {error, code} if the session is missing/inactive or required text fields are absent. Coordinates use PDF points with the origin at the bottom-left of the page.

Call repeatedly to build up pages, then call save_pdf. This only works on a session from create_pdf — to add notes/highlights to an existing PDF file use annotate_pdf instead.

NameRequiredDescriptionDefault
xNoHorizontal position in PDF points from the left edge. Required when content_type='text'.
yNoVertical position in PDF points from the bottom edge (origin is bottom-left). Required when content_type='text'.
fontNoFont name (e.g. 'Helvetica', 'Courier', 'Times-Roman'). Defaults to Helvetica when omitted.
contentNoText to draw. Required when content_type='text'.
font_sizeNoFont size in points for text content.
session_idYesSession id returned by create_pdf. Must be active.
content_typeYes'text' draws a text string at (x, y) on the current page; 'new_page' appends a blank page and makes it current.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

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

Discloses in-memory mutation, no disk write until save_pdf, return formats, coordinate system, and prerequisites. No contradiction with annotations.

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?

Three paragraphs with front-loaded purpose, efficient behavioral details, and usage guidelines. No redundant sentences.

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

Completeness5/5

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

Covers preconditions, mutations, errors, return format, and alternatives. With output schema present, return description is sufficient.

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%, so baseline is 3. Description adds some value (e.g., coordinate origin) but mostly repeats schema info.

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?

The description clearly states the verb 'append' and resource 'open create_pdf session' and positions it as step 2 of 3, distinguishing it from sibling tools like annotate_pdf.

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

Usage Guidelines5/5

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

Explicitly says when to use (after create_pdf, before save_pdf) and when not (use annotate_pdf for existing files). Provides clear workflow context.

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

analyze_pdfAnalyze / validate a PDFA
Read-onlyIdempotent

Inspect a PDF's structural health or conformance (does not read content).

Returns JSON keyed by the chosen check: validate → {valid, error_count, warning_count}; corruption → {corrupted, corruption_type, severity, found_pages, file_size, errors}; compliance → {level, is_valid, error_count, warning_count, compliance_percentage}; compare → {structurally_equivalent, content_equivalent, similarity_score, difference_count}. Read-only.

Use this to verify a file is well-formed, archival-grade, or identical to another. To read titles/author/page counts use read_pdf; for the text use extract_text.

NameRequiredDescriptionDefault
pathYesPath to the PDF file to analyze, relative to the workspace.
checkNoWhich analysis to run: 'validate' = structural validity with error/warning counts; 'corruption' = damage severity and type; 'compliance' = PDF/A conformance at compliance_level; 'compare' = diff against compare_path.validate
compare_pathNoSecond PDF to diff against. Required when check='compare', ignored otherwise.
compliance_levelNoPDF/A conformance level to test. Used only when check='compliance'. Letter = conformance class (a/b/u), number = PDF/A part (1/2/3).a1b

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already provide readOnlyHint and idempotentHint. Description adds that it is read-only and details return JSON structure for each check, going beyond annotations. No contradictions.

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 paragraphs, well-organized. First paragraph describes output, second provides usage guidance. No fluff or redundancy. Could be slightly more concise but still efficient.

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

Completeness5/5

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

Given the tool's complexity (4 parameters, 2 enums, output schema), the description thoroughly covers behavior, return structure, and use cases. Annotations and schema further enrich completeness.

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 coverage is 100%, so baseline is 3. Description adds context by explaining each check value and when compare_path is required. Provides meaning beyond schema enums.

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?

The description clearly states the tool inspects structural health or conformance, not content. It distinguishes from siblings like read_pdf and extract_text by specifying that it does not read content.

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

Usage Guidelines5/5

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

Explicitly states when to use: verify well-formed, archival-grade, or identical. Also provides exclusions: use read_pdf for titles/author/page counts and extract_text for text. Clear alternatives.

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

annotate_pdfAnnotate a PDFA
Destructive

Stamp a sticky note or highlight onto a page of an existing PDF.

Writes the annotated copy to output_path (overwriting any existing file) and returns JSON {status, annotation_type}; out-of-range pages or coordinates outside the page bounds return {error, code}. Coordinates are in PDF points with the origin at the bottom-left.

Use this to mark up a document. To reorder/rotate/overlay whole pages use manipulate_pdf; to author a new PDF use create_pdf.

NameRequiredDescriptionDefault
xYesHorizontal anchor in PDF points from the left edge.
yYesVertical anchor in PDF points from the bottom edge (origin is bottom-left).
pageYes0-based index of the page to annotate.
widthNoHighlight width in points. Used only for 'highlight'.
heightNoHighlight height in points. Used only for 'highlight'.
contentsNoNote text for a 'text' annotation. Ignored for 'highlight'.
input_pathYesSource PDF to annotate, relative to the workspace.
output_pathYesDestination .pdf path; overwritten if it already exists.
annotation_typeYes'text' adds a sticky-note marker at (x, y); 'highlight' draws a highlight rectangle of width×height anchored at (x, y).

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already indicate destructiveHint=true and readOnlyHint=false. The description adds that it overwrites the output file, returns JSON with status/error for out-of-range pages or coordinates, and explains the coordinate system. This provides useful context beyond annotations, though it does not detail potential side effects like file locking.

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?

The description is three sentences plus a brief usage note, conveying all essential information without redundancy. It front-loads the core action and follows with behavior and alternatives, making it efficient and easy to process.

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

Completeness5/5

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

Given the detailed input schema and annotations, the description covers all necessary context: purpose, behavior, error handling, coordinate system, and alternatives. It is sufficiently complete for an agent to select and invoke the tool correctly.

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?

Input schema has 100% coverage, but the description adds value by explaining the return format ({status, annotation_type} or {error, code}) and clarifying that width/height only apply to 'highlight'. This goes beyond the schema descriptions, compensating for the high coverage baseline.

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?

The description clearly states the tool stamps a sticky note or highlight onto an existing PDF page, with specific verb 'stamp' and resource 'page of an existing PDF'. It distinguishes from sibling tools by mentioning manipulate_pdf for page reordering and create_pdf for authoring new PDFs, making the purpose unambiguous.

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

Usage Guidelines5/5

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

The description explicitly states when to use: 'Use this to mark up a document.' It provides clear alternatives: 'To reorder/rotate/overlay whole pages use manipulate_pdf; to author a new PDF use create_pdf.' It also notes that output_path is overwritten, guiding correct invocation.

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

convert_pdfConvert PDF to text representationA
Read-onlyIdempotent

Convert a whole PDF into a text representation for downstream LLM use.

Returns JSON: {content, format} for 'markdown', or {chunks, format} for 'chunks'/'rag' (each chunk carries its index and page_numbers; rag chunks add token_estimate and heading_context). Read-only.

Use this when you need structure or chunking. If you just want the raw reading text use extract_text; for per-run coordinates use extract_entities.

NameRequiredDescriptionDefault
pathYesPath to the PDF file, relative to the configured workspace.
formatYesOutput representation: 'markdown' = one structured Markdown document; 'chunks' = fixed-size token windows; 'rag' = heading-aware semantic chunks for retrieval pipelines.
overlapNoToken overlap carried between consecutive chunks. Applies to format='chunks' only; ignored for 'markdown' and 'rag'.
passwordNoUser password to unlock an encrypted PDF before conversion.
max_tokensNoTarget maximum tokens per chunk. Applies to format='chunks' only; ignored for 'markdown' and 'rag' (rag uses heading-aware semantic chunking with a fixed internal budget).

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint and idempotentHint. The description adds detailed return structure per format (JSON with content/format or chunks with index/page_numbers/heading_context), and confirms read-only nature, exceeding what annotations provide.

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?

The description is concise: two paragraphs. First paragraph covers purpose and return format. Second paragraph provides usage guidelines. No unnecessary words, front-loaded with key information.

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

Completeness5/5

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

Given 5 parameters (2 required), output schema present, and sibling tools, the description covers all essential aspects: purpose, return format, usage guidance, and behavioral notes (read-only). It is complete and useful for an agent.

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% so each parameter is documented. The description does not add additional parameter-level semantics beyond the schema, but it explains how parameter choices affect output format (e.g., overlap applies only to chunks). This is adequate for the baseline.

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?

The description clearly states the tool converts a whole PDF to a text representation for LLM use, specifying verb (convert) and resource (PDF). It distinguishes from siblings like extract_text and extract_entities by mentioning alternative use cases.

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

Usage Guidelines5/5

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

Explicitly states when to use this tool ('when you need structure or chunking') and when to use alternatives ('for raw reading text use extract_text; for per-run coordinates use extract_entities'). Provides clear decision guidance.

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

create_pdfStart a PDF creation sessionA

Open an in-memory PDF building session; the first step of authoring a PDF.

Returns JSON {session_id, status, page_size}. No file is written here — this only allocates a session (with one blank starting page) held in server memory and subject to a TTL. Not idempotent: each call creates a new session.

Workflow: create_pdf → add_pdf_content (text / new pages, repeatable) → save_pdf (writes the file and closes the session). To annotate or fill an existing PDF instead of authoring one, use annotate_pdf or manage_forms.

NameRequiredDescriptionDefault
titleYesDocument title; stored in the PDF metadata on save.
authorNoDocument author; stored in the PDF metadata on save.
page_sizeNoPage size for every page in this document.a4

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

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

Adds significant context beyond annotations: returns session_id/status/page_size, no file written, session in memory with TTL, not idempotent. Annotations already indicate no idempotency, but description adds specific behavioral detail.

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?

Efficient and well-structured: first sentence states purpose, then return info, then workflow and alternatives. No wasted words.

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

Completeness5/5

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

Covers purpose, workflow, return values, behavioral notes (TTL, idempotency), and alternatives. Adequate for a simple session creation tool with good schema and output 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?

Input schema has 100% description coverage for all three parameters, so description adds minimal extra meaning. Schema already defines title, author, page_size clearly.

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?

Clearly states it opens an in-memory PDF building session as the first step of authoring a PDF. Distinguishes from siblings by outlining the workflow and explicitly mentioning alternatives for annotating or filling existing PDFs.

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

Usage Guidelines5/5

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

Provides explicit workflow (create_pdf → add_pdf_content → save_pdf) and warns against using it for annotating/filling existing PDFs, directing to annotate_pdf or manage_forms.

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

extract_entitiesExtract positioned text runsA
Read-onlyIdempotent

Extract every text run of a PDF together with its layout geometry.

Returns JSON {path, entities, entity_count, page_count} where each entity is {text, page (0-based), x, y, font_size, font_name}. Coordinates are in PDF points with the origin at the bottom-left of the page. Read-only.

Use this for layout-aware tasks (table reconstruction, positional lookup, locating a label on the page). If you only need the reading text without coordinates, use extract_text; for Markdown or RAG chunks use convert_pdf.

NameRequiredDescriptionDefault
pathYesPath to the PDF file, relative to the configured workspace.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already provide readOnlyHint=true and idempotentHint=true. The description adds valuable behavioral context: output structure (JSON with fields), coordinate system (PDF points, bottom-left origin), and explicitly states 'Read-only.' No contradiction with annotations.

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?

The description is three sentences: first sentence states purpose, second explains output, third gives usage guidelines. It is concise, front-loaded with key information, and every sentence serves a purpose.

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

Completeness5/5

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

The description explains the output structure in detail (JSON with path, entities, entity_count, page_count, and entity fields). Output schema exists but description covers it sufficiently. For a 1-parameter tool, all necessary context is provided.

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% with a clear parameter description in the schema itself. The tool description does not repeat or add significant value beyond the schema's definition of the 'path' parameter. Baseline 3 is appropriate.

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?

The description clearly states 'Extract every text run of a PDF together with its layout geometry.' It distinguishes from siblings by explicitly naming alternatives: 'use extract_text' for reading text without coordinates, and 'use convert_pdf' for Markdown or RAG chunks.

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

Usage Guidelines5/5

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

The description gives explicit usage context: 'Use this for layout-aware tasks...' and clearly states when not to use it by specifying alternatives, e.g., 'If you only need the reading text without coordinates, use extract_text; for Markdown or RAG chunks use convert_pdf.'

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

extract_textExtract plain textA
Read-onlyIdempotent

Extract the raw, unformatted text of a PDF as a single string.

Returns JSON {text, page_count} (plus page when a specific page was requested). Read-only.

Use this when you want the plain reading text. If you need Markdown structure or chunking for LLM/RAG pipelines use convert_pdf; if you need each text run with its on-page coordinates and font use extract_entities.

NameRequiredDescriptionDefault
pageNo0-based page index to extract. Omit to extract every page joined by newlines. Out-of-range indices return an error.
pathYesPath to the PDF file, relative to the configured workspace.
passwordNoUser password to unlock an encrypted PDF before extraction.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

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

Even though annotations already mark readOnlyHint=true and idempotentHint=true, the description adds further behavioral context. It states the tool is 'read-only' and describes the return JSON structure ({text, page_count} and 'page' when a specific page is requested). It also mentions that out-of-range page indices return an error. This goes beyond the annotations without contradicting them.

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?

The description is exceptionally concise: three sentences. The first sentence states purpose, the second describes return format and readonly nature, and the third provides usage alternatives. No filler or redundant information. Every sentence earns its place, and the structure is front-loaded with the core action.

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

Completeness5/5

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

Given the tool's simplicity (3 parameters, 1 required, simple output schema), the description is complete. It covers return format, error behavior (out-of-range page), and usage context vs. siblings. The output schema exists but the description still explains return fields. All necessary information for correct invocation is present.

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 the baseline is 3. The description adds some extra context beyond the schema: it mentions that 'page' parameter accepts a 0-based index and that out-of-range values return an error (already partly in schema but reinforced). It also implies that 'password' is for encrypted PDFs. However, it does not detail each parameter extensively; the schema already does that well. The added value justifies one point above baseline.

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?

The description clearly states the tool's purpose: 'Extract the raw, unformatted text of a PDF as a single string.' It identifies the verb (extract), resource (raw text of PDF), and output format (a single string in JSON). The title 'Extract plain text' reinforces this. It also distinguishes from siblings by specifying when to use convert_pdf (for Markdown structure) and extract_entities (for coordinates and font). This is specific and differentiates among 12 sibling tools.

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

Usage Guidelines5/5

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

The description explicitly says 'Use this when you want the plain reading text.' and provides clear alternatives: 'if you need Markdown structure or chunking for LLM/RAG pipelines use convert_pdf; if you need each text run with its on-page coordinates and font use extract_entities.' This gives concrete when-to-use and when-not-to-use guidance, making it easy for the agent to select the correct tool.

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

manage_formsManage PDF form fieldsA
Destructive

Create, fill, read or validate PDF form fields.

Returns JSON per operation: create→{status, fields_created}; fill→{status, fields_filled}; read→{path, fields, page_count}; validate→{valid, fields}. 'create'/'fill' write output_path (overwriting); 'read'/'validate' are read-only computations.

Honest limitations: 'fill' lays the values into a new overlay at computed positions rather than mapping them onto the original AcroForm widgets; 'read' returns the page's text runs (not declared AcroForm field objects); 'validate' currently enforces only a non-empty (required) rule per value. For page-structure edits use manipulate_pdf; to read prose use extract_text.

NameRequiredDescriptionDefault
fieldsNoField definitions for 'create'. Each: {name, type:'text', x, y, width, height (points), default_value?}.
valuesNoMap of field name to value. Required for 'fill' and 'validate'.
operationYes'create' a new PDF with text fields; 'fill' values onto a copy of an existing PDF; 'read' the text content of a form; 'validate' supplied values. 'read' and 'validate' do not write a file.
input_pathNoSource PDF. Required for 'fill', 'read' and 'validate'; unused for 'create'.
output_pathNoDestination .pdf path (overwritten if present). Required for 'create' and 'fill'; unused for 'read'/'validate'.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

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

The description honestly discloses limitations: 'fill' creates a new overlay rather than mapping onto original AcroForm widgets, 'read' returns text runs not declared AcroForm field objects, and 'validate' only enforces non-empty rule. Annotations already indicate destructiveHint=true, but the description adds valuable context beyond annotations.

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?

The description is well-structured with a clear overview, return types listed by operation, behavioral details, limitations, and sibling references. Every sentence is informative and earns its place. It is concise yet complete.

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

Completeness5/5

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

Given the tool's complexity (4 operations, 5 parameters, output schema, annotations), the description covers all essential aspects: return values per operation, side effects (overwriting), limitations, and usage context. No gaps remain for an AI agent to misuse the tool.

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 coverage is 100%, so baseline is 3. However, the description adds meaningful context: it explains which parameters are required per operation and provides detailed format for 'fields' (e.g., {name, type:'text', x, y, width, height, default_value?}). This goes beyond the schema's minimal descriptions.

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?

The description explicitly states 'Create, fill, read or validate PDF form fields,' clearly identifying the verb (multiple operations) and resource (PDF form fields). It distinguishes from sibling tools like manipulate_pdf and extract_text in the last sentence.

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

Usage Guidelines5/5

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

The description provides explicit guidance on when to use which operation (e.g., 'For page-structure edits use manipulate_pdf; to read prose use extract_text') and notes that 'create'/'fill' write and 'read'/'validate' are read-only. It also explains required parameters for each operation.

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

manipulate_pdfRestructure PDF pagesA
Destructive

Restructure the pages of existing PDF file(s) and write a new PDF.

Each operation writes to output_path (overwriting any existing file) and returns JSON {status, operation}; on a missing required argument it returns {error, code}. Per-operation requirements: merge→input_paths; rotate→degrees; extract_pages→page_indices; overlay→overlay_path; split→output_path is a directory. Page indices are 0-based.

Use this for page-level structure. To stamp notes/highlights use annotate_pdf; to fill form fields use manage_forms; to encrypt use secure_pdf.

NameRequiredDescriptionDefault
degreesNoClockwise rotation in degrees (e.g. 90, 180, 270). Required for 'rotate'.
operationYesPage operation: 'split' one PDF into per-page files; 'merge' several PDFs into one; 'rotate' all pages; 'extract_pages' a subset; 'reverse' page order; 'overlay' one PDF on top of another.
input_pathNoSource PDF. Required for every operation except 'merge' (which uses input_paths).
input_pathsNoOrdered list of PDFs to combine. Required for 'merge'.
output_pathNoOutput location, overwritten if it exists. For 'split' this is an existing directory; for all other operations a .pdf file.
overlay_pathNoPDF stamped on top of input_path. Required for 'overlay'.
page_indicesNo0-based page indices to keep, in order. Required for 'extract_pages'.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

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

Annotations indicate destructiveHint=true and readOnlyHint=false. The description reinforces this by stating it 'overwrites any existing file' and describes return JSON for success and error cases. It adds specific behavioral details beyond annotations, such as the return format and overwrite behavior.

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?

The description is concise (6 sentences), well-structured, and front-loaded with purpose. Each sentence earns its place: purpose, return format, per-operation requirements, sibling links. No fluff.

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

Completeness5/5

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

Given the tool's complexity (6 operations, 7 parameters), the description covers all operations and required parameters. It mentions return format, and with an output schema present (context signals), further details are not needed. It is fully adequate.

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 every parameter already has a description. The tool description adds value by grouping parameters per operation in a compact summary, which aids quick understanding of dependencies. This goes beyond the schema by providing operational context.

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?

The description clearly states that the tool restructures PDF pages and writes new PDFs, listing six specific operations. It distinguishes itself from siblings by explicitly saying 'Use this for page-level structure' and pointing to alternatives for other tasks (annotate_pdf, manage_forms, secure_pdf).

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

Usage Guidelines5/5

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

The description provides explicit when-to-use guidance ('page-level structure') and when-not-to-use with sibling references. It also lists per-operation required parameters (e.g., 'merge→input_paths'), helping the agent decide which parameters are needed for each operation.

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

read_pdfRead PDF metadataA
Read-onlyIdempotent

Read a single PDF's document-level metadata without parsing its content.

Returns a JSON object with: page_count, is_encrypted, version, title, author, subject, keywords, and (when include_page_details=true) a pages array of {index, width, height, rotation}. Read-only: never modifies the file.

Use this to inspect what a PDF is before deciding how to process it. For structural validation, corruption/PDF-A checks, or comparing two files use analyze_pdf instead; for the actual text use extract_text. Encrypted files without a password return {is_encrypted, locked, message} rather than metadata.

NameRequiredDescriptionDefault
pathYesPath to the PDF file, relative to the configured workspace.
passwordNoUser password to unlock an encrypted PDF. Omit for unencrypted files; if omitted on an encrypted file the tool reports it as locked instead of failing.
include_page_detailsNoWhen true, also return per-page width, height (in PDF points) and rotation. Off by default to keep the response small.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already declare readOnlyHint and idempotentHint; description reinforces read-only nature, details return object structure, and explains behavior for encrypted files without password. No contradiction.

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?

Five sentences with clear structure: purpose, return format, read-only emphasis, use cases with alternatives, and edge case handling. No unnecessary words.

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

Completeness5/5

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

Covers tool purpose, return data, read-only behavior, usage guidance with alternatives, and edge cases. Output schema exists; description complements it fully. No gaps.

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 coverage is 100%, but description adds context: explains include_page_details reduces response size and how password parameter affects output for encrypted files. Adds value beyond schema.

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?

Description clearly states 'Read a single PDF's document-level metadata' with specific verb and resource, distinguishes from sibling tools like analyze_pdf and extract_text, and lists exact return fields.

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

Usage Guidelines5/5

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

Explicitly states when to use ('inspect what a PDF is before deciding how to process it') and when not to (use analyze_pdf for structural validation, extract_text for actual text). Also covers edge case of encrypted files.

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

save_pdfSave and close a PDF sessionA
Destructive

Render an open create_pdf session to a PDF file (step 3 of 3, terminal).

Builds a Document from the session's accumulated pages, writes it to output_path (overwriting any existing file), then deletes the session — so the session_id is no longer usable afterwards. Returns JSON {status, path, page_count}, or {error, code} if the session is missing.

Only finalizes sessions created via create_pdf/add_pdf_content. To encrypt an already-saved PDF use secure_pdf; to add annotations use annotate_pdf.

NameRequiredDescriptionDefault
session_idYesSession id returned by create_pdf to finalize.
output_pathYesDestination .pdf path inside the workspace. An existing file at this path is overwritten.
user_passwordNoIf set together with owner_password, the saved PDF is encrypted; this is the password required to open it.
owner_passwordNoOwner/permissions password. Encryption is applied only when both user_password and owner_password are provided.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.9/5.0
Behavior5/5

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

The description discloses key behaviors beyond annotations: overwrites existing file, deletes the session making session_id unusable, and returns JSON {status, path, page_count} or error. Annotations already indicate destructiveHint=true, but the description adds specific details. No contradiction with annotations.

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?

The description is five well-structured sentences. It front-loads the core action, then explains behavior, return format, constraints, and alternatives. No redundant or extraneous information.

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

Completeness5/5

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

Given the tool's complexity and available annotations, the description covers all essential aspects: purpose, lifecycle (step 3 of 3), behavior, return values, error handling, prerequisites, and alternatives. It is complete for an agent to understand when and how to invoke the tool.

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 coverage is 100%, so baseline is 3. The description adds contextual meaning: explains that session_id must come from create_pdf/add_pdf_content, that output_path can overwrite, and that encryption only applies when both passwords are provided. This adds value beyond the schema descriptions.

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?

The description clearly states the tool's purpose: 'Render an open create_pdf session to a PDF file (step 3 of 3, terminal).' It specifies the verb (render/save), resource (PDF session), and its terminal nature. It distinguishes from siblings by noting that only sessions from create_pdf/add_pdf_content are finalized, and mentions alternative tools for encryption (secure_pdf) and annotations (annotate_pdf).

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

Usage Guidelines5/5

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

Explicit usage guidelines are provided: 'Only finalizes sessions created via create_pdf/add_pdf_content.' It also gives when-not-to-use: 'To encrypt an already-saved PDF use secure_pdf; to add annotations use annotate_pdf.' This clearly directs the agent to alternatives and conditions.

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

secure_pdfEncrypt / inspect PDF securityA
Destructive

Encrypt a PDF, report its encryption status, or verify its signatures.

Returns JSON per operation: encrypt→{status, operation, page_count, note}; permissions→{path, is_encrypted, unlocked, permissions}; verify_signatures→ {path, signatures, signature_count}. 'permissions' and 'verify_signatures' are read-only; 'encrypt' writes output_path (overwriting).

Caveat: 'encrypt' rebuilds the document from its text, preserving content and layout but possibly dropping images, embedded fonts and vector graphics (the current API has no in-place encryption). To encrypt a PDF you are authoring, pass the passwords to save_pdf instead.

NameRequiredDescriptionDefault
passwordNoPassword used to unlock the file when checking 'permissions' on an encrypted PDF. Unused by other operations.
operationYes'encrypt' writes a password-protected copy; 'permissions' reports encryption status; 'verify_signatures' checks digital signatures. Only 'encrypt' writes a file.
input_pathNoSource PDF. Required for all three operations.
output_pathNoDestination .pdf for the encrypted copy (overwritten if present). Required for 'encrypt'; unused otherwise.
user_passwordNoOpen password for the encrypted copy. Required for 'encrypt'.
owner_passwordNoOwner/permissions password. Required for 'encrypt'.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and readOnlyHint=false. Description adds detailed behavioral context: encrypt overwrites output_path, rebuilds document and may drop images/embedded fonts/vector graphics. No contradiction with annotations.

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?

Description is somewhat lengthy but well-structured with clear sections. Front-loaded with operation summary, then output details, then caveats. Slightly verbose but all sentences add value.

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

Completeness5/5

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

Tool has 6 parameters, output schema exists describing return JSON per operation. Description covers output structure, caveats, and usage alternatives. Complete and actionable for an agent.

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?

With 100% schema coverage, baseline is 3. Description adds value by explaining which parameters are required for each operation, and the overwriting behavior of output_path. Goes beyond schema descriptions.

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?

Description clearly states three distinct operations (encrypt, report encryption status, verify signatures) with specific verbs and resources, and distinguishes from sibling tool save_pdf for authoring use cases.

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

Usage Guidelines5/5

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

Explicitly says when to use each operation, provides caveats about image/font loss for encrypt, and recommends alternative save_pdf for authoring PDFs. Full guidance on when-not-to-use.

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. 1 tool update
    • Changedconvert_pdf1 field changed
      • changedInput schema / properties / max_tokens / description
        Previous value: -"Target maximum tokens per chunk. Applies to format='chunks' and 'rag' only; ignored for 'markdown'."New value: +"Target maximum tokens per chunk. Applies to format='chunks' only; ignored for 'markdown' and 'rag' (rag uses heading-aware semantic chunking with a fixed internal budget)."
  2. 12 tool updatesv0.12.0
    • Changedadd_pdf_content8 fields changed
      • addedInput schema / properties / content / description
        Added value: +"Text to draw. Required when content_type='text'."
      • addedInput schema / properties / content_type / description
        Added value: +"'text' draws a text string at (x, y) on the current page; 'new_page' appends a blank page and makes it current."
      • addedInput schema / properties / content_type / enum
        Added value: +[
        +  "text",
        +  "new_page"
        +]
      • addedInput schema / properties / font / description
        Added value: +"Font name (e.g. 'Helvetica', 'Courier', 'Times-Roman'). Defaults to Helvetica when omitted."
      • addedInput schema / properties / font_size / description
        Added value: +"Font size in points for text content."
      • addedInput schema / properties / session_id / description
        Added value: +"Session id returned by create_pdf. Must be active."
      • addedInput schema / properties / x / description
        Added value: +"Horizontal position in PDF points from the left edge. Required when content_type='text'."
      • addedInput schema / properties / y / description
        Added value: +"Vertical position in PDF points from the bottom edge (origin is bottom-left). Required when content_type='text'."
    • Changedanalyze_pdf6 fields changed
      • addedInput schema / properties / check / description
        Added value: +"Which analysis to run: 'validate' = structural validity with error/warning counts; 'corruption' = damage severity and type; 'compliance' = PDF/A conformance at compliance_level; 'compare' = diff against compare_path."
      • addedInput schema / properties / check / enum
        Added value: +[
        +  "validate",
        +  "corruption",
        +  "compliance",
        +  "compare"
        +]
      • addedInput schema / properties / compare_path / description
        Added value: +"Second PDF to diff against. Required when check='compare', ignored otherwise."
      • addedInput schema / properties / compliance_level / description
        Added value: +"PDF/A conformance level to test. Used only when check='compliance'. Letter = conformance class (a/b/u), number = PDF/A part (1/2/3)."
      • addedInput schema / properties / compliance_level / enum
        Added value: +[
        +  "a1a",
        +  "a1b",
        +  "a2a",
        +  "a2b",
        +  "a2u",
        +  "a3a",
        +  "a3b",
        +  "a3u"
        +]
      • addedInput schema / properties / path / description
        Added value: +"Path to the PDF file to analyze, relative to the workspace."
    • Changedannotate_pdf10 fields changed
      • addedInput schema / properties / annotation_type / description
        Added value: +"'text' adds a sticky-note marker at (x, y); 'highlight' draws a highlight rectangle of width×height anchored at (x, y)."
      • addedInput schema / properties / annotation_type / enum
        Added value: +[
        +  "text",
        +  "highlight"
        +]
      • addedInput schema / properties / contents / description
        Added value: +"Note text for a 'text' annotation. Ignored for 'highlight'."
      • addedInput schema / properties / height / description
        Added value: +"Highlight height in points. Used only for 'highlight'."
      • addedInput schema / properties / input_path / description
        Added value: +"Source PDF to annotate, relative to the workspace."
      • addedInput schema / properties / output_path / description
        Added value: +"Destination .pdf path; overwritten if it already exists."
      • addedInput schema / properties / page / description
        Added value: +"0-based index of the page to annotate."
      • addedInput schema / properties / width / description
        Added value: +"Highlight width in points. Used only for 'highlight'."
      • addedInput schema / properties / x / description
        Added value: +"Horizontal anchor in PDF points from the left edge."
      • addedInput schema / properties / y / description
        Added value: +"Vertical anchor in PDF points from the bottom edge (origin is bottom-left)."
    • Changedconvert_pdf5 fields changed
      • addedInput schema / properties / format / description
        Added value: +"Output representation: 'markdown' = one structured Markdown document; 'chunks' = fixed-size token windows; 'rag' = heading-aware semantic chunks for retrieval pipelines."
      • addedInput schema / properties / max_tokens / description
        Added value: +"Target maximum tokens per chunk. Applies to format='chunks' and 'rag' only; ignored for 'markdown'."
      • addedInput schema / properties / overlap / description
        Added value: +"Token overlap carried between consecutive chunks. Applies to format='chunks' only; ignored for 'markdown' and 'rag'."
      • addedInput schema / properties / password / description
        Added value: +"User password to unlock an encrypted PDF before conversion."
      • addedInput schema / properties / path / description
        Added value: +"Path to the PDF file, relative to the configured workspace."
    • Changedcreate_pdf4 fields changed
      • addedInput schema / properties / author / description
        Added value: +"Document author; stored in the PDF metadata on save."
      • addedInput schema / properties / page_size / description
        Added value: +"Page size for every page in this document."
      • addedInput schema / properties / page_size / enum
        Added value: +[
        +  "a4",
        +  "a4_landscape",
        +  "letter",
        +  "letter_landscape",
        +  "legal",
        +  "legal_landscape"
        +]
      • addedInput schema / properties / title / description
        Added value: +"Document title; stored in the PDF metadata on save."
    • Changedextract_entities1 field changed
      • addedInput schema / properties / path / description
        Added value: +"Path to the PDF file, relative to the configured workspace."
    • Changedextract_text3 fields changed
      • addedInput schema / properties / page / description
        Added value: +"0-based page index to extract. Omit to extract every page joined by newlines. Out-of-range indices return an error."
      • addedInput schema / properties / password / description
        Added value: +"User password to unlock an encrypted PDF before extraction."
      • addedInput schema / properties / path / description
        Added value: +"Path to the PDF file, relative to the configured workspace."
    • Changedmanage_forms6 fields changed
      • addedInput schema / properties / fields / description
        Added value: +"Field definitions for 'create'. Each: {name, type:'text', x, y, width, height (points), default_value?}."
      • addedInput schema / properties / input_path / description
        Added value: +"Source PDF. Required for 'fill', 'read' and 'validate'; unused for 'create'."
      • addedInput schema / properties / operation / description
        Added value: +"'create' a new PDF with text fields; 'fill' values onto a copy of an existing PDF; 'read' the text content of a form; 'validate' supplied values. 'read' and 'validate' do not write a file."
      • addedInput schema / properties / operation / enum
        Added value: +[
        +  "create",
        +  "fill",
        +  "read",
        +  "validate"
        +]
      • addedInput schema / properties / output_path / description
        Added value: +"Destination .pdf path (overwritten if present). Required for 'create' and 'fill'; unused for 'read'/'validate'."
      • addedInput schema / properties / values / description
        Added value: +"Map of field name to value. Required for 'fill' and 'validate'."
    • Changedmanipulate_pdf8 fields changed
      • addedInput schema / properties / degrees / description
        Added value: +"Clockwise rotation in degrees (e.g. 90, 180, 270). Required for 'rotate'."
      • addedInput schema / properties / input_path / description
        Added value: +"Source PDF. Required for every operation except 'merge' (which uses input_paths)."
      • addedInput schema / properties / input_paths / description
        Added value: +"Ordered list of PDFs to combine. Required for 'merge'."
      • addedInput schema / properties / operation / description
        Added value: +"Page operation: 'split' one PDF into per-page files; 'merge' several PDFs into one; 'rotate' all pages; 'extract_pages' a subset; 'reverse' page order; 'overlay' one PDF on top of another."
      • addedInput schema / properties / operation / enum
        Added value: +[
        +  "split",
        +  "merge",
        +  "rotate",
        +  "extract_pages",
        +  "reverse",
        +  "overlay"
        +]
      • addedInput schema / properties / output_path / description
        Added value: +"Output location, overwritten if it exists. For 'split' this is an existing directory; for all other operations a .pdf file."
      • addedInput schema / properties / overlay_path / description
        Added value: +"PDF stamped on top of input_path. Required for 'overlay'."
      • addedInput schema / properties / page_indices / description
        Added value: +"0-based page indices to keep, in order. Required for 'extract_pages'."
    • Changedread_pdf3 fields changed
      • addedInput schema / properties / include_page_details / description
        Added value: +"When true, also return per-page width, height (in PDF points) and rotation. Off by default to keep the response small."
      • addedInput schema / properties / password / description
        Added value: +"User password to unlock an encrypted PDF. Omit for unencrypted files; if omitted on an encrypted file the tool reports it as locked instead of failing."
      • addedInput schema / properties / path / description
        Added value: +"Path to the PDF file, relative to the configured workspace."
    • Changedsave_pdf4 fields changed
      • addedInput schema / properties / output_path / description
        Added value: +"Destination .pdf path inside the workspace. An existing file at this path is overwritten."
      • addedInput schema / properties / owner_password / description
        Added value: +"Owner/permissions password. Encryption is applied only when both user_password and owner_password are provided."
      • addedInput schema / properties / session_id / description
        Added value: +"Session id returned by create_pdf to finalize."
      • addedInput schema / properties / user_password / description
        Added value: +"If set together with owner_password, the saved PDF is encrypted; this is the password required to open it."
    • Changedsecure_pdf7 fields changed
      • addedInput schema / properties / input_path / description
        Added value: +"Source PDF. Required for all three operations."
      • addedInput schema / properties / operation / description
        Added value: +"'encrypt' writes a password-protected copy; 'permissions' reports encryption status; 'verify_signatures' checks digital signatures. Only 'encrypt' writes a file."
      • addedInput schema / properties / operation / enum
        Added value: +[
        +  "encrypt",
        +  "permissions",
        +  "verify_signatures"
        +]
      • addedInput schema / properties / output_path / description
        Added value: +"Destination .pdf for the encrypted copy (overwritten if present). Required for 'encrypt'; unused otherwise."
      • addedInput schema / properties / owner_password / description
        Added value: +"Owner/permissions password. Required for 'encrypt'."
      • addedInput schema / properties / password / description
        Added value: +"Password used to unlock the file when checking 'permissions' on an encrypted PDF. Unused by other operations."
      • addedInput schema / properties / user_password / description
        Added value: +"Open password for the encrypted copy. Required for 'encrypt'."

TDQS

A4.7/5.0

Scored across 12 tools

Disambiguation5/5

Each tool targets a distinct operation (creation workflow, metadata, text extraction, layout analysis, annotation, form management, page manipulation, security, conversion, structural analysis). Descriptions clearly differentiate overlapping areas like extract_text vs convert_pdf vs extract_entities.

Naming Consistency5/5

All 12 tools follow a consistent verb_noun snake_case pattern (e.g., create_pdf, extract_text, secure_pdf). No mixed conventions or ambiguous names.

Tool Count5/5

12 tools cover the full PDF lifecycle without bloat. Each tool has a clear purpose and the count is well-scoped for a PDF manipulation server.

Completeness4/5

The surface covers creation, reading, editing, conversion, security, and analysis. Minor gaps like lacking an explicit page deletion tool (handled via manipulate_pdf's extract_pages) or OCR, but core workflows are complete.

Maintenance

ActivityNo data
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers