Skip to main content
Glama
haoxinm

pdf-chart-parser

by haoxinm
README.md
# pdf-chart-parser

An MCP server and Python library that extracts energy-usage charts from utility-bill PDFs. It locates the chart, calibrates the axes from the PDF's text layer, and returns structured time-series data alongside an annotated PNG for visual verification — entirely deterministic, no LLM required.

## Features

- **Bar, line, and hybrid (bar + line, dual y-axis)** chart types
- **Vector-first extraction** via PyMuPDF `get_drawings()` / `get_text("dict")`; OpenCV raster fallback for scanned PDFs
- **Scanned-PDF support** via an OCRmyPDF text-layer step (optional `[ocr]` extra): image-only pages get a searchable text layer so they flow through the same high-accuracy text-layer calibration as digital PDFs
- **Full page text** returned as LLM-friendly Markdown (via `pymupdf4llm`)
- **MCP tool** (`extract_usage_chart`) compatible with Claude and other MCP-aware LLMs
- Supports `stdio` transport (local) and `streamable-http` (containerized deployment)
- Returns structured JSON + annotated PNG; numeric data is always text content

## Installation

### Prerequisites

- Python 3.12+
- [`uv`](https://github.com/astral-sh/uv) package manager
- **Tesseract OCR** (required only for the `[raster]` extra): `apt-get install tesseract-ocr` or `brew install tesseract`
- **OCRmyPDF system tools** (required only for the `[ocr]` extra, which adds a searchable text layer to scanned PDFs): `apt-get install ghostscript qpdf unpaper pngquant tesseract-ocr` or `brew install ocrmypdf`

### Quickstart

```bash
# Install (vector path only)
uv sync

# Install with raster fallback
uv sync --extra raster

# Install with the OCR text-layer step for scanned PDFs
uv sync --extra ocr

# Install everything
uv sync --extra raster --extra ocr

# Run the CLI
uv run pdf-chart-parser --help

# Run the MCP server (stdio)
uv run python -m pdf_chart_parser.server
```

## Usage

### Python library

```python
from pdf_chart_parser.pipeline import extract_usage_chart

result = extract_usage_chart(pdf_path="bill.pdf", return_annotated_image=True)
print(result["chart_type"])   # "bar" | "line" | "hybrid"
for series in result["series"]:
    print(series["label"], series["points"])
```

### CLI

```bash
uv run pdf-chart-parser extract bill.pdf --output result.json
```

### MCP server

Add to your MCP config (`~/.claude/claude_desktop_config.json` or similar):

```json
{
  "mcpServers": {
    "pdf-chart-parser": {
      "command": "uv",
      "args": ["run", "python", "-m", "pdf_chart_parser.server"],
      "cwd": "/path/to/pdf-chart-parser"
    }
  }
}
```

The server exposes the `extract_usage_chart` tool. It returns:
1. **Page text** — full page as Markdown
2. **Chart reading** — structured JSON (series, axes, confidence, warnings)
3. **Annotated PNG** — cropped chart with calibrated gridlines and data-point markers

## Docker / ECR deployment

```bash
# Build and run locally
./scripts/run_local_server.sh

# Build and push to ECR (set ECR_REPO first)
export ECR_REPO=<account>.dkr.ecr.<region>.amazonaws.com/pdf-chart-parser
./scripts/build_and_push.sh
```

The container starts the server on `streamable-http` at port 8000. A local PDF directory can be bind-mounted to `/data` for ad-hoc testing (see `docker/docker-compose.yml`).

## Manual testing (no LLM)

```bash
# In-process test against fixtures
uv run python scripts/run_manual_tests.py

# Against a running HTTP server
uv run python scripts/run_manual_tests.py --http http://localhost:8000
```

Output is written to `manual_test_output/`.

## License

[AGPL-3.0-or-later](LICENSE). This license is required because the project links against [PyMuPDF](https://pymupdf.readthedocs.io/), which is itself AGPL-3.0 licensed.

TDQS

A4.4/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is zero ambiguity. An agent cannot confuse it with any other tool in the set.

Naming Consistency5/5

With a single tool, naming consistency is trivially maintained. The name 'extract_usage_chart' follows a clear verb_noun pattern.

Tool Count5/5

A single tool is appropriate for the narrow, focused domain of extracting chart data from utility bill PDFs. Adding more tools would likely be unnecessary.

Completeness5/5

For its stated purpose of extracting energy-usage chart data, the tool provides text, structured JSON, and optional annotated images. There are no obvious gaps in the surface for this specific task.

Maintenance

ActivitySlowing
ResponsivenessNo issues