GitHunt
IS

ishumilin/schwaizer-geo-mcp

Schwaizer Geo MCP — Swiss building registry, geospatial analytics, and company-location enrichment via geo.admin.ch

Schwaizer Logo

Schwaizer

MCP Server: Geo MCP

An unofficial MCP server for interacting with Swiss building and geographic data via geo.admin.ch.

This is a community project by Schwaizer and is not an official implementation by the Swiss government.


About Schwaizer

SHAPING SWITZERLAND'S AI FUTURE
Empowering Swiss businesses and society through responsible AI adoption.
Founded in 2025, Schwaizer is a non-profit organization dedicated to accelerating the responsible adoption of artificial intelligence across Switzerland.


Schwaizer Geo MCP Server

MCP (Model Context Protocol) server providing access to Swiss building registry and geography from geo.admin.ch, with optional company-location enrichment (via Zefix MCP).

Features

  • Building identification and search (BFS GWR: ch.bfs.gebaeude_wohnungs_register)
  • Coordinate conversions (WGS84 ↔ LV95, LV03 ↔ LV95)
  • Spatial analytics (radius searches, area statistics)
  • Company-location integration (companies registered at a building, company → buildings)
  • Unified missing data semantics (see Data Model)

Data Model & Missing Values (Important)

To ensure consistent downstream processing across tools, the server uses the following conventions:

  • Missing/unknown values are returned as null.
  • Coordinates are numeric (LV95 easting/northing). When coordinates are not available, fields are null.
  • Postal code is numeric when available, otherwise null.
  • Category, floors, dwellings, area, volume are numeric when present; otherwise null.
  • When upstream sources do not publish an attribute (e.g., some buildings have no ganzwhg/dwellings or gvol*/volume), the server returns null for that field (no estimation by default).

Example normalized building object snippet:

{
  "egid": "2372938",
  "address": "Bahnhofquai 15",
  "municipality": "Zürich",
  "canton": "ZH",
  "postalCode": 8001,
  "city": "Zürich",
  "buildingCategory": 1060,
  "constructionYear": 1894,
  "numberOfFloors": 8,
  "numberOfDwellings": null,
  "area": 1229,
  "volume": null,
  "coordinates": { "easting": 2683276.64, "northing": 1247892.09 }
}

Available Tools

Coordinate Tools

  1. wgs84_to_lv95
    Convert latitude/longitude (WGS84, EPSG:4326) → LV95 (EPSG:2056).

Arguments:

{ "latitude": 47.378177, "longitude": 8.540192 }

Returns:

{ "easting": 2683187.XX, "northing": 1248065.XX, "system": "LV95" }
  1. lv95_to_wgs84
    Convert LV95 → WGS84.

Arguments:

{ "easting": 2683188.02, "northing": 1248065.98 }

Returns:

{ "latitude": 47.378177, "longitude": 8.540192, "system": "WGS84" }
  1. lv03_to_lv95
    Grid‑based precise conversion (LV03 → LV95).

Arguments:

{ "easting": 683187.12, "northing": 248066.14 }
  1. lv95_to_lv03
    Grid‑based precise conversion (LV95 → LV03).

Arguments:

{ "easting": 2683188.02, "northing": 1248065.98 }

Building Tools (BFS GWR)

  1. identify_building
    Identify buildings at LV95 coordinates.

Arguments:

{
  "easting": 2683260.75,
  "northing": 1247919.125,
  "tolerance": 12,
  "lang": "en"
}

Returns: array of normalized building objects (see Data Model), with missing values = null.

  1. search_building
    Search a building by address or location name.

Arguments:

{
  "address": "Bahnhofplatz 1, 8001 Zürich",
  "canton": "ZH",
  "lang": "en"
}

Returns:

{
  "found": 1,
  "buildings": [ { "egid": "2372938", "... normalized fields ..." } ]
}
  1. get_building_details
    Get normalized building details by EGID.

Arguments:

{ "egid": "263016183", "lang": "en" }

Returns (example):

{
  "egid": "263016183",
  "address": "Hausmatt 508",
  "municipality": "Schlossrued",
  "canton": "AG",
  "buildingCategory": 1020,
  "constructionYear": 2016,
  "numberOfFloors": 4,
  "numberOfDwellings": 8,
  "area": 269,
  "volume": null,
  "coordinates": { "easting": 2649057.8, "northing": 1237971.7 }
}

