Skip to main content
Glama
lakafior
by lakafior
README.md
# 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

A4.7/5.0

Scored across 3 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness5/5

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.

Maintenance

ActivitySlowing
ResponsivenessNo issues