# ChatHome — Grande Région real estate (chathome.lu) ## About ChatHome ChatHome is the Grande Région's AI-native property search and decision-intelligence platform for buyers, renters, and agents. It combines region-wide conventional and natural-language search with Luxembourg-specific commune market data and decision tools. - Coverage: the whole Grande Région for listing search. Luxembourg-specific commune guides and datasets cover all 100 communes. Six languages — English, French, German, Luxembourgish, Portuguese, and Spanish - Agent access: authenticated MCP and A2A endpoints, plus public structured listing pages - Ranking policy: relevance and listing quality determine order; paid placement does not - Evidence: public data sources, ranking rules, price-index methodology, and limitations are documented on the verification pages below For the full machine-readable content index (property listings, commune guides, blog posts, market data, and a timestamped current-inventory snapshot), see [llms-full.txt](https://chathome.lu/llms-full.txt). An authenticated MCP server and A2A endpoint are documented further below. ## Key pages Human-readable entry points. Each is localized to en/fr/de/lu/pt (Spanish content at /es/) — swap the `/en/` prefix for another locale. - Homes for sale: https://chathome.lu/en/buy - Homes for rent: https://chathome.lu/en/rent - Apartments for rent: https://chathome.lu/en/apartments-for-rent - Houses for sale: https://chathome.lu/en/houses-for-sale - Houses for rent: https://chathome.lu/en/houses-for-rent - Commune real-estate guides (100 communes): https://chathome.lu/en/commune/luxembourg - Estate agents by town (15-town pilot): https://chathome.lu/en/estate-agents/luxembourg - Commune price data (€/m² to buy & rent): https://chathome.lu/en/prices/luxembourg - Market report (national & commune trends): https://chathome.lu/en/market-report - Real-estate glossary: https://chathome.lu/en/glossary - Free property price estimate: https://chathome.lu/en/financial-tools/estimate - Mortgage affordability calculator: https://chathome.lu/en/financial-tools/mortgage - Blog & buyer/renter guides: https://chathome.lu/en/resources/blog - Mission and operating principles: https://chathome.lu/en/mission - Data sources and coverage: https://chathome.lu/en/mission/data - Information for real-estate agencies: https://chathome.lu/en/mission/for-agents - Search ranking policy: https://chathome.lu/en/mission/ranking - Press room and Luxembourg Price Index: https://chathome.lu/en/press - Luxembourg Price Index methodology: https://chathome.lu/en/prices/methodology - Full machine-readable content index: https://chathome.lu/llms-full.txt ## MCP Endpoint URL: https://chathome.lu/api/mcp Transport: Streamable HTTP (MCP spec 2025-11-25) Auth: no account for public property discovery; API key (X-API-Key) or OAuth 2.1 with PKCE for higher limits and protected tools Health/server info (no credential): https://chathome.lu/api/mcp/health OpenAPI spec for the public REST API (no credential): https://chathome.lu/openapi.json ## Access Discovery is anonymous. POST `initialize`, `ping`, `tools/list`, `prompts/list` and `resources/list` need no credential, so any client or registry can complete the MCP handshake and read the catalogue before signing up. The full public property-discovery journey EXECUTES anonymously — no account, no key: - `search_vibe` — natural-language property search - `search_rentals` — structured rental search - `search_for_sale` — structured for-sale search - `render_listing_cards` — photo carousel for exact search results - `get_listing` — the full fail-closed listing-page record, normalized decision facts, gallery photos, and canonical URL - `compare_listings` — normalized evidence-aware comparison of 2-8 listings, including costs, property checks, and mobility - `get_market_context` — bounded, source-backed commune property context Anonymous calls are capped per source IP and, for model-backed calls, against a shared hourly budget; a 429 tells you to retry or authenticate. Sign up for a much larger allowance. Every other tool CALL requires authentication — an API key or an OAuth 2.1 token — and answers 401 with a `WWW-Authenticate` header pointing at the OAuth metadata. That includes everything that writes (`submit_inquiry`, `analyze_lease`) and everything user-specific (`get_inquiry_status`). Requests are rate limited per credential and per IP. ## Public REST API (no credential) OpenAPI 3.1 spec: https://chathome.lu/openapi.json (mirrored at https://chathome.lu/.well-known/openapi.json) It documents only the anonymous read surface — property search, one listing by id, commune market intelligence, trends, forecasts, the Luxembourg Price Index, and neighbour-country indicators. No write endpoint and no admin route appears in it. ## Authentication ### Option A — OAuth 2.1 with PKCE (recommended) This server uses PUBLIC OAuth clients — no client secret is required. The PKCE code_challenge replaces the secret. Authorization server: https://chathome.lu/.well-known/oauth-authorization-server Flow: authorization_code with PKCE (S256) Client ID reference (leave Client Secret blank or enter any placeholder): AI Client | Client ID | Secret ---------------------------|-----------------|------------------- Claude Desktop | claude-desktop | (none required) Claude.ai (web) | claude-desktop | (none required) Claude Code (CLI) | claude-desktop | (none required) Cursor | cursor | (none required) ChatGPT | chatgpt | (none required) Claude Code config (~/.claude/claude_code_config.json): { "mcpServers": { "chathome": { "type": "http", "url": "https://chathome.lu/api/mcp" } } } ### Option B — API Key Generate a key at: https://chathome.lu/dashboard/agent-access Pass as header: X-API-Key: cht_ Config with API key: { "mcpServers": { "chathome": { "type": "http", "url": "https://chathome.lu/api/mcp", "headers": { "X-API-Key": "cht_YOUR_KEY_HERE" } } } } ## Rate Limits - 200 tool calls / hour per user, 300 calls / hour per IP - OAuth tokens expire after 1 hour (clients auto-refresh) ## Tools ### search_vibe (PRIMARY — natural-language search) Semantic "vibe" search. Pass the user's request in plain prose and the engine uses embeddings + LLM reasoning to match on lifestyle, mood, location proximity, and soft preferences — not just structured filter values. PREFER this tool whenever the user describes what they want in words. Healthy runs are ranked with a 0-100 request-fit `score` (not a probability) and up to 3 source-grounded `matchReasons`. Degraded runs omit both and set `trust.fallback: true`. Parameters: - query (string, required): natural-language description, e.g. "quiet 2-bed near Kirchberg with a balcony under €2000" - intent (string, optional): "rent" or "buy" (default "rent") - limit (number, optional): max results 1-50, default 20 - locale (string, optional): en, fr, de, lu, pt ### search_rentals Structured rental search for properties across the Grande Région. Parameters: - commune (string, optional): commune name or slug - minPrice (number, optional): minimum monthly rent in EUR - maxPrice (number, optional): maximum monthly rent in EUR - minBeds (number, optional): minimum bedrooms - minAreaSqm (number, optional): minimum area in m² - maxAreaSqm (number, optional): maximum area in m² - minBathrooms (number, optional): minimum bathrooms - propertyType (string, optional): apartment, house, studio, room - furnished, petsAllowed, elevator, balcony, garden (optional true): require positive source evidence; unknown values never count as no - parkingType (optional): off_street, garage, covered - sort (optional): relevance, newest, price-asc, price-desc. Rental price sorts use base rent, not an all-in monthly-cost claim. - limit (number, optional): max results 1-50, default 20 - locale (string, optional): en, fr, de, lu, pt ### search_for_sale Search properties for sale across the Grande Région. Parameters: same as search_rentals (maxPrice = sale price) ### render_listing_cards Render 1-8 exact results from a prior search as an interactive, cross-platform MCP Apps photo carousel in compatible hosts such as ChatGPT and Claude. Call a search tool first, then copy its listing IDs and any score/matchReasons exactly. The tool re-reads canonical public facts and drops listings that are no longer eligible for display. Parameters: - listings (array, required): objects with id and optional exact score and matchReasons from the prior search - heading (string, optional): short heading grounded in the user's request - locale (string, optional): en, fr, de, lu, pt Scope: listings:read ### get_listing Get the complete public listing-page projection plus an AgentListing v2 decision record. The normalized record includes known and estimated rental costs, availability, property facts, official flood/noise screening, exact-point property checks, station frequency, commute summaries, value context, provenance, and explicit unknowns. `listingPage` is the same fail-closed public projection used by the human listing page. `listingPageContext` contains the same public server data around it: price history, area price trend, family amenities, neighbourhood facts, country evidence, transport, commute, property passport, exact property checks, and parcel dossier. Unavailable blocks are null. Parameters: - id (string, required): listing ID - locale (string, optional): en, fr, de, lu, pt - includePhotos (boolean, optional, default true): attach up to three thumbnails ### compare_listings Normalize and compare two to eight exact search results in one response. Parameters: - ids (string array, required): 2-8 listing IDs - locale (string, optional): en, fr, de, lu, pt Returns `listing-comparison.v1` with AgentListing v2 records, explicit not-found IDs, complete known/estimated costs, availability and property facts, official flood/noise checks, station frequency, commute summaries, value context, provenance, freshness, location precision, unknown fields, and confirmed duplicate-source counts. ### get_listing_truth Return the full fail-closed listing-page projection and `listingPageContext`, plus normalized decision data, sources, freshness, important unknowns, and a privacy-safe correction summary. Use it before a consequential decision. Parameters: id (required), locale (optional: en, fr, de, lu, pt) Scope: listings:read ### submit_inquiry Send a message about a listing after calling get_listing. For chathome-hosted listings this creates a conversation thread; for externally managed listings it returns the source agency contact details so the agent can hand off cleanly. Parameters: - listingId (string, required): listing ID from search_vibe/search_rentals/search_for_sale - message (string, required): buyer/renter inquiry, 10-5000 characters - contactEmail (string, optional): reply address for the inquirer - agentName (string, optional): display name for the inquirer Scope: inquiries:write ### get_inquiry_status Poll a ChatHome-hosted inquiry conversation for replies and return its current status and complete message thread. Parameters: conversationId (required), locale (optional: en, fr, de, lu, pt) Scope: inquiries:read ### get_market_context Get bounded, source-backed property context for a commune, including dates and unknown states. Parameters: - commune (string, required): commune slug e.g. luxembourg, esch-sur-alzette - locale (string, optional): en, fr, de, lu, pt ### get_town_agencies Get every estate agency with active listings in one of the 15 supported Luxembourg towns. Returns the measurement date, total stock, buy/rent split, and each agency's live stock and asking-price ranges. The figures come from the same service as the localized town page. Parameters: - commune (string, required): town name or slug e.g. Mersch, diekirch - locale (string, optional): en, fr, de, lu, pt Scope: market:read ### get_price_estimate AI-powered sale or rental price estimate for a Luxembourg property. Scope: market:read ### get_affordability Mortgage affordability simulation using income, debts, savings, residency, employment type, current Luxembourg rates, and acquisition costs. Apply Bëllegen Akt only when `residenceUse` is explicitly `primary`, `bellegenAktEligibleBuyers` gives the confirmed eligible acquirer count, and `bellegenAktCreditAvailable` gives their confirmed aggregate unused balance. Omitted confirmation fields or `residenceUse: "unknown"` preserve the safe unconfirmed state; zero is a confirmation, not a default. The legacy `isPersonalResidence` flag is optional and deprecated. `isFirstTimeBuyer` is an optional deprecated compatibility signal (default false) and never proves entitlement or balance. The response's `maxPropertyPriceCostBasis` is either `complete` or `known_costs_only`. A known-cost-only figure excludes `acquisitionCosts.unknownComponents`; do not use it as a hard property-search ceiling or definitive affordability verdict. Scope: market:read ### get_commute_times Commute times from a Luxembourg commune to Luxembourg City, Kirchberg EU Quarter, or Belval. Scope: market:read ### analyze_lease Analyze a user-provided lease PDF (base64) or raw lease text. No URL input is accepted. Requires OAuth user authentication and persists the full user-scoped analysis while returning a compact risk summary under the MCP response cap. Scope: documents:write ## Prompts ### match-properties Use get_affordability first when financial inputs are available. Only use its `maxPropertyPrice` as a hard search ceiling, and only give definitive within budget, stretch, or over-budget labels, when `maxPropertyPriceCostBasis` is `complete`. For `known_costs_only`, keep the wider inventory, disclose the unknown acquisition-cost components, and make price comparisons provisional. A budget stated directly by the user may still be applied as a hard ceiling. ## Claude Agent Skill A packaged Agent Skill teaches Claude (and any Agent-Skills-compatible runtime) how to use chathome well — tool selection, workflows, Luxembourg context, and the no-auth fallback endpoints. - Raw skill (agent-readable): https://chathome.lu/skills/chathome/SKILL.md - Installable zip (claude.ai → Settings → Capabilities → upload skill): https://chathome.lu/skills/chathome-luxembourg-property.zip - Claude Code plugin (bundles the skill + this MCP server): /plugin marketplace add chathome-lu/chathome-claude-plugin ## A2A Endpoint Agent Card: https://chathome.lu/.well-known/agent-card.json Task endpoint: https://chathome.lu/api/a2a ## Response Format Search tools separately expose `returnedCount`, `matchingCount`, `matchingCountKind` (`exact`, `lower_bound`, or `unavailable`), `marketMatchingUpperBound`, `hasMore`, `nextCursor`, and `searchExhaustive`. `nextCursor` is null because neither the bounded classic scan nor pool-relative vibe ranking currently has a stable cursor. All listing tools return JSON with an AgentListing v2 shape. `get_listing` and `get_listing_truth` also return `listingPage`, the complete fail-closed public projection used by the human listing page, and `listingPageContext`, its public price history, area data, amenities, passport, parcel and mobility blocks: { schemaVersion, id, url, title, price, currency, listingType, propertyType, city, district, beds, baths, areaSqm, energyClass, imageUrl, agencyName, source, rent, location, availability, property, history, duplicates, amenities, officialScreening, propertyChecks, transport, valueContext, fieldProvenance, missingFields, match, latitude, longitude } For agency-sourced listings, `source` contains the original agency URL, agency name, source listing ID when known, latest catalogue re-sighting timestamp, evidence-based status, and a separate direct-agency confirmation timestamp. `source.lastSeenAt` does not claim that the detail page was fetched at that time. `rent.monthlyTotal` is the source-grounded total. It stays null until every required published component is known. `knownMonthlyLowerBound` is the amount already established. `estimatedMonthlyTotal` is the clearly labelled listing-page estimate with utilities and indicative renter insurance. `knownUpfrontCost`, `exactUpfrontCost`, `unknownComponents`, mandatory fees, deposit ceiling, and agency-fee basis expose the existing move-in-cost model. `officialScreening` carries the stored official flood/noise and nearest-stop facts. `propertyChecks` carries all seven exact point-in-polygon checks only when the property point is precise and publishable. `transport.scope` says whether station frequency and commute figures use the property or a commune point. Amenity nulls mean unknown, never no. Successful tool calls expose this JSON both as a text content block for broad client compatibility and as MCP `structuredContent`; each tool advertises an output schema plus read/write safety annotations. ## Machine-readable listing data Standard-format listing surfaces for LLMs/agents (no auth, more data than the UI shows): Bulk feed (quality-gated active listings, JSON): https://chathome.lu/listings/feed.json Per-listing Markdown (full record): https://chathome.lu/listings/{id}/md Per-listing JSON (full record): https://chathome.lu/api/listings/public/{id} ## Agent usage and attribution Public facts may be retrieved to answer the current user. When presenting a property, link the ChatHome page and the original `source.listingUrl`. API availability does not itself grant bulk redistribution, photo/description republication, caching, or model-training rights. Respect rate limits and the published terms: https://chathome.lu/terms#content-license ## Human-readable docs https://chathome.lu/mcp