AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Sms

mcp-markuman-sms · by markuman

simple mbtiles server

— No reviews yet
0 installs
30 views
0.0% view→install

Install

$ agentstack add mcp-markuman-sms

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ● Network access Used
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-markuman-sms)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 17d ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Sms? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

🗺️ OSM - self host the entire planet 🌎 in ~30 minutes 🚀

simple mbtiles server

SETUP - TL;DR
mkdir osm
wget --continue -O osm/planet.mbtiles https://nx93856.your-storageshare.de/public.php/dav/files/apP4zEacCwqPfWd/planet.mbtiles
wget --continue -O osm/contours.mbtiles https://nx93856.your-storageshare.de/public.php/dav/files/apP4zEacCwqPfWd/contours_90m-Z11-Z13_30m-Z14.mbtiles
podman run -ti --rm -p 9000:9000 --name sms -v "$(pwd)/osm/:/data/" registry.gitlab.com/markuman/sms:latest
firefox http://localhost:9000

requirements:

  • podman (or docker)
  • 100 GB storage is required (1 core and 512MB memory are sufficient)
  • optional 315 GB storage is required for contour lines

notes:

  • "~30 minutes" depends on your bandwidth ...and the hidrive performance of ionos.

credits

  • https://github.com/onthegomap/planetiler is used to generate the planet.mbtiles file
  • https://github.com/uktrade/mbtiles-s3-server is the origin code-base of my sms project

Usage

nextcloud GpxPod

  1. Deploy the container/service behind a webproxy (caddy, nginx, traefik,...you name it.) to get a valid SSL certificate.
  2. Goto GpxPod Settings -> Tile Servers
  • Type: Vector
  • Server address: https:///v1/styles/osm-bright-gl-style@1.0.0/style.json?fonts=fonts-gl@1.0.0&tiles=mytiles@1.0.0

URL Parameter

You can add coordinates and Zoomlevel.

https://maps.osuv.de/?lat=48.1374&lng=11.5752&zoom=9

Reverse Geolocation / Photon Integration

  • sms supports photon reverse geocoding server.

Just set -e PHOTONSERVER="https://photon.osuv.de" for your sms Container.

This also enables the geocode and reverse_geocode MCP tools; without it they are not registered at all.

Two consumers, possibly two URLs

PHOTONSERVER is substituted into index.html at startup, so the browser talks to Photon directly with that URL — it has to be publicly reachable.

The MCP tools, on the other hand, call Photon from inside the container. If both run in podman/docker, the public hostname often resolves to a LAN address the container network cannot route to (split-horizon DNS), and you get Connection refused. Changing PHOTONSERVER is not the fix: that would break the frontend.

Use PHOTONSERVER_INTERNAL for the server-side path instead:

podman network create osm

podman run -d --rm --network osm --name photon \
  -e UPDATE_STRATEGY=DISABLED \
  -p 8888:2322 \
  -v /home/m/osm/photon/:/photon/data \
  docker.io/rtuszik/photon-docker:2.1.1

podman run -ti --rm --network osm --name sms \
  -p 9000:9000 \
  -e PHOTONSERVER="https://photon.osuv.de" \
  -e PHOTONSERVER_INTERNAL="http://photon:2322" \
  -v /home/m/osm/sms/:/data/ \
  localhost/sms:dev

If PHOTONSERVER_INTERNAL is unset, server-side calls fall back to PHOTONSERVER, which is the right thing when both are reachable from everywhere (single host, no container network in between).

Configuration

Tilesets are configured through numbered environment variable groups (MBTILES__1__*, MBTILES__2__*, …):

