Skip to main content

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.

Metadata

Release files for woning-mcp 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for woning-mcp 0.1.1
File Size Uploaded Provenance
woning_mcp-0.1.1.tar.gz 145.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for woning-mcp 0.1.1
File Interpreter ABI Platform
woning_mcp-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 198.9 kB

Release files / woning_mcp-0.1.1.tar.gz

Download URL woning_mcp-0.1.1.tar.gz
Size 145.8 kB
Tags Source
SHA-256 checksum
How to use checksums
806d4a1a07b6bdb3755075c16f92bc0fdb5715bd81bc68b68531826eedb924f0
BLAKE2b-256 checksum
How to use checksums
5e30fa548aae7f1ef738ba9ba527e66f00b07738ee8437420a643e386251bc14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / woning_mcp-0.1.1-py3-none-any.whl

Download URL woning_mcp-0.1.1-py3-none-any.whl
Size 53.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e5d75923c818311b72b33d99693bc3857d5e0a41a2b189458471df510acc7c70
BLAKE2b-256 checksum
How to use checksums
f22d2bdf315ae31ebab82c72655a3a0d696b19fdad67ffc2f951131e5f3e6f3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Security for woning-mcp 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for woning_mcp-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page