Generated by /repo-architecture skill v2.0.0

Sci-Papers MCP Architecture

Sci-Papers MCP is a scientific paper management service that provides both an MCP tool interface and a REST API for searching, ingesting, and organizing academic papers from multiple sources. It aggregates content from arXiv, Semantic Scholar, OpenAlex, USPTO Patents, YouTube transcripts, and manual PDF uploads into a unified, topic-organized knowledge base with vector search capabilities.

The server runs as a dual-mode FastAPI + FastMCP application: REST endpoints at /api/* serve the web GUI and programmatic clients, while the MCP Streamable HTTP transport at /mcp exposes the same functionality as 14 MCP tools for AI agent consumption.

Key value proposition: AI agents (Claude Code, n8n workflows) can search across all ingested academic papers, patents, and YouTube transcripts using natural language via MCP tools, while human users get a web GUI for browsing and managing the same knowledge base.
Python LOC
3,480
MCP Tools
14
REST Endpoints
18
Data Sources
6
Providers
7
Test Files
10

2. Runtime Architecture

Diagram A: Runtime Request Flow

Streamable HTTP REST /api/* manages queries JSON files BoW search embeddings Claude Code MCP Client AI agent research Web GUI Browser / HTML Search and browse papers FastAPI + FastMCP Server Python / uvicorn / :8000 14 MCP tools + 18 REST endpoints TopicManager core/topic_manager.py CRUD for topics + papers SearchEngine core/search.py Hybrid BoW + Qdrant search NAS Filesystem /mnt/nas/.../Sci_Papers/ PDFs + knowledge_base.json Qdrant Vector DB / :6333 384-dim MiniLM embeddings Legend Primary path Direct call External Services Code

Request Flow

  1. MCP path: Claude Code connects via Streamable HTTP to /mcp and invokes tools (e.g., search_papers, ingest_from_arxiv).
  2. REST path: The web GUI or programmatic clients call /api/* endpoints for the same operations.
  3. Both paths invoke the same core modules: TopicManager for CRUD and SearchEngine for hybrid search.
  4. TopicManager reads/writes topic directories and JSON knowledge bases on the NAS filesystem.
  5. SearchEngine combines bag-of-words cosine similarity (SimpleVectorStore) with sentence-transformer embeddings (Qdrant) for ranked results.

3. Ingestion Architecture

Diagram B: Data Ingestion Flow

XML+PDF JSON+PDF JSON JSON subtitles raw text chunks filenames arXiv API export.arxiv.org 2M+ papers Semantic Scholar api.semanticscholar.org 200M+ papers OpenAlex api.openalex.org 250M+ works USPTO Patents api.patentsview.org US patents YouTube yt-dlp subtitles transcripts Ingestion Providers (7) providers/*.py (AbstractProvider) arXiv, S2, OpenAlex, Patents, Upload, YouTube, Ebook PDF Extractor core/pdf_extract.py PyMuPDF + pdfplumber Chunker core/chunker.py Text splitting Namer core/namer.py Filename generation NAS + SimpleVectorStore JSON knowledge base PDFs + BoW vectors Qdrant Sentence embeddings 384-dim MiniLM-L6-v2

Ingestion Pipeline

  1. User triggers ingestion via MCP tool or REST endpoint, specifying a query and topic.
  2. The appropriate Provider (arXiv, S2, OpenAlex, Patents, YouTube) searches the external API.
  3. Papers/transcripts are downloaded; PDFs are extracted via PyMuPDF or pdfplumber.
  4. Text is split into chunks by the Chunker and filenames generated by the Namer.
  5. Chunks are stored in both SimpleVectorStore (BoW, JSON on NAS) and Qdrant (sentence-transformer embeddings).
  6. An IngestionJob tracks progress (queued, downloading, extracting, ingesting, completed/failed).

4. Storage Model

Dual Vector Store Architecture

The system uses two complementary vector stores for search:

StoreTechnologyEmbeddingUse Case
SimpleVectorStore JSON files on NAS Bag-of-words (cosine) Per-topic paper search, zero-dependency fallback
QdrantStore Qdrant v1.13.6 (Docker) all-MiniLM-L6-v2 (384-dim) Semantic search, YouTube transcripts, cross-topic queries

NAS Filesystem Layout

/mnt/nas/Audiobooks/Organized/Sci_Papers/
  {topic_name}/
    _topic_meta.json              # display name, description, created_at
    {date}_{source}-{id}_{title}.pdf
    knowledge_vector/
      knowledge_base.json         # full text chunks + metadata
      vstore.json                 # bag-of-words vectors
  MD_Youtube/
    {channel_name}/
      {video_title}.md            # YouTube transcript markdown

Qdrant Collections

Each topic or content source maps to a Qdrant collection. Collections are auto-created on first ingestion. Point IDs are deterministic (MD5 of paper_id:chunk_index) to enable idempotent upserts.

5. Public Interfaces

MCP Tools (14 tools via Streamable HTTP at /mcp)

ToolDescriptionKey Parameters
list_topicsList all paper topics with paper counts and statisticsnone
get_topic_statsGet detailed statistics for a specific topictopic
search_papersSearch across all topics or a specific topic for papersquery, topic?, k?
ingest_from_arxivSearch arXiv and ingest papers into a topicquery, topic, max_results?, days_back?, field?
ingest_from_s2Search Semantic Scholar and ingest papers into a topicquery, topic, max_results?
ingest_from_openalexSearch OpenAlex (250M+ works, free, no key) and ingestquery, topic, max_results?
search_patentsSearch USPTO patents and ingest into a topicquery, topic?, max_results?
upload_paperRegister a PDF already on disk into a topic's knowledge basetopic, file_path, title?
reindex_topicRe-index existing PDFs in a topic directory into the vector storetopic, source_dir?
ingest_youtubeIngest YouTube transcript markdown files from a channel directorysource_dir, channel?
youtube_downloadDownload YouTube subtitles via yt-dlp and ingest into Qdranturl, topic?, lang?, max_videos?
purge_topicDelete all documents from a topic's vector storetopic
get_paperGet metadata for a specific paper by IDpaper_id

REST API Endpoints (18 endpoints at /api/*)

MethodPathDescription
GET/healthHealth check with topic/paper/chunk counts
GET/Web GUI (HTML)
GET/api/topicsList all topics
POST/api/topicsCreate a new topic
GET/api/topics/{topic}Get topic details + papers
DELETE/api/topics/{topic}Delete an empty topic
GET/api/papersList papers (optional topic/source filter)
GET/api/papers/{id}Get paper metadata
GET/api/papers/{id}/pdfDownload paper PDF
GET/api/searchSearch papers (hybrid BoW + Qdrant)
POST/api/ingest/arxivIngest from arXiv
POST/api/ingest/s2Ingest from Semantic Scholar
POST/api/ingest/uploadUpload PDF (multipart)
POST/api/ingest/patentsIngest from USPTO
POST/api/ingest/openalexIngest from OpenAlex
POST/api/ingest/reindexRe-index existing PDFs
POST/api/ingest/youtubeIngest YouTube transcripts
POST/api/ingest/youtube-downloadDownload + ingest YouTube subtitles
GET/api/ingest/jobsList ingestion jobs
GET/api/ingest/jobs/{id}Get job status
POST/api/purgePurge topic vector store
GET/api/statsGlobal statistics

6. Repository Structure

Component Classification

ClassificationComponentPathPurpose
PrimaryServerserver.pyFastAPI + FastMCP dual-mode server, all MCP tools and REST endpoints
PrimaryTopicManagercore/topic_manager.pyTopic CRUD, paper listing, stats, NAS filesystem operations
PrimarySearchEnginecore/search.pyHybrid search combining SimpleVectorStore and Qdrant results
SecondaryQdrantStorecore/qdrant_store.pyQdrant-backed vector store with sentence-transformer embeddings
SecondarySimpleVectorStorecore/vector_store.pyJSON-file BoW vector store (zero-dependency fallback)
SecondaryArxivProviderproviders/arxiv.pyarXiv API search + PDF download + ingestion (292 LOC)
SecondaryS2Providerproviders/semantic_scholar.pySemantic Scholar API search + ingestion (152 LOC)
SecondaryOpenAlexProviderproviders/openalex.pyOpenAlex API search + ingestion (198 LOC)
SecondaryPatentProviderproviders/patents.pyUSPTO PatentsView API search + ingestion (175 LOC)
AuxiliaryYouTube MD Ingestproviders/youtube_md.pyIngest pre-existing YouTube transcript markdown files
AuxiliaryYouTube Downloadproviders/youtube_download.pyDownload subtitles via yt-dlp + save as markdown
AuxiliaryUploadProviderproviders/upload.pyManual PDF upload ingestion
AuxiliaryPDF Extractorcore/pdf_extract.pyText extraction from PDFs (PyMuPDF + pdfplumber)
AuxiliaryChunkercore/chunker.pyText splitting into chunks
AuxiliaryNamercore/namer.pyGenerate standardized filenames for papers
AuxiliaryReindexercore/reindexer.pyRe-index existing PDFs from disk into vector stores
AuxiliaryModelsmodels/Pydantic models (PaperMetadata, Topic, IngestionJob, enums)
AuxiliaryWeb GUIgui/index.htmlSingle-page web interface for browsing papers (33K HTML)
ExternalQdrantDocker containerVector database (qdrant/qdrant:v1.13.6)

Directory Tree

_sci-papers-mcp/
  server.py                     # 704 LOC — FastAPI app + all MCP tools + REST endpoints
  Dockerfile                    # Python 3.12-slim, uvicorn :8000
  docker-compose.yml            # 2 services: sci-papers-mcp + sci-papers-qdrant
  requirements.txt              # 12 dependencies
  README.md
  core/
    topic_manager.py            # 173 LOC — Topic CRUD backed by NAS filesystem
    search.py                   # 126 LOC — Hybrid search engine
    qdrant_store.py             # 166 LOC — Qdrant vector store + sentence-transformers
    vector_store.py             # 102 LOC — SimpleVectorStore (BoW, JSON files)
    pdf_extract.py              #  75 LOC — PDF text extraction
    reindexer.py                # 152 LOC — Re-index existing PDFs
    chunker.py                  #  21 LOC — Text chunking
    namer.py                    #  34 LOC — Filename generation
  providers/
    base.py                     #  20 LOC — AbstractProvider ABC
    arxiv.py                    # 292 LOC — arXiv ingestion
    semantic_scholar.py         # 152 LOC — Semantic Scholar ingestion
    openalex.py                 # 198 LOC — OpenAlex ingestion
    patents.py                  # 175 LOC — USPTO patent ingestion
    upload.py                   # 135 LOC — Manual PDF upload
    youtube_md.py               # 214 LOC — YouTube transcript markdown ingestion
    youtube_download.py         # 222 LOC — yt-dlp subtitle download
    audiobook.py                # 179 LOC — Audiobook provider (planned)
    ebook.py                    # 243 LOC — Ebook provider (planned)
  models/
    enums.py                    #  19 LOC — PaperSource, IngestionStatus
    paper.py                    #  66 LOC — PaperMetadata, Topic, IngestionJob, SearchResult
  gui/
    index.html                  # 33K — Single-page web GUI
  tests/                        # 10 test files (68K total)
  docs/

7. Deployment & Operations

Docker Compose (2 containers)

ContainerImagePortNetworkVolumes
sci-papers-mcp Custom (Python 3.12-slim) 8000 hosting_web /mnt/nas/Audiobooks/Organized/data/library
sci-papers-qdrant qdrant/qdrant:v1.13.6 6333 (internal) hosting_web sci_papers_qdrant named volume

Environment Variables

VariableDefaultPurpose
LIBRARY_DIR/data/libraryBase path for topic storage
QDRANT_URLhttp://sci-papers-qdrant:6333Qdrant connection URL
S2_API_KEY(empty)Semantic Scholar API key (optional, higher rate limits)
AUDIOBOOKSHELF_API_KEY(empty)Audiobookshelf API key (planned)
USPTO_API_KEY(empty)USPTO PatentsView API key (optional)
ARXIV_BATCH_DELAY3.0Delay between arXiv API requests (seconds)

Health Check

Both containers have Docker health checks. The application responds at GET /health with topic count, paper count, and chunk count.

Access

InterfaceURL
Web GUIhttps://sci-papers.home/
Healthhttps://sci-papers.home/health
MCP endpointhttps://sci-papers.home/mcp
REST API docshttps://sci-papers.home/docs

Operations

# Start
cd /home/rod/_rod/_sci-papers-mcp && docker compose up -d

# Logs
docker logs -f sci-papers-mcp

# Health check
curl -s https://sci-papers.home/health | jq .

# Rebuild after code changes
docker compose build sci-papers-mcp && docker compose up -d sci-papers-mcp

8. Dependencies

Python Dependencies (requirements.txt)

PackageVersionRoleClassification
fastapi>=0.110.0REST API frameworkPrimary
uvicorn[standard]>=0.27.0ASGI serverPrimary
mcp[cli]>=1.0.0MCP protocol SDK (FastMCP)Primary
pydantic>=2.0.0Data validation and modelsPrimary
httpx>=0.27.0Async HTTP client for external APIsPrimary
qdrant-client>=1.12.0Qdrant vector DB clientSecondary
sentence-transformers>=3.0.0Embedding model (all-MiniLM-L6-v2)Secondary
PyMuPDF>=1.24.0PDF text extraction (primary)Secondary
pdfplumber>=0.10.0PDF text extraction (fallback)Auxiliary
python-multipart>=0.0.9File upload support for FastAPIAuxiliary
yt-dlp>=2024.0.0YouTube subtitle downloadAuxiliary
pytest>=8.0.0Test frameworkAuxiliary

External Services

ServiceAuth RequiredRate Limits
arXiv API (export.arxiv.org)No3-second delay enforced via ARXIV_BATCH_DELAY
Semantic Scholar APIOptional (S2_API_KEY)100 req/5min without key, higher with key
OpenAlex APINoFree, polite pool (~10 req/sec)
USPTO PatentsView APIOptionalStandard rate limits
YouTube (via yt-dlp)NoStandard YouTube rate limits
Qdrant (local container)NoN/A (local)

9. Constraints & Risks

ItemSeverityDescription
MCP tools use synchronous asyncio.run() WARN MCP tool functions call asyncio.run() inside sync functions, which blocks the event loop. Works for single-user but will not scale to concurrent MCP sessions.
SimpleVectorStore uses BoW vectors WARN Bag-of-words cosine similarity has limited semantic understanding. Qdrant with sentence-transformers is the preferred search path.
NAS dependency for storage WARN All paper storage requires /mnt/nas to be mounted. If NAS is offline, ingestion and browsing will fail.
upload_paper MCP tool is a stub INFO The MCP tool returns {"status": "stub"}; the REST upload endpoint works via multipart form.
Ebook and Audiobook providers are planned INFO Provider classes exist (179 and 243 LOC) but are not wired into MCP tools yet.
In-memory job tracking INFO Ingestion jobs are stored in a Python dict and lost on restart. No persistent job queue.
sentence-transformers cold start INFO First embedding request loads the MiniLM model into memory (~90MB). Subsequent requests are fast.