
oxidize-pdf MCP Server
io.github.bzsanti由 GitHub 账号 bzsanti 发布v0.23.0更新于 Oct 26, 2026
MCP server for PDF generation, parsing, splitting, merging, and manipulation via oxidize-pdf
概览
本地 MCP 服务器,让助手在沙箱工作目录内读取、提取、创建、编辑、合并、拆分、加密和分析 PDF 文件。
- 可以做什么
- 读取 PDF 元数据、页数、版本与加密状态,并按页或整篇提取文本
- 将 PDF 转换为 markdown、分块或面向 RAG 的格式,并抽取结构化实体
- 创建新 PDF,添加页面、文字、图形与注释,并把会话保存到磁盘
- 拆分、合并、旋转、抽取、反转与叠加页面,并管理表单字段
- 加密文档、检查权限、验证签名,并校验结构、损坏情况与 PDF/A 合规性
- 浏览字体、页面尺寸、能力、版本与工作区文件等内置资源,并使用引导式提示
- 适用场景
- 适合需要在本机处理 PDF 的场景:总结或提取文本、为检索转换文档、拼装或拆分报告、填写表单,或检查文件结构与合规性。
- 运行要求
- 以 Python 包形式通过 stdio 在本地运行(需 Python 3.10+,用 pip 或 uvx 安装并带 mcp 附加组件),支持 Linux、macOS 与 Windows。无需账号或 API 密钥。通过环境变量配置,例如 OXIDIZE_WORKSPACE 设定沙箱根目录,所有路径必须位于其中。
安装
在 SourceWeft 中
- 打开 控制台中的 oxidize-pdf MCP Server,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
oxidize-pdf
Version 0.23.3 pins Rust core 5.5.5. See compatibility and validation.
[PyPI version] [CI] [License: MIT] [Python] [Typed] [MCP]
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
Platforms: Linux (x86_64, aarch64) | macOS (x86_64, Apple Silicon) | Windows (x86_64) Requires: Python 3.10+
Version 0.23.3 pins Rust core 5.5.5, using the MIT-licensed oxidize-webpki
RustCrypto provider for certificate verification. See the
dependency policy for supported builds and limits.
0.23.3 release notes describe the core fixes and font-aware text chunks.
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.
Why oxidize-pdf?
MCP Server
Expose the supported PDF tools to your AI agent:
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:
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:
GitHub Copilot (VS Code) integration
Copilot's agent mode speaks MCP. Add .vscode/mcp.json to your workspace:
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:
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
The server also exposes resources (session data, capabilities, version info) and prompts (guided workflows for summarization, data extraction, form filling, and more).
Configuration
The server is configured entirely through environment variables:
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:
Python API
Create a PDF
Parse an existing PDF
Inspect tagged PDF structure
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
Graphics
Types
Error handling
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:
Claude Desktop
Add this to your claude_desktop_config.json:
Claude Code
Available tools
Resources
oxidize://fonts— Available built-in PDF fontsoxidize://page-sizes— Standard page sizes with dimensionsoxidize://capabilities— Server capabilities and tool listingoxidize://version— Version informationoxidize://workspace— PDF files in the workspace directoryoxidize://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_pdfextracts each embedded image as-is (e.g. aDCTDecodeJPEG is written byte-for-byte). Image preprocessing — auto rotation-correction, contrast enhancement, denoise, upscaling, force-grayscale — is not available, because the build excludes the upstreamexternal-imagesfeature (and itsimage-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
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_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.
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.
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
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.
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.
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.
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.
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.
来源:README.md,提交 fb72b43
工具
0版本历史
5- v0.23.0最新Oct 26, 2026
- v0.21.0Oct 9, 2026
- v0.20.0Sep 26, 2026
- v0.19.0Sep 25, 2026
- v0.17.0Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分