| Variable | Required | Description | |---|---|---| | PORT | yes | Listen port | | MBTILES__n__URL | yes | Path to the .mbtiles file | | MBTILES__n__IDENTIFIER | yes | Tileset name used in URLs | | MBTILES__n__VERSION | yes | Tileset version used in URLs | | MBTILES__n__MIN_ZOOM | yes | Minimum zoom level | | MBTILES__n__MAX_ZOOM | yes | Maximum zoom level | | HTTP_ACCESS_CONTROL_ALLOW_ORIGIN | no | CORS header value | | PHOTONSERVER | no | Public Photon base URL; enables geocoding and is embedded into the map UI | | PHOTONSERVER_INTERNAL | no | Photon URL used for server-side calls (MCP tools) when the public one is not reachable from inside the container | | TILE_CACHE_SIZE | no | Decoded road tiles kept in memory (default 2000; contour and POI caches get a quarter of that each) | | ROUTE_MAX_TILES | no | Corridor tile limit per segment (default 1200) | | ROUTE_MAX_CROW_KM | no | Straight-line limit per segment in km (default 50) | | GPX_DIR | no | Where generated GPX files are stored (default $TMPDIR/sms-gpx) | | GPX_MAX_FILES | no | Keep at most this many GPX files (default 200) | | GPX_TTL_SECONDS | no | Delete GPX files older than this (default 86400) |

HELP WANTED

  • Improve Style
  • special for Nextcloud GxpPod
  • provide more style? remove some?

planet.mbtiles

My provided planet.mbtiles is generated by using https://github.com/onthegomap/planetiler I just followed this tutorial: https://github.com/onthegomap/planetiler/blob/main/PLANET.md

contours.mbtiles

Contours will be displayed if a file called contours.mbtiles is places next to planet.mbtiles. There are two different contours files provided. 90m resolution in Zoom Level 11 to 14 and 90m resolution in Zoom Level 11 to 13 with 30m resolution in Zoom Level 14.

> GLO-30: "produced using Copernicus WorldDEM-30 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved" > GLO-90: "produced using Copernicus WorldDEM-90 © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved"

Core API

GET /

Serves the map UI frontend (index.html) with optional URL parameters for coordinates and zoom level.

URL Parameters:

  • lat — Latitude coordinate
  • lng — Longitude coordinate
  • zoom — Zoom level

Example: https://maps.osuv.de/?lat=48.1374&lng=11.5752&zoom=9

GET /v1/capabilities

Returns JSON capabilities indicating available optional features.

Response:

{
  "contours":  true,
  "routing":   true,
  "mcp":       true,
  "gpx":       true,
  "geocoding": false,
  "mcp_tools": ["search_poi", "plan_route", "export_gpx"],
  "difficulty_data": {
    "sac_scale": true, "via_ferrata_scale": true,
    "trail_visibility": true, "smoothness": true
  },
  "routing_limits": { "max_tiles": 1200, "max_crow_km": 50.0, "zoom": 14 },
  "tile_cache": {
    "roads":    { "entries": 812, "max_entries": 2000, "hits": 4210, "misses": 812 },
    "contours": { "entries": 240, "max_entries": 500,  "hits": 190,  "misses": 240 },
    "poi":      { "entries": 0,   "max_entries": 500,  "hits": 0,    "misses": 0 }
  }
}
  • contours — whether contours.mbtiles was detected and loaded
  • routing — always true; indicates the /v1/route/ endpoint is available
  • mcp — the MCP endpoint at POST /mcp is available
  • gpx — GPX export is available
  • geocoding — whether PHOTONSERVER is configured (enables the geocode

and reverse_geocode MCP tools)

  • mcp_tools — names of the registered MCP tools
  • difficulty_data — which hiking attributes the loaded tiles actually carry.

All false on stock OpenMapTiles tiles (see Hiking attributes below), and in that case a missing warning means missing data, not easy terrain. The flags flip to true as soon as a tile containing them has been decoded.

  • sac_scale_values — accepted values for max_sac_scale, easiest first
  • tile_cache — live hit/miss counters of the decoded-tile caches

GET /v1/poi/{identifier}@{version}?lat={lat}&lon={lon}&category={category}&radius={radius}

Search for Points of Interest (POI) within a radius and return GeoJSON features.

Path Parameters:

  • identifier — Tileset identifier
  • version — Tileset version

Query Parameters:

  • lat — Latitude coordinate (required)
  • lon — Longitude coordinate (required)
  • category — POI category (required): supermarket, pharmacy, hospital,

fuel, charging_station, alpine_hut, camp_site, shelter, drinking_water, cave, viewpoint, or emergency

