ANNO MCP Server
The ANNO MCP server lets you search and retrieve content from the ANNO historical newspaper archive, which contains around 28 million pages from over 1,600 titles spanning 1735 to the present.
Full-text search (
search_anno): Search OCR content with keywords, exact phrases, Boolean operators (AND/OR/NOT), and wildcards. Results are paginated (10 per issue) and return issue-level metadata (title, date, place, language, type) with hit counts and citation URLs. Supports sorting by relevance or date. Useadvanced_search_annoto optionally filter by date, title, place, language, subject, and medium.Snippet retrieval (
get_snippets): For a given issue and query, finds which pages contain matches, showing text in context with stable citation URLs and IIIF image crops. Works for both newspapers and periodicals.OCR text download (
download_text): Download plain OCR text for a newspaper issue or a specific page (page-by-page for the whole issue). Cached locally. Not available for periodicals (identifiers starting withANNOP_); useget_snippetsfor those instead.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ANNO MCP Serversearch for 'Weltausstellung' in 1873"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ANNO MCP Server
MCP server for ANNO (AustriaN Newspapers Online), the historical newspaper archive of the Österreichische Nationalbibliothek. Roughly 28 million pages from over 1,600 titles, 1735 to the present, overwhelmingly German-language, with over 91% of holdings full-text searchable.
search_anno: Full-text search with AND/OR/NOT, exact phrases and wildcards. Results resolve to an issue, with the number of hits inside it.
get_snippets: The page-level step — which page of an issue a term appears on, with the match in context and a citation URL for that exact page.
download_text: OCR plain text for a newspaper issue or a single page of it, cached locally.
advanced_search_anno: Search with date, title, place, language, subject and medium filters.
There are two ways to use it: an MCP server for clients that speak MCP, and an anno CLI for agents driven through a shell. Both share one client, one cache and one set of behaviours. The CLI is what the bundled anno-search skill uses, and it exposes every filter unconditionally rather than hiding them behind an install flag.
ANNO publishes no API. This client talks to the REST backend of the AnnoSearch app at anno.onb.ac.at/anno-suche/rest, found by reading the app's JavaScript bundle, plus the annoshow CGI that the page viewer's "Text" button opens. Both are unauthenticated and need no key — but nobody has promised to keep them stable, so the client detects endpoint drift explicitly rather than failing obscurely.
Installation
Install the code
uv syncInstall the CLI
uv tool install . # puts `anno` on your PATHInstall to MCP CLIs
Installs to Claude Code, Codex CLI, and Gemini CLI:
# Basic installation (search_anno + get_snippets + download_text)
uv run anno-mcp-install
# With advanced search enabled (adds advanced_search_anno)
uv run anno-mcp-install --enable-advanced-searchVerify the installation:
claude mcp list # For Claude Code
codex mcp list # For Codex CLI
gemini mcp list # For Gemini CLIRelated MCP server: MEK MCP
Usage
CLI
anno search 'Weltausstellung' # first page, best matches first
anno search '"Wiener Weltausstellung"' --from-year 1873 --to-year 1873
anno search 'Ringstraße Neubau' --from-year 1865 --to-year 1875 --sort date_asc
anno search 'Semmeringbahn' --place Wien --title nfp --pages all
anno search 'Weltausstellung' --format periodical # illustrated & trade weeklies only
anno snippets ANNO_nfp18731202 'Ringstraße' # which page, in context
anno get ANNO_nfp18731202 --page 3 # cached OCR text pathFilters: --from-year, --to-year, --title, --place, --language, --subject, --format, --sort. Facet values are taken verbatim, so copy them from a result rather than translating them — the place value for Prague is Praha (Prag), and Prag is a different, nearly empty one.
--format splits the archive in the way that matters for cost. It takes newspaper (ANNO's own value for it is journal) or periodical, and the two partition the results exactly: Weltausstellung reports 153,804, of which 145,799 are newspapers and 8,005 periodicals. The split is worth knowing because only newspapers have an OCR text endpoint — --format periodical selects precisely the material anno get will refuse, and --format newspaper precisely the material it will serve.
The workflow is search → snippets to find the page and judge it cheaply → get only what is worth reading. Add --json for machine-readable output.
Search resolves to an issue, not a page. A result says "this issue of the Neue Freie Presse contains 7 hits"; snippets is what turns that into "page 3, and here is the sentence". That middle step is where most of the value is, because it is also enough to quote in a report.
Result totals are true match counts. Unlike Gallica, ANNO filters rather than ranks: Weltausstellung reports 153,804, Weltausstellung AND Ringstraße 19,958, Weltausstellung NOT Ringstraße 133,846 — and 19,958 + 133,846 = 153,804 exactly. A total can therefore be quoted as a count, and --sort date_asc is safe on any query you intend to sweep.
Ten results per page, fixed. ANNO offers no way to raise it, so sweeps are request-hungry: 1,385 results is 139 requests. Combining variants into one (A OR B OR C) query is how you keep that down.
Downloads are cached in $XDG_CACHE_HOME/anno-mcp (override with --cache-dir or ANNO_CACHE_DIR). The cache location does not depend on the working directory, so the CLI can be run from anywhere.
Requests are paced one every 3 seconds by default, overridable with ANNO_MIN_REQUEST_INTERVAL. The ÖNB publishes no rate limit and none was observed in testing, so this is caution rather than a measured ceiling.
get is the expensive call, and unusually so here. ANNO serves OCR one page at a time — a whole-issue download costs one paced request per page, so a 104-page issue is over five minutes of requests. Use --page once snippets has told you which page you want. Periodicals (ANNOP_) have no text endpoint at all and get refuses them with an explanation; their snippets work normally.
MCP server
Run the server directly:
uv run anno-mcpTest with MCP Inspector:
uv run fastmcp dev src/anno_mcp/server.pyAvailable Tools
3 toolsdownload_textA
Download OCR plain text for an ANNO newspaper issue and cache it locally.
Args: identifier: Document id, e.g. "ANNO_dmo19330626" page: Single page to fetch. Omit for the whole issue.
Returns: Path to the cached text file (as string)
IMPORTANT:
ANNO serves text one page at a time, so a whole issue costs one request
per page — a 104-page issue is 104 paced requests. Pass page whenever
get_snippets has already told you which page you want.
Periodicals (identifiers starting ANNOP_) have no text endpoint at all
and will raise. Use get_snippets for those.Example: path = download_text("ANNO_dmo19330626", page=7)
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | ||
| identifier | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, description fully discloses behavior: one request per page, caching, periodicals raise errors, and return type (path to cached file). No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with sections (main, Args, Returns, IMPORTANT, Example), each sentence is informative and no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 2 parameters, no annotations, and an output schema, the description covers purpose, parameters, behavior, limitations, and provides an example. Fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 0% schema coverage, the description clearly explains both parameters: identifier with an example, and page as optional single page or omit for whole issue. Adds meaning beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states the action ('Download OCR plain text') and resource ('ANNO newspaper issue') and distinguishes from sibling tool get_snippets by noting that periodicals should use get_snippets instead.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear when-to-use guidance: pass page when get_snippets has identified the page, and warns against using for periodicals (ANNOP_). Does not explicitly list when not to use but covers main scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_snippetsA
Find which pages of one ANNO issue a query appears on, with context.
This is the cheap triage step and the reason ANNO is worth using: a hit resolves to a page with the matched terms in context, so a false positive can be rejected without downloading anything. It also works for periodicals, whose OCR text cannot be downloaded at all.
Args: identifier: Document id from a search result, e.g. "ANNO_dmo19330626" query: Terms to locate within the issue
Returns: Dictionary containing: - identifier: The document id - query: The query used - snippets: List of occurrences with: - page: Page number the occurrence sits on - page_label: Page label as printed - text: Snippet with matched terms in {braces} - url: Stable citation URL for that exact page - image_url: IIIF crop of the matched region
Note: ANNO returns at most 10 snippets per issue however many hits it reports, so a heavily-covered issue shows a sample rather than every occurrence.
Examples: get_snippets("ANNO_dmo19330626", "Hanussen") get_snippets("ANNO_wsb18840309", "Cumberland")
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| identifier | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It discloses the 10-snippet limit, works for periodicals, and explains the return format. However, it does not mention authentication requirements, rate limits, or whether results are cached, leaving minor gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized with Args, Returns, Note, and Examples sections. Every sentence adds value, and the purpose is stated upfront. It is appropriately sized for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description fully defines the return structure. It covers all necessary aspects: purpose, parameters, return values, limitations, and usage context. Distinguishes from sibling tools effectively.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It provides clear definitions for both parameters: identifier as 'Document id from a search result, e.g. ANNO_dmo19330626' and query as 'Terms to locate within the issue', plus concrete examples.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource: 'Find which pages of one ANNO issue a query appears on, with context.' This clearly distinguishes from siblings like search_anno and download_text.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly frames the tool as a cheap triage step, explains why it's useful (false positives can be rejected without download), and notes it works for periodicals where OCR text cannot be downloaded. This provides clear when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_annoA
Search ANNO, the Austrian National Library's historical newspaper archive.
Covers roughly 28 million pages from over 1,600 Austrian newspaper and magazine titles, 1735 to the present, overwhelmingly German-language. Over 91% of holdings are full-text searchable.
Results resolve to an ISSUE, not a page. Use get_snippets with a returned identifier to find which page a term appears on.
Args: query: Text to search in OCR content. - Bare words are ANDed: "Hanussen Hellseher" needs both - Exact phrases: '"Erik Jan Hanussen"' - AND / OR / NOT, which MUST BE UPPERCASE - Trailing wildcard: "Hanuss*" page: Page number for pagination, 1-indexed (default: 1). ANNO fixes the page size at 10 results and offers no way to raise it. sort: Result ordering — "relevance" (default), "date_asc" or "date_desc". ANNO's totals are true match counts rather than a relevance tail, so date ordering is safe on any query you mean to sweep.
Returns: Dictionary containing: - page: Current page number - total_results: True count of matching issues - total_pages: Total number of pages available - documents: List of issues with: - identifier: Document id, e.g. ANNO_dmo19330626 - title: Issue title with its date - date: ISO date for newspapers, None for periodicals - year: Year of publication - type: Zeitung (newspaper) or Zeitschrift (periodical) - is_periodical: True when OCR download is unavailable - places, languages: Publication place and language - page_count: Pages in the issue - hits_in_document: Occurrences of the query in the issue - url: Stable citation URL
Examples: search_anno(query="Hanussen") search_anno(query='"Erik Jan Hanussen"') search_anno(query="Hellseher OR Gedankenleser") search_anno(query="Hanuss*")
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | ||
| sort | No | relevance | |
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the behavioral burden. It discloses search characteristics (bare words ANDed, operator case sensitivity, wildcards), pagination limitations, and the nature of result counts (true match counts). It also indicates that 91% of holdings are full-text searchable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and appropriately sized. It opens with a clear one-sentence summary, then provides scope, usage notes, parameter details, return structure, and examples. Every sentence adds value, and the information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite lacking an output schema, the description details the return structure with all fields (page, total_results, documents, etc.) and provides multiple examples. For a search tool with 3 parameters and no output schema, this is exceptionally complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It does so comprehensively: explains query syntax with examples, clarifies page default and fixed size, describes sort options and their implications. This adds meaning far beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool searches ANNO, a historical newspaper archive, and specifies its scope (28 million pages, 1,600 titles). It distinguishes from siblings by noting that results resolve to an issue, not a page, directing to get_snippets for page-level detail.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit guidance on when to use this tool vs. alternatives (get_snippets for page-level results). It details query syntax (AND, OR, NOT, wildcards), pagination behavior (fixed at 10, 1-indexed), and safe use of sort options (date ordering for sweeping queries).
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. Dates show when Glama detected each change.
3 tool updates
v0.1.0- First observed
download_text - First observed
get_snippets - First observed
search_anno
TDQS
Each tool serves a distinct purpose: search_anno retrieves matching issues, get_snippets locates query occurrences within an issue, and download_text obtains OCR text. There is no functional overlap.
All tool names follow a consistent verb_noun pattern in snake_case: search_anno, get_snippets, download_text. The naming is predictable and clear.
With 3 tools, the server is well-scoped for its purpose of searching and retrieving text from a large archive. Each tool adds necessary functionality without bloat.
The toolset covers the essential search-to-text workflow. Minor gaps exist, such as lack of image retrieval or browsing capabilities, but these are beyond the stated scope.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for Open Archives: Dutch genealogical records and historical page transcriptions.
An MCP server that provides congressional transcripts
Academic research MCP server for paper search, citation checks, graphs, and deep research.
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server for full-text search across PDF document collections with offline indexing, ranked results, snippets, and page rendering.4MIT
- FlicenseAqualityBmaintenanceMCP server to access the Hungarian Electronic Library (MEK) search engines, enabling agentic tools to search and retrieve content.4-
- AlicenseAqualityBmaintenanceMCP server and CLI for full-text search of the Deutsches Zeitungsportal (German newspaper collection), enabling querying ~33.8 million digitized pages with Solr syntax, date/title/place filters, and snippet highlights.3Apache 2.0
- AlicenseAqualityBmaintenanceMCP server for searching and retrieving full-text pages from the Library of Congress, including newspapers, books, and manuscripts, via the loc.gov API.3Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nestordemeure/anno-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server