Skip to main content
Glama
woonstadrotterdam

io.github.woonstadrotterdam/monumenten

Official

Monumenten MCP Server πŸ›οΈ

A Model Context Protocol (MCP) server that enables AI assistants to check monumental status of Dutch addresses relevant for woningwaardering (rent-point system). Connects to the Dutch BAG (Basisadministratie Adressen en Gebouwen) data and Ministry of Cultural Heritage (Rijksdienst voor het Cultureel Erfgoed) to identify national monuments, nationally protected cityscapes, and municipal monuments.

Built on the MCP Python SDK v2 (MCPServer). Tools return structured results; validation and lookup failures raise ToolError (is_error=true) so the model can retry.

Requires Python 3.11+.

NOTE

This MCP server is based on themonumenten package. For more information, see the monumenten package.

What This Does

This MCP server allows AI assistants to:

  • 🏠 Find verblijfsobject IDs - Convert Dutch addresses to BAG identifiers (verblijfsobject_id)

  • πŸ›οΈ Check monumental status - Determine if a property is protected as cultural heritage

  • πŸ“ Support flexible address input - Search by postal code + house number or full address

  • πŸ” Handle address variations - Support house letters and suffixes (30A, 30-2, etc.)

Available Tools

Tool

Parameters

Result

get_verblijfsobject_id

house_number, postal_code OR street + house_number + city, optional house_letter, house_suffix

Structured matches list of BAG verblijfsobject records

get_monumental_status

bag_verblijfsobject_id (16 digits)

Structured monumental status (provincie, rijksmonument, rijksbeschermd stads-/dorpsgezicht, municipal monument; provinciaal_monument is always null)

Related MCP server: homedata-mcp

Quick Setup

Add to your AI assistant's MCP configuration:

{
  "mcpServers": {
    "monumenten": {
      "command": "uvx",
      "args": ["mcp-monumenten"]
    }
  }
}

For local development:

{
  "mcpServers": {
    "monumenten": {
      "command": "uv",
      "args": ["run", "--project", "/path/to/mcp-monumenten", "mcp-monumenten"]
    }
  }
}

Streamable HTTP (mcp-monumenten --http) listens on http://127.0.0.1:8000/mcp by default.

Claude Code plugin

Requires uv. Then:

/plugin marketplace add woonstadrotterdam/mcp-monumenten
/plugin install monumenten@woonstad-rotterdam

Or add the server directly: claude mcp add monumenten -- uvx mcp-monumenten.

Usage Examples

Finding Monumental Status

"What is the monumental status of Coolsingel 30, Rotterdam?"

The AI will:

  1. Convert the address to a BAG verblijfsobject ID

  2. Check monument registries

  3. Report rijksmonument status, rijksbeschermd stads-/dorpsgezicht, or municipal monument designation

"Is 1234AB 30-2 a rijksmonument?"

The AI can handle:

  • Postal code + house number format

  • House number suffixes (30-2, 30A, etc.)

  • Direct verblijfsobject ID lookups

Address Flexibility

The server handles Dutch address formats:

  • 1234AB 30 - Basic postal code + house number

  • 1234AB 30-2 - With house number suffix

  • 1234AB 30A - With house letter

  • Coolsingel 30, Rotterdam - Full street address

Installation

uvx mcp-monumenten

Local Development

git clone https://github.com/woonstadrotterdam/mcp-monumenten.git
cd mcp-monumenten
uv sync
uv run --project . mcp-monumenten

Data Sources

License

MIT License - see LICENSE file for details.

Available Tools

2 tools
get_monumental_statusA
Read-only

Get the monumental status of a verblijfsobject.

Always mention the source for the Rijksmonument status if it is a Rijksmonument. (RCE = Rijksdienst voor het Cultureel Erfgoed.) Reply in the user's language. provinciaal_monument is not looked up.

ParametersJSON Schema
NameRequiredDescriptionDefault
bag_verblijfsobject_idYesThe verblijfsobject ID (16 digits)

Output Schema

ParametersJSON Schema
NameRequiredDescription
provincieNoCurrent Dutch province of the verblijfsobject. Null if the provincie lookup failed.
rijksmonumentNo
rijksmonument_urlNo
rijksmonument_bronNo
provinciaal_monumentNoProvincial monument. Always null: this status is not looked up. It only exists in Noord-Holland and Drenthe, and is mutually exclusive with rijksmonument and gemeentelijk_monument. If provincie is one of those or unknown, and the other monument flags are not true, tell the user this was not checked, in the user's language. Do not report it as false.
rijksmonument_nummerNo
gemeentelijk_monumentNo
bag_verblijfsobject_idYes
rijksbeschermd_gezichtNoNationally protected town or village scape (rijksbeschermd stads- of dorpsgezicht). false does not mean the address is outside a municipal or provincial beschermd stadsgezicht; those are not looked up.
rijksbeschermd_gezicht_naamNo
grondslag_gemeentelijk_monumentNo

TDQS

A3.7/5.0
Behavior4/5

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

The annotations already provide readOnlyHint=true and openWorldHint=true, so the description is not burdened with basic safety disclosure. It adds useful behavioral context: always cite the RCE source for Rijksmonument status, reply in the user's language, and do not expect provinciaal_monument to be resolved. This adds genuine value beyond 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.

Conciseness4/5

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

The purpose is front-loaded in a single clear sentence, and the additional instructions are terse and each earns its place. The structure is slightly loose due to the parenthetical abbreviation and the mixing of output formatting with tool behavior, but there is no 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?

