Skip to main content
Glama
immo.rundum/real-estate-appraisal

immo.rundum/real-estate-appraisal

Official
by Rundum-Immo

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/mcp

No 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 build

Configure 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:

  1. Call with the slot's startDateTime, name, email (optionally phone, locale). AfAMax e-mails a 6-digit code and returns a verificationId.

  2. Call again with only verificationId and the otp the 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 API

This 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:stdio

Test 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.js

Connect 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/list

Develop the HTTP transport

cp .env.example .env
pnpm dev:http

AFAMAX_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

AFAMAX_API_URL

https://afamax.de/api/v1/afa-calculation

Public calculation endpoint

AFAMAX_KPA_API_URL

https://afamax.de/api/v1/purchase-price-allocation

Public purchase-price allocation endpoint

AFAMAX_SERVICE_TOKEN

—

Required trusted-service credential in HTTP mode

AFAMAX_TIMEOUT_MS

10000

Upstream timeout

HOST

0.0.0.0

Listen address

PORT

3000

Listen port

PUBLIC_HOSTS

mcp.rundum.immo

Comma-separated allowed Host header names

LOG_LEVEL

info

debug, info, warn, or error

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-mcp

The 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

MIT

Available Tools

5 tools
book_appointmentBook a free AfAMax initial callA
Destructive
Inspect

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.

NameRequiredDescriptionDefault
otpNoStep 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.
nameNoStep 1 — the user's full name. / Vor- und Nachname.
emailNoStep 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.
phoneNoStep 1, optional — phone number for the call. / Telefonnummer für das Gespräch.
localeNoStep 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.
startDateTimeNoStep 1 — the chosen slot, copied exactly from get_available_appointments `startDateTime`.
verificationIdNoStep 2 — the `verificationId` returned by step 1.

Output Schema

NameRequiredDescription
metaYes
statusYes`verification_required`: ask the user for the code sent to `verification.maskedEmail`, then call again with verificationId + otp. `booked`: the appointment exists.
requestIdYes
appointmentYes
attributionYes
verificationYes

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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) & RestnutzungsdauerA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
localeNoLanguage for upstream explanations; defaults to German.
taxRateNoPersonal marginal income tax rate as a decimal, e.g. 0.42. / Grenzsteuersatz.
landAreaNoLand area in square metres. / Grundstücksfläche.
floorAreaYesLiving or usable floor area in square metres. / Wohn- bzw. Nutzfläche.
propertyTypeYesGerman residential property category. CONDOMINIUM = Eigentumswohnung, ROW_HOUSE = Reihenhaus, SEMI_DETACHED = Doppelhaushälfte, APARTMENT_BUILDING = Mehrfamilienhaus, RESIDENTIAL_COMMERCIAL_BUILDING = Wohn- und Geschäftshaus.
modernizationNoKnown 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.
purchasePriceNoTotal purchase price in EUR. / Kaufpreis.
constructionYearYesOriginal year of construction. / Baujahr.
includedInventoryNoMovable inventory included in the purchase price, in EUR.
standardLandValueNoStandard land value in EUR per square metre. / Bodenrichtwert.
coreRenovationYearNoYear of a qualifying core renovation, if applicable. / Jahr einer Kernsanierung.
modernizationLevelNoCoarse modernization level used when detailed component data is unavailable.
purchaseRelatedCostsNoPurchase-related costs in EUR (land transfer tax, notary, land register, broker). / Kaufnebenkosten.

Output Schema

