Upload PDFs, extract text with PyMuPDF or GLM-OCR (Ollama), and ask questions against the document with a local Ollama model. No API keys.
- PDF extraction: PyMuPDF for text layers; GLM-OCR when the PDF is scanned or image-only
- Per-document RAG: each upload gets its own Chroma collection
- Local chat: LangChain agent with inline citations; choose any installed Ollama model from the dropdown
- Markdown viewer: read extracted text, preview chunks, download markdown
1. Clone the repository
git clone https://github.com/dakshp26/PDFDashboardWithMCP.git
cd PDFDashboardWithMCP2. Install dependencies
uv sync3. Pull Ollama models
ollama pull qwen2.5:3b # chat (or another chat model)
ollama pull nomic-embed-text # embeddings
ollama pull glm-ocr # OCR for scanned PDFs4. Run the app
uv run streamlit run app/main.pyOpen http://localhost:8501 in your browser.
- Upload PDF: open Upload PDF, select a file, wait for extraction to finish
- Chat: open Chat, pick the PDF and an Ollama model, ask questions
app/
├── main.py # Entry point, page navigation
├── app_pages/
│ ├── landing.py # Home page
│ ├── process_pdf_upload.py # Upload + pipeline UI
│ ├── pdf_library.py # Browse uploaded PDFs (read-only viewer)
│ └── process_pdf.py # Viewer + chat UI
└── process_pdf/
├── extract.py # PDF → Markdown (pymupdf4llm + GLM-OCR)
├── pipeline.py # Extraction pipeline with live progress
├── rag.py # Chunking, embeddings, Chroma persistence
└── agent.py # LangChain agent with retriever tool
mcp_server/
└── server.py # MCP server (list_documents, get_document)
data/ # Runtime data (gitignored)
├── process_pdf/ # Saved PDFs and extracted markdown
└── process_chroma/ # Chroma vector collections (one per PDF)
Note
File-by-file breakdown, execution order, and data flow: APP_STRUCTURE.md.
| Page | What it does |
|---|---|
| Home | Links and setup summary |
| Upload PDF | Run extraction (text layer, OCR fallback, chunking, embedding); download markdown |
| PDF Library | Open past uploads; view markdown and chunk previews without re-running extraction |
| Chat | Query an indexed PDF with citations |
Extraction progress shows in an st.status block. After processing, the Chroma collection lives in data/process_chroma/ and loads on the next run without re-extracting.
Two tools for MCP clients (Claude Desktop, Cursor, Claude Code):
list_documents: indexed document collectionsget_document(document, query): semantic search over a collection
Claude Desktop
Add to claude_desktop_config.json (Windows: %APPDATA%\Claude\claude_desktop_config.json) or use .mcp.json in the project root:
{
"mcpServers": {
"PDFDashboardWithMCP": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/PDFDashboardWithMCP", "mcp_server/server.py"]
}
}
}Cursor
Add to .cursor/mcp.json in the project root or global ~/.cursor/mcp.json:
{
"mcpServers": {
"PDFDashboardWithMCP": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/PDFDashboardWithMCP", "mcp_server/server.py"]
}
}
}Claude Code
Project-scoped .mcp.json in the repo root keeps the server tied to this repo:
{
"mcpServers": {
"PDFDashboardWithMCP": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/PDFDashboardWithMCP", "mcp_server/server.py"]
}
}
}Claude Code reads .mcp.json when you open the project.
Replace /absolute/path/to/PDFDashboardWithMCP with your clone path.
Ollama must be running with
nomic-embed-textpulled before the MCP server can load collections.
| Component | Library |
|---|---|
| UI | Streamlit |
| PDF extraction | langchain-pymupdf4llm, PyMuPDF |
| OCR fallback | Ollama glm-ocr |
| Embeddings | Ollama nomic-embed-text |
| Vector store | Chroma (langchain-chroma) |
| LLM / agent | Ollama chat model (e.g. qwen2.5:3b), LangChain |
| Package manager | uv |
| MCP server | mcp[cli] |