Other formats agents might prefer:
markdownjsonllms.txt

Agent? You probably want markdown or json, or Pod over MCP.

Reported issues for pdf-mcp

Pod holds 12 of 12 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to pdf-mcp.

Most discussed

add support for reading simple charts with PlotDigitizer

https://apps.automeris.io/wpd4/ something like this automatically?

if this is possible, I will package it into nixpkags (making it available for all NixOS).

Read the thread · 2026-07-13 · closed · external user · 10 comments

ocr_lang is not part of the page-text cache key: first OCR language wins permanently

Summary

ocr_lang is not part of the page-text cache key, so the first language used to OCR a page wins permanently. Any later pdf_read_pages(..., ocr=True, ocr_lang=<other>) on that page silently returns the previous language's text — instantly, labeled source: "ocr", with no error or warning.

This is easy to misdiagnose as "the Tesseract language pack isn't installed" or "language X doesn't work", because the failure is indistinguishable from a bad OCR result.

Reproduction

Read the thread · 2026-08-03 · closed · external user · 6 comments

ocr_lang cache key is not normalized: case and spelling variants create distinct rows

Follow-up to #25, from @deepdmk's verification of the 2.1.0 fix (comment).

ocr_lang goes verbatim from the tool argument into both the page_text.ocr_lang column and Tesseract's -l flag. Nothing lowercases, strips, or otherwise normalizes it in server.py or extractor.py, and _is_ocr_cache_hit compares it with exact string equality. Different spellings of one language set are therefore different cache keys.

Measure

Read the thread · 2026-08-10 · closed · 3 comments

Keyword search ranking depends on unrelated cached PDFs (FTS5 bm25 uses table-wide IDF)

Summary

Keyword-mode pdf_search ranks the pages of a single PDF, but the BM25 score is computed over the entire shared FTS table. So a PDF's page ranking depends on every other document the user happens to have cached — the same query on the same PDF can return a different page order depending on unrelated cached PDFs. Intra-document ranking should arguably be stable regardless of what else is cached.

Where

PDFCache.search_fts (src/pdf_mcp/cache.py):

SELECT ..., -bm2

[Read the thread](https://github.com/jztan/pdf-mcp/issues/17) · 2026-06-24 · closed · 2 comments

### URL download fails TLS verification on relative HTTP redirects (IP-pinning drops hostname)

## Summary

`URLFetcher.fetch()` fails with a TLS certificate error when a download
follows a **relative** HTTP redirect. The IP-pinning hardening rewrites each
request to the resolved IP literal, but on a relative `Location` redirect the
hostname is lost, so the next hop verifies the server certificate against the
IP address instead of the hostname.

This breaks cold-cache downloads of any URL that issues a relative redirect
(arXiv does this today). It is masked everywhere by the download cache

[Read the thread](https://github.com/jztan/pdf-mcp/issues/16) · 2026-06-24 · closed · 1 comment

### Use per-user cache directory for URL downloads

## Summary

`URLFetcher` defaults downloaded PDF cache files to `tempfile.gettempdir() / "pdf-mcp" / "downloads"`, which is usually `/tmp/pdf-mcp/downloads` on Linux/macOS. Because the directory is created with private permissions (`chmod 0o700`), the first local user to start pdf-mcp can make the shared `/tmp/pdf-mcp` path inaccessible to other local users.

This can cause startup or runtime failures on shared machines, CI runners, or multi-user development boxes.

## Current Behavior

In `src/

[Read the thread](https://github.com/jztan/pdf-mcp/issues/15) · 2026-06-24 · closed · external user · 1 comment

### Suppress PyMuPDF swigvarlink DeprecationWarning

## Problem

PyMuPDF's SWIG bindings emit a `DeprecationWarning` on import and cleanup:

:0: DeprecationWarning: builtin type swigvarlink has no module attribute


This is a known upstream PyMuPDF/SWIG issue. The warning leaks through to any tool that uses `pdf-mcp` as an MCP server, creating noisy output for end users.

## Suggested Fix

Add a targeted warning filter before `pymupdf` is imported:

```python
import warnings
warnings.filterwarnings("ignore", category=DeprecationWar

[Read the thread](https://github.com/jztan/pdf-mcp/issues/6) · 2026-03-14 · closed · 1 comment

### PDF validation can be bypassed when URL ends with .pdf

## Description

In `src/pdf_mcp/url_fetcher.py`, the PDF validation logic skips the magic-bytes (`%PDF`) check if the original URL ends with `.pdf`, even if the response `Content-Type` is non-PDF (e.g. `text/html`). This means a `.pdf` URL returning HTML content would be cached and processed as if it were a valid PDF.

## Suggested Fix

- Base the extension check on the final URL after redirects (`current_url`/`response.url`) rather than the original URL
- Always enforce the `%PDF` magic-bytes c

[Read the thread](https://github.com/jztan/pdf-mcp/issues/3) · 2026-02-24 · closed · 1 comment

## Most recent

### pdf_extract_chart splits dashed curves when dash lengths are below 1

## Summary

`pdf_extract_chart` can split a single dashed curve into several phantom
series when the chart uses dash lengths below 1.

## Cause

`_dash_key()` in `src/pdf_mcp/chart_extractor.py` parses the dash pattern with:

```python
re.findall(r"-?\d+(?:\.\d+)?", str(raw))

That requires a digit before the decimal point. PyMuPDF writes dash lengths below 1 without a leading zero, so real output looks like:

[ 1.0834783 .4685312 ] 0

The pattern cannot match .4685312 as a whole,

Read the thread · 2026-08-22 · closed · 0 comments

Hung OCR worker still hangs: in-parent timeout fallback has no timeout

Summary

run_pages() in parallel.py (reworked in #18) adds as_completed(timeout=) to stop a hung worker from blocking forever. But the timeout only bounds the wait on the pool — the fallback then re-runs each incomplete page in the parent process with no timeout, so a page that genuinely hangs inside Tesseract's native binding still produces an unbounded hang.

This is not a regression — pre-#18 pool.map() also hung forever on a stuck worker. It's that the #18 fix doesn't fu

Read the thread · 2026-07-06 · closed · 0 comments

URL fetcher rejects valid password-protected PDFs as "truncated"

Summary

URLFetcher._validate_pdf_content() (added in #18) false-rejects valid password-protected PDFs, failing with a misleading "zero pages — likely a truncated file" error. Pre-#18 these PDFs were accepted (magic-byte check only), so this is a regression introduced by the PDF-validation change.

Impact is narrow — only URL-fetched, encrypted PDFs — which is why it was split out of #18 rather than blocking it.

Root cause

_validate_pdf_content() checks page count before the encrypti

Read the thread · 2026-07-06 · closed · 0 comments

Your MCP server is graded 🟢 SAFE on Agent Skills Hub

Hi! I run Agent Skills Hub, an open directory that security-grades & quality-scores MCP servers and agent skills. {repo} came through 🟢 SAFE — nice work. If useful, you can show the grade in your README:

Security-graded by Agent Skills Hub

It renders a 🟢 SAFE badge linking to your skill's page (grade + quality breakdown). No strings — just a trust signal fo

Read the thread · 2026-06-23 · closed · external user · 0 comments

The remaining reports are on the project's issue tracker.