{
  "markdown": "> 🇨🇭 **Part of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide)**\n\n# 📚 swiss-academic-libraries-mcp\n\n[![PyPI version](https://img.shields.io/pypi/v/swiss-academic-libraries-mcp)](https://pypi.org/project/swiss-academic-libraries-mcp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-purple)](https://modelcontextprotocol.io/)\n[![No Auth Required](https://img.shields.io/badge/auth-not%20required-lightgrey)](https://github.com/malkreide/swiss-academic-libraries-mcp)\n![CI](https://github.com/malkreide/swiss-academic-libraries-mcp/actions/workflows/ci.yml/badge.svg)\n\n> MCP server providing access to Swiss academic libraries — swisscovery, e-rara, e-periodica, e-manuscripta. No API key required.\n\n[🇩🇪 Deutsche Version](README.de.md)\n\n### Demo\n\n![Demo: Claude using swisscovery_search and erara_list_records](docs/assets/demo.svg)\n\n---\n\n## Overview\n\n**swiss-academic-libraries-mcp** connects AI models to the full Swiss academic library infrastructure via standardised, open protocols. It covers the [swisscovery](https://swisscovery.slsp.ch) union catalogue (500+ libraries, 10M+ records) and three digitalisation platforms: historical prints ([e-rara](https://www.e-rara.ch)), periodicals ([e-periodica](https://www.e-periodica.ch)) and manuscripts ([e-manuscripta](https://www.e-manuscripta.ch)).\n\nAll 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).\n\nBeyond the catalogue, the server also covers **Swiss open-access legal literature** — freely readable legal scholarship from [sui generis](https://sui-generis.ch), [ex/ante](https://ex-ante.ch) and [Repositorium.ch](https://www.repositorium.ch) — as **metadata** (title, authorship, year, licence, DOI, link), never full text.\n\nIt also adds the **international metadata layer**: DOI resolution and international research literature via [Crossref](https://www.crossref.org), and preprints via [arXiv](https://arxiv.org). 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.\n\n**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>\")`.\n\n**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?\"*\n\n**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.\n\n---\n\n## Features\n\n- **16 tools** across 4 catalogue sources + 3 open-access legal-literature sources + 2 international metadata sources — all read-only, no API key required\n- **swisscovery search** with full CQL syntax: full-text, title, author, subject, ISBN/ISSN\n- **OAI-PMH harvesting** with date range and collection filters plus pagination via resumption tokens\n- **MARC21 parser** extracting 20+ fields (title, creator, publication info, subjects, abstract, URLs)\n- **Dublin Core parser** for all three digitalisation portals\n- **Dual transport**: stdio for Claude Desktop · Streamable HTTP for cloud/self-hosted deployments\n- **OA legal-literature search** across sui generis, ex/ante and Repositorium.ch with a declarative source registry (new sources = one config entry), best-effort Crossref licence enrichment, and graceful per-source degradation\n- **International metadata layer**: DOI resolution and bibliographic search via Crossref (polite pool via `CROSSREF_MAILTO`), preprint search via arXiv with automatic phrase quoting and request throttling — clean title/ISSN/ISBN/author fields for pivoting into swisscovery\n- **3 built-in prompts**: `research-workflow`, `education-research` and `doi-to-swiss-shelf`\n- **Markdown and JSON output** for all tools\n- **97 unit/mocked tests** (no network) + 30 live smoke tests\n\n---\n\n## Data Sources\n\n| Source | Protocol | Content | Records |\n|--------|----------|---------|---------|\n| [swisscovery (SLSP)](https://swisscovery.slsp.ch) | SRU / MARC21 | 500+ Swiss libraries | 10M+ |\n| [e-rara](https://www.e-rara.ch) | OAI-PMH / Dublin Core | Digitised historical prints | 250k+ |\n| [e-periodica](https://www.e-periodica.ch) | OAI-PMH / Dublin Core | Digitised periodicals (1750–today) | 1M+ articles |\n| [e-manuscripta](https://www.e-manuscripta.ch) | OAI-PMH / Dublin Core | Manuscripts & archival material | 100k+ |\n\n### Open-Access legal literature (metadata only)\n\n| Source | Protocol | Content | DOI coverage |\n|--------|----------|---------|--------------|\n| [sui generis](https://sui-generis.ch) | OAI-PMH / Dublin Core | OA legal journal & non-profit publisher | ~100 % (`10.21257/…`) |\n| [ex/ante](https://ex-ante.ch) | OAI-PMH / Dublin Core | Peer-reviewed journal for (young) legal scholarship, multilingual | none (persistent URL) |\n| [Repositorium.ch](https://www.repositorium.ch) | Supabase / PostgREST (JSON) | Subject repository for Swiss law | partial |\n\n### International metadata layer (metadata only)\n\n| Source | Protocol | Content | Licence |\n|--------|----------|---------|---------|\n| [Crossref](https://www.crossref.org) | REST / JSON | DOI resolution + international research literature | Metadata CC0 1.0 (public domain) |\n| [arXiv](https://arxiv.org) | Atom / XML | Preprints (CS, physics, maths, stats, …) | Metadata CC0 1.0; preprints per author licence |\n\n---\n\n## Tools\n\n| Tool | Source | Function |\n|------|--------|----------|\n| `library_info` | — | Entry point: overview of all sources and tools |\n| `swisscovery_search` | swisscovery | Full-text / CQL search across the union catalogue |\n| `swisscovery_get_record` | swisscovery | Single record by MMS-ID |\n| `erara_list_records` | e-rara | Prints filtered by date / collection |\n| `erara_get_record` | e-rara | Single item by OAI identifier |\n| `erara_list_collections` | e-rara | All participating libraries |\n| `eperiodica_list_records` | e-periodica | Articles filtered by date |\n| `eperiodica_get_record` | e-periodica | Single article by OAI identifier |\n| `emanuscripta_list_records` | e-manuscripta | Manuscripts filtered by date / collection |\n| `emanuscripta_get_record` | e-manuscripta | Single object by OAI identifier |\n| `emanuscripta_list_collections` | e-manuscripta | All archives / collections |\n| `oa_law_search` | OA legal (all 3) | Search OA legal scholarship (title/abstract/author) with source, language, year and peer-review filters |\n| `oa_law_get` | OA legal (all 3) | Single OA legal article by DOI or resolvable URL |\n| `resolve_doi` | Crossref | Resolve a DOI to full metadata (title/ISSN/ISBN/authors → pivot into swisscovery) |\n| `search_publications` | Crossref | Search international research literature; every hit carries a DOI |\n| `search_preprints` | arXiv | Search preprints with automatic phrase quoting; linked journal DOIs bridge to `resolve_doi` |\n\n### Example Use Cases\n\n| Query | Tool |\n|-------|------|\n| *\"Which books about Swiss primary schools are held in Swiss libraries?\"* | `swisscovery_search` |\n| *\"Show digitised historical works from ETH Library\"* | `erara_list_records` |\n| *\"Which Swiss periodicals were digitised in 2023?\"* | `eperiodica_list_records` |\n| *\"What manuscript collections does e-manuscripta hold?\"* | `emanuscripta_list_collections` |\n| *\"Which OA legal articles exist on facial recognition?\"* | `oa_law_search` |\n| *\"Resolve DOI 10.1038/nature14539 and give me its ISSN\"* | `resolve_doi` |\n| *\"Find recent preprints on model context protocol\"* | `search_preprints` |\n| *\"Find this paper's DOI, check for a preprint, and see if a Swiss library holds it\"* | `resolve_doi` → `search_preprints` → `swisscovery_search` |\n\n---\n\n## Architecture\n\nThree independent paths share one HTTP client (retry with exponential backoff, shared connection pool, project `User-Agent`):\n\n```\n                    ┌──────────────────────────────────────────────────────┐\n                    │            swiss-academic-libraries-mcp               │\n                    │            (FastMCP · stdio / HTTP)                   │\n                    └────────┬───────────────┬────────────────┬────────────┘\n                             │               │                │\n          ┌── CATALOGUE ─────┘   ┌── OA-LEGAL ┘     ┌── INTERNATIONAL ──┐\n          │   (api_client.py)    │   (oa_legal.py)  │  (intl_metadata.py)│\n          │                      │                  │                    │\n ┌────────┴────────┐   ┌─────────┴──────────┐   ┌───┴──────────────────┐ │\n │ swisscovery SRU │   │ source registry     │   │ Crossref  REST/JSON  │ │\n │ e-rara      OAI │   │  ├ sui generis  OAI │   │  ├ resolve_doi        │ │\n │ e-periodica OAI │   │  ├ ex/ante      OAI │   │  └ search_publications│ │\n │ e-manuscripta   │   │  └ Repositorium REST│   │ arXiv     Atom/XML   │ │\n └────────┬────────┘   │ harvest→cache→filter│   │  └ search_preprints   │ │\n          │            │ Crossref licence ⟳  │   │ (phrase-quote+throttle)│ │\n MARC21 / Dublin Core  └─────────┬──────────┘   └───┬──────────────────┘ │\n → catalogue records   OaLegalPublication         CrossrefWork / Preprint │\n                       (metadata, no full text)   (metadata, no full text) │\n```\n\n- **Catalogue path** returns bibliographic records (books, digitised prints, periodicals, manuscripts).\n- **OA-legal path** harvests OA legal-scholarship metadata once, caches it in memory (small corpus), and filters locally — because OAI-PMH has no keyword search of its own. Adding a fourth OA source is a single registry entry, not new code.\n- **International path** resolves DOIs and searches Crossref/arXiv live (Architecture A — the endpoints are small, stable and public, so no dump/cache is needed). Each response carries its **own source attribution** (Crossref CC0 · arXiv acknowledgement), never a pooled one, and returns clean top-level title/ISSN/ISBN/author fields so the model can pivot into the catalogue path. A code-layer egress allow-list restricts outbound calls to `api.crossref.org` and `export.arxiv.org`.\n\n---\n\n## Licensing & Scope\n\nThis server is deliberately conservative about what it emits — a portfolio that treats governance as a feature cannot be careless here.\n\n- **Metadata, not full text.** For OA legal literature the server returns **title, authorship, year, licence, DOI/link and — where the source provides it as a metadatum — the abstract**. It never ingests, stores, or outputs the article body. The full-text PDF path exposed by Repositorium.ch is intentionally *not* carried into any field.\n- **`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`.\n- **Citation integrity.** Every result carries a resolvable reference — a DOI where present, otherwise a persistent URL. No result is emitted without one. A fabricated citation in legal literature is worse than no citation, so the server prefers one hit fewer over one hit invented.\n- **Language is carried, never silently filtered.** ex/ante and Repositorium.ch are multilingual (DE/FR/IT/EN). The `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.\n- **Attribution per source, not pooled.** Crossref, arXiv and the OA-legal sources have different licence and citation conditions, so each response carries the attribution of the source it actually came from. Crossref bibliographic metadata is distributed under **CC0 1.0** (facts are free); arXiv metadata is **CC0 1.0** with the requested acknowledgement *\"Thank you to arXiv for use of its open access interoperability\"*; preprint and article full texts remain under their own per-work licences (never emitted here).\n- **Scope boundary.** OA legal literature belongs here because it is the *same capability* already in this server — authentication-free bibliographic-metadata harvesting of Swiss scholarly sources over standard protocols — with the same output contract (metadata, not full text). It is institution-independent and subject-focused, which is why it does **not** overlap [`eth-library-mcp`](https://github.com/malkreide/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.\n\n---\n\n## Known Limitations\n\n- **Small, focused corpus.** The three OA sources together hold on the order of a few hundred articles. Results are ranked by relevance — articles matching all query terms first, then partial matches — so a topical query like *\"Datenschutz im Bildungsbereich\"* returns the privacy-law corpus (ranked) rather than nothing; if no article covers the full topic intersection, the closest real matches are returned, never a fabricated one. A query that matches no term at all still returns an honest empty result.\n- **No full-text search.** Matching runs over metadata (title, abstract, authorship) only — never the article body.\n- **Uneven DOI coverage.** sui generis ≈ 100 %, Repositorium.ch partial, ex/ante has **no DOIs** (persistent URLs only). Aggregators (Crossref/OpenAlex) therefore cover sui generis well but miss ex/ante entirely and do not index Repositorium.ch as a source — which is why the server harvests each source natively rather than relying on an aggregator.\n- **Licence gaps.** The native metadata rarely carries a machine-readable licence; `\"unknown\"` is common and only lifted where a DOI resolves in Crossref.\n- **Not a substitute for a paid legal database.** This surfaces *freely accessible* Swiss legal scholarship only — it is not Swisslex/Weblaw and does not cover commercial or paywalled legal publishing.\n\n### Known findings — international layer (live probe 2026-07-20)\n\n| Finding | Detail | Consequence |\n|---|---|---|\n| **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. |\n| **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. |\n| **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. |\n| **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.** |\n| **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. |\n\n---\n\n## Prerequisites\n\n- Python 3.11 or higher\n- [uv](https://docs.astral.sh/uv/) / uvx (recommended) or pip\n- Internet access (all APIs are publicly available)\n\n---\n\n## Installation\n\n### Claude Desktop (recommended)\n\nAdd to `claude_desktop_config.json`:\n\n**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`  \n**Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"swiss-academic-libraries\": {\n      \"command\": \"uvx\",\n      \"args\": [\"swiss-academic-libraries-mcp\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop — the server starts automatically on first use.\n\n### Cloud / Self-hosted (Streamable HTTP)\n\n```bash\nuvx swiss-academic-libraries-mcp --http --port 8000 [--host 127.0.0.1]\n```\n\n**Security & Deployment Notes**\n\n- **Default binding is `127.0.0.1`** (loopback only). The server has no\n  built-in authentication.\n- Use `--host 0.0.0.0` only when running **behind a reverse proxy that\n  provides authentication and per-IP rate limits** (e.g. nginx with\n  `limit_req` + OAuth2-Proxy). Non-loopback bindings emit a WARN log.\n- Logs go to **stderr**; set verbosity with `MCP_LOG_LEVEL=DEBUG|INFO|WARNING`.\n\n### Development\n\n```bash\ngit clone https://github.com/malkreide/swiss-academic-libraries-mcp\ncd swiss-academic-libraries-mcp\npip install -e .\n```\n\n---\n\n## Quickstart\n\nStart by calling `library_info` for a full overview. Then:\n\n```\n\"Which books about Swiss primary schools are held in Swiss libraries?\"\n→ swisscovery_search(query='subject = \"Volksschule\"', max_records=20)\n\n\"Show digitised historical works from ETH Library\"\n→ erara_list_records(set_spec=\"zut\")\n\n\"Which Swiss periodicals were digitised in 2023?\"\n→ eperiodica_list_records(from_date=\"2023-01-01\", until_date=\"2023-12-31\")\n\n\"What manuscript collections does e-manuscripta hold?\"\n→ emanuscripta_list_collections()\n\n→ [More use cases by audience](EXAMPLES.md) →\n```\n\n> 💡 *\"No API key — just install and query.\"*\n\n### CQL Search Syntax (swisscovery)\n\n```\nFull text:     Volksschule Zürich\nTitle:         title = \"education reform\"\nAuthor:        creator = \"Pestalozzi\"\nSubject:       subject = \"pedagogy\"\nISBN:          isbn = \"978-3-05-006234-0\"\nCombined:      title = \"school\" AND creator = \"Pestalozzi\"\nPagination:    start_record = 11\n```\n\n---\n\n## Configuration\n\nNo API keys required. All environment variables are optional.\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| `--http` | off | Enable Streamable HTTP transport |\n| `--port` | 8000 | Port for HTTP transport |\n| `MCP_LOG_LEVEL` | `INFO` | Log verbosity (`DEBUG`/`INFO`/`WARNING`) |\n| `OA_LAW_CROSSREF_ENRICH` | `1` | OA legal: DOI→licence enrichment via Crossref; set `0` to disable |\n| `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) |\n| `CROSSREF_MAILTO` | *(unset)* | International: contact e-mail for Crossref's \"polite pool\" (better throughput). If unset, requests use the anonymous pool — functional, just slower. |\n| `ARXIV_MIN_INTERVAL_SECONDS` | `3.0` | International: minimum spacing between arXiv requests (arXiv asks for restraint). |\n\n---\n\n## Project Structure\n\n```\nswiss-academic-libraries-mcp/\n├── src/\n│   └── swiss_academic_libraries_mcp/\n│       ├── __init__.py       # Package init\n│       ├── server.py         # FastMCP server, 16 tools, 3 prompts, 3 resources\n│       ├── api_client.py     # HTTP client (+ retry), MARC21 + OAI-PMH/DC parsers\n│       ├── oa_legal.py       # OA legal-literature registry, adapters, model\n│       └── intl_metadata.py  # International layer: Crossref + arXiv adapters, models\n├── tests/\n│   ├── test_server.py        # catalogue unit tests + live smoke tests\n│   ├── test_20_scenarios.py  # end-to-end catalogue scenarios\n│   ├── test_oa_legal.py      # OA legal-literature tests (mocked + live)\n│   └── test_intl_metadata.py # International-layer tests (mocked + live)\n├── pyproject.toml\n├── CHANGELOG.md\n├── CONTRIBUTING.md            # Contributing guide (English)\n├── CONTRIBUTING.de.md         # Contributing guide (German)\n├── SECURITY.md               # Security policy (English)\n├── SECURITY.de.md            # Security policy (German)\n├── LICENSE\n├── README.md                 # This file (English)\n└── README.de.md              # German version\n```\n\n---\n\n## MCP Protocol Version\n\nThis server speaks **two protocol eras** over the same endpoint. The client's\nfirst request on a connection decides which one applies; a later claim from the\nother era is refused.\n\n| Era | Revision | Who reaches it |\n|---|---|---|\n| `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. |\n| Per-request envelope | **`2026-07-28`** | A request carrying the `2026-07-28` `_meta` envelope opens a modern connection. |\n\nBoth revisions are pinned in\n[`tests/test_protocol_version.py`](tests/test_protocol_version.py) and asserted\nagainst the installed SDK, so a Dependabot bump of `mcp` cannot move either one\nsilently. This server builds no ASGI app to send an `initialize` through, so\nthe gate asserts the SDK constants rather than a measured response — the\nweaker form, named rather than left unsaid.\n\nNote that the SDK's `LATEST_PROTOCOL_VERSION` is an alias for the **modern**\nera, not for the handshake era — pinning against it alone would leave the era\nthat current clients actually negotiate free to drift.\n\n**Update policy.** When the gate fails, do not edit the constant blindly: read\nthe spec changelog between the two revisions, verify the server still behaves,\nthen move the constant, this section, `README.de.md` and\n[`CHANGELOG.md`](CHANGELOG.md) together.\n\n---\n\n## Testing\n\n```bash\n# Unit tests (no network required)\nPYTHONPATH=src pytest tests/ -m \"not live\"\n\n# Live smoke tests (internet required)\nPYTHONPATH=src pytest tests/ -m \"live\"\n```\n\n### Where the test data comes from\n\nThe fixtures under `tests/fixtures/` are **recorded from the live sources**, with\nthe same request parameters the production code sends, and dated. Source,\nretrieval date, selection rule and SHA-256 for every file:\n[`tests/fixtures/PROVENANCE.md`](tests/fixtures/PROVENANCE.md).\n\n```bash\npython scripts/record_fixtures.py   # re-record\n```\n\nThis matters because a hand-written mock encodes its author's assumption and can\ntherefore never refute it — production code and fixture come from the same\nreading of the docs. The invented `ListSets` response carried no\n`resumptionToken`, so no test could notice that nobody followed one: e-rara\nserves 105 collections in pages of 10, and the tool reported 10.\n\nWhere a fixture is trimmed, the count fields (`numberOfRecords`,\n`completeListSize`, `total-results`) and the `resumptionToken` keep their real\nvalues — they say how much is *not* in the file.\n\n`tests/fixtures/oai_ex_ante_listrecords.xml` is deliberately **not** well-formed\nXML: ex/ante emits a raw control character inside a `dc:description`, and\nrecording it verbatim is what proves `strip_invalid_xml_chars` is load-bearing.\n\n---\n\n## Safety & Limits\n\n- **Read-only:** All tools perform HTTP GET requests against public SRU and OAI-PMH endpoints — no data is written, modified, or deleted.\n- **No personal data:** The APIs return bibliographic metadata (titles, authors, publication info, subject headings) and public digitisation records. No personally identifiable information (PII) about library users is processed or stored.\n- **Rate limits:** swisscovery SRU and the OAI-PMH endpoints are public and have no documented hard limits, but OAI-PMH harvesting is paginated via resumption tokens — use `from_date` / `until_date` and keep `max_records` reasonable. The server enforces a 30s timeout per request.\n- **Data freshness:** Results reflect the upstream catalogues at query time. No caching is performed by this server; indexing latency is controlled by SLSP and the digitisation platforms.\n- **Terms of service:** Data is subject to the ToS and licences of each source — [swisscovery / SLSP](https://slsp.ch), [e-rara](https://www.e-rara.ch), [e-periodica](https://www.e-periodica.ch), [e-manuscripta](https://www.e-manuscripta.ch). Most digitised material is in the public domain or under Creative Commons licences; always check the rights statement on the individual record before redistribution.\n- **No guarantees:** This is a community project, not affiliated with SLSP, ETH Library, or any of the participating institutions. Availability depends on the upstream APIs.\n\n---\n\n## Contributing\n\nContributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on:\n\n- Reporting bugs and requesting features\n- Setting up the development environment\n- Code style and test requirements\n- Submitting pull requests\n\nThis project follows the conventions of the [Swiss Public Data MCP Portfolio](https://github.com/malkreide).\n\n---\n\n## Security\n\nTo report a vulnerability, please follow the responsible disclosure process in [SECURITY.md](SECURITY.md). The server is read-only and requires no API key; see the *Safety & Limits* section above for the security model.\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n## Deployment for Swiss Public Administration\n\nIf you self-host this server for a Swiss school authority, archive,\nor municipal use case:\n\n- **Data residency:** prefer on-premise or a CH-based cloud provider.\n  The query patterns themselves (which library searches a civil\n  servant runs) may reveal ongoing research and are best kept on\n  Swiss infrastructure.\n- **Upstream calls** go exclusively to CH-hosted services: SLSP /\n  swisscovery, ETH-Bibliothek (e-rara, e-periodica, e-manuscripta).\n  No data leaves Switzerland.\n- **Logging:** logs are written to stderr; configure your IT\n  retention policy accordingly (e.g. systemd-journal `MaxRetentionSec`).\n- **HTTP transport** must run behind a reverse proxy with\n  authentication and per-IP rate limits (see *Security & Deployment\n  Notes* above).\n\n---\n\n## License\n\nMIT License — see [LICENSE](LICENSE)\n\n---\n\n## Author\n\nHayal Oezkan · [github.com/malkreide](https://github.com/malkreide)\n\n---\n\n## Credits & Related Projects\n\n- **Data (catalogue):** [swisscovery / SLSP](https://swisscovery.slsp.ch) · [e-rara](https://www.e-rara.ch) · [e-periodica](https://www.e-periodica.ch) · [e-manuscripta](https://www.e-manuscripta.ch)\n- **Data (OA legal literature):** [sui generis](https://sui-generis.ch) · [ex/ante](https://ex-ante.ch) · [Repositorium.ch](https://www.repositorium.ch) — licence enrichment via [Crossref](https://www.crossref.org). Each source's metadata is used under its own terms; open-access status does not imply an open reuse licence.\n- **Protocol:** [Model Context Protocol](https://modelcontextprotocol.io/) — Anthropic / Linux Foundation\n- **Related:** [eth-library-mcp](https://github.com/malkreide/eth-library-mcp) — ETH Library Discovery & Persons API\n- **Portfolio:** [Swiss Public Data MCP Portfolio](https://github.com/malkreide)\n\n| Server | Description |\n|--------|-------------|\n| [`zurich-opendata-mcp`](https://github.com/malkreide/zurich-opendata-mcp) | City of Zurich Open Data |\n| [`eth-library-mcp`](https://github.com/malkreide/eth-library-mcp) | ETH Library Discovery & Persons API |\n| [`swiss-statistics-mcp`](https://github.com/malkreide/swiss-statistics-mcp) | Swiss Federal Statistics (BFS) |\n| [`fedlex-mcp`](https://github.com/malkreide/fedlex-mcp) | Swiss Federal Law via Fedlex SPARQL |\n| [`swiss-transport-mcp`](https://github.com/malkreide/swiss-transport-mcp) | OJP journey planning, SIRI-SX disruptions |\n\n<!-- mcp-name: io.github.malkreide/swiss-academic-libraries-mcp -->\n\n<!-- BEGIN GENERATED: install -->\n## Installation\n\nRun via [`uv`](https://docs.astral.sh/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`):\n\n```json\n{\n  \"mcpServers\": {\n    \"swiss-academic-libraries-mcp\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"swiss-academic-libraries-mcp\"\n      ]\n    }\n  }\n}\n```\n<!-- END GENERATED: install -->\n",
  "bytes": 29573,
  "sha": "736354a131eeaa69a8a6f6c154bdb8b1af66d400a8badf1f9adefc6b6ee61b45",
  "repo_slug": "malkreide/swiss-academic-libraries-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_malkreide_swiss_academic_libra_8c362c57/readme"
}