Skip to main content

woning — MCP server for Dutch house hunting: funda.nl search, rental sources, and Dutch mortgage borrowing capacity

Project description

woning-mcp

Your AI makelaar (real estate agent) for the Dutch housing market. Search funda.nl, compare listings, calculate Dutch mortgages, and estimate buying costs — all from Claude.

PyPI CI

Quick start

Prerequisites: Install uv (Python package runner) if you don't have it:

curl -LsSf https://astral.sh/uv/install.sh | sh

Claude Code

From a local checkout (use this while developing — it picks up your edits):

claude mcp add makelaar -- uv run --project /ABSOLUTE/PATH/TO/woning-mcp woning-mcp

Or the released version from PyPI:

claude mcp add makelaar -- uvx woning-mcp

Note: the PyPI release is pinned to an older pyfunda. funda's search API now rejects those requests with HTTP 400, so install from a local checkout until a new version is published.

Verify it registered:

claude mcp list          # should show: makelaar

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "makelaar": {
      "command": "FULL_PATH_TO_UV",
      "args": [
        "run",
        "--project",
        "/ABSOLUTE/PATH/TO/woning-mcp",
        "woning-mcp"
      ]
    }
  }
}

Replace FULL_PATH_TO_UV with the output of which uv (e.g. /Users/you/.local/bin/uv). Claude Desktop doesn't inherit your shell PATH, so the full path is required.

Then restart Claude Desktop.


What you can ask

Search for apartments in Amsterdam under €400k with energy label A or better

I earn €80,000/year, my partner earns €50,000. Can we afford this €500,000 house? We're first-time buyers, both 29. Include NHG.

Compare these two listings: https://www.funda.nl/detail/koop/amsterdam/appartement-rapenburgerstraat-103-f/43363178/ and https://www.funda.nl/detail/koop/amsterdam/huis-arendonksingel-54/43363912/

What are the total buying costs for a €350,000 apartment? I'm a starter, age 27.

Show me the price history for this listing: https://www.funda.nl/detail/koop/amsterdam/huis-arendonksingel-54/43363912/


Tools

search_listings

Search funda.nl with filters for location, price, area, property type, energy label, and more. Supports multi-city search and pagination (15 results/page).

get_listing

Full property details by listing ID or funda.nl URL.

get_price_history

Historical asking prices, sale prices, and WOZ values for a listing.

compare_listings

Side-by-side comparison of 2–10 properties: price, area, price/m², bedrooms, year built, energy label, garden.

calculate_dutch_mortgage

Dutch-specific mortgage calculator covering:

  • Annuity & linear mortgage types
  • NHG (Nationale Hypotheek Garantie) — eligibility, premium, interest discount
  • Hypotheekrenteaftrek — mortgage interest deduction (36.97%) minus eigenwoningforfait
  • Startersvrijstelling — 0% transfer tax for first-time buyers age 18–34
  • NIBUD max mortgage — income-based borrowing limit with partner income and student debt

calculate_total_cost

Itemized breakdown of bijkomende kosten (additional buying costs):

Cost Typical amount
Overdrachtsbelasting (transfer tax) 0% / 2% / 10.4%
Notariskosten (notary) €1,500 – €2,500
Taxatiekosten (appraisal) ~€600
Hypotheekadviseur (mortgage advisor) ~€2,500
Bankgarantie (bank guarantee) ~€500
Kadaster (land registry) ~€150
NHG premium (optional) 0.6% of mortgage
Aankoopmakelaar (optional) ~1.5% of price

These costs must come from savings — you can't borrow them.


Disclaimer

This project is not affiliated with, endorsed by, or connected to funda.nl or Funda B.V. Property data is sourced from third-party services and may be incomplete or outdated.

Mortgage calculations and cost estimates are approximations for informational purposes only — not financial advice. Dutch mortgage rules, tax rates, and NHG limits change annually. Always consult a licensed mortgage advisor (hypotheekadviseur) before making financial decisions.

See LICENSE for the full MIT license.


Acknowledgements

Built on pyfunda by 0xMH — the Python library that makes funda.nl data accessible.


Development

git clone https://github.com/victorlane/woning-mcp.git
cd woning-mcp
uv sync
uv run pytest                              # run tests
uv run ruff check src/ tests/              # lint
uv run mcp dev src/woning/server.py  # MCP Inspector

Listing monitor

A standalone script that watches funda.nl on a schedule and appends only listings it has never seen before to a CSV, ready for review by you or an agent. It does not need Claude or the MCP server — it is plain Python and safe to run from cron.

uv run woning-monitor            # one pass
uv run woning-monitor --dry-run  # show what would be written, touch nothing