Spatial Analytics

  1. find_buildings_in_radius
    Find buildings near a WGS84 point with optional filters.

Arguments:

{
  "lat": 47.378177,
  "lon": 8.540192,
  "radius_km": 0.5,
  "filters": {
    "constructionYearMin": 2000,
    "minFloors": 3
  }
}

Returns summary with buildings[], each entry includes address, EGID (if found), coordinates (WGS84+LV95), distance_m, and key attributes.

  1. get_area_building_statistics
    Aggregate statistics for a municipality, canton, or bbox.

Arguments:

{
  "bbox": {
    "minLat": 47.376,
    "minLon": 8.538,
    "maxLat": 47.380,
    "maxLon": 8.543
  }
}

Returns:

  • totalBuildings
  • constructionYearHistogram (decade bins)
  • buildingCategoryBreakdown (counts + percentages)
  • avgFloors
  • oldestBuilding, newestBuilding
  • residentialShare (computed based on category ranges; may include mixed use per BFS categories)

Company‑Location Tools (optional)

These tools enrich building data with company information. They can operate standalone using the public UID Webservice; for best results, run Schwaizer Zefix MCP alongside and configure its URL in your environment if applicable.

  1. get_building_companies
    List companies registered at a building address.

Arguments:

{ "egid": 2372938 }

Returns:

{
  "egid": 2372938,
  "address": { "street": "Bahnhofquai", "houseNumber": "15", "postalCode": 8001, "city": "Zürich" },
  "totalCompanies": 5,
  "companies": [
    { "uid": "CHE-106.018.335", "name": "Nextlane Switzerland AG", "legalForm": "Corporation (AG)", "status": "ACTIVE" }
    // ...
  ]
}
  1. find_company_buildings
    Resolve all buildings (HQ + branches) for a company UID.

Arguments:

{ "companyUid": "CHE-106.018.335" }

Returns:

{
  "company": { "uid": "CHE-106.018.335", "name": "...", "legalForm": "...", "status": "..." },
  "locations": [
    {
      "locationType": "soap",
      "address": "Bahnhofquai 11, 8001 Zürich",
      "egid": "2372938",
      "coordinates": { "wgs84": { "lat": 47.37660, "lon": 8.54133 }, "lv95": { "easting": 2683276.64, "northing": 1247892.09 } },
      "buildingDetails": { "constructionYear": 1894, "buildingCategory": 1060, "floors": 8 }
    }
  ]
}

Note: Debug details were intentionally removed from the response for cleaner outputs.

  1. enrich_building_with_companies
    Return building info plus detailed company data.

Arguments:

{ "egid": 2372938 }
  1. analyze_building_occupancy
    Heuristic classification of occupancy type using category + presence of companies.

Arguments:

{ "egid": 2372938 }

Returns:

{
  "egid": 2372938,
  "address": "Bahnhofquai 15, 8001 Zürich",
  "buildingCharacteristics": { "category": 1060, "categoryType": "residential", "isMixedUse": false, "floors": 8, "dwellings": null, "constructionYear": 1894 },
  "commercialOccupancy": { "totalCompanies": 5, "hasCommercialActivity": true, "companiesPerFloor": "0.63" },
  "occupancyType": "residential_with_commercial"
}

Installation

npm install

Quickstart with Schwaizer Zefix MCP (Option 1 — Sibling directories)

Schwaizer Geo MCP can enrich building data with company information via the Schwaizer Zefix MCP. The simplest setup is to clone both repositories side‑by‑side and use the local dependency already defined in package.json.

Directory layout:

parent/
  schwaizer-zefix-mcp/
  schwaizer-geo-mcp/

Why this works:

  • Schwaizer Geo MCP’s package.json references the Zefix MCP locally:
    "schwaizer-zefix-mcp": "file:../schwaizer-zefix-mcp"
  • Placing both repos as siblings allows npm install in Schwaizer Geo MCP to resolve the Zefix MCP locally without extra configuration.

Steps:

  1. Clone both repositories
# from your parent directory
git clone https://github.com/schwaizer/schwaizer-zefix-mcp.git
git clone https://github.com/schwaizer/schwaizer-geo-mcp.git
  1. Install & configure Zefix MCP first
