Skip to main content
Glama
dear-pi

arxiv-mcp

by dear-pi

arxiv-mcp

An MCP server that turns arXiv into a set of tools an LLM agent can call: search, fetch metadata, extract PDF text, and summarise how active a research category is.

Built as a learning project while studying for a CS degree, targeting LLM-application / agent engineering internships.

you / an agent
      │  tools/call  "search_papers"
      ▼
┌──────────────┐   throttled + cached HTTP    ┌──────────────┐
│  arxiv-mcp   │ ───────────────────────────▶ │  arXiv API   │
│  (MCP server)│ ◀─────────────────────────── │  Atom feed   │
└──────────────┘        retry w/ backoff      └──────────────┘
      │  validated pydantic models
      ▼
structuredContent the model can actually reason about

What it exposes

Tools

Tool

What it does

Why it is not just a proxy

search_papers(query, max_results, sort_by, category)

arXiv search with field syntax (au:, ti:, abs:)

Optional category filter is composed into cat:X AND (...) rather than silently ignored

get_paper(arxiv_id)

Full metadata for one paper

Strips version suffixes, validates both modern (2401.12345) and legacy (cs.LO/0112017) identifier forms

get_pdf_text(arxiv_id, max_pages)

Downloads the PDF and extracts text from the first pages

Reports page count, how many were parsed, and whether it truncated — so the model knows what it is missing

category_trends(category, window_days)

Submission volume per day + most frequent title terms

An analysis tool, not a lookup: it aggregates and tokenises so an agent can answer "is this field heating up?"

Resources

  • arxiv://paper/{arxiv_id} — a Markdown card with the paper's full metadata

  • arxiv://categories — the arXiv taxonomy the server can filter on

Prompts

  • literature_review(topic, max_papers) — a multi-step workflow that instructs an agent to check category trends first, try several phrasings, then group findings and cite every claim with an arxiv_id

Related MCP server: Research MCP Server

Install and run

pip install -e ".[pdf]"      # the pdf extra enables get_pdf_text
arxiv-mcp                    # serves over stdio

Point any MCP host at it. For Claude Desktop / Claude Code:

{
  "mcpServers": {
    "arxiv": {
      "command": "uv",
      "args": ["run", "--with", "arxiv-mcp[pdf]", "arxiv-mcp"]
    }
  }
}

To poke at it interactively:

uv run mcp dev src/arxiv_mcp/server.py    # opens the MCP Inspector

Design notes

Rate limiting is a feature, not an afterthought. arXiv asks clients to keep requests at least 3 seconds apart and to send a descriptive User-Agent. ArxivClient enforces both: an asyncio.Lock serialises requests and a monotonic clock enforces the gap, so concurrent tool calls cannot stampede the API. Getting rate-limited by arXiv would break every tool at once.

Retries with full jitter, capped, and Retry-After-aware. Transient 429/5xx responses and transport errors are retried up to max_retries times with a backoff of uniform(0, min(base * 2**attempt, cap)). The cap matters: arXiv's rate limiter can sit on an IP for minutes, and unbounded backoff is worse than a clear error. When the server sends Retry-After it is honoured (subject to the same cap) instead of guessing.

This is not theoretical. During development the first version retried 3 times with sub-second backoff and no Retry-After handling; testing against the live API got the sandbox IP rate-limited, and the failure was slow and opaque. Both the backoff and the error message (HTTP 429 rather than an empty string) were rewritten after that.

The API URL is https://. http://export.arxiv.org answers 301 to https://, which silently doubles every request and interacts badly with retries. Found the same way - by actually running it.

Caching raw response bodies. Responses are cached keyed by the fully-built request URL in SQLite with a TTL. Keying on the URL (rather than a hand-built cache key) means the key is correct by construction.

Error policy follows the MCP spec. Every failure is placed in one of three buckets:

Raised

Model sees

Used here for

ToolError

the message, is_error=True

paper not found, arXiv unreachable, empty search result

MCPError

nothing — the request itself fails

missing optional dependency (pypdf not installed)

anything else

only Error executing tool X

bugs

The deciding question is "could a smarter model have avoided this?". A misspelled arXiv id can be corrected by the model, so it gets the message. A missing Python package cannot, so it is escalated to the host.

Models everywhere. Every tool returns a pydantic model, so clients receive validated structuredContent instead of untyped strings. Bad arguments are rejected by the SDK's schema validation before the handler runs, so the handlers do not re-validate type hints.

