rcs-mcp
# rcs-mcp
Serwer MCP dla Rejestru Cen Nieruchomości z geoportal.gov.pl. Udostępnia Claude Desktop narzędzia do wyszukiwania obszarów w Polsce i sprawdzania rzeczywistych transakcji nieruchomościowych z aktów notarialnych.
## Co udostępnia
- `znajdz_obszar(nazwa, poziom)` - znajduje bbox dla województwa, powiatu, gminy, miasta albo wsi.
- `wyszukaj_transakcje(...)` - zwraca listę transakcji dla bboxa.
- `statystyki_cen(...)` - liczy medianę, średnią, kwartyle i zakres ceny za m2 na próbie transakcji, plus medianę w rozbiciu na lata.
Typowy przepływ:
1. `znajdz_obszar("Zakopane", "miasto")`
2. Weź `bbox` z wyniku.
3. `statystyki_cen(bbox=...)` albo `wyszukaj_transakcje(bbox=...)`
## Jakość danych
Surowe rekordy RCN dają bardzo mylące ceny za m2 - te same dane potrafią pokazać i 65 zl/m2, i 71 tys. zl/m2 w jednym mieście. Dlatego domyślnie:
- **Cena bierze się z nieruchomości, nie z aktu notarialnego.** `tran_cena_brutto` to cena całego aktu, który często obejmuje kilka lokali (16% rekordów w próbce z Sopotu) - dzielona przez powierzchnię jednego lokalu zawyżała cenę nawet dwukrotnie.
- **Powierzchnia gruntu nie zastępuje powierzchni lokalu.** Fallback na `nier_pow_gruntu` (grunt pod całym budynkiem) działa już tylko dla działek.
- **`tylko_rynkowe=True`** odsiewa sprzedaż udziałów ułamkowych (cena za ułamek, powierzchnia całości - np. 9 zl/m2 za udział w hali garażowej) i transakcje spoza wolnego rynku (komunalne z bonifikatą, bezprzetargowe).
- **`tylko_mieszkalne=True`** odsiewa garaże, komórki lokatorskie i lokale usługowe.
- **Wartości skrajne** odrzuca reguła Tukeya (3 x IQR na logarytmie ceny), więc zwracane `min`/`max` to krańce oczyszczonej próbki. Liczbę odrzuconych widać w `odrzucone_odstajace`.
Każdy z tych filtrów można wyłączyć parametrem, żeby zobaczyć surowe dane.
Osobna sprawa to czas: RCN nie filtruje po dacie po stronie serwera, więc próbka potrafi objąć 20+ lat i wtedy jedna mediana miesza epoki cenowe (dla Łodzi bez filtra: 3,7 tys. zl/m2, a dla samego 2025-2026: 8,3 tys.). Dlatego wynik zawsze zawiera `zakres_dat` i `mediana_wg_roku`, a przy szerokiej próbce - ostrzeżenie. Do wyceny podawaj `data_od`.
Obsługiwane typy nieruchomości:
- `lokale`
- `budynki`
- `dzialki`
## Uruchomienie
Projekt działa przez Docker Compose. Lokalny venv nie jest potrzebny.
```bash
docker compose up -d
```
Przy zmianach w kodzie przebuduj obraz:
```bash
docker compose up -d --build
```
Zatrzymanie:
```bash
docker compose down
```
Cache granic administracyjnych jest trzymany w wolumenie Dockera `rcs-mcp-cache`, więc kolejne uruchomienia korzystają z zapisanych danych.
Zbudowanie cache'u od zera trwa dłużej niż limit czasu pojedynczego wywołania narzędzia MCP (ok. 30 s dla `powiat`, ok. 60 s dla `gmina`), dlatego kontener po starcie rozgrzewa w tle brakujące poziomy. Postęp widać w logach:
```bash
docker compose logs -f rcs-mcp
```
Jeśli mimo to trafisz na jeszcze niezbudowany poziom, `znajdz_obszar` nie zawiesi się do timeoutu — zwróci informację, że cache buduje się w tle. Pobieranie leci dalej, więc wystarczy powtórzyć to samo wywołanie za kilkadziesiąt sekund.
## Claude Desktop
Wpis w `claude_desktop_config.json`:
```json
{
"mcpServers": {
"rcs-mcp": {
"command": "/usr/local/bin/docker",
"args": [
"compose",
"-f",
"/Volumes/wew/Git/rcs-mcp/docker-compose.yml",
"exec",
"-T",
"rcs-mcp",
"rcs-mcp"
]
}
}
}
```
Po zmianie konfiguracji zrestartuj Claude Desktop.
## Źródła danych
- RCN: `https://mapy.geoportal.gov.pl/wss/service/rcn?language=pol`
- PRG, granice administracyjne: `https://mapy.geoportal.gov.pl/wss/service/PZGIK/PRG/WFS/AdministrativeBoundaries`
- PRNG, nazwy miejscowości: `https://mapy.geoportal.gov.pl/wss/service/PZGiK/PRNG/WFS/GeographicalNames`
Geoportal zwraca dane jako WFS/GML. Serwer pobiera je przez `httpx` i parsuje lokalnie.
## Ograniczenia
- Filtrowanie serwerowe `CQL_FILTER` w usługach Geoportalu jest ignorowane, więc projekt filtruje dane po stronie klienta.
- Realnie użyteczny filtr przestrzenny to `BBOX`.
- Pierwsze wyszukanie obszaru może pobrać większą warstwę PRG/PRNG i zbudować cache.
- Dane RCN bywają zaszumione, np. udziałami, garażami, komórkami lub błędnymi powierzchniami. Dla statystyk zwykle bardziej sensowna jest mediana niż średnia.
- Parametr `tylko_mieszkalne=True` domyślnie odrzuca część rekordów niemieszkalnych dla lokali i budynków.
## Struktura
- `rcs_mcp/server.py` - definicje narzędzi MCP.
- `rcs_mcp/wfs_client.py` - pobieranie i parsowanie transakcji RCN.
- `rcs_mcp/granice.py` - wyszukiwanie obszarów i cache PRG/PRNG.
- `Dockerfile` - obraz aplikacji.
- `docker-compose.yml` - uruchomienie kontenera dla Claude Desktop.
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: znajdz_obszar resolves a name to a bounding box, wyszukaj_transakcje returns raw transaction records, and statystyki_cen computes aggregated price statistics. There is no overlap in functionality, so an agent can easily select the right tool for the intended step.
All names use lowercase snake_case and are concise Polish terms, but the pattern is not perfectly uniform: znajdz_obszar and wyszukaj_transakcje are verb_noun, while statystyki_cen is noun_noun. This is a minor deviation from a consistent verb-first convention and still readable.
With only 3 tools, the server is tightly focused on querying real estate transactions. Each tool is essential and fills a distinct role in the workflow (geocoding, raw data, statistics), so the count is well-scoped and not too thin.
The toolset forms a complete pipeline for the domain: find an area by name, then use the returned bbox to either fetch raw transactions or compute price statistics. Since this is a read-only data service, there are no obvious missing lifecycle operations, and agents can accomplish the intended tasks without dead ends.