immo.rundum/real-estate-appraisal
OfficialThis MCP server provides German real-estate appraisal and tax helpers through AfAMax APIs.
Calculate indicative property depreciation (AfA), including annual/monthly amounts, statutory comparison, tax savings, modernization assumptions, and remaining useful life.
Calculate BMF purchase-price allocation between non-depreciable land and depreciable building, with income-method cross-check and optional garage/parking/inventory inputs.
Get current AfAMax appraisal product prices in EUR incl. VAT, with viewing, express, and per-flat surcharges.
List free 15-minute AfAMax initial-call appointment slots in Europe/Berlin time.
Book an initial-call appointment using a two-step email OTP verification flow.
Use the hosted HTTP endpoint or run locally over stdio/npm/Docker; public calculations do not require an end-user API key.
Results are indicative, non-binding, and do not replace tax, legal, or appraisal advice.
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., "@immo.rundum/real-estate-appraisalCalculate depreciation for a 1970 condominium, 85 sqm, bought for €350,000."
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.
Rundum Immo Real Estate Appraisal MCP
An open-source Model Context Protocol server for indicative German real-estate depreciation (AfA / Gebäudeabschreibung), remaining useful life (Restnutzungsdauer) and purchase-price allocation (Kaufpreisaufteilung). It helps answer questions such as whether a rental property can be depreciated faster than the statutory rate under § 7 Abs. 4 EStG, or whether a Restnutzungsdauergutachten would pay off. It exposes calculate_property_depreciation and calculate_purchase_price_allocation, delegating both calculations to the public AfAMax APIs, plus get_price_by_product, get_available_appointments, and book_appointment for AfAMax appraisal prices and free initial-call bookings. Proprietary appraisal formulas remain in AfAMax.
Hosted server
The public Streamable HTTP endpoint is:
https://mcp.rundum.immo/mcpNo end-user API key is required. Calls are subject to AfAMax per-client and service-wide rate limits and abuse protections.
Related MCP server: OpenLMNP
Run from source over stdio
Node.js 22.12 or newer is required.
git clone https://github.com/Rundum-Immo/real-estate-appraisal-mcp.git
cd real-estate-appraisal-mcp
pnpm install --frozen-lockfile
pnpm buildConfigure your MCP client to run the built server, replacing the path with the absolute path to your checkout:
{
"mcpServers": {
"rundum-real-estate-appraisal": {
"command": "node",
"args": ["/absolute/path/to/real-estate-appraisal-mcp/dist/transports/stdio.js"]
}
}
}The stdio server calls the anonymous AfAMax API directly. Its public limits are 30 requests per minute and 500 requests per rolling day per IP; a tenant-wide ceiling may also apply. It writes protocol messages only to stdout and operational logs only to stderr.
Install from npm over stdio
Node.js 22.12 or newer is required. Configure your MCP client to launch the published package with npx:
{
"mcpServers": {
"rundum-real-estate-appraisal": {
"command": "npx",
"args": ["-y", "@rundum-immo/real-estate-appraisal-mcp"]
}
}
}Tools
Property depreciation
calculate_property_depreciation accepts the complete public AfAMax request contract:
Required: property type, construction year, and floor area.
Optional: purchase price, land area, standard land value, inventory, purchase costs, core-renovation year, marginal tax rate, locale, coarse modernization level, and eight detailed modernization component states.
Results: annual and monthly AfA, comparison with statutory AfA, estimated tax savings, building-value assumptions, modernization points, and remaining useful life.
Omitting modernization data means AfAMax assumes no modernization, producing an upper-bound estimate. For multi-unit buildings, provide whole-building figures or calculate individual units separately. Results are indicative and do not replace tax, legal, or appraisal advice.
Example input:
{
"propertyType": "CONDOMINIUM",
"constructionYear": 1970,
"floorArea": 85,
"purchasePrice": 350000,
"taxRate": 0.42,
"locale": "en"
}Purchase-price allocation
calculate_purchase_price_allocation divides acquisition costs between non-depreciable land and the depreciable building using the German Federal Ministry of Finance (BMF) method.
Required: property type, total purchase price, purchase date, construction year, floor area, land area, and standard land value.
Condominiums also require the numerator and denominator of the co-ownership share.
Mixed-use residential/commercial buildings also require whether the commercial share is under or over 50%.
Optional: purchase costs, included inventory, garage and underground-parking counts, monthly net cold rent, and locale.
Results: the applied method, meaningful alternatives, land/building shares and values, depreciation base, unavailable or unusable method reasons, and disclosed asset-method defaults.
Providing monthly net cold rent enables the income method as a cross-check against the asset method. Garage and covered underground-parking counts improve the asset method because those spaces are valued separately. Comparative valuation is unavailable because the public contract excludes surveyor-only factors. Results are indicative and do not replace tax or legal advice.
Example input:
{
"propertyType": "CONDOMINIUM",
"totalPurchasePrice": 500000,
"purchaseRelatedCosts": 40000,
"purchaseDate": "2024-06-15",
"constructionYear": 1975,
"floorArea": 75,
"landArea": 1200,
"standardLandValue": 2500,
"coOwnershipNumerator": 75,
"coOwnershipDenominator": 1000,
"undergroundParkingSpaces": 1,
"monthlyNetColdRent": 1400,
"locale": "en"
}Appraisal prices
get_price_by_product returns the current AfAMax list price in EUR, including German VAT, for one product: rnd (Restnutzungsdauergutachten), kurzgutachten, vollgutachten, kaufpreisaufteilung, versetzbarkeitsgutachten, or afa_verteidigung (AfA-Verteidigung, a second remaining-useful-life appraisal to defend an AfA the tax office rejected).
Optional: property inspection (
none,exterior,interior_exterior), express processing, and property type with flat count for the per-flat price of multi-unit buildings.Results: the total with its breakdown, net price, a table of every surcharge for comparing options, and a source link to the product page.
Discount codes are not applied; the price shown when ordering is binding.
{ "product": "rnd", "viewingType": "interior_exterior", "expressDelivery": true, "locale": "en" }Initial-call appointments
get_available_appointments lists open slots for a free, non-binding 15-minute phone call with the AfAMax team, on weekdays in Europe/Berlin, grouped by day. It looks up to 21 days ahead (default 7) and lists up to 32 slots per day (default 8), with each day's full free-slot count.
{ "days": 1, "maxSlotsPerDay": 32, "locale": "en" }book_appointment books one of those slots in two steps, so a booking is confirmed only by someone with access to the given mailbox:
Call with the slot's
startDateTime,name,email(optionallyphone,locale). AfAMax e-mails a 6-digit code and returns averificationId.Call again with only
verificationIdand theotpthe user read from the e-mail. The response confirms the booking and AfAMax sends a calendar invitation.
Codes expire after 15 minutes and allow 5 attempts. Refusals such as a slot taken meanwhile or an e-mail that belongs to an existing AfAMax account come back with what to do next and a link to continue on the AfAMax website.
Architecture
MCP client -> this public adapter -> AfAMax public HTTPS APIThis repository contains transport, validation, error mapping, and presentation code only. It contains no appraisal formulas, databases, tenant logic, or report-generation internals. The transport-neutral server factory is shared by stdio and stateless Streamable HTTP.
Development
pnpm install
pnpm check
pnpm dev:stdioTest with MCP Inspector
Build and launch the local stdio server through MCP Inspector:
pnpm build
npx -y @modelcontextprotocol/inspector \
node --env-file-if-exists=.env dist/transports/stdio.jsConnect in the browser, open Tools, and call a calculation tool with its example input above. Successful responses contain a readable summary, structured output, disclosed assumptions/defaults, and a source link to the corresponding AfAMax calculator. The source link carries the submitted inputs so the calculator opens prefilled for refinement or documentation.
To inspect the registered tools from the command line:
npx -y @modelcontextprotocol/inspector \
--cli \
node --env-file-if-exists=.env dist/transports/stdio.js \
--method tools/listDevelop the HTTP transport
cp .env.example .env
pnpm dev:httpAFAMAX_SERVICE_TOKEN is mandatory for HTTP mode. Trusted per-client rate limiting works only with a service credential issued by Rundum Immo and configured with the matching AfAMax backend value; arbitrary tokens do not enable trusted forwarding. HTTP mode forwards that token and the validated rightmost proxy client address in X-Afamax-Service-Token and X-Afamax-Client-IP. Never expose this token to MCP clients. Deploy behind a proxy that replaces, rather than blindly appends to, incoming forwarding headers.
Configuration:
Variable | Default | Purpose |
|
| Public calculation endpoint |
|
| Public purchase-price allocation endpoint |
| — | Required trusted-service credential in HTTP mode |
|
| Upstream timeout |
|
| Listen address |
|
| Listen port |
|
| Comma-separated allowed Host header names |
|
|
|
The HTTP server exposes /mcp and /health, limits request bodies to 64 KiB, validates Host and Origin syntax, and returns permissive CORS headers for anonymous browser clients.
Deploy with Docker
Production HTTP hosting is intended for Rundum Immo or explicitly authorized operators because it requires a matching AfAMax service credential. Public users can run the stdio transport without one.
docker build -t real-estate-appraisal-mcp .
docker run --rm -p 3000:3000 \
-e AFAMAX_SERVICE_TOKEN='credential-issued-by-rundum-immo' \
-e PUBLIC_HOSTS='localhost,mcp.rundum.immo' \
real-estate-appraisal-mcpThe image runs as the non-root node user. Configure mcp.rundum.immo in Coolify and proxy it to port 3000.
Data and privacy
The adapter is stateless and does not persist tool inputs or raw client IP addresses. Calculation input and the client IP are sent to AfAMax, which uses the address for abuse prevention. AfAMax stores a salted hash of the address and limited usage metadata for 30 days; it does not store the raw address in its usage records. Avoid placing personal identifiers in calculation and price inputs. book_appointment necessarily sends the user's name, e-mail address, and optional phone number to AfAMax, which uses them to send the confirmation code and to hold the appointment; the adapter neither stores nor logs them. See AfAMax privacy information and SECURITY.md.
Roadmap
The repository can grow beyond its current calculation, pricing, and booking tools. Potential future capabilities include:
Property and market-value estimation
Appraisal and valuation-report workflows
Additional German real-estate tax and appraisal tools
Future tools will follow the same boundary: this repository contains the public MCP integration, while proprietary appraisal logic remains in AfAMax.
License
Available Tools
5 toolsbook_appointmentBook a free AfAMax initial callADestructiveInspect
Books a free 15-minute initial phone call with the AfAMax team, in two steps.
STEP 1: after the user picked a slot from get_available_appointments, confirm the slot, their name and their own e-mail address with them, then call with startDateTime, name, email (and optionally phone, locale). AfAMax e-mails a 6-digit code to that address and returns status "verification_required" with a verificationId.
STEP 2: ask the user for the code from that e-mail and call again with only verificationId and otp. The code proves the address belongs to the user: use only the code the user gives you — never guess, invent or repeatedly retry codes. On success status is "booked" and a calendar invitation goes to the user.
Codes expire after 15 minutes and allow 5 attempts. If the slot was taken meanwhile, list slots again and offer another. If the e-mail belongs to an existing AfAMax account, the user must log in and book on the website (link in the error). Only book for the person you are talking to.
| Name | Required | Description | Default |
|---|---|---|---|
| otp | No | Step 2 — the 6-digit code the user received by e-mail. Only ever the code the user gives you; never guess or retry codes. / Der 6-stellige Code aus der E-Mail. | |
| name | No | Step 1 — the user's full name. / Vor- und Nachname. | |
| No | Step 1 — the user's own e-mail address; the 6-digit confirmation code is sent there. Confirm it with the user before calling. / E-Mail-Adresse für den Bestätigungscode. | ||
| phone | No | Step 1, optional — phone number for the call. / Telefonnummer für das Gespräch. | |
| locale | No | Step 1 — language of the confirmation e-mail, the appointment and the links (`de` if omitted); match the user's language. Step 2 keeps step 1's language unless set. / Sprache der E-Mails und des Termins. | |
| startDateTime | No | Step 1 — the chosen slot, copied exactly from get_available_appointments `startDateTime`. | |
| verificationId | No | Step 2 — the `verificationId` returned by step 1. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | Yes | |
| status | Yes | `verification_required`: ask the user for the code sent to `verification.maskedEmail`, then call again with verificationId + otp. `booked`: the appointment exists. |
| requestId | Yes | |
| appointment | Yes | |
| attribution | Yes | |
| verification | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Well beyond the annotations, it discloses the e-mail verification side effect, the 15-minute code expiry, the 5-attempt limit, the 'verification_required'/'booked' status transitions, the calendar invitation, and the existing-account error path. The only gap is that destructiveHint=true is never reconciled with what is actually consumed (a slot), though the booking context makes that implicit.
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 clearly labelled steps with the critical constraint ('never guess, invent or repeatedly retry codes') stated inline. It is dense but front-loaded and nearly every sentence carries operational information; the German-language duplication lives in the schema, not here, so the description itself stays lean.
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 multi-step, stateful, non-idempotent booking tool, the description covers prerequisites, both call shapes, failure modes, and success outcome. An output schema exists, yet the status values are still named, which removes any ambiguity about how to interpret the two-step response.
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 baseline is 3, but the description adds genuine meaning: it partitions parameters into step 1 (startDateTime, name, email, phone, locale) versus step 2 (verificationId, otp), and stresses that startDateTime must be copied exactly from get_available_appointments and that otp must never be guessed. That goes beyond the per-field schema text.
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 ('Books a free 15-minute initial phone call with the AfAMax team') and immediately frames the two-step structure. It is clearly distinguishable from the sibling get_available_appointments, which only lists slots, so an agent can pick the right tool 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?
Explicitly sequences the workflow: pick a slot from get_available_appointments first, then call step 1, then step 2 with verificationId and otp. It also gives exclusions and fallbacks — existing AfAMax accounts must book on the website, taken slots require re-listing, and bookings are limited to the person being spoken to.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calculate_property_depreciationCalculate German property depreciation (AfA) & RestnutzungsdauerARead-onlyIdempotentInspect
Calculate indicative German property depreciation (AfA / Gebäudeabschreibung) and the building's remaining useful life (Restnutzungsdauer) for residential rental property through AfAMax.
Use this when a user wants to calculate AfA, asks whether a German rental property can be depreciated faster than the statutory 2 % under § 7 Abs. 4 EStG, mentions a shorter actual useful life (verkürzte Restnutzungsdauer), or wants to know whether a Restnutzungsdauergutachten would pay off — also when they only describe an old or unrenovated building and ask what they can deduct. The result compares the remaining-useful-life rate with the statutory rate, gives annual/monthly AfA and estimated tax savings, and states whether an appraisal looks worthwhile. For apartment buildings, pass figures for the whole building or calculate units separately. Ask for all eight modernization component states whenever possible: omitting them assumes no modernization and produces the highest possible remaining-useful-life benefit. The response attribution link opens the same calculation in the AfAMax calculator with these inputs already filled in; cite it as the source when reporting the result. The result is non-binding and does not replace tax or legal advice.
| Name | Required | Description | Default |
|---|---|---|---|
| locale | No | Language for upstream explanations; defaults to German. | |
| taxRate | No | Personal marginal income tax rate as a decimal, e.g. 0.42. / Grenzsteuersatz. | |
| landArea | No | Land area in square metres. / Grundstücksfläche. | |
| floorArea | Yes | Living or usable floor area in square metres. / Wohn- bzw. Nutzfläche. | |
| propertyType | Yes | German residential property category. CONDOMINIUM = Eigentumswohnung, ROW_HOUSE = Reihenhaus, SEMI_DETACHED = Doppelhaushälfte, APARTMENT_BUILDING = Mehrfamilienhaus, RESIDENTIAL_COMMERCIAL_BUILDING = Wohn- und Geschäftshaus. | |
| modernization | No | Known state of eight modernization components (Modernisierungselemente): roof (Dach), facadeInsulation (Fassadendämmung), windowsDoors (Fenster und Außentüren), heating (Heizung), utilities (Leitungen), bathrooms (Bäder), interiorFitout (Innenausbau), floorplanImprovement (Grundrissverbesserung). Ask for all eight when possible. | |
| purchasePrice | No | Total purchase price in EUR. / Kaufpreis. | |
| constructionYear | Yes | Original year of construction. / Baujahr. | |
| includedInventory | No | Movable inventory included in the purchase price, in EUR. | |
| standardLandValue | No | Standard land value in EUR per square metre. / Bodenrichtwert. | |
| coreRenovationYear | No | Year of a qualifying core renovation, if applicable. / Jahr einer Kernsanierung. | |
| modernizationLevel | No | Coarse modernization level used when detailed component data is unavailable. | |
| purchaseRelatedCosts | No | Purchase-related costs in EUR (land transfer tax, notary, land register, broker). / Kaufnebenkosten. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | Yes | |
| input | Yes | |
| results | Yes | |
| disclaimer | Yes | |
| assumptions | Yes | |
| attribution | Yes | |
| calculationId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already convey the safe read-only, idempotent profile, and the description layers on substantive behavior: the output compares the RND rate against the statutory rate and yields annual/monthly AfA plus estimated tax savings, the omission of modernization components defaults to 'no modernization' and inflates the benefit, it returns a non-binding result that does not replace tax advice, and it mandates citing the attribution link as the source. This is far beyond what the 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?
Purpose is front-loaded in the first sentence and every subsequent sentence carries distinct content (usage triggers, building-vs-unit rule, modernization default, attribution requirement, disclaimer). It is a single dense block that would scan better segmented, which keeps it from a 5, but there is little waste.
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 13-parameter domain calculator with an output schema, the description covers what an agent needs: when to invoke, the apartment-building input rule, the consequence of omitting customization data, the attribution-citation duty, and the non-binding nature of the result. Return values are also summarized despite the output schema existing.
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 the baseline is 3 and the schema does most of the work. The description still adds genuine semantic guidance: pass whole-building figures or compute units separately for apartment buildings, and supply all eight modernization component states because omission implies no modernization and produces the maximum RND benefit.
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+resource ('Calculate German property depreciation (AfA / Gebäudeabschreibung) and the building's remaining useful life (Restnutzungsdauer)') and scopes it to residential rental property through AfAMax. The purpose is unambiguous and readily separable from the unrelated siblings (appointments, price/product lookups), and even the adjacent real-estate calculator sibling covers a different calculation.
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?
Rich when-to-use triggers: calculating AfA, asking about faster-than-2% depreciation under § 7 Abs. 4 EStG, mentioning verkürzte Restnutzungsdauer, evaluating whether a Restnutzungsdauergutachten pays off, or just describing an old/unrenovated building and asking what is deductible. It lacks any explicit 'when not to use' or named alternative tool, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calculate_purchase_price_allocationCalculate German purchase price allocationARead-onlyIdempotentInspect
Calculate a free, non-binding German property purchase-price allocation (Kaufpreisaufteilung) through AfAMax using the BMF Arbeitshilfe.
Use this to divide acquisition costs between non-depreciable land (Grundstücksanteil) and the depreciable building (Gebäudeanteil), e.g. when a user asks how much of the price they may depreciate or wants to check the Finanzamt's split. Its depreciationBase is the base for the building's AfA. Inventory is deducted before allocation. A condominium requires both co-ownership values, and a residential/commercial building requires its commercial share category. Comparative valuation is unavailable because this public contract excludes surveyor-only factors. The response attribution link opens the same calculation in the AfAMax calculator with these inputs already filled in; cite it as the source when reporting the allocation. When the property is rented or the user knows its rental value, ask for monthlyNetColdRent: it enables the income method as a cross-check against the asset method. Ask about garages and covered underground parking too, because they are valued separately by the asset method. Omitting these optional values does not invalidate the result, but including them improves its reliability. The result does not replace tax or legal advice.
| Name | Required | Description | Default |
|---|---|---|---|
| locale | No | Language for the disclaimer and AfAMax attribution link. Defaults to German. | de |
| garages | No | Number of above-ground garages. Improves the asset-method allocation by valuing parking separately. Ask the user if the property includes garages. | |
| landArea | Yes | Plot area in square metres. | |
| floorArea | Yes | Living or usable floor area in square metres. | |
| propertyType | Yes | German residential property category. | |
| purchaseDate | Yes | Date of the notarized purchase contract in YYYY-MM-DD format; must be from 1990-01-01 through today. | |
| commercialShare | No | Required for RESIDENTIAL_COMMERCIAL_BUILDING: whether the commercial share is under or over 50%. | |
| constructionYear | Yes | Original construction year; it cannot be later than the purchase year. | |
| includedInventory | No | Movable inventory included in the purchase price, in EUR. It is deducted before allocation. | |
| standardLandValue | Yes | Standard land value (Bodenrichtwert) in EUR per square metre. | |
| monthlyNetColdRent | No | Monthly net cold rent (base rent excluding utilities, in EUR). Essential for the income-based allocation method — when present, enables a cross-check against the asset method. Ask the user if the property is rented or if they know the rental value. | |
| totalPurchasePrice | Yes | Total notarized purchase price in EUR. | |
| coOwnershipNumerator | No | Co-ownership numerator (Miteigentumsanteil); required with the denominator for a condominium. | |
| purchaseRelatedCosts | No | Land transfer tax, notary, land-register, and broker costs in EUR. Defaults to 0. | |
| coOwnershipDenominator | No | Co-ownership denominator; required with the numerator for a condominium. | |
| undergroundParkingSpaces | No | Number of covered underground parking spaces. Improves the asset-method allocation by valuing parking separately. Ask the user if the property includes underground parking. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | Yes | |
| input | Yes | |
| applied | Yes | |
| methods | Yes | |
| skipped | Yes | |
| degenerate | Yes | |
| disclaimer | Yes | |
| attribution | Yes | |
| calculationId | Yes | |
| assetMethodDefaults | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description layers on real behavioral context: free/non-binding, inventory deducted before allocation, depreciationBase meaning, the attribution link's behavior and citation obligation, and that omitting optional values does not invalidate the result. This is well 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?
Front-loaded with the purpose and scope, then moves to required-vs-optional prompting. It is dense across many sentences, and the garage/underground-parking advice is somewhat repetitive of the schema's own descriptions, but every sentence carries actionable content.
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 16 parameters, 7 required, three enums and an output schema present, the description covers the pieces an agent needs that structured fields do not: conditional parameter requirements, the depreciation use case, the optional-value tradeoff, and the disclaimer. Return values need not be explained since an output schema exists.
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 the baseline is 3, but the description adds cross-parameter conditions not obvious from the schema alone: a condominium needs both co-ownership values, a residential/commercial building needs its commercial share category, and monthlyNetColdRent enables the income method as a cross-check. It adds genuine meaning rather than restating field docs.
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 ('Calculate a free, non-binding German property purchase-price allocation (Kaufpreisaufteilung)') and names the mechanism (BMF Arbeitshilfe via AfAMax). An agent can distinguish this from calculate_property_depreciation 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?
Explicitly frames the use case ('e.g. when a user asks how much of the price they may depreciate or wants to check the Finanzamt's split'), and states a negative constraint (comparative valuation unavailable because the public contract excludes surveyor-only factors). It also tells the agent which optional inputs to solicit and why, which is guidance a schema cannot give.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_available_appointmentsList free AfAMax initial-call appointment slotsARead-onlyInspect
Lists open slots for a free, non-binding 15-minute initial phone call with the AfAMax team (Erstgespräch), on weekdays in the Europe/Berlin time zone, grouped by day.
Each day lists a sample spread across the day (maxSlotsPerDay, default 8) and its freeSlotCount; for a specific time, call again for that day with days: 1 and maxSlotsPerDay: 32. Show the user the day and localTime; to book one, pass its startDateTime unchanged to book_appointment. Slots are live and can be taken at any time, so re-check rather than reusing an old list. The response attribution links to the contact page, where the user can also book directly.
| Name | Required | Description | Default |
|---|---|---|---|
| days | No | How many calendar days to look ahead (1-21, default 7). / Anzahl Tage. | |
| from | No | ISO 8601 date-time to start looking from. Defaults to now. / Frühester Zeitpunkt. | |
| locale | No | Language of the link handed back and the response labels. Match the user's language (most users are German-speaking). | de |
| maxSlotsPerDay | No | How many slots to list per day (1-32, default 8), spread evenly across the day. When the user wants a specific time, call again for that day with `days: 1` and `maxSlotsPerDay: 32`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| days | Yes | |
| meta | Yes | |
| timeZone | Yes | |
| requestId | Yes | |
| attribution | Yes | |
| description | Yes | |
| appointmentType | Yes | |
| durationMinutes | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=true, destructiveHint=false), so the bar is lower. The description adds genuine behavioral context beyond that: slots are live and can be taken at any time, so results go stale, and the response attribution links to a contact page for direct booking.
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-loaded with what the tool returns and its constraints, then the booking handoff and the freshness warning. Dense but every sentence carries operational value; no 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?
An output schema exists so return-value explanation isn't required. The description still completes the workflow picture: timezone, weekday constraint, locale behavior, booking handoff, and staleness warning. Nothing an agent needs to route or call 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 coverage is 100%, so the baseline is 3. The description goes further by explaining how to use maxSlotsPerDay in practice (a sample spread across the day, default 8) and the specific re-call pattern with days:1 and maxSlotsPerDay:32 for a targeted time, which is not derivable from the schema alone.
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 (lists) and resource (open appointment slots) with scope details: free, non-binding, 15-minute initial phone call, weekdays, Europe/Berlin, grouped by day. An agent can instantly distinguish this from book_appointment and the unrelated calculation 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?
Explicit routing: for a specific time, re-call with days:1 and maxSlotsPerDay:32; to book, pass startDateTime unchanged to book_appointment; and re-check rather than reusing an old list. The alternative (book_appointment) and the conditions selecting each path are named directly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_price_by_productGet the price of an AfAMax appraisal productARead-onlyIdempotentInspect
Returns AfAMax's current list price in EUR, incl. German VAT (19%), for one product — Restnutzungsdauergutachten (rnd), Wertgutachten (kurzgutachten), Verkehrswertgutachten (vollgutachten), Kaufpreisaufteilung, Versetzbarkeitsgutachten or AfA-Verteidigung (afa_verteidigung, a second remaining-useful-life appraisal to defend an AfA the tax office rejected) — including the options the customer picks when ordering: property inspection (exterior or on-site), express processing and, for multi-unit buildings, a per-flat price.
price.totalPrice is the sum; surchargeTable lists every option so alternatives can be compared without another call. Discount codes are not applied, and the price shown when ordering is binding — say so when quoting. This tool only quotes prices: to estimate whether a Restnutzungsdauergutachten would pay off, use calculate_property_depreciation. The response attribution links to the product page; cite it as the source when reporting the price.
| Name | Required | Description | Default |
|---|---|---|---|
| locale | No | Language of labels and of the link handed back. Match the user's language. | de |
| product | Yes | Which AfAMax product to price. `rnd` — Restnutzungsdauergutachten / remaining-useful-life appraisal, the basis for a higher AfA rate. `kurzgutachten` — Wertgutachten / short value appraisal. `vollgutachten` — Verkehrswertgutachten / full market-value appraisal (§ 194 BauGB). `kaufpreisaufteilung` — Kaufpreisaufteilung / purchase-price allocation report (land vs. building). `versetzbarkeitsgutachten` — Versetzbarkeitsgutachten / relocatability certificate for tiny houses, mobile homes, modular units and containers. `afa_verteidigung` — AfA-Verteidigung / second remaining-useful-life appraisal that defends an AfA the tax office rejected. | |
| viewingType | No | Property inspection: `none` (remote, default), `exterior` (outside only / Außenbesichtigung) or `interior_exterior` (on-site, inside and outside / Innen- und Außenbesichtigung). Each adds a surcharge. | none |
| propertyType | No | Optional. Together with totalFlatsCount, a two-family, apartment or mixed-use building with more than one flat adds a per-flat price. | |
| expressDelivery | No | Express processing (3-5 business days / Expressbearbeitung). Adds a surcharge unless a free-express promotion is running. | |
| totalFlatsCount | No | Optional. Number of flats in the building / Anzahl Wohneinheiten. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | Yes | |
| input | Yes | |
| price | Yes | |
| product | Yes | |
| quoteId | Yes | |
| currency | Yes | |
| disclaimer | Yes | |
| attribution | Yes | |
| productLabel | Yes | |
| surchargeTable | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description still adds real behavioral context beyond them: no discount handling, binding price at order time, per-flat pricing triggered only for multi-unit buildings, and an attribution link that should be cited as the source. The only shortfall is that it repeats some output-shape information (price.totalPrice, surchargeTable) that the output schema already carries.
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-loaded with purpose and price/VAT scope in the first clause, then caveats, then the sibling redirect. It is dense but long, and the parenthetical product enumeration duplicates the enum descriptions in the schema, which is the one clearly removable block.
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?
Covers pricing scope, VAT, surcharge sources, caveats about discounts and binding prices, and a redirect to the sibling tool, with return values handled by the output schema. Only the product list is redundant; nothing an agent needs to call or quote 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%, so the schema already documents every parameter including the productType + totalFlatsCount interaction and the surcharge effect of viewingType. The description restates those meanings rather than adding new semantics, so it sits at the baseline for a fully documented schema.
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 ('Returns AfAMax's current list price in EUR, incl. German VAT') and scopes it to exactly one product, enumerating the enum values with short glosses so the agent can map a user request to a product code without opening the schema. It also draws a hard boundary against the closest sibling, calculate_property_depreciation.
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 states what the tool is not for ('This tool only quotes prices') and names the alternative to use instead ('use calculate_property_depreciation' for payoff estimation). It also supplies operational guidance the agent must relay: discount codes are not applied and the order-time price is binding.
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.
2 tool updates
v0.2.2- Changed
calculate_property_depreciation18 fields changed- changed
Input schema / properties / constructionYear / descriptionPrevious value: -"Original year of construction."New value: +"Original year of construction. / Baujahr." - changed
Input schema / properties / coreRenovationYear / descriptionPrevious value: -"Year of a qualifying core renovation, if applicable."New value: +"Year of a qualifying core renovation, if applicable. / Jahr einer Kernsanierung." - changed
Input schema / properties / floorArea / descriptionPrevious value: -"Living or usable floor area in square metres."New value: +"Living or usable floor area in square metres. / Wohn- bzw. Nutzfläche." - changed
Input schema / properties / landArea / descriptionPrevious value: -"Land area in square metres."New value: +"Land area in square metres. / Grundstücksfläche." - changed
Input schema / properties / modernization / descriptionPrevious value: -"Known state of eight modernization components. Ask for all eight when possible."New value: +"Known state of eight modernization components (Modernisierungselemente): roof (Dach), facadeInsulation (Fassadendämmung), windowsDoors (Fenster und Außentüren), heating (Heizung), utilities (Leitungen), bathrooms (Bäder), interiorFitout (Innenausbau), floorplanImprovement (Grundrissverbesserung). Ask for all eight when possible." - changed
Input schema / properties / propertyType / descriptionPrevious value: -"German residential property category."New value: +"German residential property category. CONDOMINIUM = Eigentumswohnung, ROW_HOUSE = Reihenhaus, SEMI_DETACHED = Doppelhaushälfte, APARTMENT_BUILDING = Mehrfamilienhaus, RESIDENTIAL_COMMERCIAL_BUILDING = Wohn- und Geschäftshaus." - changed
Input schema / properties / purchasePrice / descriptionPrevious value: -"Total purchase price in EUR."New value: +"Total purchase price in EUR. / Kaufpreis." - changed
Input schema / properties / purchaseRelatedCosts / descriptionPrevious value: -"Purchase-related costs in EUR."New value: +"Purchase-related costs in EUR (land transfer tax, notary, land register, broker). / Kaufnebenkosten." - changed
Input schema / properties / standardLandValue / descriptionPrevious value: -"Standard land value in EUR per square metre."New value: +"Standard land value in EUR per square metre. / Bodenrichtwert." - changed
Input schema / properties / taxRate / descriptionPrevious value: -"Personal marginal tax rate as a decimal, e.g. 0.42."New value: +"Personal marginal income tax rate as a decimal, e.g. 0.42. / Grenzsteuersatz." - added
Output schema / properties / results / properties / afaRatePerYear / descriptionAdded value: +"AfA rate per year from the estimated remaining useful life, as a decimal (0.0333 = 3.33 %)." - added
Output schema / properties / results / properties / annualAfaAmount / descriptionAdded value: +"Annual AfA in EUR at afaRatePerYear." - added
Output schema / properties / results / properties / annualTaxSavings / descriptionAdded value: +"Estimated income tax saved per year by annualAfaAmount at taxRate." - added
Output schema / properties / results / properties / appraisalWorthwhile / descriptionAdded value: +"Whether the remaining-useful-life rate is high enough for a Restnutzungsdauergutachten to look worthwhile." - added
Output schema / properties / results / properties / buildingValue / descriptionAdded value: +"Depreciable building value in EUR (excluding land)." - added
Output schema / properties / results / properties / defaultAfaAmount / descriptionAdded value: +"Annual AfA in EUR at the statutory rate." - added
Output schema / properties / results / properties / defaultAfaRatePerYear / descriptionAdded value: +"Statutory AfA rate under § 7 Abs. 4 EStG for comparison, as a decimal." - added
Output schema / properties / results / properties / remainingUsefulLifeYears / descriptionAdded value: +"Estimated remaining useful life (Restnutzungsdauer) in years."
- Changed
get_price_by_product3 fields changed- changed
Input schema / properties / product / descriptionPrevious value: -"Which AfAMax product to price. `rnd` — Restnutzungsdauergutachten / remaining-useful-life appraisal, the basis for a higher AfA rate. `kurzgutachten` — Wertgutachten / short value appraisal. `vollgutachten` — Verkehrswertgutachten / full market-value appraisal (§ 194 BauGB). `kaufpreisaufteilung` — Kaufpreisaufteilung / purchase-price allocation report (land vs. building). `versetzbarkeitsgutachten` — Versetzbarkeitsgutachten / relocatability certificate for tiny houses, mobile homes, modular units and containers."New value: +"Which AfAMax product to price. `rnd` — Restnutzungsdauergutachten / remaining-useful-life appraisal, the basis for a higher AfA rate. `kurzgutachten` — Wertgutachten / short value appraisal. `vollgutachten` — Verkehrswertgutachten / full market-value appraisal (§ 194 BauGB). `kaufpreisaufteilung` — Kaufpreisaufteilung / purchase-price allocation report (land vs. building). `versetzbarkeitsgutachten` — Versetzbarkeitsgutachten / relocatability certificate for tiny houses, mobile homes, modular units and containers. `afa_verteidigung` — AfA-Verteidigung / second remaining-useful-life appraisal that defends an AfA the tax office rejected." - changed
Input schema / properties / product / enumPrevious value: -[ - "rnd", - "kurzgutachten", - "vollgutachten", - "kaufpreisaufteilung", - "versetzbarkeitsgutachten" -]New value: +[ + "rnd", + "kurzgutachten", + "vollgutachten", + "kaufpreisaufteilung", + "versetzbarkeitsgutachten", + "afa_verteidigung" +] - changed
Output schema / properties / product / enumPrevious value: -[ - "rnd", - "kurzgutachten", - "vollgutachten", - "kaufpreisaufteilung", - "versetzbarkeitsgutachten" -]New value: +[ + "rnd", + "kurzgutachten", + "vollgutachten", + "kaufpreisaufteilung", + "versetzbarkeitsgutachten", + "afa_verteidigung" +]
5 tool updates
v0.2.0- Added
book_appointment - Changed
calculate_property_depreciation2 fields changed- removed
Output schema / properties / attribution / properties / provider / constRemoved value: -"AfaMax" - added
Output schema / properties / attribution / properties / provider / enumAdded value: +[ + "AfaMax", + "AfAMax" +]
- Changed
calculate_purchase_price_allocation2 fields changed- removed
Output schema / properties / attribution / properties / provider / constRemoved value: -"AfaMax" - added
Output schema / properties / attribution / properties / provider / enumAdded value: +[ + "AfaMax", + "AfAMax" +]
- Added
get_available_appointments - Added
get_price_by_product
1 tool update
v0.1.1- Changed
calculate_purchase_price_allocation4 fields changed- changed
Input schema / properties / garages / descriptionPrevious value: -"Number of enclosed garage spaces. Defaults to 0."New value: +"Number of above-ground garages. Improves the asset-method allocation by valuing parking separately. Ask the user if the property includes garages." - changed
Input schema / properties / locale / descriptionPrevious value: -"Language for the disclaimer and AfaMax attribution link. Defaults to German."New value: +"Language for the disclaimer and AfAMax attribution link. Defaults to German." - changed
Input schema / properties / monthlyNetColdRent / descriptionPrevious value: -"Total monthly net cold rent in EUR. A positive value enables the income method; omitting or passing 0 leaves it unavailable."New value: +"Monthly net cold rent (base rent excluding utilities, in EUR). Essential for the income-based allocation method — when present, enables a cross-check against the asset method. Ask the user if the property is rented or if they know the rental value." - changed
Input schema / properties / undergroundParkingSpaces / descriptionPrevious value: -"Number of underground parking spaces. Defaults to 0."New value: +"Number of covered underground parking spaces. Improves the asset-method allocation by valuing parking separately. Ask the user if the property includes underground parking."
2 tool updates
v0.1.0- First observed
calculate_property_depreciation - First observed
calculate_purchase_price_allocation
TDQS
Scored across 5 tools
Each tool targets a distinct action-object pair: two different calculations (AfA depreciation vs. purchase-price allocation), a price lookup, and a list/book pair for appointments. The descriptions actively cross-reference and rule out the nearest confusable alternative (e.g. get_price_by_product explicitly says to use calculate_property_depreciation for a pay-off estimate). No two tools could plausibly be swapped by an agent.
All five names use snake_case with a leading verb and a concrete object (calculate_*, get_*, book_*), and the get_/book_ pairing for appointments follows an obvious convention. Minor variation in prepositional phrasing (get_price_by_product) does not break the pattern.
Five tools is a tight, well-scoped surface where each tool has a clear role and none is redundant. It leans slightly thin because two of the five slots are consumed by the appointment list/book pair, leaving only three tools for the actual appraisal/calculation domain.
The surface covers two calculators (AfA and Kaufpreisaufteilung), pricing, and booking, but the product catalogue implies appraisals that can be ordered (Restnutzungsdauergutachten, Wertgutachten, Verkehrswertgutachten) with no order, status, or report-retrieval tool — the descriptions push those steps to the website. An agent can quote a price but cannot complete or follow up on the core purchase workflow, which is a notable gap.
Maintenance
Related MCP Connectors
MCP server for real-estate agents: listings, CRM, German market data and AI media tools.
MCPCalc gives agents access to a comprehensive library of calculators spanning finance, math, health, construction, engineering, food, automotive, and more. It includes a full Computer Algebra System (CAS) and a grid-based Spreadsheet calculator.
Search German property listings and estimate what any German address is worth.
Property Records MCP — address-level US property records (sales history, assessed value, owner…
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables AI-powered real estate analysis with built-in EU AI Act compliance, providing a production-ready MCP server for property insights and governance.1MIT
- AlicenseBqualityAmaintenanceAccounting MCP server for the French LMNP tax status (furnished rentals, e.g. Airbnb hosts). 44 tools to manage properties, income and expenses, compute component-based depreciation and fiscal results, and generate the official French tax return (2031/2033) and FEC accounting export.4515AGPL 3.0
- AlicenseAqualityBmaintenanceEnables users to retrieve Danish property information from official registers, covering addresses, buildings, valuations, trades, and more, through MCP tools.13MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to estimate Austrian property acquisition costs, underwrite residential buy-to-let investments, and retrieve statutory rules and default assumptions with legal citations through read-only MCP tools.AGPL 3.0