ImotAI MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ImotAI MCP ServerHow much do 2 and 3-bedroom apartments cost in Sofia's Lozenets district?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ImotAI MCP Server
A Model Context Protocol server that gives AI agents (Claude Desktop, Claude Code, …) a read-only window onto Bulgarian real-estate data from imotai.bg.
It is a thin client over the site's public API — no API keys, no writes, no personal data (broker names, phones and free-text descriptions never leave the API). Numbers are based on current asking prices of active listings, not on closed transactions.
Status
M6 — v1 complete. Five tools (list_districts, search_properties,
get_property, get_market_stats, estimate_value), each with an explicit output
allowlist; the property tools carry a marketContext block; outbound calls are rate
limited to 60/min per process.
Related MCP server: Idealista7 MCP Server
Install
git clone https://github.com/pdimiev-prog/imotai-mcp.git
cd imotai-mcp
npm install
npm run build # produces dist/index.jsUse it in Claude Desktop
Add this to claude_desktop_config.json (use the absolute path to dist/index.js):
{
"mcpServers": {
"imotai": {
"command": "node",
"args": ["/absolute/path/to/imotai-mcp/dist/index.js"],
"env": { "IMOTAI_API_URL": "https://imotai.bg/api" }
}
}
}Restart Claude Desktop, then ask something like:
Which districts does ImotAI track in Sofia, and how many active listings are in each?
Claude will call list_districts and answer from the result.
Tools
list_districts
Input: { city: string } — a city slug ("sofia") or its Bulgarian name ("София").
Output:
{
"city": { "slug": "sofia", "name": "София" },
"districts": [
{ "name": "Лозенец", "slug": "lozenets", "activeListings": 64 },
{ "name": "Витоша", "slug": "vitosha", "activeListings": null }
],
"note": "activeListings is filled only for districts with a public price page (>= 8 offers); a null count does not mean zero."
}An unknown city returns { "error": "Unknown city \"…\". Recognised city slugs: …" }.
Call this first — the slugs it returns are the valid district values for the
other tools.
search_properties
Input:
Field | Type | Notes |
| string, required | Slug or Bulgarian name |
|
| |
| string | Slug from |
| enum |
|
| number | EUR |
| number | m² |
| string | CSV, e.g. |
| integer 1–25 | Default 10 |
Output: { results: [...], total, shown }. Each result has structured fields
plus a url to the listing. No contact details, no free-text description, no
coordinates.
get_property
Input: { id: string } — the listing's UUID or its URL slug.
Output: { found: true, url, ...structured fields, features: [] }, or
{ found: false, reason } if there is no such listing. Never returns the broker
description, phone/name, or exact coordinates.
get_market_stats
Input: { city: string, district?: string } — slugs or Bulgarian names from
list_districts. Omit district for city-level stats.
Output: { available: true, city, district, count, avgPrice, avgPricePerSqm, price: {p25,p50,p75}, pricePerSqm: {p25,p50,p75}, byRooms: [...], neighbouringDistricts: [...], basis, url }. Sale offers, all residential types.
Below 8 offers → { available: false, count, reason }. Unknown city/district →
{ error } pointing at list_districts.
Market context
Both property tools attach a marketContext — "is this listing priced above or
below its district":
get_property.marketContextis the authoritative per-listing figure from ImotAI's own calculation:districtAveragePricePerSqm,districtMedianPricePerSqm,deviationPercent(+ = pricier than the district),comparableActiveListings,basedOnCount,pricePageUrl.search_properties[].marketContextis a lighter district-level approximation for scanning (one price-page lookup per distinct district):districtAveragePricePerSqm,deviationPercent,comparableListings,basis.
Both are null when the district has fewer than 8 offers or no price page. The
two levels use slightly different denominators, so their numbers are close but
not identical — the get_property figure is the precise one.
estimate_value
Input:
Field | Type | Notes |
| string, required | Slug or Bulgarian name from |
| string, required | Slug or Bulgarian name from |
| enum, required |
|
| number, required | Living area in m² |
| integer | Accepted but not used for filtering |
Output:
{
"available": true,
"estimate": 208000,
"rangeLow": 184000,
"rangeHigh": 240000,
"basedOnCount": 40,
"confidence": "medium", // from sample size; forced "low" for the market blend
"disclaimer": "…", // ALWAYS present — show verbatim
"referenceUrl": "https://imotai.bg/ceni/sofia/lozenets"
}Below 5 comparable offers it returns { "available": false, "reason": "…", "disclaimer": "…" } — still with a disclaimer. An unknown city or district
returns { "error": "…" } pointing at list_districts (no disclaimer there).
confidence is high at ≥ 50 comparable offers, medium at ≥ 15, otherwise
low; it is always low when external market data (imot.bg / alo.bg / Grok)
was blended into the figure, and the disclaimer then says so.
⚠️ This is positioning against the CURRENT ASKING PRICES of comparable active listings — not an appraisal, not an official valuation, not based on closed transactions. The
disclaimerfield is present on everyavailableresult; show it to the user verbatim whenever you cite any number from this tool.
Rate limiting
The server allows 60 downstream requests per minute per process (token bucket,
refills continuously). Every call to imotai.bg/api counts — a search_properties
that resolves three distinct districts spends four requests (one search + three price
pages).
When the bucket is empty a tool returns, without throwing:
{ "error": "Твърде много заявки за кратко — изчакайте минута." }Wait about a minute and retry. The limit is per running server process; a single
Claude Desktop client is one process. Override it with IMOTAI_RATE_LIMIT_PER_MIN
if you run your own instance.
Configuration
Env var | Default | Meaning |
|
| Base URL of the ImotAI public API |
|
| Per-request timeout |
|
| Max downstream requests per minute (token bucket, per process) |
How it works
stdio MCP server (Node ≥ 20, ESM). Each tool maps to one or two calls against
the public imotai.bg/api endpoints and reshapes the response through an
explicit field allowlist. No state, no cache, no credentials.
Licence
MIT.
Available Tools
5 toolsestimate_valueImotAI rough value positioningA
Rough price positioning for a property by city, district, type and area — based on the CURRENT ASKING PRICES of comparable active listings, not on closed transactions. Returns an estimate, a low–high range, a confidence level from sample size, and a mandatory disclaimer. Refuses below 5 comparable offers. You MUST show the "disclaimer" field to the user verbatim whenever you cite any number from this tool. Never call this an official or precise property valuation.
| Name | Required | Description | Default |
|---|---|---|---|
| area | Yes | Living area in m². | |
| city | Yes | City slug ("sofia") or Bulgarian name. | |
| rooms | No | Accepted but not used for filtering. | |
| district | Yes | District slug or name from list_districts. | |
| propertyType | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses the data source, the return shape (estimate, low-high range, confidence from sample size, disclaimer), a refusal threshold, and two hard behavioral obligations (verbatim disclaimer display, never call it an official valuation).
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?
Four sentences, each load-bearing: scope and data basis first, then return shape, then the refusal rule, then the compliance constraints. No redundancy or filler.
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?
No output schema exists, and the description compensates by enumerating the returned fields and the confidence derivation. The refusal condition and disclaimer obligations round out everything an agent needs to call and report this tool correctly.
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 already 80%, so the schema documents city/district/propertyType/area on its own. The description restates the filter dimensions but adds no format or syntax detail, and says nothing about the accepted-but-unused 'rooms' parameter, which the schema itself flags.
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 ('rough price positioning for a property') plus the filtering dimensions and, crucially, the data basis (current asking prices of active listings, not closed transactions). This distinguishes it immediately from get_property and get_market_stats.
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 clear conditions: usable only with at least 5 comparables, and grounded in asking rather than closed prices. It doesn't explicitly name sibling alternatives to use instead, but the scope statement effectively tells the agent when this tool is and isn't the right call.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_market_statsImotAI market statistics for a city or districtA
The market picture for a Bulgarian city or district on imotai.bg: number of offers, average and P25/P50/P75 for total price and price per m², a breakdown by room count, and the neighbouring districts for comparison. Sale offers, all residential types. Returns { available: false } below 8 offers.
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City slug ("sofia") or Bulgarian name. | |
| district | No | District slug or name from list_districts; omit for city-level stats. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose a genuine behavioral edge case: returns { available: false } below 8 offers, plus the scope restriction to sale offers and all residential types. It omits auth/rate-limit context, but for a read-only aggregate endpoint the disclosed sample threshold is meaningful beyond the structured fields.
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?
Front-loads the core purpose then lists outputs compactly, with the useful return caveat last. Two tight sentences with no filler; the dense middle list is slightly hard to scan but every clause earns its place.
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?
There is no output schema, so the description must summarize returns, and it does so adequately (count, average and percentile price metrics, room-count breakdown, neighbouring districts). With only two well-documented parameters and a disclosed fallback response, an agent has enough to call it correctly, though pagination or response-shape detail is absent.
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%, so both parameters are already documented, including the 'omit for city-level stats' behavior. The description adds no syntax or format detail beyond the schema, so the 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 (aggregate market statistics for a Bulgarian city or district on imotai.bg) and enumerates exactly what is produced: offer count, average and P25/P50/P75 for total price and price per m², and a room-count breakdown. It is clearly distinct from get_property/estimate_value/search_properties in substance, though it never names a sibling explicitly.
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?
The scope ('Sale offers, all residential types') and the city-vs-district choice imply when this tool applies, and the district guidance is covered in the schema. However, there is no explicit statement of when to prefer this over estimate_value or search_properties, so routing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_propertyGet one ImotAI listingA
Get the full structured data for one ImotAI listing by its id or slug: price, price per m², area, rooms, floor, construction, heating, location (city/district), features, and an authoritative per-listing marketContext (district average and median price per m², deviation percent, comparable active count). Does NOT return the broker free-text description, contact details, or exact coordinates.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | The listing id (UUID) or its URL slug. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does well: it discloses the negative scope (no broker free-text, contact details, or exact coordinates) and characterizes marketContext as authoritative per-listing. It does not cover auth requirements or error/not-found behavior, keeping it short of a 5.
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?
A single front-loaded sentence that leads with the action and key, then enumerates return content. Dense but every clause adds information; the long field list makes it slightly heavier than ideal.
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?
No output schema exists, so the description correctly enumerates what is returned and what is excluded. Combined with a single required parameter, an agent has enough to call it correctly; only permission/error context is missing.
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% and only one parameter exists, so the schema already documents the UUID-or-slug semantics. The description merely restates 'by its id or slug' without adding format constraints, so the 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 precise verb (Get) and resource (one ImotAI listing) plus the lookup key (id or slug), and enumerates the returned data fields. An agent can distinguish this from list_search-oriented siblings without opening the schema.
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 implied: 'one listing by its id or slug' contrasts with search_properties and get_market_stats, but there is no explicit when-to-use statement or advice such as 'use search_properties first to obtain an id'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_districtsList ImotAI districts for a cityA
List the districts (neighbourhoods) of a Bulgarian city that ImotAI has data for, with the count of active listings in each where a public price page exists. Call this first to learn which "district" values are valid for the other ImotAI tools in a given city.
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City slug ("sofia") or its Bulgarian name ("София"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does disclose a genuine data-completeness caveat: counts exist only 'where a public price page exists', and results are limited to cities ImotAI has data for. It does not state the read-only nature, error behavior for unknown cities, or ordering, so some behavioral gaps remain.
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 sentences, zero filler, front-loaded with what is returned before the call-first guidance. Every phrase ('where a public price page exists', 'in a given city') carries information.
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?
With one required parameter, no annotations, and no output schema, the description covers both return content (districts plus active-listing counts) and usage context adequately. Minor gaps remain on response shape details and unknown-city behavior, but nothing essential to calling it correctly is missing.
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% and the single 'city' parameter already documents slug vs Bulgarian-name formats, so the description adds no parameter-level detail. Baseline 3 applies when the schema does the heavy lifting.
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 (List) and resource (districts/neighbourhoods) scoped to Bulgarian cities where ImotAI has data, and clarifies what each entry contains (listing counts). The framing as a prerequisite for 'the other ImotAI tools' separates it from search_properties, get_property and get_market_stats without ambiguity.
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?
Explicitly instructs 'Call this first to learn which "district" values are valid for the other ImotAI tools in a given city', which is concrete ordering guidance and a clear rationale. It stops short of stating when not to call it or naming the specific downstream tools, so it is strong but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_propertiesSearch ImotAI listingsB
Search Bulgarian real-estate listings on imotai.bg by city, district, property type, price range, area range and rooms. Returns up to 25 listings with structured fields, a link to each, and a district-level marketContext block (district average price per m² and how far this listing deviates from it). No contact details or free-text descriptions.
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City slug ("sofia") or Bulgarian name. | |
| limit | No | Default 10, max 25. | |
| rooms | No | CSV of room counts, e.g. "2,3"; "5+" for 5 or more. | |
| maxArea | No | ||
| minArea | No | ||
| district | No | District slug from list_districts, e.g. "lozenets". | |
| maxPrice | No | ||
| minPrice | No | ||
| propertyType | No | ||
| transactionType | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full behavioral burden and does a decent job: it discloses the result cap ('up to 25 listings'), the shape of the payload including the district-level marketContext block, and explicitly what is NOT returned ('No contact details or free-text descriptions'). It omits sorting/ranking behaviour, pagination beyond the limit, and any auth or rate-limit notes.
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 dense sentences with the core purpose front-loaded and no filler. The return-shape sentence is long and multi-clause but each clause (structured fields, links, marketContext) carries distinct information.
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?
Ten parameters at 40% schema coverage and no annotations means the description must do more work; it handles the return contract well (compensating for the absent output schema) but leaves filter syntax and the district-slug dependency unstated, and gives no routing guidance among the four siblings.
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 only 40%, so the description should compensate, and it partially does by naming the filter dimensions (city, district, property type, price, area, rooms). However it adds no syntax or format detail beyond the schema (no CSV format for rooms, no '5+' convention, no slug guidance), leaving several undocumented numeric params (minArea, maxArea, minPrice, maxPrice) with no semantic explanation in either place.
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 and resource ('Search Bulgarian real-estate listings on imotai.bg') and enumerates the filter dimensions, so the agent immediately knows this is the filtered listing-search tool. It does not explicitly contrast itself with get_property, estimate_value, or get_market_stats, so sibling differentiation is only implied by the verb choice.
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?
The description never says when to use this tool versus get_property or get_market_stats, nor does it state exclusions or prerequisites (e.g. that district slugs must come from list_districts). Usage is only inferable from the enumerated filters, which is minimal guidance.
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.
5 tool updates
v0.1.0- First observed
estimate_value - First observed
get_market_stats - First observed
get_property - First observed
list_districts - First observed
search_properties
TDQS
Scored across 5 tools
Each tool targets a distinct action and resource: searching listings, retrieving one listing, getting district-level market aggregates, estimating a specific property's value, and listing valid districts. The descriptions explicitly clarify boundaries (e.g., market_stats vs. estimate_value differ in scope and output), so an agent can reliably choose the right tool.
All tool names use a consistent verb_noun snake_case pattern: get_property, get_market_stats, estimate_value, list_districts, search_properties. There are no mixed conventions or vague verbs.
Five tools is well-scoped for a read-only real-estate data server, covering search, detail, market context, estimation, and district lookup. Each tool clearly earns its place without redundancy.
The surface covers the core workflows: search, property detail, market statistics, valuation estimate, and district enumeration. Minor gaps exist, such as no tool to list valid cities (needed as input to other tools) and no pagination beyond 25 search results, but these are workable.
Maintenance
Related MCP Connectors
AI-native real estate discovery with structured property search and market intelligence.
Search homes for sale and rent in Bulgaria; get price stats and alerts for new matches.
Search real-estate deals, rank top areas, run rental/BRRRR/flip analysis, pull sold comps.
UK area & property intelligence for AI agents: reports, EPC, comparables, with source provenance.
Related MCP Servers
- FlicenseBqualityNot gradedmaintenanceEnables AI assistants to search and analyze Swedish real estate data from Booli.se using natural language queries. Supports property searches with comprehensive filtering options and location discovery through GraphQL API integration.31-
- AlicenseCqualityDmaintenanceEnables access to Idealista API for searching and retrieving property listings across Spain, Portugal, and Italy. Supports various property types including homes, apartments, garages, commercial properties, offices, and land with detailed filtering options.143MIT
- AlicenseNot gradedqualityDmaintenanceProvides tools to access the Repliers API for real estate listings, property search, market analytics, and AI-powered data through natural language queries.205MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to search for German property listings (rent/buy) on immowelt.de with structured JSON output, no API key required.-