cd schwaizer-zefix-mcp
cp .env.example .env
# Optional: only needed for Zefix REST enrichment (UID Public Services need no auth)
# ZEFIX_USERNAME=your_username
# ZEFIX_PASSWORD=your_password
# UID_PUBLIC_URL=https://www.uid-wse.admin.ch/V5.0/PublicServices.svc?wsdl
npm install
  1. Install Schwaizer Geo MCP
cd ../schwaizer-geo-mcp
cp .env.example .env
# Optional: adjust
# GEOADMIN_BASE_URL=https://api3.geo.admin.ch
# CACHE_TTL=300000
# LOG_LEVEL=info
npm install
  1. Run both servers (separate terminals)
# Terminal 1
cd schwaizer-zefix-mcp
npm start

# Terminal 2
cd schwaizer-geo-mcp
npm start
  1. Register both servers in your MCP‑compatible client (e.g., Claude Desktop)
  • Add one MCP server entry for Zefix
  • Add a second MCP server entry for Schwaizer Geo MCP
  • You’ll see both tool catalogs available. Schwaizer Geo MCP can enrich buildings with company info, and you can also call Zefix tools directly.

Validation

  • From schwaizer-geo-mcp, npm ls schwaizer-zefix-mcp should resolve to the sibling schwaizer-zefix-mcp folder.
  • If it doesn’t resolve (wrong paths/structure), use the same parent folder layout or consider npm link/workspaces.

Notes

  • Zefix credentials are optional: UID Public Services require no auth; Zefix REST enrichments (e.g., purpose/registration date) require credentials.
  • All missing values are returned as null (never "N/A"), consistent across both servers.

Configuration

Copy .env.example to .env and adjust:

# Optional: Custom base URL for geo.admin.ch API
GEOADMIN_BASE_URL=https://api3.geo.admin.ch

# Optional: Cache TTL in milliseconds (default: 5 minutes)
CACHE_TTL=300000

# Log level (trace|debug|info|warn|error|fatal), default: info
LOG_LEVEL=info

If you intend to integrate with Schwaizer Zefix MCP for richer company data, run it separately and configure a URL/environment according to that server’s README. This server primarily queries UID Public Services directly for addresses and enriches via Zefix REST when available.

Usage

Running the Server

npm start

Example Tool Calls

Identify building:

{
  "name": "identify_building",
  "arguments": {
    "easting": 2683260.75,
    "northing": 1247919.125,
    "tolerance": 12,
    "lang": "en"
  }
}

Search building:

{
  "name": "search_building",
  "arguments": {
    "address": "Bahnhofplatz 1, 8001 Zürich",
    "canton": "ZH",
    "lang": "en"
  }
}

Find buildings in radius:

{
  "name": "find_buildings_in_radius",
  "arguments": {
    "lat": 47.378177,
    "lon": 8.540192,
    "radius_km": 0.5,
    "filters": { "constructionYearMin": 2000, "minFloors": 3 }
  }
}

Get area stats:

{
  "name": "get_area_building_statistics",
  "arguments": {
    "bbox": { "minLat": 47.376, "minLon": 8.538, "maxLat": 47.380, "maxLon": 8.543 }
  }
}

Get building companies:

{
  "name": "get_building_companies",
  "arguments": { "egid": 2372938 }
}

API Documentation & Sources

Caching & Rate Limits

  • Default in-memory cache TTL is 5 minutes (CACHE_TTL).
  • geo.admin.ch endpoints are public and rate-limited; caching reduces repeated requests.
  • Cache resets on server restart.

Development

Project Structure

  schwaizer-geo-mcp/
├── src/
│   ├── index.js                  # MCP server entry point
│   ├── config.js                 # Configuration loader
│   ├── api/
│   │   ├── geoadmin-client.js    # geo.admin.ch client
│   │   └── zefix-clients.js      # UID/Zefix clients (integration helpers)
│   ├── tools/
│   │   ├── building-tools.js     # identify/search/details
│   │   ├── coordinate-tools.js   # conversions
│   │   ├── spatial-analytics.js  # radius & area stats
│   │   └── company-location.js   # company & occupancy tools
│   └── utils/
│       ├── logger.js             # logging
│       └── cache.js              # caching
├── tests/
│   ├── setup.js
│   ├── unit/
│   └── integration/
├── docs/
│   └── CHANGELOG.md
├── .env.example
├── eslint.config.js
├── vitest.config.js
├── package.json
└── README.md

Running Tests

npm test

Linting

npm run lint

License

MIT

Support