proptech-inquiry
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., "@proptech-inquiryFind pet-friendly flats in Hamburg under €2,000 and tell me if heating is included."
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.
proptech-inquiry-agent
An agent that answers and triages inquiries about property listings. It is built on an MCP server, so the same tools work from Claude Desktop, Claude Code, the MCP Inspector or the agent loop in this repo.
All listing data is synthetic.
Status
Slice | What it adds | State |
1 | MCP server: structured search, listing lookup, hand-off to a human | done |
2 | Retrieval over listing text and a policy FAQ, exposed as an MCP tool | next |
3 | Agent loop that answers inquiries using the tools | planned |
4 | Eval suite: correct answers, correct escalations, no invented facts | planned |
5 | Architecture write-up | planned |
Related MCP server: Repliers MCP Server
The one design rule
The agent answers facts from the record and escalates everything else. Viewings, negotiation, contract and
legal questions, complaints, personal data requests and anything the record does not cover go to
hand_off_to_human. The boundary sits in the tool contract, not only in a prompt, so it can be tested.
Tools
Tool | Kind | Purpose |
| read | Hard-criteria search. Returns summaries without description text. |
| read | One full record. The model is told to answer only from its fields. |
| write | Creates a ticket with a fixed reason code. Rejects unknown listing ids. |
Worked example
One inquiry that uses all three tools: search on hard criteria, answer a detail from the record, and hand off the part the agent may not handle.
I have a small dog and want a flat in Hamburg under 2,000 € a month. Is heating included, and can I view it on Saturday?
1. Search. The dog and the budget are hard criteria.
search_listings { "city": "Hamburg", "offerType": "rent", "propertyType": "apartment", "maxPrice": 2000, "hasPet": true }This returns HH-1001 (Eimsbüttel, 1,650 EUR cold, pets on-request). HH-1002 is left out because it allows no pets.
2. Look up the detail. Heating is not a structured field, so the agent opens the full record.
get_listing { "id": "HH-1001" }The description says utilities are 240 EUR per month with heating included, which makes 1,890 EUR in total. It also says "pets on request, small dogs usually fine".
3. Hand off the viewing. Booking a viewing is on the escalation list, so the agent does not agree to a date.
hand_off_to_human {
"reason": "viewing_request",
"listingId": "HH-1001",
"summary": "Wants a Saturday viewing of HH-1001. Has a small dog; pets are on request, so the landlord needs to confirm.",
"contactEmail": null
}Expected answer
HH-1001 fits the budget: 1,650 EUR cold plus 240 EUR utilities, 1,890 EUR in total.
Heating is included in the utilities.
Pets are on request. The listing says small dogs are usually fine, but that is not a yes.
The Saturday request has gone to the letting team, and they will confirm the time. The agent asks for an email address if it does not have one.
The agent must not
say the dog is allowed,
confirm or suggest a viewing slot,
quote a heating figure that is not in the record.
Run it
npm install
npm test # unit tests plus MCP client round-trip tests
npm run inspect # open the MCP Inspector against the serverClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"proptech-inquiry": { "command": "npx", "args": ["tsx", "/path/to/proptech-inquiry-agent/src/mcp/stdio.ts"] }
}
}Layout
data/listings.json synthetic catalogue, validated with zod at load
src/domain/ listing model, repository, hand-off queue (no MCP imports)
src/mcp/server.ts tool definitions; takes its dependencies as arguments
src/mcp/stdio.ts stdio entry point
test/ repository tests and in-memory MCP client testsLicense
MIT
Available Tools
3 toolsget_listingGet listingARead-only
Return the full record of one listing, including its description text. Answer only from fields present here. If the answer is not in the record, say so and hand off with reason not_answerable_from_listing instead of guessing.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Listing id, e.g. HH-1001 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered by structured data. The description adds genuinely useful behavioral policy not present in the annotations: answer strictly from returned fields and refuse/redirect rather than guess when data is absent.
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 short sentences, front-loaded with the purpose and followed by the grounding and fallback rules. Nothing is redundant and every sentence changes agent behavior.
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, full schema coverage, and no output schema, the definition covers what an agent needs: what comes back ('full record' incl. description) and how to behave when the record lacks the answer. It could briefly note behavior for a missing/invalid id, which keeps it from a 5.
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 id parameter is documented with an example ('HH-1001'), so the schema carries the full burden. The description adds no syntax, format, or validation detail beyond it, which matches the baseline 3 for a fully documented parameter.
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 verb and resource ('Return the full record of one listing') plus what the record contains ('including its description text'). It implicitly distinguishes itself from sibling search_listings by scoping to a single record, though it never names the alternative 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?
It gives a clear grounding rule ('Answer only from fields present here') and an explicit when-not path ('If the answer is not in the record... hand off with reason not_answerable_from_listing instead of guessing'), which routes the agent to the hand_off_to_human sibling. It does not state prerequisites for obtaining the listing id, but the context of use is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hand_off_to_humanHand off to a human agentA
Create a ticket for the letting or sales team. You MUST use this, and not answer yourself, for: booking viewings, any price or rent negotiation, contract or legal questions, complaints, requests about personal data, and any question the listing record does not answer. Summarise the inquiry in one or two sentences so the human does not have to reread the thread.
| Name | Required | Description | Default |
|---|---|---|---|
| reason | Yes | ||
| summary | Yes | ||
| listingId | Yes | Listing the inquiry is about, or null | |
| contactEmail | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-read-only, non-destructive, non-idempotent, closed-world write, and the description's "Create a ticket" is consistent with that. It adds useful behavioural context about how the summary is consumed ("so the human does not have to reread the thread"), but does not say whether the customer is notified or what happens after the ticket is filed.
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, front-loaded with the action and then the escalation trigger list. The long MUST clause is dense but every item earns its place as a routing rule; only the trailing summary instruction could be tightened slightly.
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 4-required-parameter escalation tool with no output schema, the description covers routing, required intent categories, and summary style. It leaves contactEmail semantics and any post-creation behaviour (ticket id, notification, SLA) unspecified, which is a minor but real gap.
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 only 25% (only listingId is documented in the schema). The enumerated use cases in the description map well onto the reason enum values, and the summary wording gives a length/style expectation, but contactEmail (whose address, required even when the listing is unrelated) and listingId's null case are never explained in the description.
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 opens with a specific verb and resource: "Create a ticket for the letting or sales team." That is unambiguous and clearly distinct from the sibling tools search_listings and get_listing, which read listing data rather than escalate an inquiry to a human.
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?
It gives an explicit MUST-use list (viewing bookings, price/rent negotiation, contract or legal, complaints, personal data requests, and anything the listing record cannot answer) plus the exclusion "not answer yourself," which implicitly routes answerable questions to get_listing. When-to-use and when-not-to-use are both covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_listingsSearch listingsARead-only
Find listings by hard criteria (city, rent or sale, price ceiling, rooms, area, features, move-in date). Use this when the inquirer states requirements. Returns summaries sorted by price, cheapest first. Do not use it to answer questions about a specific listing's details; call get_listing for that.
| Name | Required | Description | Default |
|---|---|---|---|
| city | No | City name, e.g. Hamburg, Berlin, München, Köln | |
| limit | No | ||
| hasPet | No | True if the inquirer has a pet. 'on-request' listings are kept; never promise them. | |
| features | No | All must match, e.g. balcony, lift, garden, accessible | |
| maxPrice | No | EUR per month for rent, EUR total for sale | |
| minRooms | No | ||
| offerType | No | ||
| minAreaSqm | No | ||
| availableBy | No | Latest acceptable move-in date, YYYY-MM-DD | |
| propertyType | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds return behavior not present in structured data: results are summaries sorted by price, cheapest first. It does not address pagination or how the limit truncates results, so it stops short of full transparency.
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 tight sentences with zero filler. The filter criteria and the primary usage condition are front-loaded, and the exclusion/alternative follows immediately after.
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 read-only search tool with no output schema, the description supplies the return shape (summaries sorted by price) and the routing rule to get_listing. It omits the limit/default page size and never mentions propertyType, which is a minor but real gap.
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 50%, so several parameters (offerType, minRooms, minAreaSqm, propertyType, limit) lack schema descriptions. The description compensates by naming rent-or-sale, price ceiling, rooms, area and move-in date as filter criteria, covering most undocumented params, though propertyType and limit remain unmentioned anywhere.
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 opens with a specific verb+resource ("Find listings") and enumerates the hard criteria it filters on, so the agent knows exactly what the tool retrieves. It also explicitly contrasts itself with get_listing, making it distinguishable from its sibling without opening either 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?
It states the triggering condition ("Use this when the inquirer states requirements") and an explicit exclusion with the correct alternative ("Do not use it to answer questions about a specific listing's details; call get_listing for that"). Both when-to-use and when-not-to-use are covered.
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.
3 tool updates
v0.1.0- First observed
get_listing - First observed
hand_off_to_human - First observed
search_listings
TDQS
Scored across 3 tools
Each tool targets a distinct action: search by criteria, retrieve one record, or escalate to a human. The descriptions explicitly define boundaries (search_listings says do not use it for a specific listing; get_listing says answer only from the record), so misselection is unlikely.
All three names follow a clear verb_noun pattern (search_listings, get_listing, hand_off_to_human) with consistent snake_case.
Three tools is lean for the narrow inquiry-handling scope, and each earns its place, though it sits near the lower bound where coverage could feel thin for edge cases.
The read-plus-escalate lifecycle is well covered: criteria search, full record retrieval, and human handoff for bookings, negotiation, legal, and unanswerable questions. Only listing management/creation is absent, which is outside an inquiry agent's stated purpose.
Maintenance
Related MCP Connectors
AI-native real estate discovery with structured property search and market intelligence.
Read-only property facts, indicative availability, authorised booking links and guest-safe support.
- HAVNOAuthapp.havnre
Read-only AI access to HAVN properties, leads, tasks, files, media, and analytics.
Redfin listings, sale-comps, and neighborhood market data via natural-language queries.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables users to access the Realty In Ca1 API to search for residential and commercial property listings, view detailed property information, and retrieve neighborhood demographics or statistics. It supports filtering by geographic location, price range, building type, and other real estate characteristics.MIT

Repliers MCP Serverofficial
AlicenseNot gradedqualityBmaintenanceProvides AI assistants access to real-time MLS data via the Repliers API, enabling natural language property search, market statistics, and listing details.325 npm16MIT- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to operate property management systems via natural language, covering repair orders, owner info, payments, notices, and inspections. Features a full agentic workflow with human-in-the-loop and observability.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to look up real estate agents, search MLS listings, submit buyer/seller leads, and access proprietary seller-intent signals in SC and GA.MIT