The arXiv layer has no MCP imports. arxiv_mcp.client is plain async code, which is what makes it unit-testable without starting a server — see tests/test_client.py.

Tests

pytest                       # 53 tests
ruff check .                # lint
  • tests/test_client.py — feed parsing, cache hit/miss, retry behaviour, identifier validation. HTTP is mocked with respx, so nothing touches the network. Includes the backoff policy: Retry-After honoured, capped, jittered.

  • tests/test_server.py — the whole server exercised through an in-memory MCP Client: tool/resource/prompt discovery, structured output, and that each error surfaces in the right bucket.

  • tests/test_models.py, tests/test_cache.py — validation and TTL expiry.

Verification status

Check

Result

pytest

53 passed

ruff check / ruff format --check

clean

Live get_paper("1706.03762")

returns "Attention Is All You Need", 3 authors, cs.CL/cs.LG

Live category_trends("cs.AI")

returns daily counts and title terms

Live search_papers(...)

rate-limited from the dev IP — see below

The free-text search endpoint is the one thing not verified end-to-end from the machine this was built on: arXiv rate-limited that IP (the sandbox shares an egress address with other traffic) and kept returning 429/ReadTimeout for search queries while id_list and cat: queries kept succeeding. The client handles it correctly — it retries with backoff, then raises a ToolError with the HTTP status — but the happy path for search is covered by the respx-mocked tests, not by a live call. Run it once from a residential connection to confirm.

Limitations

  • search_papers returns one page (max 100). arXiv's start offset works but is not exposed as a tool argument.

  • get_pdf_text needs the pdf extra and is slow — it downloads the whole PDF.

  • arXiv has no relevance-ranked "related papers" endpoint, so nothing here does citation-graph traversal.

  • The server holds one shared ArxivClient; there is no per-session configuration.

Attribution

The code in this repository was written for this project. It follows the patterns in the MCP Python SDK documentation (which is MIT licensed) but contains no copied code.

License

MIT — see LICENSE.

Available Tools

4 tools
get_paperA

Fetch full metadata for a single arXiv paper by identifier.

NameRequiredDescriptionDefault
arxiv_idYesarXiv identifier, with or without version. Modern form '2401.12345', legacy form 'cs.LO/0112017'.

Output Schema

NameRequiredDescription
doiNoJournal DOI when one is registered.
titleYesPaper title, whitespace-normalised.
abs_urlYesCanonical abstract page.
authorsYesAuthor names in the order arXiv lists them.
commentNoAuthor comment, e.g. page count or venue.
pdf_urlYesCanonical PDF URL.
updatedYesDate of the latest revision.
versionNoVersion of the record that was returned.
abstractYesAbstract text, whitespace-normalised.
arxiv_idYesBare arXiv identifier without version, e.g. '2401.12345'.
publishedYesDate of v1 submission.
categoriesYesAll categories the paper is tagged with.
journal_refNoJournal reference, when present.
primary_categoryYesPrimary arXiv category, e.g. 'cs.AI'.

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavior burden. 'Fetch' signals a non-mutating read and 'full metadata' characterizes the payload, but nothing is said about behavior on an unknown or malformed identifier, version resolution defaults, or rate limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, front-loaded with the verb and the resource, with no filler. Nothing could be removed without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists and the sole parameter is richly described, so this short description is close to sufficient for a simple lookup tool. A note on how an identifier with versus without a version is resolved would close the remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single arxiv_id parameter is fully documented with modern and legacy forms. The description adds nothing beyond 'by identifier', so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Fetch') and resource ('full metadata for a single arXiv paper'), and the word 'single' implicitly contrasts with the bulk-oriented search_papers sibling. It stops short of naming or explicitly distinguishing itself from any sibling, so a 4 rather than a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: an agent can infer it should call this when it already holds a paper identifier, and that search_papers is the discovery alternative. There is no explicit when-to-use/when-not-to-use statement or named alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_pdf_textA

Download a paper's PDF and extract text from its first pages.

Slow and bandwidth-heavy - use only when the abstract is insufficient.

NameRequiredDescriptionDefault
arxiv_idYesarXiv identifier, e.g. '2401.12345'.
max_pagesNoHow many pages to parse.

Output Schema