Given the single fully documented parameter corrections and the presence of an output schema, the description covers the important non-obvious behaviors: source citation, language handling, and the provinciaal_monument limitation. An explicit pointer to the sibling for obtaining the ID would improve completeness, but that is more of a usage-routing enhancement than a critical gap.

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 coverage is 100%: the schema already describes bag_verblijfsobject_id as the 16-digit verblijfsobject ID. The description does not add further meaning about the parameter, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the action and resource: it gets the monumental status of a verblijfsobject. The sibling get_verblijfsobject_id is clearly a different operation, but the description does not explicitly contrast the two, so it falls just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

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

The description implies when to use the tool (when a verblijfsobject ID is known and monumental status is needed) and explicitly states that provinciaal_monument is not looked up. However, it does not mention the sibling get_verblijfsobject_id or explain when that alternative should be used, leaving the routing largely implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_verblijfsobject_idA
Read-only

Get verblijfsobject ID using address.

Use postal_code + house_number OR street + house_number + city. Additional filters like house_letter and house_suffix can be provided for more precise matching.

ParametersJSON Schema
NameRequiredDescriptionDefault
cityNoCity name for search mode 2, e.g. 'Rotterdam'
streetNoStreet name for search mode 2, e.g. 'Coolsingel'
postal_codeNoDutch postal code for search mode 1, e.g. '1234AB'
house_letterNoHouse letter, e.g. 'A' in '30A'
house_numberYesThe house number, e.g. '30'
house_suffixNoHouse number suffix/addition, e.g. '2' in '30-2'

Output Schema

ParametersJSON Schema
NameRequiredDescription
matchesYes

TDQS

A4.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true, so the read-only nature is covered. The description adds behavioral context about search modes and filtering precision, but it does not disclose potential behavior such as multiple matches, no-match handling, or whether the result is unique. Given the annotation coverage, a 3 is appropriate.

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 extremely conciseβ€”four short linesβ€”and front-loads the purpose. Each sentence serves a clear function: stating the outcome, giving the two search modes, and noting optional filters. There is no repetition or wasted text.

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 tool has 6 parameters, an output schema, and 100% schema description coverage, so the description need not explain return values. It adequately explains how to invoke the tool via the two search modes and optional filters. The only minor gap is a lack of explicit guidance about when to prefer the sibling tool, but that is not critical for correct invocation.

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 significant meaning by explaining the valid parameter combinations (postal_code+house_number vs street+house_number+city) and clarifying that house_letter and house_suffix are precision-enhancing filters. This goes beyond the schema's individual field descriptions and helps the agent choose the right parameters.

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 'Get verblijfsobject ID using address,' which states a specific verb, resource, and purpose. It clearly distinguishes the tool from its only sibling, get_monumental_status, since that tool concerns monumental status rather than ID lookup. No ambiguity remains about what the tool does.

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 clear usage instructions by specifying two valid search modes: postal_code + house_number OR street + house_number + city. It also notes that house_letter and house_suffix are additional filters for 'more precise matching.' However, it does not explicitly contrast with the sibling tool or state when not to use this tool, leaving that to inference.

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. 1 tool updatev3.0.1
    • Changedget_monumental_status3 fields changed
      • addedOutput schema / properties / provinciaal_monument
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "boolean"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Provincial monument. Always null: this status is not looked up. It only exists in Noord-Holland and Drenthe, and is mutually exclusive with rijksmonument and gemeentelijk_monument. If provincie is one of those or unknown, and the other monument flags are not true, tell the user this was not checked, in the user's language. Do not report it as false.",
        +  "title": "Provinciaal Monument"
        +}
      • addedOutput schema / properties / provincie
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "description": "Current Dutch province of the verblijfsobject. Null if the provincie lookup failed.",
        +  "title": "Provincie"
        +}
      • addedOutput schema / properties / rijksbeschermd_gezicht / description
        Added value: +"Nationally protected town or village scape (rijksbeschermd stads- of dorpsgezicht). false does not mean the address is outside a municipal or provincial beschermd stadsgezicht; those are not looked up."
  2. 2 tool updatesv3.0.0
    • First observedget_monumental_status
    • First observedget_verblijfsobject_id

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one resolves an address to a verblijfsobject ID, and the other retrieves monumental status for a verblijfsobject. There is no overlap or ambiguity between them.

Naming Consistency4/5

Both tools follow a consistent get_<object>_<attribute> pattern, though the second tool's name (get_verblijfsobject_id) describes the return value rather than the resource attribute, which is a minor deviation.

Tool Count3/5

Two tools is on the thin side for a server, but the narrow domain (looking up monumental status by address) makes the count defensible. It is borderline but not unreasonable.

Completeness3/5

The two tools form a complete workflow: address β†’ verblijfsobject ID β†’ monumental status. However, there is no direct lookup by verblijfsobject ID, no monument details endpoint, and no support for provinciaal monument lookups, leaving some gaps.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables natural language search and exploration of the Dutch WWII Oorlogsbronnen archives, allowing users to query historical documents, photographs, and personal accounts through AI assistants.
    9 npm
    15
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to query UK property data including EPCs, sale history, planning, flood risk, council tax, demographics, and more via the Homedata API.
    18
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    Enables querying over 3,200 Dutch statutes (AVG, Wetboek van Strafrecht, Burgerlijk Wetboek, etc.) with verbatim, citation-grounded text from official sources, directly from MCP-compatible AI assistants.
    18
    48 npm
    13
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to query the Swiss Federal Register of Buildings and Dwellings (GWR/RegBL) β€” including building lookups, address geocoding, construction statistics, and housing pipeline analysis β€” through MCP tools.
    9
    1
    MIT