ishumilin/schwaizer-geo-mcp
Schwaizer Geo MCP — Swiss building registry, geospatial analytics, and company-location enrichment via geo.admin.ch
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 orgvol*/volume), the server returnsnullfor 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
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" }lv95_to_wgs84
Convert LV95 → WGS84.
Arguments:
{ "easting": 2683188.02, "northing": 1248065.98 }Returns:
{ "latitude": 47.378177, "longitude": 8.540192, "system": "WGS84" }lv03_to_lv95
Grid‑based precise conversion (LV03 → LV95).
Arguments:
{ "easting": 683187.12, "northing": 248066.14 }lv95_to_lv03
Grid‑based precise conversion (LV95 → LV03).
Arguments:
{ "easting": 2683188.02, "northing": 1248065.98 }Building Tools (BFS GWR)
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.
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 ..." } ]
}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
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.
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:
totalBuildingsconstructionYearHistogram(decade bins)buildingCategoryBreakdown(counts + percentages)avgFloorsoldestBuilding,newestBuildingresidentialShare(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.
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" }
// ...
]
}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.
enrich_building_with_companies
Return building info plus detailed company data.
Arguments:
{ "egid": 2372938 }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 installQuickstart 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.jsonreferences the Zefix MCP locally:"schwaizer-zefix-mcp": "file:../schwaizer-zefix-mcp"
- Placing both repos as siblings allows
npm installin Schwaizer Geo MCP to resolve the Zefix MCP locally without extra configuration.
Steps:
- 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- 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- 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- Run both servers (separate terminals)
# Terminal 1
cd schwaizer-zefix-mcp
npm start
# Terminal 2
cd schwaizer-geo-mcp
npm start- 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-mcpshould resolve to the siblingschwaizer-zefix-mcpfolder. - 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=infoIf 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 startExample 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
- geo.admin.ch services:
- API overview: https://api3.geo.admin.ch/services/sdiservices.html
- Search API: https://api3.geo.admin.ch/rest/services/api/SearchServer
- MapServer Identify: https://api3.geo.admin.ch/rest/services/api/MapServer/identify
- Height services: https://api3.geo.admin.ch/rest/services/height
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 testLinting
npm run lintLicense
MIT
Support
- geo.admin.ch: https://api3.geo.admin.ch/
- This MCP Server: Open an issue on GitHub