NameRequiredDescription
metaYes
inputYes
resultsYes
disclaimerYes
assumptionsYes
attributionYes
calculationIdYes

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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 allocationA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
localeNoLanguage for the disclaimer and AfAMax attribution link. Defaults to German.de
garagesNoNumber of above-ground garages. Improves the asset-method allocation by valuing parking separately. Ask the user if the property includes garages.
landAreaYesPlot area in square metres.
floorAreaYesLiving or usable floor area in square metres.
propertyTypeYesGerman residential property category.
purchaseDateYesDate of the notarized purchase contract in YYYY-MM-DD format; must be from 1990-01-01 through today.
commercialShareNoRequired for RESIDENTIAL_COMMERCIAL_BUILDING: whether the commercial share is under or over 50%.
constructionYearYesOriginal construction year; it cannot be later than the purchase year.
includedInventoryNoMovable inventory included in the purchase price, in EUR. It is deducted before allocation.
standardLandValueYesStandard land value (Bodenrichtwert) in EUR per square metre.
monthlyNetColdRentNoMonthly 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.
totalPurchasePriceYesTotal notarized purchase price in EUR.
coOwnershipNumeratorNoCo-ownership numerator (Miteigentumsanteil); required with the denominator for a condominium.
purchaseRelatedCostsNoLand transfer tax, notary, land-register, and broker costs in EUR. Defaults to 0.
coOwnershipDenominatorNoCo-ownership denominator; required with the numerator for a condominium.
undergroundParkingSpacesNoNumber 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

NameRequiredDescription
metaYes
inputYes
appliedYes
methodsYes
skippedYes
degenerateYes
disclaimerYes
attributionYes
calculationIdYes
assetMethodDefaultsYes

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 slotsA
Read-only
Inspect

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.

NameRequiredDescriptionDefault
daysNoHow many calendar days to look ahead (1-21, default 7). / Anzahl Tage.
fromNoISO 8601 date-time to start looking from. Defaults to now. / Frühester Zeitpunkt.
localeNoLanguage of the link handed back and the response labels. Match the user's language (most users are German-speaking).de
maxSlotsPerDayNoHow 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

NameRequiredDescription
daysYes
metaYes
timeZoneYes
requestIdYes
attributionYes
descriptionYes
appointmentTypeYes
durationMinutesYes

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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 productA
Read-onlyIdempotent
Inspect

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.

NameRequiredDescriptionDefault
localeNoLanguage of labels and of the link handed back. Match the user's language.de
productYesWhich 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.
viewingTypeNoProperty 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
propertyTypeNoOptional. Together with totalFlatsCount, a two-family, apartment or mixed-use building with more than one flat adds a per-flat price.
expressDeliveryNoExpress processing (3-5 business days / Expressbearbeitung). Adds a surcharge unless a free-express promotion is running.
totalFlatsCountNoOptional. Number of flats in the building / Anzahl Wohneinheiten.

Output Schema