NameRequiredDescription
textYesExtracted text, pages joined by form feeds.
arxiv_idYes
truncatedYesTrue when the PDF was longer than max_pages.
page_countYesPages in the downloaded PDF.
extracted_pagesYesPages that were actually parsed.

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral load and does so well: it discloses cost/latency characteristics (slow, bandwidth-heavy) and the extraction scope limit (first pages only). It does not mention failure modes for bad or unavailable arXiv IDs, which is the remaining gap for a network-dependent tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler, with the action stated first and the cost/when-to-use caveat immediately after. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter fetch tool with a full schema and an output schema, the description covers purpose, cost, and invocation conditions adequately. Minor gaps are error behavior and how much text typically comes back, but the output schema covers return shape.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both arxiv_id and max_pages are already well documented in the schema with format examples and bounds. The phrase "first pages" loosely reinforces max_pages but adds no new parameter semantics, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: download a paper's PDF and extract text from its first pages. It implicitly contrasts with the abstract-level tools, but never names search_papers or get_paper, so the differentiation is by implication rather than explicit routing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"use only when the abstract is insufficient" is a clear positive condition plus an implicit exclusion, and "slow and bandwidth-heavy" reinforces the threshold for calling it. It stops short of naming the cheaper alternative tool the agent should try first.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_papersB

Search arXiv and return a page of matching papers.

NameRequiredDescriptionDefault
queryYesSearch text. arXiv field syntax is supported, e.g. 'retrieval augmented generation', 'au:hinton AND cat:cs.NE', 'ti:"chain of thought"'.
sort_byNoRanking to apply.relevance
categoryNoOptional arXiv category filter, e.g. 'cs.AI'.
max_resultsNoPage size.

Output Schema

NameRequiredDescription
queryYesThe query that produced this result.
startYesZero-based offset into the full result set.
papersYesThe papers in this page.
sort_byYesSort order arXiv applied.
returnedYesNumber of papers actually returned in this page.
total_resultsYesTotal matches arXiv reports, ignoring pagination.

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. It implies a read-only paginated search by saying 'return a page', but does not disclose rate limits, arXiv API constraints, or pagination mechanics. This is minimally adequate for a simple read operation but leaves notable gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no wasted words. It states the action and result format efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is conceptually simple, the input schema is fully documented, and an output schema exists. The description is complete enough for an agent to call the tool correctly, though it could have added sibling-routing guidance.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents query syntax, sort options, category filtering, and page size. The description adds no parameter meaning beyond what the schema provides, which is acceptable at this coverage level.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: search arXiv and return matching papers. An agent can distinguish it from get_paper and get_pdf_text, though the description does not explicitly name those siblings as alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no when-to-use guidance and does not mention when to choose get_paper, get_pdf_text, or category_trends instead. Usage is only implied by the word 'search'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedcategory_trends
    • First observedget_paper
    • First observedget_pdf_text
    • First observedsearch_papers

TDQS

A3.7/5.0

Scored across 4 tools

Disambiguation4/5

search_papers (discovery), category_trends (aggregate stats), and get_pdf_text (heavy full-text path) are clearly distinct. get_paper and get_pdf_text both operate on a single paper by identifier, but the metadata-vs-full-text distinction and the explicit 'use only when the abstract is insufficient' caveat keep them separable.

Naming Consistency4/5

Three of four tools follow a clean verb_noun snake_case pattern (search_papers, get_paper, get_pdf_text). category_trends breaks the pattern as a noun_phrase, though it remains readable and unambiguous.

Tool Count4/5

Four tools is on the lean side but each covers a genuinely different retrieval mode (search, metadata, full text, trends) with no filler. It is slightly thin for a research-paper server but not under-scoped.

Completeness4/5

The core lifecycle — find papers, read metadata, read full text, observe category activity — is covered with no dead ends. Minor gaps exist: no author-based lookup, no listing of recent papers by date, and no citation/export (BibTeX) helper, which agents must approximate via search queries.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    Not graded
    maintenance
    Enables AI assistants to search and retrieve academic papers from arXiv through MCP tools, supporting search by various criteria, detailed paper information, category browsing, and PDF content extraction.
    4
    53 npm
    2
    -
  • F
    license
    A
    quality
    D
    maintenance
    Enables agents to search papers across Semantic Scholar and arXiv, read and extract text from arXiv PDFs, align records across sources, and produce structured literature-analysis digests.
    10
    1
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables searching arXiv papers, extracting paper information, listing paper topics, and generating structured research prompts through MCP tools, resources, and prompts.
    -