kira-mcp
Generates Google Maps directions links for listings when a walking-distance target is specified.
Geocodes places via Nominatim/OpenStreetMap and calculates estimated walking distances to a specified place, enabling filters and sorting by walking time.
Uses Wikidata’s locality dataset to resolve Romanian cities, towns, and communes, including diacritics-insensitive search and ambiguous name handling.
kira-mcp
Ask your AI to find you a flat to rent in Romania — any city, town or commune.
An MCP server (and a CLI) that searches storia.ro, publi24.ro and lajumate.ro live, merges the results, removes duplicates and non-rentals, and reads the ad text for the things the site filters don't give you: heating type, parking, private owner vs agency, and walking distance to a place you care about.
Works with Claude (Code & Desktop), Cursor, VS Code, Windsurf, Gemini CLI, Codex and any other MCP client. Also usable as a plain CLI.
"Find me a 2-room flat in Cluj under 500 €, own boiler, with parking, max 20 minutes on foot from UMF."
$ kira search Brasov --rooms 2 --max-price 500 --heating centrala_proprie --limit 3
Brașov, Brașov — 10 matching, showing 3
storia: ok (74 fetched, 374 on site)
publi24: ok (24 fetched)
lajumate: ok (56 fetched, 382 on site)
1. Studio Maurer Villas partial mobilat
500 EUR | 2 cam | 45 mp | heating: centrala_proprie | parking: included
Tractorul, Brasov, Brasov
https://www.storia.ro/ro/oferta/studio-maurer-villas-partial-mobilat-IDIPoX
2. Apartament pe 2 niveluri, 62 mp, zona Faget-Tractorul
400 EUR | 2 cam | 62 mp | heating: centrala_proprie
...Why
Romanian rental sites each cover part of the market, their filters are thin, and the details that decide a viewing — centrală proprie or termoficare? is the parking spot included or "posibilitate de parcare în zonă"? — live in free text. kira-mcp does the tab-juggling and the reading, so an agent can answer the question you actually asked.
Related MCP server: BoligWatch MCP Server
Features
All of Romania: 3,181 localities (municipii, orașe, comune) from Wikidata. Diacritics optional, common shortcuts understood (
cluj,tg mures,buc), ambiguous names resolved (Floresti, Prahova).3 sites in parallel, deduplicated across sites (the same flat posted on two sites shows once, with
also_listed_on).Filters: price (EUR; RON converted), rooms, heating, parking, private owner, keywords.
Text extraction tuned on real Romanian ads: heating (
centrala_proprie/termoficare), parking (included,likely_included,area_possible,none), rooms, surface, floor, street. When a filter needs it, the full ad is read, not just the snippet.Walking distance from any place (
near="UMF"), geocoded with OpenStreetMap, with a Google Maps directions link.Drops non-rentals: sale ads, parking-only ads, hotel-style short stays.
Parking spots search (
search_parking).Privacy: phone numbers and e-mails are redacted from returned text. The user contacts the owner on the original site.
Quick start (5 minutes)
Step 1 — install uv (once)
kira-mcp runs through uv, which downloads it and its Python on first use. Nothing else to install.
Windows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"macOS / Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
Close and reopen the terminal, then check: uvx --version.
Step 2 — connect it to your AI
claude mcp add kira -s user -- uvx kira-rentals-mcpCheck with claude mcp list → kira: ... ✓ Connected.
Settings → Developer → Edit Config (opens
claude_desktop_config.json).Paste (merge with what is there):
{
"mcpServers": {
"kira": {
"command": "uvx",
"args": ["kira-rentals-mcp"]
}
}
}Quit Claude Desktop completely (tray icon → Quit) and start it again. "kira" appears under the tools/connectors menu.
If it says the server failed, use the full path of uvx as command
(where uvx on Windows, which uvx on macOS — e.g.
C:\Users\you\.local\bin\uvx.exe).
Same JSON as Claude Desktop, in ~/.cursor/mcp.json,
~/.codeium/windsurf/mcp_config.json or ~/.gemini/settings.json.
.vscode/mcp.json in your workspace:
{
"servers": {
"kira": {
"type": "stdio",
"command": "uvx",
"args": ["kira-rentals-mcp"]
}
}
}~/.codex/config.toml:
[mcp_servers.kira]
command = "uvx"
args = ["kira-rentals-mcp"]uvx kira-rentals-mcp --transport streamable-http --port 8000serves MCP at http://localhost:8000/mcp.
Step 3 — ask
Just talk to it, in English or Romanian:
"Caută-mi o garsonieră în Sibiu sub 350 €, cu centrală proprie."
"2-room flat in Timișoara, max 500 €, private owner, sorted by price."
"Apartamente cu 2 camere în Cluj la max 20 de minute pe jos de UMF."
"Is parking included in this one? "
"Loc de parcare de închiriat în Brașov sub 60 €."
The first call takes a few extra seconds while uv downloads the package.
No MCP? Use the agent skill or the CLI
Agent skill: copy
skills/romania-rentals/into your skills folder (e.g.~/.claude/skills/). It teaches the agent when to search and how to read the results, via the MCP tools or the CLI.CLI: see below.
Tools
Tool | What it does |
| Search one locality. Filters: |
| Full description, photos, heating/parking with confidence, for one ad URL. |
| Parking spots / garages for rent in a locality. |
| Resolve an ambiguous or misspelled place name; lists candidates with county. |
All tools are read-only. Every result carries the original ad URL.
CLI
uvx --from kira-rentals-mcp kira search "Cluj-Napoca" --rooms 2 --max-price 500 --near UMF --max-walk 20 --sort distance
kira search Iasi --heating centrala_proprie --parking included likely_included
kira listing https://www.storia.ro/ro/oferta/...
kira parking Brasov --max-price 80
kira find Floresti
kira --json search Sibiu # machine-readableConfiguration
Env var | Default | |
|
| Exchange rate for RON prices. |
|
| Seconds between requests to the same site. |
|
| |
| OS cache dir | Where geocoding results are cached. |
Sources and limits
Site | Status |
storia.ro | ✅ server-side price/room filters, pagination |
publi24.ro | ✅ newest page per search |
lajumate.ro | ✅ pagination |
olx.ro | ❌ blocks automated access (HTTP 403) |
imobiliare.ro | ❌ blocks automated access (HTTP 403) |
kira-mcp does not try to get around a site that blocks it. Results are therefore a large share of the market, not all of it — the tool says so in every response.
Be a good citizen. kira-mcp identifies itself, waits between requests, caches pages for 10 minutes, fetches only what one search needs and returns links back to the sites. It is meant for personal flat-hunting, not bulk harvesting. Check each site's terms before using it for anything else.
Distances are estimates (straight line × 1.3, walking 4.7 km/h). They use the
location the poster entered; an ad with only a neighbourhood gets a
district-precision distance, an ad with neither is listed without one.
Development
git clone https://github.com/Cezart3/kira-mcp && cd kira-mcp
uv sync --group dev
uv run pytest # offline, against synthetic fixtures
KIRA_LIVE=1 uv run pytest -m live # against the real sites
uv run ruff check .The locality list is rebuilt with uv run python scripts/build_localities.py.
Site parsers live in src/kira_mcp/sites/. Adding a site = one class with
search_rentals and fetch_detail; see sites/base.py.
Credits
Locality data: Wikidata (CC0).
Geocoding: Nominatim / © OpenStreetMap contributors (ODbL), at ≤1 request/second, cached.
Extraction rules grew out of Kira, a personal flat-hunting bot.
License
MIT © 2026 Cezar Tocaciu
Available Tools
4 toolsfind_localityARead-onlyIdempotent
Which Romanian localities match a name (with county, type and population). Use it when a name is ambiguous — there are e.g. several 'Florești'.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Locality name, diacritics optional. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and idempotentHint, so safety is covered; the description usefully adds that results carry county, type and population. It says nothing about matching semantics (exact, prefix, fuzzy) or result caps/ordering, which materially affects how an agent interprets an ambiguous-name result set.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero padding; the what-is-returned clause comes first and the usage cue second. The 'Florești' example earns its place by making ambiguity concrete.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read tool with no output schema, naming the returned fields is a strong compensation. The remaining gap is matching behavior — whether a partial or diacritic-variant name returns results — which an agent must guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema itself documents diacritics being optional. The description adds only that names may be ambiguous, which is context rather than parameter semantics, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (Romanian localities) and operation (name matching), plus the returned fields (county, type, population). It is unmistakable against siblings like search_rentals or search_parking, which operate on entirely different domains.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete trigger condition — use it when a name is ambiguous, with 'Florești' as a worked example. It does not state a when-not case or name an alternative, but no sibling tool overlaps this lookup, so there is nothing to exclude.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_listing_detailsARead-onlyIdempotent
Full description, photos and extracted facts for one ad. Use it before recommending a listing: heating and parking are often only in the full text.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL of an ad on storia.ro, publi24.ro or lajumate.ro (e.g. from search_rentals). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, open-world behavior, so the description does not need to restate safety. It adds useful context about what is returned and why full-text detail matters, though it omits format and error behavior details.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the return contents and followed by the decision-relevant usage rule. No wasted wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read-only detail tool with full schema coverage and clear annotations, the description supplies enough about return contents and when to call it. No output schema exists, but the description still names the key returned information.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the schema already explains that the URL must be from storia.ro, publi24.ro, or lajumate.ro and can come from search_rentals. The description adds no URL-specific semantics beyond what the schema provides, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific resource and contents: full description, photos, and extracted facts for one ad. Clearly distinguishes a single-listing detail lookup from bulk search siblings, though it does not explicitly name those siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear usage trigger: 'Use it before recommending a listing,' and explains why by noting heating and parking are often only in the full text. No explicit exclusions or sibling names, but the context is strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_parkingBRead-onlyIdempotent
Parking spots and garages for rent in a Romanian locality (storia.ro).
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | Romanian locality. | |
| limit | No | ||
| county | No | County when the name is ambiguous. | |
| max_price | No | Max monthly rent, EUR. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, so the safety profile is covered. The description adds the source domain and geographic scope, but says nothing about result format, pagination, sorting, or rate limits beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is a single short fragment with no wasted text and the key scope is front-loaded. It is concise but slightly under-specified rather than verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter search tool with a decent schema and annotations, the description covers the core resource and geography. It still leaves usage decisions and result behavior implicit, which matters when three sibling tools could be candidates.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 75%: city, county, and max_price are documented, and only limit lacks a schema description. The description adds no parameter detail beyond restating that city is a Romanian locality, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource (parking spots and garages for rent) and a specific geographic scope (Romanian locality, storia.ro). It is clearly narrower than the sibling search_rentals, though it omits an explicit verb and does not directly address the other siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool instead of search_rentals, get_listing_details, or find_locality. The only implicit usage signal is the Romanian locality requirement, which does not help an agent choose between siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_rentalsARead-onlyIdempotent
Search apartments for rent in a Romanian locality, live, across storia.ro, publi24.ro and lajumate.ro. Returns de-duplicated listings with extracted facts (price in EUR, rooms, surface, heating type, parking) and links to the original ads, plus per-site status.
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | Romanian locality, diacritics optional: 'Cluj-Napoca', 'Iasi', 'Floresti', 'Brasov'. 'Floresti, Prahova' disambiguates. | |
| near | No | A place in the same locality to measure walking distance from: 'UMF', 'Piata Unirii', 'Iulius Mall', 'strada Observatorului 34'. | |
| sort | No | 'distance' needs `near`. | newest |
| limit | No | How many results to return. | |
| pages | No | Result pages fetched per site; more = slower but wider. | |
| rooms | No | Room counts; 1 = garsonieră/studio, 4 means 4+. E.g. [2, 3]. | |
| county | No | County (județ) when the name is ambiguous, e.g. 'Prahova'. | |
| heating | No | centrala_proprie = own gas boiler; termoficare = district or shared building heating. | |
| parking | No | Keep only these parking statuses, e.g. ['included'] or ['included', 'likely_included']. | |
| keywords | No | Words that must all appear in the ad, e.g. 'balcon mobilat' or 'pet'. | |
| max_price | No | Max monthly rent, EUR. | |
| min_price | No | Min monthly rent, EUR. | |
| owner_only | No | Only ads posted by private owners (no agency). Only storia reports this; other sites' ads are then excluded. | |
| max_walk_minutes | No | With `near`: drop listings farther than this many minutes on foot. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent and open-world, so the description is free to add real value — and it does: results are de-duplicated across three live sources, facts are pre-extracted (price in EUR, rooms, surface, heating, parking), and per-site status is returned, implying partial-source failures are visible. It does not mention latency or rate-limit behavior beyond the schema's 'more = slower' note.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, zero filler, front-loaded with source and scope before the return contract. Each clause carries distinct information (sources, de-duplication, extracted fields, links, per-site status).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter read tool with no output schema, the description does the important work of characterizing what comes back (de-duplicated records with extracted facts and source links, plus per-site status). What remains unstated is how partial site failures surface and how limit interacts with per-site paging, though the schema covers the latter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every one of the 14 parameters is already documented, including non-obvious ones like owner_only's site-coverage caveat and parking statuses. The description adds nothing parameter-specific beyond confirming that prices are in EUR and that parking/heating are extracted facts; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (search apartments for rent) with an explicit geographic scope (a Romanian locality) and named sources (storia.ro, publi24.ro, lajumate.ro), plus the return shape. It does not name any sibling, so the distinction from get_listing_details or search_parking must be inferred from the nouns alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: the description tells you what it returns, which signals 'use this for discovery, then get_listing_details for a full ad', but never says so or states exclusions. No prerequisites or when-not-to-use guidance is given, leaving the agent to infer the workflow from sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.1- First observed
find_locality - First observed
get_listing_details - First observed
search_parking - First observed
search_rentals
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: rental search, single-listing details, parking search, and locality disambiguation. There is no meaningful overlap between searching rentals and searching parking, and get_listing_details is clearly a follow-up to search_rentals.
All tools follow a consistent snake_case verb_noun pattern: search_rentals, get_listing_details, search_parking, find_locality. The slight verb variation (search/get/find) is semantically natural and does not break the predictable structure.
Four tools is well-scoped for a focused rental-search server. Each tool earns its place by covering a distinct step in the workflow: locate the locality, search rentals, inspect details, and optionally search parking.
The core workflow is covered: find a locality, search rentals or parking, and inspect a listing in detail. Minor gaps exist, such as no explicit tool for comparing listings or retrieving listing status/contact information, but agents can work around these.
Maintenance
Related MCP Connectors
Search Czech rental listings from 27 portals, merged into one offer per flat.
Find, rank, and compare student rooms from UniAffitti public listings.
Search classified ads from European marketplaces: rentals, homes, vehicles, second-hand goods, jobs.
Watches Czech home listings for a buyer. Takes no contact; active only after e-mail opt-in.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables searching and retrieving details from OLX classifieds across multiple domains (Portugal, Poland, Bulgaria, Romania, Ukraine) using browser automation.263 npm28MIT
- AlicenseNot gradedqualityDmaintenanceMonitors boligportal.dk for new rental listings in Denmark. Provides tools to search, filter, and track apartment listings via natural language.1MIT
- AlicenseAqualityDmaintenanceEnables AI agents to search Dutch housing listings on Kamernet.nl, retrieve full listing details, and optionally reply to landlords; designed for personal use in finding rooms, studios, and apartments.320 PyPI1MIT
- AlicenseAqualityBmaintenanceEnables searching cian.ru apartment listings through a live authorized browser session, including login/session management, filtered offer search, and detailed offer cards with price history.41MIT