Each run:

  1. searches every enabled stream across its locations,
  2. compares each listing's ID against the seen-IDs state file,
  3. fetches one detail page per genuinely new listing (for coordinates, construction year and photo URL, which funda's search results omit),
  4. appends the new ones to new_listings.csv and records their IDs.

Configure

Edit monitor_config.toml. Three streams ship pre-configured — rent (≤ €1.300), nieuwbouw (new build ≤ €400k) and koop (resale ≤ €400k) — each with its own locations, price ceiling, filters and enabled toggle.

[monitor]
csv_path = "new_listings.csv"
state_path = "monitor_state.json"
request_delay = 1.0        # seconds between requests
seed_on_first_run = true   # see "First run" below
fetch_details = true
detail_fetch_limit = 40

[streams.koop]
enabled = true
locations = ["utrecht", "amersfoort", "woerden", "den-haag", "amstelveen", "nieuwegein", "ijsselstein"]
category = "buy"           # "buy" | "rent" | "sold"
construction_type = "resale"
max_price = 400000
sort = "newest"
max_pages = 3              # 15 listings per page, per location

Location slugs must be lowercase and hyphenated: den-haag, not den haag. A wrong slug is not an error — funda returns an empty result set — so any location yielding 0 listings is logged as a WARNING.

Valid filters: category, status, min_price/max_price, min_area/max_area, min_plot/max_plot, min_rooms/max_rooms, min_bedrooms/max_bedrooms, min_construction_year/max_construction_year, object_type, energy_label, construction_type, radius_km, sort. Anything else is rejected at startup.

First run

On a virgin state file the monitor seeds: it records everything currently listed as already seen and writes nothing to the CSV, so you don't get a few hundred pre-existing listings dumped on you. From then on only genuinely new listings are reported. Use --no-seed if you do want the current market written out on the first run.

CSV output

Appended in place, header written once. Columns: first_seen (UTC), stream, listing_id, global_id, address, postcode, city, neighbourhood, price, living_area, plot_area, price_per_m2, rooms, bedrooms, object_type, construction_year, energy_label, status, publication_date, latitude, longitude, project, broker, url, photo_url, and a blank reviewed column for you or an agent to fill in.

Cron

Write your own crontab entry (crontab -e). Every 30 minutes, with output appended to a log:

*/30 * * * * cd /Users/victorbrinkhorst/Personal/Projects/woning-mcp && /Users/victorbrinkhorst/.local/bin/uv run woning-monitor >> monitor.log 2>&1

Both paths must be absolute — cron does not inherit your shell PATH or working directory. Use the output of which uv for the binary. Overlapping runs are prevented by a lock file, so a slow run can never double-write.

CLI

Flag Effect
--config PATH Config file (default: monitor_config.toml in the working directory)
--csv PATH Override CSV output path
--state PATH Override state file path
--stream NAME Run only this stream; repeatable. Also runs disabled streams
--dry-run Report findings without writing CSV or state
--seed / --no-seed Override seed_on_first_run
--verbose Debug logging

Logs go to stderr with timestamps. Exit code is 0 on success, 1 if every stream failed, 2 on a bad config.

Unattended-safety

  • One failing stream or location never aborts the run; the rest continues.
  • A stream that had any location fail does not get its IDs marked as seen, so nothing is silently lost.
  • The state file is written atomically; a corrupt state file is backed up (monitor_state.json.corrupt.<ts>) rather than wiped.
  • A lock file guards against overlapping cron invocations, and a lock left by a killed process is detected and reclaimed.
  • Pagination is capped per location, so a broken filter can't paginate forever.

Rental sources beyond funda

funda's free-sector rental supply below ~€1300 is thin, and in some towns it is empty — Woerden and Amersfoort both return zero rentals on funda but have listings elsewhere. So rentals are searched across more than one site.

Source Access Reliability
funda.nl pyfunda (mobile API) Reliable
ikwilhuren.nu server-rendered HTML Reliable — no bot protection
Pararius pypararius Best-effort; Cloudflare returns intermittent HTTP 403

Use the search_rentals_all_sources tool to query them together. Pararius is off by default because it is frequently blocked; enable it with include_pararius=true and expect it to sometimes return nothing.

Not integrated, and why:

  • Huurwoningen.nl — serves a Cloudflare managed challenge site-wide (cf-mitigated: challenge), including /sitemap.xml. There is no sanctioned automated path, so it is not scraped. Check it manually.
  • Kamernet — rooms only.
  • WoningNet / sociale huur — login-gated, and irrelevant above the ~€48.6k/yr income ceiling.

All adapters are read-only. They never contact an agent, request a viewing, or submit an application — which is also what these sites' robots.txt files disallow (/contact/, /plan-een-bezichtiging, /request-details/).

Monitoring ikwilhuren

Set source = "ikwilhuren" on a stream in monitor_config.toml. Because ikwilhuren ignores query-string filters, such a stream sweeps the national feed (~40 pages) and filters locally, so it accepts min_price/max_price only. Keep min_price set — the feed also carries parking spaces and garages, some as low as €97/mnd.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

woning_mcp-0.1.1.tar.gz (145.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

woning_mcp-0.1.1-py3-none-any.whl (53.1 kB view details)

Uploaded Python 3

File details

Details for the file woning_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: woning_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 145.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for woning_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 806d4a1a07b6bdb3755075c16f92bc0fdb5715bd81bc68b68531826eedb924f0
MD5 132e2ebe03511e6f3a57f13f0840dc07
BLAKE2b-256 5e30fa548aae7f1ef738ba9ba527e66f00b07738ee8437420a643e386251bc14

See more details on using hashes here.

Provenance

The following attestation bundles were made for woning_mcp-0.1.1.tar.gz:

Publisher: publish.yml on victorlane/woning-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file woning_mcp-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: woning_mcp-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 53.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for woning_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e5d75923c818311b72b33d99693bc3857d5e0a41a2b189458471df510acc7c70
MD5 c58f9af17e8fd7d99d0b302ddd8fac69
BLAKE2b-256 f22d2bdf315ae31ebab82c72655a3a0d696b19fdad67ffc2f951131e5f3e6f3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for woning_mcp-0.1.1-py3-none-any.whl:

Publisher: publish.yml on victorlane/woning-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page