NameRequiredDescription
metaYes
inputYes
priceYes
productYes
quoteIdYes
currencyYes
disclaimerYes
attributionYes
productLabelYes
surchargeTableYes

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 2 tool updatesv0.2.2
    • Changedcalculate_property_depreciation18 fields changed
      • changedInput schema / properties / constructionYear / description
        Previous value: -"Original year of construction."New value: +"Original year of construction. / Baujahr."
      • changedInput schema / properties / coreRenovationYear / description
        Previous value: -"Year of a qualifying core renovation, if applicable."New value: +"Year of a qualifying core renovation, if applicable. / Jahr einer Kernsanierung."
      • changedInput schema / properties / floorArea / description
        Previous value: -"Living or usable floor area in square metres."New value: +"Living or usable floor area in square metres. / Wohn- bzw. Nutzfläche."
      • changedInput schema / properties / landArea / description
        Previous value: -"Land area in square metres."New value: +"Land area in square metres. / Grundstücksfläche."
      • changedInput schema / properties / modernization / description
        Previous 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."
      • changedInput schema / properties / propertyType / description
        Previous 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."
      • changedInput schema / properties / purchasePrice / description
        Previous value: -"Total purchase price in EUR."New value: +"Total purchase price in EUR. / Kaufpreis."
      • changedInput schema / properties / purchaseRelatedCosts / description
        Previous value: -"Purchase-related costs in EUR."New value: +"Purchase-related costs in EUR (land transfer tax, notary, land register, broker). / Kaufnebenkosten."
      • changedInput schema / properties / standardLandValue / description
        Previous value: -"Standard land value in EUR per square metre."New value: +"Standard land value in EUR per square metre. / Bodenrichtwert."
      • changedInput schema / properties / taxRate / description
        Previous 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."
      • addedOutput schema / properties / results / properties / afaRatePerYear / description
        Added value: +"AfA rate per year from the estimated remaining useful life, as a decimal (0.0333 = 3.33 %)."
      • addedOutput schema / properties / results / properties / annualAfaAmount / description
        Added value: +"Annual AfA in EUR at afaRatePerYear."
      • addedOutput schema / properties / results / properties / annualTaxSavings / description
        Added value: +"Estimated income tax saved per year by annualAfaAmount at taxRate."
      • addedOutput schema / properties / results / properties / appraisalWorthwhile / description
        Added value: +"Whether the remaining-useful-life rate is high enough for a Restnutzungsdauergutachten to look worthwhile."
      • addedOutput schema / properties / results / properties / buildingValue / description
        Added value: +"Depreciable building value in EUR (excluding land)."
      • addedOutput schema / properties / results / properties / defaultAfaAmount / description
        Added value: +"Annual AfA in EUR at the statutory rate."
      • addedOutput schema / properties / results / properties / defaultAfaRatePerYear / description
        Added value: +"Statutory AfA rate under § 7 Abs. 4 EStG for comparison, as a decimal."
      • addedOutput schema / properties / results / properties / remainingUsefulLifeYears / description
        Added value: +"Estimated remaining useful life (Restnutzungsdauer) in years."
    • Changedget_price_by_product3 fields changed
      • changedInput schema / properties / product / description
        Previous 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."
      • changedInput schema / properties / product / enum
        Previous value: -[
        -  "rnd",
        -  "kurzgutachten",
        -  "vollgutachten",
        -  "kaufpreisaufteilung",
        -  "versetzbarkeitsgutachten"
        -]New value: +[
        +  "rnd",
        +  "kurzgutachten",
        +  "vollgutachten",
        +  "kaufpreisaufteilung",
        +  "versetzbarkeitsgutachten",
        +  "afa_verteidigung"
        +]
      • changedOutput schema / properties / product / enum
        Previous value: -[
        -  "rnd",
        -  "kurzgutachten",
        -  "vollgutachten",
        -  "kaufpreisaufteilung",
        -  "versetzbarkeitsgutachten"
        -]New value: +[
        +  "rnd",
        +  "kurzgutachten",
        +  "vollgutachten",
        +  "kaufpreisaufteilung",
        +  "versetzbarkeitsgutachten",
        +  "afa_verteidigung"
        +]
  2. 5 tool updatesv0.2.0
    • Addedbook_appointment
    • Changedcalculate_property_depreciation2 fields changed
      • removedOutput schema / properties / attribution / properties / provider / const
        Removed value: -"AfaMax"
      • addedOutput schema / properties / attribution / properties / provider / enum
        Added value: +[
        +  "AfaMax",
        +  "AfAMax"
        +]
    • Changedcalculate_purchase_price_allocation2 fields changed
      • removedOutput schema / properties / attribution / properties / provider / const
        Removed value: -"AfaMax"
      • addedOutput schema / properties / attribution / properties / provider / enum
        Added value: +[
        +  "AfaMax",
        +  "AfAMax"
        +]
    • Addedget_available_appointments
    • Addedget_price_by_product
  3. 1 tool updatev0.1.1
    • Changedcalculate_purchase_price_allocation4 fields changed
      • changedInput schema / properties / garages / description
        Previous 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."
      • changedInput schema / properties / locale / description
        Previous 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."
      • changedInput schema / properties / monthlyNetColdRent / description
        Previous 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."
      • changedInput schema / properties / undergroundParkingSpaces / description
        Previous 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."
  4. 2 tool updatesv0.1.0
    • First observedcalculate_property_depreciation
    • First observedcalculate_purchase_price_allocation

TDQS

A4.4/5.0

Scored across 5 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness3/5

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

ActivityMaintained
ResponsivenessLimited data

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI-powered real estate analysis with built-in EU AI Act compliance, providing a production-ready MCP server for property insights and governance.
    1
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Accounting 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.
    45
    15
    AGPL 3.0
  • A
    license
    A
    quality
    B
    maintenance
    Enables users to retrieve Danish property information from official registers, covering addresses, buildings, valuations, trades, and more, through MCP tools.
    13
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables 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