Skip to main content
Glama
skaosqkf0-del

file-analyzer-mcp

README.md
<p align="center">
  <img src="./assets/readme/hero.svg" width="100%" alt="file-analyzer-mcp — personal read-only MCP server. Point it at a folder and pdf, docx, pptx, svg, png come back as text, tables, and images.">
</p>

## Proof

Point it at a folder and it hands back a real tree, not a guess:

```text
$ analyze_folder_structure({ "folder_path": "tests/fixtures" })
```

```json
{
  "status": "OK",
  "file_count": 6,
  "supported_file_count": 6,
  "extension_stats": [
    { "extension": ".pdf",  "count": 2, "bytes": 723995 },
    { "extension": ".docx", "count": 1, "bytes": 35505 },
    { "extension": ".pptx", "count": 1, "bytes": 33067 },
    { "extension": ".svg",  "count": 1, "bytes": 319 },
    { "extension": ".png",  "count": 1, "bytes": 74 }
  ],
  "tree_text": "fixtures/\n├── sample.docx (34.7KB)\n├── sample.pdf (431B)\n├── sample.png (74B)\n├── sample.pptx (32.3KB)\n├── sample.svg (319B)\n└── sample_scanned.pdf (706.6KB)"
}
```

Then read one of them — headings, paragraphs, and tables come back structured, not flattened:

```text
$ read_docx({ "file_path": "tests/fixtures/sample.docx" })
```

```json
{
  "status": "OK",
  "headings": ["테스트 문서"],
  "text": "테스트 문서\n본문 첫 문단입니다.",
  "tables": [{ "index": 0, "rows": [["A", "B"], ["1", "2"]] }]
}
```

Both calls are reproducible — clone this repo, run `uv sync --extra dev`, and call them
against `tests/fixtures/` yourself.

## What it is

A personal MCP server that reads whatever's in a folder — PDF, Word, PowerPoint, SVG,
PNG — and hands the structure and raw content back to whichever agent called it
(Claude Code, Claude Desktop, Codex). It doesn't summarize anything itself.

<p align="center">
  <img src="./assets/readme/flow.svg" width="100%" alt="Request flow: your folder goes into file-analyzer-mcp, which extracts text, tables, and image content, which Claude or Codex then reads and summarizes.">
</p>

That's the whole design: the server extracts, the host interprets. No LLM API key
lives in this server. A PNG comes back as base64 image content, not a caption —
your host's own vision reads it. A scanned PDF only gets OCR'd when you ask for it.

## Tools

| Tool | Role |
|---|---|
| `analyze_folder_structure` | Recursive tree + per-extension stats for a folder |
| `list_supported_files` | Just the pdf/docx/pptx/svg/png paths, filtered |
| `read_pdf` | Per-page text. `ocr=True` runs Tesseract on pages with no text layer |
| `read_docx` | Paragraphs, headings, tables (.doc not supported) |
| `read_pptx` | Per-slide title, body, speaker notes (.ppt not supported) |
| `read_svg` | Size, tag counts, `<text>` content — no rasterizing |
| `read_image` | PNG as metadata + image content, for the host to look at directly |

Large documents paginate: `page_start`/`page_end` for PDF, `slide_start`/`slide_end`
for PPTX. Default caps are 30 pages / 60 slides — past that, the response's
`next_actions` tells you the next range to ask for.

## Install

```bash
uv sync --extra dev
```

Register with Claude Code:

```bash
claude mcp add -s user file-analyzer -- "<uv.exe path>" --directory "<this folder>" run python src/file_analyzer_mcp/server.py
```

On Windows, if `uv` was installed via pip it won't be on Claude's PATH — use `uv.exe`'s
full path (`pip show uv` to find it). Claude Desktop and Codex examples are in
[config/](config/): [claude_code.example.md](config/claude_code.example.md),
[claude_desktop_config.example.json](config/claude_desktop_config.example.json),
[codex-config.example.toml](config/codex-config.example.toml).

## Security

Sensitive paths are refused deterministically — this doesn't depend on the model
deciding not to read them. See [paths.py](src/file_analyzer_mcp/paths.py).

| Pattern | What it protects |
|---|---|
| `.ssh`, `.aws`, `.gnupg`, `.azure`, `.kube`, `.docker` | Credential and cloud-config directories |
| Browser profile roots (e.g. `User Data`) | Saved logins and cookies |
| `.env*`, `*.pem`, `*.key`, `*.pfx`, `*.p12` | Secret files, matched by name pattern |
| `id_rsa`, `id_ed25519`, `known_hosts`, `.netrc`, `credentials`, `credentials.json`, `login data`, `cookies`, `web data` | Specific credential filenames |

Every call is also audited — tool name, arguments, and outcome (success or blocked)
get appended to `logs/audit.jsonl`. See [audit.py](src/file_analyzer_mcp/audit.py).
A 50MB file size cap applies on top of all of this.

Conventions for anyone extending this server are in [AGENTS.md](AGENTS.md).

## Errors

Every failure raises `ToolFailure` with a code, a plain-language reason, and how
to recover — the message is written for the calling model to read and act on,
not just for a human.

| Code | Raised when |
|---|---|
| `PATH_NOT_FOUND` | The folder or file path doesn't exist |
| `NOT_A_DIRECTORY` / `NOT_A_FILE` | A tool got the wrong kind of path |
| `UNSUPPORTED_EXTENSION` | The file isn't pdf/docx/pptx/svg/png |
| `WRONG_TOOL_FOR_EXTENSION` | e.g. `read_pdf` called on a `.docx` |
| `FILE_TOO_LARGE` | File exceeds the 50MB cap |
| `SENSITIVE_PATH_BLOCKED` | Path matches the security table above |
| `OCR_ENGINE_NOT_FOUND` | `ocr=True` but Tesseract isn't installed/configured |
| `SVG_PARSE_ERROR` | The `.svg` file isn't valid XML |

## Scanned-PDF OCR

`read_pdf(ocr=True)` needs Tesseract:

```bash
winget install UB-Mannheim.TesseractOCR
uv run python scripts/setup_ocr.py   # copies eng/osd, downloads kor.traineddata
```

`ocr_lang` defaults to `"kor+eng"`. Wrong Tesseract path? Set `TESSERACT_CMD`.

## Testing

```bash
uv run pytest -q                        # parser / path / audit unit tests
uv run python scripts/smoke_stdio.py    # real stdio round-trip against the server
```

Both should pass before a change counts as done — pytest checks modules in
isolation, the smoke test is the only thing that exercises the actual MCP
protocol and catches schema-level breakage.

## Limits

| Limit | Why / what to do |
|---|---|
| `.doc` / `.ppt` not supported | Legacy binary formats — save as `.docx`/`.pptx` first |
| Scanned PDFs return empty text by default | Pass `ocr=True` (off by default — it's slower) |
| SVGs aren't rasterized | Parsed as XML for structure, not rendered as an image |

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

Each read_* tool targets a distinct file format (pptx, svg, image, pdf, docx) with clear boundaries; list_supported_files and analyze_folder_structure have separate discovery roles. No overlap or ambiguity exists.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern: read_<format> for content extraction, plus list_supported_files and analyze_folder_structure. Conventions are uniform and predictable.

Tool Count5/5

Seven tools is well-scoped for a file analyzer server: five format readers plus two discovery/analysis helpers. Each tool has a clear purpose and none feel redundant or missing.

Completeness5/5

The server covers all explicitly supported formats (pdf, docx, pptx, svg, png) with read operations, and provides folder-level discovery through list_supported_files and analyze_folder_structure. The surface is complete for its stated analysis purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues