3DCityDB MCP Server

MCP

From TUM's Chair of Geoinformatics (a 3DCityDB co-developer): query CityGML 3D city models in 3DCityDB v5 in plain language, covering both spatial and semantic attributes, with read-only SQL. Needs a deployed 3DCityDB database and Python; register it from the folder with your .env config.

tum-gis/3dcitydb-mcp-server18
Auf GitHub ansehen
README

3DCityDB MCP Server

PyPI Python versions License: Apache-2.0 3DCityDB v5 OGC CityGML MCP server PostgreSQL / PostGIS Docker ready

Install from PyPI Read the paper Report an issue

A Model Context Protocol (MCP) server giving AI assistants direct, natural language access to semantic 3D city models in CityGML managed within a 3DCityDB v5 geodatabase.

It dynamically resolves CityGML object classes, properties, codelists, and generic attributes from the database so the AI can answer both spatial and semantic queries stated in natural language, write and execute SQL queries, and reason about CityGML data — without any manual prompt engineering. By including the MCP server in agentic coding environments, it becomes easy to create software that can read and write complex structured 3D city models compliant to the OGC CityGML standard (and using 3DCityDB V5 as the data repository).

Furthermore, a Chat Assistant is included offering a simple GUI for interactive query asking, reasoning, and answering. It is an agentic AI tool based on LangChain utilising the ReAct pattern for carrying out multi-step reasoning and automated error corrections. The Chat Assistant currently can be configured to work with OpenAI and Anthropic commercial LLMs, any OpenAI-compatible endpoint, as well as with locally running Ollama LLMs. For example, when using the qwen3.8:27b LLM running in Ollama, the Chat Assistant is capable of performing very complex analyses on any kind of stored 3D city model, including multi-step SQL workflows with live reasoning traces, rendered diagrams, and PDF export.

The evaluation of the MCP Server for the accompanying paper was done using 100 queries of 4 different complexity levels. The complete list of the queries can be found here.


Features

  • Dynamic schema resolution — walks the CityGML class hierarchy to discover available object classes and their properties
  • Property filtering — only includes properties that actually exist in the database
  • Country-aware codelist resolution — qualified <namespace>:<Class>.<attribute> keys, country selected via the database EPSG code; only codes actually present in the data are exposed
  • Generic attribute enrichment — automatic categorical detection for generic attributes
  • Read-only query execution — run_query enforces SELECT-only; writes are blocked at the application layer and the database layer, where every pooled connection runs in a read-only transaction (default_transaction_read_only = on) so even a crafted CTE hiding a write is rejected by PostgreSQL
  • Prompt assembly — assemble_prompt orchestrates all tools into a complete system prompt in one call
  • Gradio chat UI — browser-based interface with multi-LLM support (Anthropic, OpenAI, Ollama), thinking-level control, live reasoning trace, mermaid/LaTeX rendering, and PDF export
  • Integrated 3D viewer (optional) — embeds the 3DCityDB Web Map Client into the chat UI (fullstack Docker mode, see Option 3b); the model is streamed as 3D Tiles generated on demand by a separate citydb-3dtiler container. Features from an answer are highlighted in 3D and the camera flies to them, and selected viewer objects are passed to the chatbot so questions can refer to them ("these buildings", "the selected ones")
  • CityGML 1.0-3.0/CityJSON/IFC import — one-click import via the Gradio UI (fullstack Docker mode only); .ifc files are converted to CityGML 3.0 first

Deployment Options

There are four ways to run the 3DCityDB MCP Server (Option 3b is a 3D-viewer variant of Option 3):

Option 1: PyPIOption 2: Docker BYODOption 3: Docker FullstackOption 3b: Fullstack + 3D Viewer
Best forClaude Code / Claude Desktop power usersExisting 3DCityDB instancesStarting from a .gml/.json/.ifc file3D exploration & highlighting of query results
RequiresPython 3.10+, running 3DCityDBDocker, running 3DCityDBDocker onlyDocker only (same as Option 3)
Gradio UINo (uses your AI client directly)Yes (localhost:7860)Yes (localhost:7860)Yes (localhost:7860) with embedded 3D viewer
CityGML/CityJSON/IFC importManualManualVia Gradio UIVia Gradio UI
DatabaseYour ownYour ownBundled (PostgreSQL + PostGIS + SFCGAL)Bundled (same as Option 3)

Option 1: PyPI Package

Install the MCP server as a Python package and connect it to Claude Code, Claude Desktop, or any MCP-compatible client.

Prerequisites

  • Python 3.10 or later
  • A running 3DCityDB v5 PostgreSQL instance with PostGIS

Installation

pip install 3dcitydb-mcp-server

Or install from source for development:

git clone https://github.com/tum-gis/3dcitydb-mcp-server.git
cd 3dcitydb-mcp-server
pip install -e .

Configuration

Copy the example environment file and edit it:

# Linux / macOS
cp .env.example .env

# Windows (PowerShell)
Copy-Item .env.example .env

Then fill in your connection details:

# 3DCityDB PostgreSQL connection
CITYDB_HOST=localhost
CITYDB_PORT=5432
CITYDB_NAME=citydb
CITYDB_USER=postgres
CITYDB_PASSWORD=your_password_here
CITYDB_SCHEMA=citydb

# Query behaviour (optional)
CATEGORICAL_THRESHOLD=20
SAMPLE_VALUES_COUNT=5

# LLM API keys (only needed for the LangChain agent CLI, not for Claude Code/Desktop)
ANTHROPIC_API_KEY=sk-ant-...
# OPENAI_API_KEY=sk-...
# OLLAMA_BASE_URL=http://localhost:11434

The server loads .env automatically by searching upward from the working directory.

Verify your installation

3dcitydb-doctor

Checks Python version, required packages, database connectivity, PostGIS/SFCGAL extensions, and the 3DCityDB v5 schema. Exits 0 if all critical checks pass.

From the directory containing your .env:

claude mcp add 3dcitydb -- 3dcitydb-mcp
claude

The MCP server starts automatically when you open a Claude session. Use /mcp inside the session to confirm it is connected.

Connect to Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "3dcitydb": {
      "command": "3dcitydb-mcp",
      "cwd": "/path/to/your/project"
    }
  }
}

Restart Claude Desktop. The MCP server will be listed in Settings → Developer → MCP Servers.

SSE transport (remote / production)

Run the server over HTTP for remote clients:

3dcitydb-mcp-sse --host 0.0.0.0 --port 8080
  • Clients connect via: http://your-server:8080/sse
  • Modern clients (e.g. VS Code) prefer the Streamable HTTP transport: http://your-server:8080/mcp
  • Health check: http://your-server:8080/health

LangChain agent CLI (optional)

A standalone CLI agent that uses the MCP tools directly:

3dcitydb-agent

Requires ANTHROPIC_API_KEY, OPENAI_API_KEY, or OLLAMA_BASE_URL in your .env.


Option 2: Docker — BYOD (Bring Your Own Database)

Run the Gradio chat UI as a Docker container, connected to your existing 3DCityDB instance.

Prerequisites

  • Docker with Compose (V2)
  • A running 3DCityDB v5 PostgreSQL instance accessible from the Docker host

⚠️ Spatial function support: The AI agent uses SFCGAL functions (CG_Volume, CG_3DArea, CG_MakeSolid) for geometry calculations. These require PostGIS to be compiled with SFCGAL support.

If your database lacks SFCGAL, volume and 3D area queries will fail silently or return errors. To get full spatial support, use Option 3 (Fullstack) instead — it ships a 3dcitydb-pg image with PostGIS + pre-patched SFCGAL already enabled. The SFCGAL patch circumvents the much too strict geometry planarity checks normally compiled into SFCGAL (checking for nano meter planarity), so working with 3D geometry works e.g. with volume calculation, 3D surface area calculation as well as 3D boolean operations.

You can verify SFCGAL availability on your instance with:

SELECT postgis_sfcgal_version();

If this returns an error, many spatial queries will not work.

Quick Start

# 1. Clone the repository (or just download docker-compose.byod.yml + .env.example)
git clone https://github.com/tum-gis/3dcitydb-mcp-server.git
cd 3dcitydb-mcp-server/production

# 2. Copy and edit the environment file
cp .env.example .env   # Linux / macOS
# Copy-Item .env.example .env   # Windows PowerShell

Edit .env with your database connection and at least one LLM API key:

# Your existing 3DCityDB instance
CITYDB_HOST=your-db-host
CITYDB_PORT=5432
CITYDB_NAME=citydb
CITYDB_USER=citydb
CITYDB_PASSWORD=your_password
CITYDB_SCHEMA=citydb

# At least one LLM provider (the UI auto-selects based on what is available)
ANTHROPIC_API_KEY=sk-ant-...
# OPENAI_API_KEY=sk-...
# OLLAMA_BASE_URL=http://host.docker.internal:11434
# 3. Pull the pre-built image and start (works on all platforms, no build needed)
docker compose -f docker-compose.byod.yml up -d

# 4. Open the UI
# http://localhost:7860

The pre-built image (ghcr.io/tum-gis/3dcitydb-mcp-agent:latest) is pulled automatically from the GitHub Container Registry on first run.

What it includes

  • Gradio chat UI — natural-language interface to your 3DCityDB
  • MCP server — spawned automatically as a subprocess inside the container
  • Auto provider detection — the UI selects Anthropic, OpenAI, or Ollama based on which keys are present in .env

Gradio UI overview

TabWhat it does
ChatSend natural-language questions; the agent writes and executes SQL automatically
MCP InspectorShows the MCP server's self-description and lists all active MCP tools
System PromptDisplays the full assembled system prompt sent to the LLM and offers Re-assemble system prompt to rebuild it from the current database contents — useful for debugging

While the agent is working, the chat bubble shows live status: Thinking… → Running query… → Interpreting results…. Right next to the chat log, the Agent activity panel streams the full ReAct trace — each thought, tool call, and observation — with per-step timing.

Chat settings (above the input field):

SettingOptionsDescription
Provider / APIanthropic / openai / ollamaAuto-selected from .env; can be overridden per session
Model(populated per provider)Dropdown with free-text entry possibility; Refresh models re-discovers models from the Ollama endpoint
Set temperaturecheckbox + value (0.0–1.0, default 0.1)Unchecked = provider default; enable to pin a fixed temperature
Thinkingoff / low / medium / high (+ max for OpenAI)Reasoning level for thinking-capable models. Ollama uses native think; OpenAI-compatible endpoints use reasoning_effort. Higher levels are slower but more thorough
Prompt modeauto / compact / fullauto picks compact for small local models; override for complex queries
Keep a distilled summary of how the answer was foundon / off (default on; Ollama + OpenAI)After each DB turn, distills the successful path (purpose + working SQL + short result) into a compact story carried into later turns. Adds one extra blocking generation per tool-using turn
Include all reasoning steps in contexton / off (default off; Ollama + OpenAI)Feeds each turn's full reasoning trace back into context on later turns. Off by default: the full trace consumes a large share of the context window, and the distilled summary (above) already carries the essential path
Context window (Ollama)8K / 32K / 64K / 128K / 256K (default 128K)Tokens available to the model; a smaller window uses less memory but is too small for complex queries

Rendering and export:

  • Mermaid diagrams are rendered inline in the chat, with a copy toolbar (SVG / PNG / source code) and a visible fallback box if a diagram fails to parse
  • Inline LaTeX is rendered in chat messages; the agent activity panel shows the unrendered source
  • PDF export (🖨 button next to the send button) prints the current conversation to a multi-page PDF via the browser's print dialog

Local (Ollama) model support

The Chat Assistant is deliberately built to work with small local models, not just commercial APIs:

  • Robust tool-call parser — local models frequently emit ReAct actions in non-standard formats (e.g. a full sentence as the action name, extra whitespace, missing arguments). The parser normalises these automatically and repairs common malformations instead of aborting the run.
  • Model profiling — a built-in registry (webui/model_profiles.py) classifies known models by empirically observed behaviour (works, sentence-as-tool, wrong-sql, thinking-then-empty, unknown). The class drives a warning line under the model dropdown (e.g. a model known to emit broken SQL is forced into full prompt mode with an explanatory note), so you get an honest assessment of what a model can do before the first query.
  • Endpoint & model discovery — the Refresh models button queries the Ollama endpoint and repopulates the model dropdown, including custom or fine-tuned models.
  • Native thinking — for Ollama models with a thinking capability (e.g. qwen3.8:27b, gpt-oss:20b), the Thinking dropdown controls the native think parameter; no prompt engineering needed. However, it seems that many Ollama models do not consider this parameter properly.

See production/docs/local-model-probing.md for the full probing methodology, the per-model results, and how to add a new model profile.

Building locally (optional)

If you want to build the image from source instead of pulling it:

# Linux / macOS
docker compose -f docker-compose.byod.yml up -d --build

# Windows — Docker BuildKit has a known ordering bug on Windows/NTFS.
# Disable it for local builds:
$env:DOCKER_BUILDKIT=0; docker compose -f docker-compose.byod.yml up -d --build

Windows note: The DOCKER_BUILDKIT=0 flag is only needed when building locally. Pulling the pre-built image (docker compose up -d without --build) works on Windows without any workaround.

Useful commands

# View logs
docker compose -f docker-compose.byod.yml logs -f

# Stop
docker compose -f docker-compose.byod.yml down

Option 3: Docker — Fullstack (Bundled PostgreSQL)

Run everything — PostgreSQL (with PostGIS and SFCGAL), the 3DCityDB schema, the MCP server, and the Gradio UI — in a single Docker Compose stack. No pre-existing database needed. If you also want to see the model in 3D, continue with Option 3b — the 3D viewer after the quick start below.

Prerequisites

  • Docker with Compose (V2)
  • A CityGML, CityJSON, or IFC file to import (optional — the database starts empty)

Quick Start

# 1. Clone the repository (or just download docker-compose.fullstack.yml + .env.example)
git clone https://github.com/tum-gis/3dcitydb-mcp-server.git
cd 3dcitydb-mcp-server/production

# 2. Copy and edit the environment file
cp .env.example .env   # Linux / macOS
# Copy-Item .env.example .env   # Windows PowerShell

Edit .env:

# PostgreSQL settings for the bundled database
POSTGRES_DB=citydb
POSTGRES_USER=citydb
POSTGRES_PASSWORD=citydb
SRID=25832          # choose the right EPSG code of the coordinate reference system for the dataset you want to import

# At least one LLM provider
ANTHROPIC_API_KEY=sk-ant-...
# OPENAI_API_KEY=sk-...
# OLLAMA_BASE_URL=http://host.docker.internal:11434
# 3. (Optional) Place your CityGML file in the data directory
mkdir -p data
cp /path/to/your/city.gml data/

# 4. Pull the pre-built image and start (works on all platforms, no build needed)
docker compose -f docker-compose.fullstack.yml up -d

# 5. Open the UI
# http://localhost:7860

Both images are pulled automatically from the GitHub Container Registry on first run. The first start takes ~60 seconds while PostgreSQL initialises.

Recommended: pre-pull the importer's helper images so the first import doesn't silently block for several minutes:

# citydb-tool (~350 MB) — used for every CityGML/CityJSON import
docker compose -f docker-compose.fullstack.yml --profile importer pull

# ifc-to-citygml3 (~2 GB) — only needed if you plan to import .ifc files
docker compose -f docker-compose.fullstack.yml --profile ifc pull

# citydb-3dtiler (~1.7 GB) — generates the 3D tiles for the optional 3D view (see below)
docker compose -f docker-compose.fullstack.yml -f docker-compose.fullstack.viz.yml --profile tiler pull

These are pulled on demand anyway on first use if you skip this step — it just moves the wait earlier and shows normal docker pull progress instead of Gradio's import log.

Option 3b: add the 3D viewer (optional)

Add the viewer to the fullstack stack by layering docker-compose.fullstack.viz.yml on top of it:

docker compose -f docker-compose.fullstack.yml -f docker-compose.fullstack.viz.yml up -d

The Chat tab then shows the 3DCityDB Web Map Client next to the chat. The client streams the model as 3D Tiles: they are generated on demand by the citydb-3dtiler container (ghcr.io/tum-gis/citydb-3dtiler), which the stack starts as a sibling of the agent and writes into a shared tiles_data volume served at /tiles. After an import, tick Generate 3D tiles after import, or press 🔄 Refresh 3D tiles above the viewer at any time (e.g. after editing the database directly). When an answer lists objectids, those features are highlighted and the camera flies to them; features without geometry of their own, such as a Room, are expanded to the surfaces they contain (see resolve_highlight_targets below).

Selecting objects for the chatbot. The viewer also supports selection: turn on the ON/OFF toggle in the viewer's selection panel and click features in the 3D scene — a click on a boundary surface, e.g. a wall, opens a small panel offering the picked surface, its containing feature such as the building, and its siblings, so you can add the level you actually mean (Add to selection). The selected features are listed in the panel (removable with −) and appear live as a 🖱 … features selected badge in the chat UI. Press ✓ Finish selecting when done. The chatbot receives the selection as a [VIEWER SELECTION] context block, so questions like "What is the total floor area of these buildings?" or "Wie alt sind die ausgewählten Objekte?" are scoped to exactly the selected objectids — no coordinate or address filtering needed.

The viewer is baked into the citydb-mcp-agent image, so until the published image is updated rebuild it locally with docker compose -f docker-compose.fullstack.yml build citydb-agent.

Building locally (optional)

# Linux / macOS
docker compose -f docker-compose.fullstack.yml up -d --build

# Windows — disable BuildKit to avoid a known NTFS ordering bug:
$env:DOCKER_BUILDKIT=0; docker compose -f docker-compose.fullstack.yml up -d --build

Windows note: Only needed when building locally with --build. The default docker compose up -d (pull from the GitHub Container Registry) works on Windows without any workaround.

Import CityGML/CityJSON/IFC

Once the UI is open:

  1. Go to the Import CityGML / CityJSON / IFC tab
  2. Click Refresh to see files in ./production/data/, or use Upload file to add one from your computer
  3. Select your file and click Import
  4. Watch the live log — the import runs using the Docker container ghcr.io/3dcitydb/citydb-tool. Note, this container is pulled automatically, if it is not available in your Docker environment so far. In this case, please be patient as it might take 30 seconds before the import process really starts.

The data directory is mounted at ./production/data/ on the host and /app/data/ inside the container.

Importing IFC files: selecting an .ifc file (or overriding the Format selector to ifc) reveals three conversion options before import:

OptionEffect
Georeference to Oktoberfest / MunichUse when the IFC model carries no real-world georeferencing of its own
Skip storeysDon't create Storey features from the IFC building structure
Group unrelated doors/windows into a dummy BuildingConstructiveElementFor IFC models where door/window openings aren't properly related to a wall

The file is first converted to CityGML 3.0 via ghcr.io/tum-gis/ifc-to-citygml3 (written next to the .ifc file as <name>.gml), then imported the same way as any other CityGML file. Like citydb-tool, this image (~2 GB) is pulled automatically on first use if not already present — see the pre-pull commands above to avoid that wait during the import itself.

Coordinate reference system

Set SRID to the EPSG code for your data before the first start. Common values:

RegionCRSSRID
Germany (UTM Zone 32N)ETRS89 / UTM Zone 32N25832
Germany (UTM Zone 33N)ETRS89 / UTM Zone 33N25833
USA (NAD83 / UTM Zone 14N)NAD8326914
Global (WGS84)WGS 844326

Note that you should name a 3D SRID, if available. However, often the CRS used in CityGML datasets use a compound CRS with separate SRIDs for planimetry and height, and for these combinations often no predefined SRIDs are defined in PostGIS. In this case, just name the SRID of the planimetric coordinates (2D), like in the examples shown above.

Useful commands

# View logs
docker compose -f docker-compose.fullstack.yml logs -f

# Stop (preserves database volume)
docker compose -f docker-compose.fullstack.yml down

# Stop and delete all data
docker compose -f docker-compose.fullstack.yml down -v

Configuration Reference

All options are set via environment variables (.env file or Docker Compose environment block).

Database connection

VariableDefaultDescription
CITYDB_HOSTlocalhostPostgreSQL host
CITYDB_PORT5432PostgreSQL port
CITYDB_NAMEcitydbDatabase name
CITYDB_USERcitydbDatabase user
CITYDB_PASSWORD(required)Database password
CITYDB_SCHEMAcitydb3DCityDB schema name
DATABASE_URL(auto-built)Full PostgreSQL URL (overrides individual vars)

Fullstack only

VariableDefaultDescription
POSTGRES_DBcitydbDatabase name for bundled PostgreSQL
POSTGRES_USERcitydbDatabase user for bundled PostgreSQL
POSTGRES_PASSWORDcitydbDatabase password for bundled PostgreSQL
SRID25832EPSG code for the 3DCityDB spatial reference
POSTGIS_SFCGALtrueEnable SFCGAL extension (required for, e.g., CG_Volume, CG_3DArea)

3D viewer (only used when ENABLE_VIZ=true)

Default terrain and base map for the 3D view. Both default to Germany-wide, freely licensed sources (basemap.de) and are applied unconditionally — not gated to one region's SRID. Set a *_URL to an empty string to disable that layer, or override all of these for another country.

