The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Swiss Academic Libraries MCP listing page.
🇨🇭 Part of the Swiss Public Data MCP Portfolio
MCP server providing access to Swiss academic libraries — swisscovery, e-rara, e-periodica, e-manuscripta. No API key required.
swiss-academic-libraries-mcp connects AI models to the full Swiss academic library infrastructure via standardised, open protocols. It covers the swisscovery union catalogue (500+ libraries, 10M+ records) and three digitalisation platforms: historical prints (e-rara), periodicals (e-periodica) and manuscripts (e-manuscripta).
All data sources use open, authentication-free protocols (SRU/MARC21, OAI-PMH/Dublin Core). The server supports both local use via Claude Desktop (stdio transport) and cloud deployment (Streamable HTTP).
Beyond the catalogue, the server also covers Swiss open-access legal literature — freely readable legal scholarship from sui generis, ex/ante and Repositorium.ch — as metadata (title, authorship, year, licence, DOI, link), never full text.
It also adds the international metadata layer: DOI resolution and international research literature via Crossref, and preprints via arXiv. This lets one conversation answer both "is this held in Switzerland?" (national layer) and "what is this, and where else does it live?" (international layer). Every resolved DOI returns title, ISSN, ISBN and authors as clean top-level fields, so you can pivot straight into swisscovery.
Anchor demo query (national ↔ international): "Find the original publication for this DOI, check whether a preprint version exists, and show whether a Swiss library holds it." → resolve_doi → search_preprints → swisscovery_search(query="<ISSN from resolve_doi>").
Anchor demo query (catalogue): "Which Swiss university dissertations on primary school pedagogy are held in Swiss libraries, and are any of them digitised in e-rara?"
Anchor demo query (OA legal literature): "Which freely accessible legal-scholarship articles exist on data protection in education? Give me title, authorship, year, licence and DOI." → oa_law_search(query="Datenschutz im Bildungsbereich") — results are ranked by relevance: articles matching all terms rank first, articles matching only the core term (Datenschutz) follow, so the query returns the real privacy-law corpus rather than an empty set.
CROSSREF_MAILTO), preprint search via arXiv with automatic phrase quoting and request throttling — clean title/ISSN/ISBN/author fields for pivoting into swisscoveryresearch-workflow, education-research and doi-to-swiss-shelf| Source | Protocol | Content | Records |
|---|---|---|---|
| swisscovery (SLSP) | SRU / MARC21 | 500+ Swiss libraries | 10M+ |
| e-rara | OAI-PMH / Dublin Core | Digitised historical prints | 250k+ |
| e-periodica | OAI-PMH / Dublin Core | Digitised periodicals (1750–today) | 1M+ articles |
| e-manuscripta | OAI-PMH / Dublin Core | Manuscripts & archival material | 100k+ |
| Source | Protocol | Content | DOI coverage |
|---|---|---|---|
| sui generis | OAI-PMH / Dublin Core | OA legal journal & non-profit publisher | ~100 % (10.21257/…) |
| ex/ante | OAI-PMH / Dublin Core | Peer-reviewed journal for (young) legal scholarship, multilingual | none (persistent URL) |
| Repositorium.ch | Supabase / PostgREST (JSON) | Subject repository for Swiss law | partial |
| Source | Protocol | Content | Licence |
|---|---|---|---|
| Crossref | REST / JSON | DOI resolution + international research literature | Metadata CC0 1.0 (public domain) |
| arXiv | Atom / XML | Preprints (CS, physics, maths, stats, …) | Metadata CC0 1.0; preprints per author licence |
| Tool | Source | Function |
|---|---|---|
library_info | — | Entry point: overview of all sources and tools |
swisscovery_search | swisscovery | Full-text / CQL search across the union catalogue |
swisscovery_get_record | swisscovery | Single record by MMS-ID |
erara_list_records | e-rara | Prints filtered by date / collection |
erara_get_record | e-rara | Single item by OAI identifier |
erara_list_collections | e-rara | All participating libraries |
eperiodica_list_records | e-periodica | Articles filtered by date |
eperiodica_get_record | e-periodica | Single article by OAI identifier |
emanuscripta_list_records | e-manuscripta | Manuscripts filtered by date / collection |
emanuscripta_get_record | e-manuscripta | Single object by OAI identifier |
emanuscripta_list_collections | e-manuscripta | All archives / collections |
oa_law_search | OA legal (all 3) | Search OA legal scholarship (title/abstract/author) with source, language, year and peer-review filters |
oa_law_get | OA legal (all 3) | Single OA legal article by DOI or resolvable URL |
resolve_doi | Crossref | Resolve a DOI to full metadata (title/ISSN/ISBN/authors → pivot into swisscovery) |
search_publications | Crossref | Search international research literature; every hit carries a DOI |
search_preprints | arXiv | Search preprints with automatic phrase quoting; linked journal DOIs bridge to resolve_doi |
| Query | Tool |
|---|---|
| "Which books about Swiss primary schools are held in Swiss libraries?" | swisscovery_search |
| "Show digitised historical works from ETH Library" | erara_list_records |
| "Which Swiss periodicals were digitised in 2023?" | eperiodica_list_records |
| "What manuscript collections does e-manuscripta hold?" | emanuscripta_list_collections |
| "Which OA legal articles exist on facial recognition?" | oa_law_search |
| "Resolve DOI 10.1038/nature14539 and give me its ISSN" | resolve_doi |
| "Find recent preprints on model context protocol" | search_preprints |
| "Find this paper's DOI, check for a preprint, and see if a Swiss library holds it" | resolve_doi → search_preprints → swisscovery_search |
Three independent paths share one HTTP client (retry with exponential backoff, shared connection pool, project User-Agent):
api.crossref.org and export.arxiv.org.This server is deliberately conservative about what it emits — a portfolio that treats governance as a feature cannot be careless here.
licence is always set. Open Access means free to read, not free to reuse. Licences range from CC0 and CC BY to CC BY-NC-ND, and some articles are simply "free to read" with no open licence at all. When no machine-readable licence is available, the field is "unknown" — never guessed, never omitted. The native OAI metadata of all three sources carries only copyright statements, so "unknown" is the default; a best-effort Crossref lookup upgrades it to the real CC licence where a DOI resolves (e.g. sui generis → CC BY-SA 4.0). Disable with OA_LAW_CROSSREF_ENRICH=0.language field is always populated, but results are only filtered by language when you explicitly ask — otherwise half the Romandie would vanish from the results.eth-library-mcp (ETH-institution Discovery & Persons) — the catalogue finds the book, the OA-legal path finds the freely readable article with its licence and DOI."unknown" is common and only lifted where a DOI resolves in Crossref.| Finding | Detail | Consequence |
|---|---|---|
| Crossref is weak for German-language CH education literature | query.bibliographic=Lehrplan 21 returns a book chapter from 1881 as the top hit; other CH-education queries surface millions of irrelevant results with off-topic/old top hits. Crossref is strong for DOI resolution and international research, not for CH education publishing. | search_publications documents this and points to swisscovery_search / oa_law_search for CH education topics. |
| arXiv treats spaces as OR, not phrase | all:model context protocol → ~1,296,686 hits (OR); all:"model context protocol" → 462 hits (phrase). | search_preprints quotes the query automatically; users need no arXiv syntax. Field syntax (ti:, au:) and explicit quotes are respected. |
arXiv returns Atom XML, throttles, and 301-redirects http→https | Response is Atom, not JSON; arXiv asks for ~3 s between requests; the plain http:// endpoint 301-redirects. | Parser reuses defusedxml (already a dependency — no new one); a module-level throttle (ARXIV_MIN_INTERVAL_SECONDS, default 3 s) enforces spacing; the client calls the https:// endpoint directly. |
| SHARE (share.osf.io) — evaluated, not built | The _search endpoint responds (≈58.9 M records, Elasticsearch-style), but SHARE wound down harvesting in 2020, archived its database in CurateND, and carries no API-maintenance commitment ("shutting down / new phase", now the "trove" search-api). For a portfolio positioned on reliability, an index without a support guarantee is a poor dependency. | Not implemented. |
| Open Library — evaluated, gate failed | A 10-ISBN probe of real Swiss/German-language Lehrmittel (Lehrmittelverlag Zürich, Klett und Balmer; ISBNs harvested from swisscovery) returned 0/10 = 0 % (threshold 60 %). Controls confirm Open Library works (English + mainstream German trade books resolve), so it is a genuine coverage gap, not a connectivity artefact. | Not implemented. swisscovery already covers CH Lehrmittel; for the book trade, GVI or a publisher directory is the fitting route. |
Add to claude_desktop_config.json:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Restart Claude Desktop — the server starts automatically on first use.
Security & Deployment Notes
127.0.0.1 (loopback only). The server has no
built-in authentication.--host 0.0.0.0 only when running behind a reverse proxy that
provides authentication and per-IP rate limits (e.g. nginx with
limit_req + OAuth2-Proxy). Non-loopback bindings emit a WARN log.MCP_LOG_LEVEL=DEBUG|INFO|WARNING.Start by calling library_info for a full overview. Then:
💡 "No API key — just install and query."
No API keys required. All environment variables are optional.
| Parameter | Default | Description |
|---|---|---|
--http | off | Enable Streamable HTTP transport |
--port | 8000 | Port for HTTP transport |
MCP_LOG_LEVEL | INFO | Log verbosity (DEBUG/INFO/WARNING) |
OA_LAW_CROSSREF_ENRICH | 1 | OA legal: DOI→licence enrichment via Crossref; set 0 to disable |
OA_LAW_REPOSITORIUM_ANON_KEY | (public key) | OA legal: override for Repositorium.ch's public read-only Supabase anon key (allows rotation without a code change) |
CROSSREF_MAILTO | (unset) | International: contact e-mail for Crossref's "polite pool" (better throughput). If unset, requests use the anonymous pool — functional, just slower. |
ARXIV_MIN_INTERVAL_SECONDS | 3.0 | International: minimum spacing between arXiv requests (arXiv asks for restraint). |
This server speaks two protocol eras over the same endpoint. The client's first request on a connection decides which one applies; a later claim from the other era is refused.
| Era | Revision | Who reaches it |
|---|---|---|
initialize handshake | 2024-11-05 … 2025-11-25 | What today's clients speak. The server answers with the revision asked for, or with the 2025-11-25 ceiling when the request asks for something newer. |
| Per-request envelope | 2026-07-28 | A request carrying the 2026-07-28 _meta envelope opens a modern connection. |
Both revisions are pinned in
tests/test_protocol_version.py and asserted
against the installed SDK, so a Dependabot bump of mcp cannot move either one
silently. This server builds no ASGI app to send an initialize through, so
the gate asserts the SDK constants rather than a measured response — the
weaker form, named rather than left unsaid.
Note that the SDK's LATEST_PROTOCOL_VERSION is an alias for the modern
era, not for the handshake era — pinning against it alone would leave the era
that current clients actually negotiate free to drift.
Update policy. When the gate fails, do not edit the constant blindly: read
the spec changelog between the two revisions, verify the server still behaves,
then move the constant, this section, README.de.md and
CHANGELOG.md together.
The fixtures under tests/fixtures/ are recorded from the live sources, with
the same request parameters the production code sends, and dated. Source,
retrieval date, selection rule and SHA-256 for every file:
tests/fixtures/PROVENANCE.md.
This matters because a hand-written mock encodes its author's assumption and can
therefore never refute it — production code and fixture come from the same
reading of the docs. The invented ListSets response carried no
resumptionToken, so no test could notice that nobody followed one: e-rara
serves 105 collections in pages of 10, and the tool reported 10.
Where a fixture is trimmed, the count fields (numberOfRecords,
completeListSize, total-results) and the resumptionToken keep their real
values — they say how much is not in the file.
tests/fixtures/oai_ex_ante_listrecords.xml is deliberately not well-formed
XML: ex/ante emits a raw control character inside a dc:description, and
recording it verbatim is what proves strip_invalid_xml_chars is load-bearing.
from_date / until_date and keep max_records reasonable. The server enforces a 30s timeout per request.Contributions are welcome! Please read CONTRIBUTING.md for guidelines on:
This project follows the conventions of the Swiss Public Data MCP Portfolio.
To report a vulnerability, please follow the responsible disclosure process in SECURITY.md. The server is read-only and requires no API key; see the Safety & Limits section above for the security model.
See CHANGELOG.md
If you self-host this server for a Swiss school authority, archive, or municipal use case:
MaxRetentionSec).MIT License — see LICENSE
Hayal Oezkan · github.com/malkreide
| Server | Description |
|---|---|
zurich-opendata-mcp | City of Zurich Open Data |
eth-library-mcp | ETH Library Discovery & Persons API |
swiss-statistics-mcp | Swiss Federal Statistics (BFS) |
fedlex-mcp | Swiss Federal Law via Fedlex SPARQL |
swiss-transport-mcp | OJP journey planning, SIRI-SX disruptions |
Run via uv's uvx — no clone or manual install needed. Add to your MCP client config (mcpServers for Claude Desktop, Cursor and Windsurf; use a top-level servers key for VS Code in .vscode/mcp.json):