Connect AI agents to commercial real estate search across Denmark, Sweden, Norway, and Germany.
Add the server to your Claude Desktop configuration file and restart.
Open the config file for your OS:
macOS
~/Library/Application Support/Claude/claude_desktop_config.json
Windows
%APPDATA%\Claude\claude_desktop_config.json
Add the lokalebasen entry inside mcpServers:
{
"mcpServers": {
"lokalebasen": {
"url": "https://www.matchoffice.com/api/mcp"
}
}
}
Restart Claude Desktop. The Lokalebasen tool set appears automatically in the tool picker.
Any MCP-compatible client can connect. Send a POST request with Content-Type: application/json and Accept: application/json.
POST https://www.matchoffice.com/api/mcp
Content-Type: application/json
Accept: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "my-agent", "version": "1.0" }
}
}
Retrieve the full input schemas for all tools via tools/list:
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }
Search for available lease or sale listings. Geo queries are resolved via autocomplete — pass a city, district, or neighborhood name in any supported language.
| Name | Type | Description | |
|---|---|---|---|
| country_alpha2 | string | required | ISO 3166-1 alpha-2 country code, lowercase. E.g. dk, se, no, de. |
| query | string | optional | City, district, or neighborhood — resolved via autocomplete. E.g. Copenhagen, Østerbro, Stockholm, Berlin. Omit for a national search. |
| section | string | optional | lease (default) or sale. |
| kinds | string[] | optional | Property types (OR). Lease: office, warehouse, store, coworking, meeting_room, virtual_office, parking, business_center. |
| area_min / area_max | integer | optional | Area filter in m² (overlap semantics — a listing offering 50–500 m² appears for area_min: 400 because the ranges intersect). area_min must be ≥ 0 and ≤ area_max. |
| rent_min / rent_max | integer | optional | Rent range. Combine with currency and rent_format. |
| currency | string | optional | Currency for rent values. Defaults to country native (DKK for dk, SEK for se, etc.). Pass EUR to filter in euros. |
| rent_format | string | optional | Lease only. monthly (default) = total per month; yearly = per m²/year. |
| facilities | string[] | optional | Facilities filter (OR). Common values: elevator, parking, wifi, disabled, access_24_hour, electric_car_charge, shower, gym, canteen. |
| suitable_for | string[] | optional | Tenant type filter (OR). Values: cafe, showroom, clinic, education, popup_store, hair_salon, high_ceiling_storage, storage_hotel, production, workshop. |
| energy_rating | string[] | optional | Energy rating filter (OR). Values: A2020, A2015, A2010, A, B, C, D, E, F, G, Exempted. |
| sort_by | string | optional | Sort in column|direction format. E.g. yearly_rent_per_m2_amount_from|asc, area_from|asc, created_at|desc. |
| page | integer | optional | Page number starting at 1. Returns 20 results per page. |
{
"jsonrpc": "2.0", "id": 3,
"method": "tools/call",
"params": {
"name": "search_locations",
"arguments": {
"country_alpha2": "dk",
"query": "Copenhagen",
"kinds": ["office"],
"rent_max": 25000,
"currency": "DKK",
"rent_format": "monthly",
"area_min": 50
}
}
}
{
"total": 142,
"page": 1,
"per_page": 20,
"total_pages": 8,
"geo": { // present only when `query` was set
"query": "Copenhagen",
"matched_type": "district", // "district" | "province" | "city" | "street"
"postal_district_count": 1
},
"locations": [
{
"id": 48291,
"uuid": "a1b2c3...",
"title": "Lyst kontor i hjertet af København",
"description": "Moderne kontorlejemål...", // truncated at 200 chars
"kind": "office",
"section": "lease",
"area_from": 65,
"area_to": 130,
"country_alpha2": "dk", // always present; from params for country-specific, from DB per item for global
"latitude": 55.6761,
"longitude": 12.5683,
"canonical_url": "https://www.matchoffice.com/leje/...",
"postal_name": "Vesterbro",
"postal_code": "1620",
"address_title": "Vesterbrogade 1",
"provider_name": "Acme Real Estate",
"yearly_rent_per_m2_from": 1950, // numeric, native currency, lease only
"yearly_rent_per_m2_to": 2400,
"monthly_rent_from": 10563, // total per month, native currency, lease only
"monthly_rent_to": 13000,
"sale_price": null, // numeric, sale only
"currency": "DKK", // ISO 4217, derived from country_alpha2
"formatted_rent_price": "1.950 kr. pr. m²", // localized display string
"energy_rating": "B",
"secondary_kinds": [],
"suitable_for_kinds": []
}
]
}
Use id from search results to call get_location for photos, facilities, provider contact, and full pricing detail.
geo is only present when query was set. If the query couldn't be resolved to a place — no match found, or the geo lookup was temporarily unavailable — geo is null and a geo_note string explains why; results still fall back to a national search for country_alpha2 rather than returning nothing.
{
"geo": null,
"geo_note": "No geographic match found for 'Kobenhavn' in dk. If 'Kobenhavn' is in a different country, set country_alpha2 to the correct code. Showing all results for dk."
}
{
"geo": null,
"geo_note": "Geographic search temporarily unavailable for 'Copenhagen' in dk — showing national results."
}
{
"total": 0,
"geo": { "query": "Aalborg", "matched_type": "district", "postal_district_count": 1 },
"geo_note": "'Aalborg' resolved, but no listings inside it match the given filters."
}
Returns the complete record for one location: description, all photos, facilities list, and canonical URL.
| Name | Type | Description | |
|---|---|---|---|
| id | integer | required | Location ID from search_locations. |
{
"jsonrpc": "2.0", "id": 4,
"method": "tools/call",
"params": {
"name": "get_location",
"arguments": { "id": 48291 }
}
}
{
"id", "slug", "title", "description",
"section", // "lease" or "sale"
"area_from", "area_to", "kind", "secondary_kinds",
"latitude", "longitude", "full_address",
"postal_code", "postal_district_name", "country_alpha2",
"yearly_rent_per_m2_from", // numeric, native currency, lease only
"yearly_rent_per_m2_to",
"monthly_rent_from", // total per month, native currency, lease only
"monthly_rent_to",
"sale_price", // numeric, sale only
"energy_rating", // "A2020"…"G", or null if exempted
"exempted_from_energy_rating", // boolean
"provider_name", // landlord company name
"provider_phone", // landlord phone number
"primary_photo_url", // single hero photo (330×255), or null
"photos", // array of thumbnail URLs (330×255)
"full_photos", // array of full-resolution URLs (1920×1080)
"facilities", // array of facility name strings
"canonical_url" // public listing URL
}
Returns {"errors": ["Location not found"]} or {"errors": ["Location not available"]} when the location does not exist or is not publicly visible.
Sends a contact inquiry to the property provider. Always confirm with the user before calling this tool.
| Name | Type | Description | |
|---|---|---|---|
| location_id | integer | required | Location ID from search_locations. |
| name | string | required | Full name of the inquirer. |
| string | required | Email address. | |
| phone | string | required | Phone number (minimum 6 characters). |
| company | string | optional | Company name. |
| comment | string | optional | Message to the provider — preferred move-in date, team size, specific requirements, etc. |
{
"jsonrpc": "2.0", "id": 5,
"method": "tools/call",
"params": {
"name": "create_order",
"arguments": {
"location_id": 48291,
"name": "Jana Novak",
"email": "jana@example.com",
"phone": "+45 12 34 56 78",
"company": "Novak Consulting ApS",
"comment": "Looking for a 6-month lease from Q3. Team of 8."
}
}
}
{ "success": true, "order_id": 19204 }
{ "success": false, "errors": ["Invalid email address 'not-an-email'. Please provide a valid email so the provider can reply."] }
A duplicate inquiry (same email + location) within 30 minutes is rejected. So is a 6th inquiry from the same email within a rolling 24 hours, and so is any inquiry once the MCP server as a whole has processed 50 orders (across all emails) within a rolling 24 hours — all three return the same {"success":false,"errors":[...]} shape with a message explaining the reason.
phone shorter than 6 characters is rejected at the protocol level (before the tool runs), since it's enforced by minLength in the input schema — the response is a generic tool-call error, not the {"success":false,"errors":[...]} shape above.
{
"content": [{ "type": "text", "text": "Invalid arguments: ..." }],
"isError": true
}
The query parameter resolves against the Elasticsearch autocomplete index. The server accepts city names, district names, and neighborhood names in Danish, Swedish, Norwegian, German, and English transliterations. A city name resolves to the whole city — all of its postal districts — not just a single narrower district that happens to share its name. If no matching geo entity has active lease listings at all, the query returns a national result set rather than zero results.
A resolved place combined with other filters (kinds, facilities, rent_min/rent_max, area_min/area_max, ...) that match nothing inside it is not the same as an unresolved place: the response is total: 0 with geo still populated and a geo_note explaining that the place resolved but no listings there match the given filters — it does not fall back to a national result set. Only relax or remove the other filters, not query, to find results in that case.
Results are always capped at 20 per page. Use page to paginate: page = ceil(total / 20) gives the last page. The total field reflects the full result count before pagination.
Rent filters (rent_min, rent_max) are stored natively in each country's currency. Pass currency: "EUR" (or any ISO 4217 code) and the server converts your values to the country currency before filtering. An unsupported currency code returns an error rather than silently skipping conversion.
currency cannot be combined with rent_min/rent_max when country_alpha2: "global" — a global search spans multiple native currencies with no single target to convert into. Specify a country, or omit currency and the rent bounds for a global search.
monthly = total rent per calendar month. yearly = rent per m² per year (typical for Danish commercial leases). Both formats accepted; defaults to monthly. Has no effect for section: "sale".