drinking_water, cave, viewpoint and emergency need tiles built with the extended profile linked above — stock OpenMapTiles does not put springs, wells or cave entrances into the poi layer at all. Note that drinking_water includes springs and cattle troughs: that is a location, not a potability guarantee.

Note that alpine_hut covers only real mountain huts (alpine_hut, wilderness_hut, basic_hut). OpenMapTiles files bus stop shelters and public air-raid shelters under subclass=shelter, which around Garmisch is 113 of 122 hits and mostly unnamed — those live in the separate shelter category so they cannot bury the actual huts.

  • radius — Search radius in km, default 15, max 50 (optional)

Response: GeoJSON FeatureCollection of POIs, each with a properties.distance_km. Sorted by distance, but named POIs come first within the same ~500 m band — an unnamed hut slightly closer is less useful than a named one you can look up.

GET /v1/tiles/{identifier}@{version}/{z}/{x}/{y}.mvt

Fetch a tile in Mapbox Vector Tile (MVT) format.

Path Parameters:

  • identifier — Tileset identifier (e.g., from MBTILES__1__IDENTIFIER)
  • version — Tileset version (e.g., from MBTILES__1__VERSION)
  • z — Zoom level
  • x — Tile column coordinate
  • y — Tile row coordinate (converts from Web Mercator to TMS)

Special: contours@1.0.0 is auto-loaded if contours.mbtiles exists.

Gzip Negotiation: Returns gzip-compressed tiles if client sends Accept-Encoding: gzip.

Response: application/vnd.mapbox-vector-tile (MVT format)

GET /v1/styles/{identifier}@{version}/style.json

Returns a MapLibre GL style JSON with injected tile, font, and sprite URLs.

Path Parameters:

  • identifier — Style identifier (e.g., osm-bright-gl-style)
  • version — Style version (e.g., 1.0.0)

Query Parameters:

  • tiles — Tile source in format {tile_id}@{tile_version} (required)
  • fonts — Font source in format {font_id}@{font_version} (required)

Available Styles:

  • dark-matter-gl-style@1.0.0
  • fiord-color-gl-style@1.0.0
  • maptiler-3d-gl-style@1.0.0
  • maptiler-terrain-gl-style@1.0.0
  • maptiler-basic-gl-style@1.0.0
  • maptiler-toner-gl-style@1.0.0
  • osm-bright-gl-style@1.0.0
  • positron-gl-style@1.0.0
  • osuv-style@1.0.0

Response: application/json with complete style JSON including:

  • Tile source URL: /v1/tiles/{tiles}@{version}/{z}/{x}/{y}.mvt
  • Glyph URL: /v1/fonts/{fonts}@{version}/{fontstack}/{range}.pbf
  • Sprite URL: /v1/styles/{identifier}@{version}/sprite
  • Contour layers (if contours.mbtiles detected): contour-line and contour-label (visible when toggled in frontend)

GET /v1/styles/{identifier}@{version}/sprite.json

Returns the sprite JSON index file.

Path Parameters:

  • identifier — Style identifier
  • version — Style version

Response: application/json with sprite image references and bounds

GET /v1/styles/{identifier}@{version}/sprite@2x.json

Returns the high-resolution (2x) sprite JSON index file.

Path Parameters:

  • identifier — Style identifier
  • version — Style version

Response: application/json with high-res sprite references

GET /v1/styles/{identifier}@{version}/sprite.png

Returns the sprite image (1x resolution).

Path Parameters:

  • identifier — Style identifier
  • version — Style version

Response: image/png

GET /v1/styles/{identifier}@{version}/sprite@2x.png

Returns the sprite image (2x resolution for retina displays).

Path Parameters:

  • identifier — Style identifier
  • version — Style version

Response: image/png

GET /v1/fonts/{identifier}@{version}/{stack}/{range}.pbf

Returns merged glyph data in Protocol Buffer format for font rendering.

Path Parameters:

  • identifier — Font identifier (fonts-gl)
  • version — Font version (1.0.0)
  • stack — Comma-separated font stack (max 4 fonts, e.g., Noto Sans Regular,Noto Sans Bold)
  • range — Glyph range (e.g., 0-255)

