mcp-anac-crawler

mcp-anac-crawler

Enables searching, inspecting, and downloading Italian public tender notices from the ANAC Pubblicità Legale platform, supporting offset and cursor pagination, reference data lookup, and safe third-party document retrieval.

Category
访问服务器

README

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:

  1. Two incompatible pagination models coexist. /avvisi is offset-based (Spring Page, with totals); /avvisi-full-text is cursor-based — page N+1 requires the token returned with page N. Conflating them silently returns page 0 forever.
  2. Documents are not on ANAC's domain. documenti_di_gara_link points 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).

Quick start

python -m venv .venv && .venv/Scripts/pip install -e ".[dev]"
.venv/Scripts/python -m anac_crawler          # speaks MCP over stdio

Register 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
anac_reference_data Taxonomies, value bands, publication dates, categories, news Call first to build valid filters
anac_search_notices Search with offset pagination Reports totals; use for page jumps
anac_search_notices_full_text Full-text search with cursor pagination Pass next_token from previous page
anac_collect_notices Multi-page traversal in one call Hard record/page ceilings
anac_get_notice Single notice by idAvviso Flattened record
anac_get_notice_history Revision chronology Upstream 404 → empty list
anac_inspect_page Fetch an HTML landing page, discover PDF links Untrusted third-party content
anac_download_document Download a document Not read-only; writes to disk, egress-guarded
anac_health Breaker state, cache stats, latency percentiles, schema-drift signal

Typical flow: anac_reference_dataanac_search_noticesanac_inspect_page on a documenti_di_gara_linkanac_download_document on a discovered PDF.

Requirements coverage

Requirement Where
Python 3.11+, src/anac_crawler/
stdio mcp_server/server.py + stdout guard
HTTP GET/POST http/client.py (request, post_json)
Cookies Persistent JSON jar + Azure ARRAffinity re-scoping
Sessions One pooled, keep-alive AsyncClient shared across all tool calls
Web protocol HTTP/2, conditional requests, Retry-After, redirects, robots.txt
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 documents/fetcher.py

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 stderr

TLS 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 stdio

smoke_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

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选