Skip to main content
Glama
Rundum-Immo

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 and purchase-price allocation. It exposes calculate_property_depreciation and calculate_purchase_price_allocation, delegating both calculations to the public AfaMax APIs. 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: einvoice-mcp

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; otherwise the calculation falls back to the asset method. 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"
}

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 either tool with its example input above. Successful responses contain a readable summary, structured output, disclosed assumptions/defaults, and AfaMax attribution.

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 tool input. See AfaMax privacy information and SECURITY.md.

Roadmap

The repository can grow beyond its initial calculation 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

2 tools
calculate_property_depreciationCalculate German property depreciationA
Read-onlyIdempotent
Inspect

Calculate an indicative German real-estate depreciation (AfA) estimate through AfaMax.

Use this for residential German property, including remaining useful life, annual/monthly AfA, statutory comparison, and estimated tax savings. 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 result is non-binding and does not replace tax or legal advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
localeNoLanguage for upstream explanations; defaults to German.
taxRateNoPersonal marginal tax rate as a decimal, e.g. 0.42.
landAreaNoLand area in square metres.
floorAreaYesLiving or usable floor area in square metres.
propertyTypeYesGerman residential property category.
modernizationNoKnown state of eight modernization components. Ask for all eight when possible.
purchasePriceNoTotal purchase price in EUR.
constructionYearYesOriginal year of construction.
includedInventoryNoMovable inventory included in the purchase price, in EUR.
standardLandValueNoStandard land value in EUR per square metre.
coreRenovationYearNoYear of a qualifying core renovation, if applicable.
modernizationLevelNoCoarse modernization level used when detailed component data is unavailable.
purchaseRelatedCostsNoPurchase-related costs in EUR.

Output Schema

ParametersJSON Schema
NameRequiredDescription
metaYes
inputYes
resultsYes
disclaimerYes
assumptionsYes
attributionYes
calculationIdYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and idempotentHint, so the safety profile is covered. The description adds useful behavioral context beyond that by stating the estimate is 'indicative' and 'non-binding,' and by disclosing that omitting modernization components 'assumes no modernization and produces the highest possible remaining-useful-life benefit.' This gives the agent meaningful expectation-setting without contradicting the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the primary action and resource, and every sentence earns its place: purpose, usage scope, modernization guidance, and legal disclaimer. It is compact enough for a complex 13-parameter financial calculation tool while avoiding redundancy or filler.

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?

The description covers the core purpose, when to use it, apartment-building handling, an important assumption about missing modernization data, and the non-binding nature of the result. With annotations covering safety and an output schema present, the description is reasonably complete, though it could be stronger by explicitly naming the sibling tool and the situations that would route to it.

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. The description adds value beyond the schema by explaining the consequence of omitting the modernization object—it assumes no modernization and affects the remaining-useful-life result. It also gives guidance on how to pass floor-area figures for apartment buildings, which helps the agent reason about propertyType and floorArea semantics.

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?

The description opens with a specific verb and resource: 'Calculate an indicative German real-estate depreciation (AfA) estimate through AfaMax.' This clearly identifies the tool's function and distinguishes it from the sibling calculate_purchase_price_allocation, which targets a different calculation even though the sibling is not named.

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?

The description gives clear use context: 'Use this for residential German property' and provides special guidance for apartment buildings. It also instructs the agent to 'Ask for all eight modernization component states whenever possible,' but it does not explicitly state when not to use this tool or mention the alternative sibling, so it stops short of full when/when-not guidance.

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 and the depreciable building. Inventory is deducted before allocation. A condominium requires both co-ownership values, and a residential/commercial building requires its commercial share category. Provide monthlyNetColdRent when known so the income method can be calculated; otherwise only the asset method is available. Comparative valuation is unavailable because this public contract excludes surveyor-only factors. The result does not replace tax or legal advice.

ParametersJSON Schema
NameRequiredDescriptionDefault
localeNoLanguage for the disclaimer and AfaMax attribution link. Defaults to German.de
garagesNoNumber of enclosed garage spaces. Defaults to 0.
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.
monthlyNetColdRentNoTotal monthly net cold rent in EUR. A positive value enables the income method; omitting or passing 0 leaves it unavailable.
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 underground parking spaces. Defaults to 0.

Output Schema

ParametersJSON 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 declare readOnly and idempotent, but the description adds substantial behavioral context beyond them: free and non-binding, inventory is deducted before allocation, income method availability depends on monthlyNetColdRent, comparative valuation is excluded, and the result is not tax/legal advice. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with the primary purpose, then supplies only decision-relevant conditions. Each sentence adds value, and the legal disclaimer is placed last without weighing the text down.

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 16-parameter tool with an output schema, the description covers the key context an agent needs: what the calculation does, prerequisites for special property types, method availability, and limitations. Return details are covered by the output schema, so nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/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, but the description meaningfully supplements the schema. It explains that includedInventory is deducted before allocation, that coOwnership numerator/denominator are both required for condominiums, that commercialShare applies only to RESIDENTIAL_COMMERCIAL_BUILDING, and that monthlyNetColdRent enables the income method versus the asset-only fallback.

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?

The description opens with a specific verb and resource: 'Calculate a free, non-binding German property purchase-price allocation (Kaufpreisaufteilung)' and immediately clarifies the mechanism (AfaMax, BMF Arbeitshilfe). It states the core function—dividing acquisition costs between land and building—which differentiates it from the 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 Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit usage context: 'Use this to divide acquisition costs...' and details conditional requirements (condominium co-ownership values, commercial share category, monthlyNetColdRent enabling the income method). It does not explicitly name an alternative tool, but the contrast with depreciation is implied.

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.1.0
    • First observedcalculate_property_depreciation
    • First observedcalculate_purchase_price_allocation

TDQS

A4.5/5.0

Scored across 2 tools

Disambiguation5/5

The two tools address clearly different calculations: one estimates depreciation/AfA and the other allocates a purchase price between land and building. Even though both are German real-estate tax calculators via AfaMax, their outputs and inputs are distinct enough that an agent would not confuse them.

Naming Consistency5/5

Both names follow the same snake_case verb_noun pattern: calculate_property_depreciation and calculate_purchase_price_allocation. The convention is uniform, descriptive, and predictable.

Tool Count4/5

With only two tools, the surface is on the light side, but each tool is substantial and non-overlapping within the narrow German real-estate tax-calculator scope. The pair is slightly under the typical range but still feels purposeful rather than incomplete or padded.

Completeness4/5

The two tools cover a coherent acquisition-and-holding tax workflow: allocate the purchase price to depreciable building basis, then compute depreciation on that basis. General property valuation or transfer-tax calculations are absent, but those appear outside the intended AfaMax calculator scope.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    MCP server for DACH accounting automation. Connect AI assistants to sevDesk and Lexoffice — create invoices, manage contacts, handle bookings and vouchers for German-speaking businesses.
    15
    27 npm
    -
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for German e-invoice compliance (XRechnung 3.0 & ZUGFeRD 2.x) enabling AI agents to validate, generate, parse, and check compliance of electronic invoices per EN 16931.
    6
    1
    MIT
  • 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
    Not graded
    quality
    A
    maintenance
    Enables AI agents to look up county assessor public records for properties, check MLS discrepancies against public data, and discover new county assessor sources, all via a local MCP server for real-estate appraisal workflows.
    6
    MIT