Response: application/vnd.google.protobuf

  • Gzip-compressed if client sends Accept-Encoding: gzip
  • Merges glyphs from all fonts in stack, deduplicating by glyph ID

GET /v1/route/{identifier}@{version}?from={lat},{lon}&to={lat},{lon}&profile={profile}

Compute a walking or cycling route between two coordinates using the vector tile road network. No external routing engine required — routing is performed entirely server-side from the MBTiles data.

Path Parameters:

  • identifier — Tileset identifier (same as used for /v1/tiles/)
  • version — Tileset version

Query Parameters:

  • from — Start point as lat,lon (required)
  • to — End point as lat,lon (required)
  • profile — Routing profile: foot (default) or bike
  • buffer_km — Corridor half-width around the straight line (optional).

Defaults to 10 % of the segment length, at least ~3.3 km. Raise it when a detour around a lake or a closed area is needed.

  • elevation — true to add ascent_m/descent_m from contours.mbtiles

and switch the duration estimate to DIN 33466 (optional)

  • max_sac_scale — hardest SAC grade allowed: hiking (T1),

mountain_hiking (T2), demanding_mountain_hiking (T3), alpine_hiking (T4), demanding_alpine_hiking (T5), difficult_alpine_hiking (T6). Ways tagged above the limit are excluded from routing. Requires tiles with hiking attributes (optional)

  • allow_via_ferrata — false excludes ways tagged as via ferrata or with

fixed ladders (optional, default true)

  • prefer_routes — true prefers ways carrying a marked hiking route

(iwn/nwn/rwn/lwn), or a cycle route for profile=bike (optional)

  • follow_route — follow one named route, e.g. Malerweg or the ref E3

(optional)

Note these are independent axes: a way can be T6 scrambling without any ferrata tag, and a cabled route can be tagged T2. On a real Zugspitze test, excluding ferratas alone still produced a T6 route — for a safe tour set both. The response says so via terrain_warnings when only one is used.

Example:

GET /v1/route/mytiles@1.0.0?from=48.137,11.575&to=48.155,11.602&profile=foot

Response: GeoJSON Feature with LineString geometry

{
  "type": "Feature",
  "geometry": {
    "type": "LineString",
    "coordinates": [[11.575, 48.137], ...]
  },
  "properties": {
    "distance_km":  3.241,
    "duration_min": 43.2,
    "profile":      "foot",
    "tiles_loaded": 36,
    "buffer_tiles": 2.0,
    "nodes":        12847,
    "cache_hits":   30,
    "cache_misses": 6,
    "snap_start_m": 12.4,
    "snap_end_m":   31.9
  }
}

Profile weights (OpenMapTiles transportation layer class attribute):

| Road class | foot | bike | |---|---|---| | footway, path, pedestrian | preferred (1.0) | not passable | | cycleway | allowed (1.3) | preferred (1.0) | | track | 1.1 | 1.2 | | residential, living_street | 1.2–1.3 | 1.1 | | tertiary | 1.8 | 1.4 | | secondary | 2.5 | 1.6 | | primary | not passable | 2.5 | | motorway, trunk | not passable | not passable | | steps | 1.2 | not passable |

Duration estimate: 4.5 km/h for foot, 15 km/h for bike. With elevation=true and contours.mbtiles present, foot switches to a DIN 33466 / SAC estimate instead (300 m ascent or 500 m descent per hour, combined as max(horizontal, vertical) + min(horizontal, vertical) / 2).

Hiking attributes: sac_scale, via ferratas, surface

OpenMapTiles is a rendering schema. Its transportation layer whitelists the attributes it keeps, and sac_scale, trail_visibility and via_ferrata_scale are not on that list — they exist in the OSM source data but are dropped during tile generation. The practical consequence is severe: a router cannot tell a T1 stroll from a T5 scramble, and via ferratas appear as ordinary paths. A route from the Zugspitze to the Alpspitze happily runs over "Stopselzieher" and "Höllentalsteig", both cabled climbing routes with ladders and a glacier crossing.

sms therefore evaluates these attributes when they are present:

| Attribute | Used for | |---|---| | `sa

…

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.