Purpose
luxembourg-mcp is used to integrate Luxembourg's scattered public APIs and open datasets into MCP tools that AI agents can call directly, reducing repetitive integration work between agents and public services. The project describes itself as "Keyless Model Context Protocol access to Luxembourg public data," meaning access to Luxembourg public data without keys, covering law, official statistics, mobility, environmental measurements, parliament, accessibility, addresses, geospatial features, and the national open data catalog, among others.
Main capabilities
- Converts fragmented public APIs and open datasets into a consistent set of tools for AI agents to call.
- The project description states it provides 44 consistent tools covering 28 public data systems; another description says 21 tools and 15 public systems (there are discrepancies between the information).
- No API key or account required.
- Every result returns a source URL.
- Supports both Stdio and Streamable HTTP transports.
- Provides /status, used to report whether the official upstream corresponding to each tool is reachable; each upstream is checked lightly at most once every 10 minutes (reading only the first few KB). One failed check is marked as degraded, and two consecutive failures are marked as down.
- Example questions given in the official description include: what weather warnings are currently in effect in Luxembourg, how many spaces are free at CFL Park and Ride, the latest water level in Mersch, find legislation related to pensions, which STATEC datasets contain population data, the latest traffic measurements on the A6, whether there is currently roadwork or lane closure on the A4, how each party voted on the latest tram bill, the meaning of the Luxembourgish word "Schiertech," find the official coordinates of an address in Luxembourg City, which bus or tram stops match Hamilius.
Usage
- Hosted endpoint, no installation required: point any MCP client to https://mcp.luxembourg-mcp.com/mcp (streamable HTTP).
- Run it yourself: use uvx luxembourg-mcp.
- Requires Python 3.11 or higher.
- Website: luxembourg-mcp.com.
- Protocol behavior: validates parameters according to the input schema of each published tool; returns -32600 Invalid Request for JSON-RPC batch requests and other non-object JSON; supports MCP 2026-07-28 (stateless: per-request _meta, server/discover, cache hints) on the same endpoint and stdio, as well as the initialize handshake revisions 2025-11-25, 2025-06-18, and 2025-03-26; for HTTP requests, validates whether the MCP-Protocol-Version, Mcp-Method, and Mcp-Name headers are consistent with the request body; tool failures are returned as MCP tool results without terminating the server; larger upstream responses are limited before being placed into the agent context.
- The built-in HTTP server is stateless and does not provide SSE streaming. By default, it only accepts browser origins from loopback addresses; hosted deployments can extend this via LUXEMBOURG_MCP_ALLOWED_ORIGINS (a comma-separated list of origins, or *), and CORS preflight and response headers are enabled for allowed origins. Public deployments should add TLS, response caching, and observability at the reverse proxy or application gateway.
Security and deployment
- HTTP request bodies are limited to 1 MiB.
- Upstream responses are limited to 25 MiB.
- Downloads derived from metadata are restricted to trusted data.public.lu HTTPS resource hosts (including redirects); fixed upstream URLs only follow HTTPS redirects on the same host.
- Upstream XML that declares DTD entities is rejected before parsing; user-provided identifiers cannot form . or .. URL path segments.
- GTFS ZIP members are checked for uncompressed size, encryption status, and suspicious compression ratios before extraction.
- The HTTP client is limited by default to 60 MCP requests per minute; IPv6 clients are grouped by /64 prefix.
- The HTTP server allows at most 32 concurrent connections by default and closes connections idle for 30 seconds.
- DThe Docker image runs as a non-privileged app user, and the base image is pinned by digest.
- The per-client per-minute limit can be changed via LUXEMBOURG_MCP_RATE_LIMIT; a value of 0 disables the built-in rate limiter. The rate limiter intentionally uses the direct TCP peer address and does not trust forwarded headers unless LUXEMBOURG_MCP_CLIENT_IP_HEADER explicitly specifies a header (for example, CF-Connecting-IP behind Cloudflare); it should only be set when a trusted proxy controls that header, otherwise clients could forge the header to bypass the limit. The concurrent connection limit can be changed via LUXEMBOURG_MCP_MAX_CONNECTIONS; connections exceeding the limit are closed immediately.
- Tool output is untrusted external data. Public texts such as dataset descriptions and legislative titles may contain misleading or adversarial instructions. MCP clients and agent hosts must treat tool results as data rather than system instructions, and require confirmation or perform a policy check before allowing powerful peer tools to act on content returned here.
- The default test suite is deterministic and does not use the network, covering JSON-RPC behavior, schema validation, provider parsers, catalog packaging, and the tools/call contract for all 28 tools. The optional live test suite calls every real upstream service; live tests may fail when sources are unavailable or change their published data, and will download large STATEC, air quality, Chamber, and GTFS resources.
Applicable Scenarios
- Need to let AI agents query Luxembourg public data such as laws, statistics, parliamentary votes, addresses, geospatial data, bus stops, transportation, and the environment.
- Want unified, key-free access to multiple Luxembourg public data sources, avoiding integrating them one by one.
- Need to run the MCP server locally or in a hosted environment via Stdio or Streamable HTTP.
Limitations
- The official ATP API (real-time arrivals and journey planning) requires a personal access key; this project instead exposes key-free static stops from the official GTFS data source.
- The cache is bounded but limited to a single process; multiple workers do not share cached data.
- Upstream availability, update frequency, schema, and licensing remain controlled by each data producer.
- Tool coverage is not yet fully aligned with Allemannsdata. Candidate directions includeparliamentary votes, court judgments, historical weather, electric vehicle charging, roadworks, and more environmental measurements.
- The project is a community project and is not affiliated with Thomas Heggelund, Allemannsdata, the Government of Luxembourg, or any institution whose data it exposes. The project does not own the upstream data, and the licenses and terms of each producer continue to apply; the source code is under the MIT License, and the upstream public data is licensed separately by each producer.
- No commercial information such as fees, prices, or paid plans is provided in the official texts; the relevant facts are not currently available.