mcp-anac-crawler
Click on "Deploy 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., "@mcp-anac-crawlerFind recent pubblicità legale notices for lavori."
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.
mcp-anac-crawler
MCP stdio server for crawling the Italian ANAC Pubblicità Legale tender platform
(pubblicitalegale.anticorruzione.it), built to the requirements in
docs/req-web-crawel.
The finding that shaped the design
The requirements assume an HTML crawler ("analizzare pagina web"). The target is not an
HTML site: /bandi is an Angular single-page application. The served HTML is a 37 KB
shell containing no tender data at all — scraping it returns nothing.
All data is delivered by a JSON API that the SPA's own BackendService calls. That API is
undocumented, so the contract was reverse-engineered from the production bundle
(main.<hash>.js) and verified against live responses. See
endpoints.py for the full contract.
Two consequences that drive most of this codebase:
Two incompatible pagination models coexist.
/avvisiis offset-based (SpringPage, with totals);/avvisi-full-textis cursor-based — page N+1 requires the token returned with page N. Conflating them silently returns page 0 forever.Documents are not on ANAC's domain.
documenti_di_gara_linkpoints at arbitrary third-party contracting-authority portals, and never directly at a file — verified across a full day of notices, zero links end in.pdf. Reaching a PDF therefore needs an HTML hop, and every such fetch is an untrusted-URL fetch (SSRF surface).
Related MCP server: Kontur.Zakupki MCP
Quick start
python -m venv .venv && .venv/Scripts/pip install -e ".[dev]"
.venv/Scripts/python -m anac_crawler # speaks MCP over stdioRegister with an MCP client:
{
"mcpServers": {
"anac-crawler": {
"command": "C:/path/to/.venv/Scripts/python.exe",
"args": ["-m", "anac_crawler"],
"env": { "ANAC_LOG_LEVEL": "INFO", "ANAC_RATELIMIT_REQUESTS_PER_SECOND": "4" }
}
}
}Tools
Tool | Purpose | Notes |
| Taxonomies, value bands, publication dates, categories, news | Call first to build valid filters |
| Search with offset pagination | Reports totals; use for page jumps |
| Full-text search with cursor pagination | Pass |
| Multi-page traversal in one call | Hard record/page ceilings |
| Single notice by | Flattened record |
| Revision chronology | Upstream 404 → empty list |
| Fetch an HTML landing page, discover PDF links | Untrusted third-party content |
| Download a document | Not read-only; writes to disk, egress-guarded |
| Breaker state, cache stats, latency percentiles, schema-drift signal |
Typical flow: anac_reference_data → anac_search_notices → anac_inspect_page on a
documenti_di_gara_link → anac_download_document on a discovered PDF.
Requirements coverage
Requirement | Where |
Python | 3.11+, |
stdio | mcp_server/server.py + stdout guard |
HTTP GET/POST | http/client.py ( |
Cookies | Persistent JSON jar + Azure |
Sessions | One pooled, keep-alive |
Web protocol | HTTP/2, conditional requests, |
Page analysis | documents/html.py (selectolax) |
Caching | http/cache.py — 2-tier, ETag revalidation, single-flight, stale-if-error |
Navigation | Unified pagination + bounded traversal in repository.py |
Targeted file access + PDF download |
Configuration
Everything is env-driven and validated at startup (ANAC_ prefix) — see
.env.example and config.py.
The knobs that matter most in production:
ANAC_RATELIMIT_REQUESTS_PER_SECOND=4 # politeness toward a public service
ANAC_RATELIMIT_MAX_CONCURRENCY=4
ANAC_DOWNLOAD_HOST_ALLOWLIST= # pin the egress surface (strongly recommended)
ANAC_DOWNLOAD_MAX_BYTES=67108864
ANAC_CA_BUNDLE= # for corporate TLS interception
ANAC_LOG_FORMAT=json # structured logs on stderrTLS note
Document downloads default to the OS trust store (via truststore), not certifi.
This is functional, not cosmetic: many Italian PA portals serve an incomplete certificate
chain, which OpenSSL cannot resolve but the Windows/macOS verifiers can (via AIA).
Verified against a live portal — certifi fails, the OS store completes TLS 1.3. Details in
http/tls.py.
Verification
.venv/Scripts/python -m pytest -q # 241 tests, no network
.venv/Scripts/python -m mypy src # strict, clean
.venv/Scripts/python -m ruff check src tests scripts
.venv/Scripts/python scripts/smoke_live.py # live API contract check
.venv/Scripts/python scripts/smoke_stdio.py # real JSON-RPC handshake over stdiosmoke_stdio.py is the one that matters most: it spawns the server as a subprocess and
drives it exactly as a client would, which is the only way to catch a stray byte on stdout.
Further reading
docs/ARCHITECTURE.md — layering, request lifecycle, design decisions
docs/ENTERPRISE.md — what is production-ready, what is not, and the recommended roadmap with known limitations stated explicitly
This server cannot be deployed
Maintenance
Related MCP Connectors
Brazilian public procurement (PNCP): search, deadlines, tender markdown, alerts and watches.
Search public procurement notices from 17 sources across Germany, the EU and the UK. Read-only.
Official EUR-Lex, UK, US, and gazette search, metadata fetch, and change-only watches.
Search EU public tenders across TED and 8 national portals. Monitor, match, and analyse procurement.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables search and analysis of European public procurement tenders, including EU above-threshold (TED) and below-threshold from 11 national sources, with hybrid search and filtering.MIT
- FlicenseNot gradedqualityCmaintenanceEnables searching and retrieving Russian tenders (закупки) through the Kontur.Zakupki API. Supports filtering by date, text, laws, procedures, statuses, and more.-
- FlicenseNot gradedqualityCmaintenanceProvides tools to search and retrieve French public procurement data (tenders, awarded contracts) and company information from official open APIs.-
- AlicenseNot gradedqualityCmaintenanceEnables searching Korean procurement notices from the public data portal, with support for integrated search across categories, flexible date ranges, and attachment extraction.MIT