VariableDefaultDescription
VIEWER_TERRAIN_URLhttps://web3d.basemap.de/cesium/dgm5-mesh/Cesium quantized-mesh terrain endpoint
VIEWER_TERRAIN_NAMEDGM5 (basemap.de)Shown in the terrain picker
VIEWER_TERRAIN_TOOLTIPbasemap.de - DGM5 (Germany)Tooltip for the terrain picker entry
VIEWER_WMS_URLhttps://sgx.geodatenzentrum.de/wms_basemapde?OGC WMS endpoint added as a selectable base map
VIEWER_WMS_LAYERde_basemapde_web_raster_farbeWMS layer name
VIEWER_WMS_NAMEWMS basemap.deShown in the base-layer picker
VIEWER_WMS_TOOLTIP(C) basemap.deTooltip for the base-layer picker entry
VIEWER_WMS_PROXY_URL(empty)Only needed for a WMS without CORS — this image does not ship the vendored client's own /proxy/ endpoint; basemap.de sends Access-Control-Allow-Origin: * so the default (no proxy) works

LLM providers

At least one must be configured for the Docker variants. The Gradio UI auto-selects the provider based on what is available (Anthropic → OpenAI → Ollama, in that priority order).

VariableDescription
ANTHROPIC_API_KEYAnthropic API key (sk-ant-...)
OPENAI_API_KEYOpenAI API key (sk-...); when using Ollama via the OpenAI-compatible endpoint this value must be ollama
OPENAI_BASE_URLBase URL for the OpenAI provider. Leave empty for models offered by OpenAI. When using a locally running LLM or a remote LLM (not hosted by OpenAI) via its OpenAI-compatible API (e.g., provided by llama.cpp, vLLM, or Ollama) provide the corresponding endpoint URL (e.g. http://host.docker.internal:11434/v1/)
OLLAMA_BASE_URLOllama base URL (e.g. http://host.docker.internal:11434)
LITELLM_TIMEOUT120
LITELLM_TIMEOUT_LOCAL300

Query behaviour

VariableDefaultDescription
CATEGORICAL_THRESHOLD20Max distinct values for CityGML feature attributes before the attribute is considered not as an enumeration but as free text (only applied when no known codelist exists). The values of enumeration attributes are prefetched and added to the system prompt.
SAMPLE_VALUES_COUNT5Number of sample values shown per non-categorical attribute

Country-specific codelists

Code-type properties (core:Code) are resolved against country-specific codelist definitions. The country is auto-selected from the database EPSG code (database_srs), falling back to a generic DEFAULT block for unknown countries:

  • DE (EPSG 25831–25833, 31466–31469, 5650) — ALKIS/AdV: function (31 codes), usage, roofType (roof form)
  • JP (EPSG 6668–6692, 2443–2461) — J-PLATEAU/MLIT: class, roofType (29 codes), usage
  • DEFAULT — SIG3D-standard roofType

Each codelist is keyed by a qualified composite key of the form <namespace-alias>:<Classname>.<attribute> — e.g. bldg:Building.function. The alias comes from the 3DCityDB namespace.alias column and the class name is the concrete feature class. Lookup is exact (case-insensitive); there is no fallback to bare attribute names, so same-named attributes in different classes (e.g. bldg:Building.function vs. brid:Bridge.function) never collide. Only codes that actually occur in the imported data are exposed to the LLM, with no artificial cap.

Codelists are defined in src/citydb_mcp/tools/dynamic_tools.py (COUNTRY_CODELISTS). Adding a class or country later (e.g. bldg:BuildingPart.*) is a pure data change — no code changes required.

Ollama tuning (optional)

VariableDefaultDescription
OLLAMA_NUM_CTX131072Context window size (tokens) passed to the Ollama model
LOCAL_MAX_TOKENS16000Maximum tokens the local model may generate per response
OLLAMA_TIMEOUT300Timeout in seconds for Ollama requests
AGENT_MAX_ITERATIONS10Maximum ReAct tool-call iterations per question

WebUI story distillation (optional)

The "Keep a distilled summary" feature (chat WebUI) distills each turn's reasoning trace into a compact story. Both knobs apply to the distillation call regardless of provider (Ollama or OpenAI-compatible).

VariableDefaultDescription
STORY_TRANSCRIPT_MAX_CHARS30000Maximum characters of the reasoning transcript fed into the distillation prompt. Tail-preserving: if the trace is longer, the beginning is cut (early exploration) so the final successful queries are always kept
STORY_MAX_TOKENS2000Maximum tokens the story itself may contain (OpenAI-compatible provider; the Ollama path is fixed at 700)

Available MCP Tools

Static (cached per session)

ToolDescription
get_server_versionVersion of the 3DCityDB MCP server package
get_database_schema3DCityDB v5 table structures and foreign key relationships
get_query_guidelinesSQL best practices and optimisation tips for 3DCityDB

Dynamic (called at session start)

ToolDescription
scan_objectclassesDiscover available object classes with full CityGML hierarchy
resolve_properties(objectclass_id)Resolve properties with codelists for a given class
get_generic_attributesGeneric attributes with categorical detection
get_db_context_snapshotSRS, bounding box, feature counts, database statistics
get_lod_configAvailable Levels of Detail in the database
get_examples(objectclass_ids)SQL examples filtered to existing object classes
resolve_highlight_targets(objectids)Expands GML ids (e.g. a Room) to the geometry-owning features a 3D viewer can highlight, plus a WGS84 camera target
get_feature_tree(objectid)Containment tree around one picked feature: its ancestors, children, and siblings
describe_selection(objectids)Summarizes a multi-feature viewer selection: resolved features, per-class counts, tileable ids

Runtime (per query)

ToolDescription
run_query(sql)Execute read-only SQL (SELECT/WITH only) against 3DCityDB
get_session_contextSession management and state
update_module_selectionNarrow scope to specific object classes
get_historyConversation history for a session
submit_feedbackLog query feedback

Assembly

ToolDescription
assemble_promptOrchestrates all tools into a complete system prompt in one call

Note: The three 3D viewer tools — resolve_highlight_targets, get_feature_tree, and describe_selection — are only offered when the stack runs with the viewer enabled (ENABLE_VIZ=true, see Option 3b). In non-viz deployments they are omitted from the tool list entirely, so they never appear to clients such as the MCP Inspector.


Architecture

  Claude Code / Claude Desktop / any MCP client
                      │
               MCP Protocol (stdio / SSE)
                      │
       ┌──────────────┴──────────────┐
       │   3DCityDB MCP Server       │
       │   assemble_prompt()         │
       │   scan_objectclasses()      │
       │   run_query()               │
       └──────────────┬──────────────┘
                      │
                 3DCityDB v5
               (PostgreSQL + PostGIS)


  Browser ──► Gradio UI (port 7860)           [Docker variants only]
                      │
          ┌───────────┴──────────────────────────────┐
          │                                          │
   Anthropic / OpenAI                        Ollama (local)
   LiteLLM cloud backend                     LangChain ReAct (langchain-ollama)
   (incl. OpenAI-compatible endpoints         robust tool-call parser
    via OPENAI_BASE_URL)                      model profiles + native thinking
          │                                          │
          └──────────────────────┬───────────────────┘
                      │
               MCP Client (spawns citydb-mcp subprocess)
                      │
               3DCityDB MCP Server
                      │
                 3DCityDB v5

Citation

This work was developed at the Chair of Geoinformatics, Technical University of Munich (TUM). Main developer: Khaoula Kanna; additional testing and programming: Thomas H. Kolbe.

The accompanying paper, Enabling AI Agents for Semantic 3D City Models through Automated Domain Context Generation, was published in the ISPRS Annals of the Photogrammetry, Remote Sensing and Spatial Information Sciences (Volume XII-4/W1-2026, 21st ISPRS 3D GeoInfo Conference) and can be cited as:

@inproceedings{kanna2026enabling,
    author    = {Kanna, Khaoula and Kolbe, Thomas H.},
    title     = {Enabling AI Agents for Semantic 3D City Models through Automated Domain Context Generation},
    booktitle = {ISPRS 21st 3D GeoInfo Conference, ISPRS Ann. Photogramm. Remote Sens. Spatial Inf. Sci.},
    volume    = {XII-4/W1-2026},
    pages     = {211--218},
    year      = {2026},
    doi       = {10.5194/isprs-annals-XII-4-W1-2026-211-2026},
    url       = {https://isprs-annals.copernicus.org/articles/XII-4-W1-2026/211/2026/isprs-annals-XII-4-W1-2026-211-2026.html},
}

Ideas for Future Developments

We (the developers) have a number of ideas for future improvements and extensions of the ChatBot and the MCP server. They are described in these documents:


License

The 3DCityDB MCP server is distributed under the Apache License 2.0. See LICENCE for details.