Gives Claude direct access to Switzerland's academic library infrastructure through open protocols. You get 11 tools across four sources: swisscovery (the union catalog covering 500+ libraries and 10M+ records via SRU/MARC21), plus three digitization platforms for historical prints, periodicals, and manuscripts (all via OAI-PMH/Dublin Core). No API keys needed. Search the union catalog with full CQL syntax, harvest records by date range or collection, and parse MARC21 or Dublin Core metadata into structured output. Built on FastMCP with dual transport support. Reach for this when you need to query Swiss academic holdings, track down digitized historical materials, or build research workflows that span physical and digital collections.
🇨🇭 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):
┌──────────────────────────────────────────────────────┐
│ swiss-academic-libraries-mcp │
│ (FastMCP · stdio / HTTP) │
└────────┬───────────────┬────────────────┬────────────┘
│ │ │
┌── CATALOGUE ─────┘ ┌── OA-LEGAL ┘ ┌── INTERNATIONAL ──┐
│ (api_client.py) │ (oa_legal.py) │ (intl_metadata.py)│
│ │ │ │
┌────────┴────────┐ ┌─────────┴──────────┐ ┌───┴──────────────────┐ │
│ swisscovery SRU │ │ source registry │ │ Crossref REST/JSON │ │
│ e-rara OAI │ │ ├ sui generis OAI │ │ ├ resolve_doi │ │
│ e-periodica OAI │ │ ├ ex/ante OAI │ │ └ search_publications│ │
│ e-manuscripta │ │ └ Repositorium REST│ │ arXiv Atom/XML │ │
└────────┬────────┘ │ harvest→cache→filter│ │ └ search_preprints │ │
│ │ Crossref licence ⟳ │ │ (phrase-quote+throttle)│ │
MARC21 / Dublin Core └─────────┬──────────┘ └───┬──────────────────┘ │
→ catalogue records OaLegalPublication CrossrefWork / Preprint │
(metadata, no full text) (metadata, no full text) │
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
{
"mcpServers": {
"swiss-academic-libraries": {
"command": "uvx",
"args": ["swiss-academic-libraries-mcp"]
}
}
}
Restart Claude Desktop — the server starts automatically on first use.
uvx swiss-academic-libraries-mcp --http --port 8000 [--host 127.0.0.1]
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.git clone https://github.com/malkreide/swiss-academic-libraries-mcp
cd swiss-academic-libraries-mcp
pip install -e .
Start by calling library_info for a full overview. Then:
"Which books about Swiss primary schools are held in Swiss libraries?"
→ swisscovery_search(query='subject = "Volksschule"', max_records=20)
"Show digitised historical works from ETH Library"
→ erara_list_records(set_spec="zut")
"Which Swiss periodicals were digitised in 2023?"
→ eperiodica_list_records(from_date="2023-01-01", until_date="2023-12-31")
"What manuscript collections does e-manuscripta hold?"
→ emanuscripta_list_collections()
→ [More use cases by audience](EXAMPLES.md) →
💡 "No API key — just install and query."
Full text: Volksschule Zürich
Title: title = "education reform"
Author: creator = "Pestalozzi"
Subject: subject = "pedagogy"
ISBN: isbn = "978-3-05-006234-0"
Combined: title = "school" AND creator = "Pestalozzi"
Pagination: start_record = 11
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). |
swiss-academic-libraries-mcp/
├── src/
│ └── swiss_academic_libraries_mcp/
│ ├── __init__.py # Package init
│ ├── server.py # FastMCP server, 16 tools, 3 prompts, 3 resources
│ ├── api_client.py # HTTP client (+ retry), MARC21 + OAI-PMH/DC parsers
│ ├── oa_legal.py # OA legal-literature registry, adapters, model
│ └── intl_metadata.py # International layer: Crossref + arXiv adapters, models
├── tests/
│ ├── test_server.py # catalogue unit tests + live smoke tests
│ ├── test_20_scenarios.py # end-to-end catalogue scenarios
│ ├── test_oa_legal.py # OA legal-literature tests (mocked + live)
│ └── test_intl_metadata.py # International-layer tests (mocked + live)
├── pyproject.toml
├── CHANGELOG.md
├── CONTRIBUTING.md # Contributing guide (English)
├── CONTRIBUTING.de.md # Contributing guide (German)
├── SECURITY.md # Security policy (English)
├── SECURITY.de.md # Security policy (German)
├── LICENSE
├── README.md # This file (English)
└── README.de.md # German version
# Unit tests (no network required)
PYTHONPATH=src pytest tests/ -m "not live"
# Live smoke tests (internet required)
PYTHONPATH=src pytest tests/ -m "live"
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):
{
"mcpServers": {
"swiss-academic-libraries-mcp": {
"command": "uvx",
"args": [
"swiss-academic-libraries-mcp"
]
}
}
}