file-analysis
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@file-analysisScan the reports folder and summarize the key findings from the PDFs."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
A personal local MCP that reads unstructured documents (pdf docx pptx svg png) in a specified folder and helps summarize key content and analyze file structure. It plugs into Claude Code · Codex · Claude Desktop.
Document | What it contains |
README.md (this document) | How to use it |
What is being built — data contracts · tool contracts · guardrails · permanent refusal list | |
Coding agent workflow — workflow · review checklist · common mistakes |
If the same fact appears in two places, AGENTS.md is the source of truth.
This server does not summarize
This is the most important design decision.
Layer | What it does |
MCP server | Extraction · structure analysis · grounding anchor attachment · summary verification |
Host model (Claude Code / Codex) | Writes the summary — citing anchors while doing so |
Human | Approval |
If the server also summarized, it would have to call a model again with its own API key, and the host would receive only the summary result, making it impossible to verify the grounding. That opens a path for incorrect summaries to pass silently. So the server only outputs the source text and anchors.
Related MCP server: file-analyzer
Quick start
Prerequisites: Python 3.11+, uv
uv sync --extra devuv run python scripts/make_samples.pyuv run python scripts/smoke_stdio.pyIf smoke_stdio.py prints PASS, the server is working — it launches the server over the real MCP protocol, checks all 17 harness contracts, and runs a full cycle from DISCOVER to SAVED.
To visually inspect the tools with MCP Inspector:
uv run mcp dev src/file_mcp/server.pySpecifying the folder to analyze
Edit allowed_roots in config/roots.toml. This file is the server's security boundary.
allowed_roots = [
"data/samples",
"C:/Users/<사용자>/Desktop/분석대상",
]Do not add an entire parent folder like C:/Users/<user> — that is effectively no guard at all. The server will never open a path outside this list under any circumstances.
Host connection
Claude Code
claude mcp add file-analysis -- uv --directory "<이-저장소를-클론한-절대경로>" run python src/file_mcp/server.pyCodex — paste the contents of config/codex-config.example.toml into ~/.codex/config.toml.
Claude Desktop — see config/claude_desktop_config.example.json.
Pipeline
flowchart LR
S["scan_folder<br/><i>추정 등급 B?</i>"] --> I["inspect_document<br/><i>확정 등급 A/B/C</i>"]
I --> P["build_analysis_prompt<br/><i>앵커 붙은 원문</i>"]
P --> D(["초안 작성<br/><i>호스트 모델</i>"])
D --> G["check_summary_grounding<br/><i>GR-01 … GR-04</i>"]
G --> V["preview_save_report<br/><i>승인 토큰 발급</i>"]
V --> H{{"사람의 승인"}}
H --> W["save_approved_report<br/><i>유일한 쓰기</i>"]
classDef server fill:#ddf4ff,stroke:#54aeff,color:#1f2328
classDef notserver fill:#ffffff,stroke:#afb8c1,stroke-dasharray:5 4,color:#656d76
classDef write fill:#fff8c5,stroke:#d4a72c,color:#1f2328
class S,I,P,G,V server
class D,H notserver
class W writeThe dashed line marks what the server does not do. The draft is written by the host model, and approval is done by a human.
Step | Tool | Read/Write |
DISCOVER |
| Read |
DISCOVER |
| Read |
INSPECT |
| Read |
READ |
| Read |
READ |
| Read |
DRAFT |
| Read |
CHECK |
| Read |
PREVIEW |
| Read |
APPROVE | (human) | — |
SAVED |
| Write |
save_approved_report is the only write tool. It never writes without an approval token.
scripts/smoke_stdio.py checks the write tool list, so if you add more tools, you must update the smoke test as well.
Grades are determined by content, not file extension
flowchart TD
X["파일"] --> Y{"확장자"}
Y -->|"docx · pptx"| A["<b>등급 A</b><br/>구조까지"]
Y -->|"png"| C1["<b>등급 C</b><br/>이미지 판독"]
Y -->|"pdf"| PQ{"공백 제거 후 페이지 텍스트<br/>8자 이상?"}
Y -->|"svg"| SQ{"내용 있는<br/>text 노드?"}
PQ -->|"있음"| B1["<b>등급 B</b><br/>본문만"]
PQ -->|"없음"| C2["<b>등급 C</b><br/>스캔 PDF"]
SQ -->|"있음"| B2["<b>등급 B</b><br/>본문만"]
SQ -->|"없음"| C3["<b>등급 C</b><br/>그림"]
classDef ga fill:#dafbe1,stroke:#2da44e,color:#1f2328
classDef gb fill:#ddf4ff,stroke:#54aeff,color:#1f2328
classDef gc fill:#fff8c5,stroke:#d4a72c,color:#1f2328
class A ga
class B1,B2 gb
class C1,C2,C3 gcGrade | Meaning | How to read |
A | Extracts structure too (heading levels · tables · slide units) |
|
B | Extracts body text only |
|
C | No text |
|
| Undetermined. You have to open it to know | Exists only in |
scan_folder does not open files, so it cannot confirm the grade. pdf·svg remain B?, and inspect_document opens them to confirm. Do not read B? in scan results as a confirmed value.
The samples are arranged to prove this — 흐름도.svg has text nodes so it is B, while 도형만.svg has only shapes so it is C. Same extension, different grades.
Read it with the host model's vision. No additional dependencies, and Korean accuracy is better than tesseract. If offline batch processing becomes necessary, an extract_text_ocr tool will be added separately.
Scanned PDFs are also read without a rasterizer. A scanned page is entirely one embedded image, so you can extract that image with pypdf — no PyMuPDF (AGPL) or poppler binaries needed.
Pages drawn purely as vectors cannot be extracted, and in that case the PDF_PAGE_HAS_NO_IMAGE error tells the human to "capture the screen." It does not silently return an empty result.
It only gives the table of contents · block count · character count · confirmed grade, and estimated_read_calls (the number of calls needed to read the whole document). Its reason for existing is to prevent dumping the body of a 300-page PDF into the context just to see what it looks like.
The cost of opening a file is the same as read_document — what is saved is not time but context.
Citation anchor contract
Format | Anchor | Meaning |
|
| 14th block (paragraph or table row) |
|
| 7th slide, 2nd line / speaker notes |
|
| page 3 |
|
| 2nd |
| (none) | no text, so no anchor |
Each format has a different unit, but read_document's interface is one. All formats are flattened into a 1-dimensional list of blocks, so you only need start/end. The response's unit tells you what a single block is.
If you change the anchor format, you must update grounding.ANCHOR_PATTERN and the golden set together. If they fall out of sync, valid citations will all be blocked by GR-02.
What grounding verification can and cannot check
Whether the sentence cites an anchor —
GR-01Whether that anchor exists in the document —
GR-02Whether figures and dates exist in the cited block's source text —
GR-03Whether direct quotes (inside quotation marks) match the source —
GR-04
Whether the summary faithfully conveys the source's meaning
Whether something important was omitted
Whether the cited anchor is an appropriate anchor (
GR-05is only a lexical overlap hint)
Passing does not mean 'correct.' The response's not_verifiable states this limitation every time — if you pretend to verify what cannot be verified, people will believe "it passed, so it must be right," and that is more dangerous than no verification at all.
Paraphrasing the source is normal. Verification only looks at anchors, figures, and direct quotes.
Save gate
preview_save_report checks both structure (ST-*) and grounding (GR-*), and issues an approval token only when there are zero errors. The token is sha256(original relative path + draft), so changing even one character of the draft invalidates it — this blocks the path of previewing with a clean draft and saving a different one.
save_approved_report re-checks every gate. It does not trust the model's claim that the preview passed.
Order | Check | On failure |
0 | Is |
|
1 | Structure ( |
|
2 | Grounding ( |
|
3 | Approval token |
|
If an existing artifact exists, it is overwritten, and the hash of the previous content is recorded in the audit log. The audit log (data/outputs/_audit.jsonl) is append-only.
Harness layers (CAR)
Divided into three axes: Control–Agency–Runtime. First decide which axis the file you are changing belongs to. If the axis is unclear, that is a sign the design is wrong.
Axis | Question | Files |
Control | What is it preventing? |
|
Agency | What does the model choose and how? |
|
Runtime | What happened is recorded? |
|
Per-axis detailed contracts and dependency directions are in AGENTS.md chapter 2.
The server does not track progress (how much has been read). The model owns it, and the server only tells next_actions to "continue with start=N." That is why the server is stateless, and the write tool stays as a single save.
Self-verification
Invariants are checked right before returning a response; if they break, the server returns an error instead of a wrong answer.
Check | What it prevents |
Anchor uniqueness and non-emptiness | Grounding verification pointing at the wrong block |
Body line ↔ block consistency | Truncation cutting mid-block, causing grounding verification to fail |
Aggregate sum = row count | The code not counting or double-counting numbers |
Grade ↔ block contradiction | Reporting grade B but no blocks to read |
What gets caught here is not a user input problem but a server bug. That is why the error message says "this is a server defect — stop the task and report it," not "please check the file."
Observability
Each tool call is recorded as one line in data/traces/YYYY-MM-DD.jsonl.
uv run python scripts/trace_report.pyWhat is not recorded is more important. When analyzing real internal documents, traces could become copies of those documents.
Rule | Enforcement |
No body, excerpt, or TOC text |
|
No draft body | Not registered in |
No absolute paths | Folded as |
No error | Only |
This is enforced by code, not by convention, and verified by tests (tests/test_trace.py).
If trace_dir is inside allowed_roots, tracing turns itself off — to avoid polluting the analyzed folder with its own records.
All 8 read tools have readOnlyHint: True, yet traces write files.
That hint means the analyzed documents are not modified. Traces are instrumentation logs outside allowed_roots and are not exposed through any tool. The only thing exposed through a write tool is save_approved_report, and the smoke test checks that list.
Evaluation
uv run python scripts/eval_extract.pyCompares the expected values in evals/golden/samples.json against actual extraction results and records the results in evals/reports/. pytest only tells you "does it pass right now," while this report records when and what passed.
The expected values are hand-written by looking at what scripts/make_samples.py put into the files. They are not copies of the extractor's output. If you adjust the golden set to match the results, the evaluation passes itself. The only legitimate reasons to change them are when the anchor contract, grade definitions, or sample contents change.
Dependencies
Package | License | Purpose |
MIT | FastMCP server | |
BSD | pdf text and embedded images | |
MIT | docx | |
MIT | pptx | |
MIT-CMU | png metadata and image downscaling |
svg is read with the standard xml.etree — zero dependencies.
Why not use
PyMuPDF(fitz): performance is better, but it is AGPL-3.0, which imposes distribution conditions if included in an internal tool. If table extraction actually becomes necessary, addpdfplumber(MIT).
What is not committed
Path | Reason |
| Generated by |
| Analysis results and audit log. Contains summaries of real documents |
| Execution logs. No body text, but filenames and paths remain |
| Local run results. The golden set is committed |
| Personal paths |
Do not place real documents to be analyzed inside this repository.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to interact with local documents (PDF, Markdown, TXT) through tools for discovery, reading, extraction, summarization, comparison, keyword extraction, search, and analysis, ensuring privacy and offline capability.
- FlicenseAqualityCmaintenanceEnables read-only analysis of local unstructured documents by scanning a folder, extracting text and structural metadata, and passing content with truncation and error-awareness to an LLM for summarization.9
- AlicenseAqualityCmaintenanceEnables reading and extracting text from local documents (PDF, Word, Excel, PowerPoint, HWP, Markdown, CSV, etc.) without network access, and provides approval-gated summary saving and file organization.11MIT
- FlicenseNot gradedqualityCmaintenanceEnables local, read-only extraction of text and structure from PDF, DOCX, PPTX, SVG, and PNG files, including OCR for images, directory tree and metadata reporting, with strict path isolation and audit logging.
Related MCP Connectors
AI reasoning checks any document against known international standards before your agent acts on it.
Turn any PDF into structured JSON via AI + OCR: invoices, bank statements, contracts.
Certified SEC EDGAR fact memory for AI agents with zero hallucination and filing provenance.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/goods9999-ai/personal-file-analysis-mcp_test_20260826'
If you have feedback or need assistance with the MCP directory API, please join our Discord server