The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Semantic Scholar MCP listing page.
A 14-tool Semantic Scholar MCP server for academic research workflows. Direct access to 200M+ papers from Semantic Scholar — paper search, citation graph traversal, author profiles, and recommendations — from any Model Context Protocol client (e.g., Claude Desktop, Claude Code, Cursor, Cline, Continue, and others).
Every release ships verifiable supply-chain provenance: Sigstore-signed SLSA build-provenance attestations on the wheel, sdist, and container image; PEP 740 attestations on the PyPI upload; and a CycloneDX SBOM — so you can prove the artifact you installed was built from this repo. See Provenance & supply chain.
Author: Santiago Maniches · ORCID 0009-0005-6480-1987 · TOPOLOGICA LLC
No API key is needed to start (public rate limit: 1 req/sec); set
SEMANTIC_SCHOLAR_API_KEY for 10 req/sec. Claude Desktop, Docker, pip, and
remote (Streamable HTTP) setups are in Installation.
A research tool is only as trustworthy as the chain from its source to the binary you run. Every release of this server ships cryptographically verifiable supply-chain evidence, all generated in CI from the tagged commit:
| Guarantee | What it proves | Where it is produced |
|---|---|---|
| SLSA build provenance (wheel + sdist) | the published distributions were built by this repo's publish.yml from the released tag, not hand-uploaded | publish.yml — actions/attest-build-provenance (build job) |
| SLSA build provenance (container image) | the ghcr.io image digest was built by this repo's docker.yml | docker.yml — actions/attest-build-provenance, push-to-registry (lines 141–147) |
| PEP 740 attestations | the PyPI upload itself carries Sigstore-backed attestations under Trusted Publishing | publish.yml — attestations: true (publish-pypi job) |
| CycloneDX SBOM | a machine-readable bill of materials, generated in an unprivileged job from the exact wheel's statically resolved dependency metadata (wheels only, none of it executed), SHA-256-bound to that wheel, then attested against the wheel alone | publish.yml — cyclonedx-py + scripts/release_sbom.py (sbom job) + actions/attest-sbom (attest-sbom job) |
| SHA-pinned Actions | every CI action is pinned to a commit SHA, so the release pipeline itself cannot silently change | all jobs in .github/workflows/ (e.g. publish.yml, docker.yml) |
Verify the wheel and the container image against their attestations with the GitHub CLI:
The full supply-chain posture, including the known-limitations list, is in SECURITY.md. This is release-time provenance (proving how the artifact was built); the server does not currently attach a per-response receipt to individual API results.
There is no public Semantic Scholar MCP standard, so the most useful comparison is against the obvious alternative: calling the Semantic Scholar REST API yourself from an agent. Everything in the right-hand column is plumbing this server already owns and the caller would otherwise reimplement.
| This server | Raw S2 REST API from an agent | |
|---|---|---|
| Tool surface | 14 typed MCP tools (search, retrieval, recommendations, status) | caller composes raw HTTP requests |
| Citation graph | both directions (citations and references) in get_paper | manual paging over two endpoints |
| Bulk operations | papers (≤500) and authors (≤1000) in one call | caller batches and paginates |
| Full-text snippet search | snippet_search with surrounding context | separate endpoint, caller-assembled |
| Paper-ID resolution | seven formats — Semantic Scholar ID, DOI, ArXiv, PubMed, Corpus ID, ACL, URL — validated pre-flight (validators.py) | caller normalizes and validates IDs |
| Rate limiting | client-side per-tier limiter, never exceeds the interval (client.py) | caller throttles by hand |
| Retry / backoff | bounded, jittered retry on 429/502/503/timeout, honors Retry-After (client.py) | caller implements retry |
| Errors | typed exception hierarchy, branchable by caller (errors.py) | parse HTTP status strings |
| Output | chat-tuned Markdown or JSON per call (formatters.py) | raw JSON |
| Supply-chain provenance | SLSA + PEP 740 + CycloneDX SBOM per release (see above) | n/a |
| Citability | minted Zenodo DOI, MIT licensed | n/a |
Add to %APPDATA%\Claude\claude_desktop_config.json:
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
See Remote access (Streamable HTTP) for client configuration, per-request API keys, and deployment guidance.
Note: Get a free API key at semanticscholar.org/product/api. Without a key, you get rate-limited public access (1 req/sec).
Module responsibilities (src/semantic_scholar_mcp/):
| Module | Responsibility |
|---|---|
server.py | FastMCP instance, 14 @mcp.tool registrations, lifespan, main() entry. Re-exports the helper surface for back-compat. |
transport.py | Streamable HTTP transport: CLI/env parsing (--transport http), uvicorn wiring, and per-request API-key extraction (header / query param / Smithery config) into a request-scoped contextvar. |
client.py | Shared httpx.AsyncClient singleton, per-tier rate limiter (1 req/s public, 10 req/s keyed), retry loop with exponential backoff + jitter on 429/502/503/timeout, HTTP→typed-exception mapping. |
models.py | Pydantic input models per tool, ResponseFormat enum, the four tiered field-set constants (PAPER_SEARCH_FIELDS, …_LITE, PAPER_BULK_SEARCH_FIELDS, PAPER_DETAIL_FIELDS, AUTHOR_FIELDS). |
validators.py | Pre-flight paper-ID validation. Rejects NUL bytes, ?, #, path traversal; accepts the seven canonical ID formats. |
cache.py | In-memory TTL cache (5 min, 200 entries, oldest-first eviction) for paper/author lookups within a session. |
formatters.py | Markdown renderers for paper and author dicts, tuned for chat-surface readability. |
errors.py | SemanticScholarError hierarchy: AuthenticationError, RateLimitError, NotFoundError, ValidationError, ServerError. |
logging_config.py | One-JSON-per-line StructuredFormatter on stderr; safe to ship through any log aggregator. |
Design choices worth knowing
httpx.AsyncClient per process. Created lazily, closed in the FastMCP lifespan teardown. Amortizes connection setup; respects keep-alive limits. The lifespan is reference-counted: under the Streamable HTTP transport the SDK enters it per request, so teardown only runs when the last holder exits.MAX_RETRIES = 3, base 1 s, capped at 30 s. Honors Retry-After when present.AuthenticationError vs RateLimitError vs NotFoundError instead of parsing strings.__version__ is derived from importlib.metadata.version("s2-mcp-server"), so bumping pyproject.toml is sufficient; release-please bumps the manifest, server.json (×2 paths), CITATION.cff, and .zenodo.json in lockstep on every release.You can provide your API key in three ways:
Environment Variable (recommended for persistent use):
Per-request HTTP header (Streamable HTTP transport only): send
x-api-key: your-key with each request — see
Remote access (Streamable HTTP).
Per-Request Parameter (overrides env var):
Deprecated: per-request
api_keyis deprecated and will be removed in v2.0.0. Tool-call arguments may be visible in MCP transcripts, client logs, and the LLM's tool-call history. Use theSEMANTIC_SCHOLAR_API_KEYenvironment variable instead. See SECURITY.md for details.
Get a free API key at: https://www.semanticscholar.org/product/api
Add to your Claude Desktop config file:
Windows: %APPDATA%\Claude\claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Linux: ~/.config/Claude/claude_desktop_config.json
Then restart Claude Desktop.
stdio remains the default transport. --transport http serves the same 14
tools over the MCP Streamable HTTP transport,
which is what remote clients — claude.ai custom connectors, Smithery
listings, mcp-remote bridges — connect to.
Requires
s2-mcp-server≥ 1.5.0. Earlier releases (≤ 1.4.0) do not parse CLI flags: they silently ignore--transport httpand start a stdio server instead, never opening the port.
| Flag | Env var | Default | Meaning |
|---|---|---|---|
--transport | MCP_TRANSPORT | stdio | stdio, http (alias: streamable-http) |
--host | MCP_HOST | 127.0.0.1 | Bind address (0.0.0.0 in the Docker image) |
--port | MCP_PORT, then PORT | 8000 | Bind port (PORT is honored for hosting platforms) |
--path | MCP_PATH | /mcp | URL path of the MCP endpoint |
| — | MCP_STATELESS_HTTP | true | One independent server interaction per request (recommended) |
| — | MCP_JSON_RESPONSE | true | Plain JSON responses instead of SSE streams |
CLI flags beat environment variables. The server is stateless and returns JSON by default — the configuration recommended for production Streamable HTTP deployments — and no tool relies on sessions, streaming, or server-initiated messages, so there is no functional trade-off.
When served over HTTP, each request may carry its own Semantic Scholar API key; concurrent users never share or observe each other's keys. Sources, in precedence order:
x-api-key HTTP header (recommended)SEMANTIC_SCHOLAR_API_KEY query parameter (Smithery session config)api_key query parameter?config= parameter (older Smithery deployments)A request without a key falls back to the server's SEMANTIC_SCHOLAR_API_KEY
environment variable, or to keyless public-tier access.
Claude Code
JSON config (clients that accept a url)
claude.ai custom connectors require a public HTTPS URL and accept either
authless servers or OAuth — API keys in the connector URL are not supported
by claude.ai. Host the server with the key supplied server-side
(SEMANTIC_SCHOLAR_API_KEY env var) and register the public /mcp URL as
the connector.
Smithery lists remote servers by URL (smithery mcp publish <url>); the
per-request key extraction above is compatible with Smithery session config
out of the box.
127.0.0.1). Expose it publicly only behind a
TLS-terminating reverse proxy, and prefer the x-api-key header over query
parameters (URLs end up in access logs).x-api-key header.)The server accepts the following paper identifier formats:
| Format | Pattern | Example |
|---|---|---|
| Semantic Scholar ID | 40-character hex | 649def34f8be52c8b66281af98ae884c09aef38b |
| DOI | DOI:xxx | DOI:10.1038/s41586-021-03819-2 |
| ArXiv | ARXIV:xxx | ARXIV:2106.15928 or ARXIV:2106.15928v2 |
| PubMed | PMID:xxx | PMID:32908142 |
| Corpus ID | CorpusId:xxx | CorpusId:215416146 |
| ACL | ACL:xxx | ACL:P19-1285 |
| URL | URL:xxx | URL:https://arxiv.org/abs/2106.15928 |
semantic_scholar_search_papersSearch for academic papers with advanced filters.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search query (supports AND, OR, NOT operators and "phrase search") |
year | string | No | Year filter: "2024", "2020-2024", or "2020-" |
fields_of_study | string[] | No | Filter by fields: ["Computer Science", "Biology"] |
publication_types | string[] | No | Filter by type: ["Review", "JournalArticle"] |
open_access_only | boolean | No | Only return open access papers (default: false) |
min_citation_count | integer | No | Minimum citation count |
limit | integer | No | Max results 1-100 (default: 10) |
offset | integer | No | Pagination offset (default: 0) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
Example:
JSON Example:
semantic_scholar_get_paperGet detailed information about a specific paper.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
paper_id | string | Yes | Paper ID in any supported format |
include_citations | boolean | No | Include citing papers (default: false) |
include_references | boolean | No | Include referenced papers (default: false) |
citations_limit | integer | No | Max citations to return 1-100 (default: 10) |
references_limit | integer | No | Max references to return 1-100 (default: 10) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
Example:
JSON Example:
semantic_scholar_search_authorsSearch for academic authors by name.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Author name to search |
limit | integer | No | Max results 1-100 (default: 10) |
offset | integer | No | Pagination offset (default: 0) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
Example:
JSON Example:
semantic_scholar_get_authorGet author profile with publications.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
author_id | string | Yes | Semantic Scholar author ID |
include_papers | boolean | No | Include publications (default: true) |
papers_limit | integer | No | Max papers to return 1-100 (default: 20) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
Example:
JSON Example:
semantic_scholar_recommendationsGet AI-powered paper recommendations based on a seed paper.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
paper_id | string | Yes | Seed paper ID in any supported format |
from_pool | string | No | Recommendation pool: "recent" (default) or "all-cs" |
limit | integer | No | Max recommendations 1-100 (default: 10) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
Example:
JSON Example:
semantic_scholar_bulk_papersRetrieve multiple papers in a single request (max 500).
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
paper_ids | string[] | Yes | List of paper IDs (max 500) |
response_format | string | No | "markdown" or "json" (default: json) |
api_key | string | No | Override environment API key |
Example:
JSON Example:
semantic_scholar_bulk_searchSearch papers with sorting and cursor-based pagination for large result sets.
Unlike search_papers, supports a sort order and returns a token for
paging through all results.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search query |
sort | string | No | Sort order, e.g. "citationCount:desc", "publicationDate:asc" |
token | string | No | Continuation token from a previous bulk_search response |
year | string | No | Year filter: "2024", "2020-2024", "2020-" |
fields_of_study | string[] | No | Filter by fields: ["Computer Science"] |
publication_types | string[] | No | Filter by type: ["Review", "JournalArticle"] |
min_citation_count | integer | No | Minimum citation count |
limit | integer | No | Max results per page 1-1000 (default: 100) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
JSON Example:
Returns: total result count, the page of papers, and a token for the
next page (when more results exist).
semantic_scholar_export_citationExport a citation for a paper in BibTeX format.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
paper_id | string | Yes | Paper ID in any supported format |
format | string | No | Citation format (currently only "bibtex") |
api_key | string | No | Override environment API key |
JSON Example:
Returns: the BibTeX string for the requested paper.
semantic_scholar_match_paperFind the single best paper matching a title string. Returns a numeric
matchScore alongside the matched paper.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Paper title to match (1-500 chars) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
JSON Example:
Returns: the best-matching paper plus its matchScore, or "No matching
paper found." if no match.
semantic_scholar_paper_authorsGet full author profiles for a paper's authors (richer than the abbreviated
author list returned by get_paper).
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
paper_id | string | Yes | Paper ID in any supported format |
limit | integer | No | Max authors to return 1-1000 (default: 100) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
JSON Example:
Returns: the list of full author records for the paper.
semantic_scholar_author_batchRetrieve multiple authors in a single request (max 1000).
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
author_ids | string[] | Yes | List of author IDs (1-1000) |
response_format | string | No | "markdown" or "json" (default: json) |
api_key | string | No | Override environment API key |
JSON Example:
Returns: counts of requested / retrieved, the retrieved author
records, and a not_found list of IDs the API did not return.
semantic_scholar_multi_recommendGet recommendations using multiple positive (and optional negative) example papers.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
positive_paper_ids | string[] | Yes | Papers to find similar results for (1-100) |
negative_paper_ids | string[] | No | Papers to steer recommendations away from (0-100) |
limit | integer | No | Max recommendations 1-500 (default: 10) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
JSON Example:
Returns: the recommended papers plus an echo of the positive/negative seeds used.
semantic_scholar_snippet_searchSearch within paper full text and return text snippets with surrounding context. Heavily rate-limited without an API key.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search query for paper text (1-500 chars) |
paper_ids | string[] | No | Limit search to specific papers (max 100) |
year | string | No | Year filter: "2024", "2020-2024", "2020-" |
fields_of_study | string[] | No | Filter by fields: ["Computer Science"] |
min_citation_count | integer | No | Minimum citation count |
limit | integer | No | Max results 1-100 (default: 10) |
response_format | string | No | "markdown" or "json" (default: markdown) |
api_key | string | No | Override environment API key |
JSON Example:
Returns: matching snippets, each with the source paper title, section, and a short text excerpt.
semantic_scholar_statusCheck server health and API connectivity status.
Parameters: None
Example:
Response:
| Tier | Requests/Second | How to Get |
|---|---|---|
| No API Key | 1 req/sec | Default |
| API Key | 10 req/sec | Sign up (free) |
| Academic Partner | 10-100 req/sec | Apply via S2 |
Note: The client-side rate limiter enforces the intervals above. The upstream Semantic Scholar API may impose stricter limits during high-traffic periods.
The server automatically handles rate limiting with:
API keys are never persisted to disk by the server. When the server makes
authenticated requests, the key is sent only to api.semanticscholar.org
over HTTPS as the x-api-key header. No telemetry is sent to any third
party. Under the default stdio transport the server runs locally on your
machine; if you connect to a remotely hosted instance over
Streamable HTTP, your per-request key also
transits that endpoint's operator before being forwarded to Semantic Scholar
— only send keys to remote endpoints you trust, and only over HTTPS.
Prefer the SEMANTIC_SCHOLAR_API_KEY environment variable over the
per-request api_key tool parameter. The per-request parameter is
deprecated (removal planned for v2.0.0) because tool-call arguments may
be visible in MCP transcripts and client logs. See SECURITY.md
for vulnerability reporting and the known-limitations list.
alphafold-sovereign-mcp — Model Context Protocol server for AlphaFold DB and other public biomedical data sources, with a local SQLite knowledge graph (pip install alphafold-sovereign-mcp).uniprot-mcp — Model Context Protocol server for UniProt Swiss-Prot and TrEMBL (pip install uniprot-mcp-server).MIT License - see LICENSE file.
Santiago Maniches
Contributions welcome! Please read our Contributing Guidelines.
Built by TOPOLOGICA LLC