Bike Router

Registre officiel Cartes, voyage et météo

Plan bike routes with a self-hosted OSM service: facts only, with weather and sights.

Installation

Ajoutez ceci à la configuration MCP de votre client (Claude Desktop : claude_desktop_config.json ; Cursor : .cursor/mcp.json ; VS Code et les autres utilisent la même forme « mcpServers »).

PYPI package bike-router
Claude Code
claude mcp add bike-router -e 'BIKE_ROUTER_API_URL=http://127.0.0.1:8000' -- uvx bike-router==0.3.0 mcp serve
Configuration JSON (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "bike-router": {
            "command": "uvx",
            "args": [
                "bike-router==0.3.0",
                "mcp",
                "serve"
            ],
            "env": {
                "BIKE_ROUTER_API_URL": "http://127.0.0.1:8000"
            }
        }
    }
}

Variables d'environnement

  • BIKE_ROUTER_API_URL obligatoire Address of the running pyBikeRouter API this server asks (http or https).

Documentation

Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.

Sommaire
  1. bike-routing-agent
  2. Easy install (no programming needed)
  3. Documentation
  4. Architecture
  5. Setup
  6. Running the API
  7. Command-line interface
  8. Running with Docker
  9. Bike types
  10. Make targets
  11. Linting and type checking
  12. Running tests
  13. Pre-commit hooks
  14. Known limitations
  15. Repository layout
  16. Licence
  17. Third-party licences

bike-routing-agent

An OSM-based bike-routing service: a typed, provider-neutral domain model, a full openrouteservice API client plus routing adapter, and a LangGraph workflow that turns a validated route request into an explainable, exportable cycling route.

The LLM (where used) may parse free text, ask clarification questions, and explain a chosen route. It never invents coordinates, geometry, elevation, or route suitability -- all of that comes from OSM-derived routing engines.

Easy install (no programming needed)

Download pybikerouter-easy-<version>.zip from the Releases page, unzip it, install Docker Desktop, and double-click the Start file for your computer. The first start asks one question -- where you ride -- downloads that area's map data, and opens the planner in your browser. Step by step, in plain words: docs/easy-install.md (and START-HERE.txt in the download).

Documentation

The full documentation suite lives in docs/:

DocumentContents
ArchitectureGraph nodes, state, branching, terminal statuses, error model
APIEndpoint contracts, response statuses, artifact serving
ConfigurationEvery environment variable, validation rules, deployment notes
ProvidersProtocols, ORS client/adapter behaviour, geocoders, BRouter/Valhalla adapters, adding a backend
Backend comparisonMeasured ORS vs BRouter behaviour per bike type, combining both engines
GeocodingNominatim vs Pelias, confidence/ambiguity semantics, tuning
Scoring & exportsScore math, uncertainty policy, explanation rules, GeoJSON/GPX
Plan from wordsDescribe a ride in a sentence; an optional LLM turns it into the structured request (never coordinates)
WeatherForecast along the route at your departure time: temperature, rain, wind vs. riding direction (free, keyless providers)
Self-hosted stackRun routing and/or geocoding locally from one OSM extract; hardware requirements and small-PC options
TestingTest layout, mocking conventions, live tests, CI
Web plannerPutting places into the trip; click a point on the map and choose where in the route it goes
iPhone / TailscaleUse the planner from an iPhone as a home-screen app over your tailnet
RoadmapPlanned work and explicit non-goals

Architecture

FastAPI
  |
  v
LangGraph RouteAgentState
  |
  +--> parse_request        (structured input first; LLM extraction is an injectable dependency)
  +--> validate_request      (Pydantic)
  +--> geocode_locations     (Nominatim by default, public or self-hosted; ambiguity -> clarification, never a guess)
  +--> route_with_provider   (openrouteservice; engine-neutral RoutingRequest in, RouteCandidate out)
  +--> score_candidates      (deterministic distance/elevation/warning scoring)
  +--> explain_and_export    (facts-only explanation + GeoJSON/GPX export)
  |
  v
JSON response + GeoJSON/GPX artifacts

Graph branches:

START -> parse_request
      -> validate_request -> (invalid | awaiting_clarification) -> END
      -> geocode_locations -> (awaiting_clarification | provider_failure) -> END
      -> route_with_provider -> (no_route | provider_failure) -> END
      -> score_candidates -> (no_route) -> END
      -> explain_and_export -> END

Every path ends with an explicit status; the graph never calls a routing provider unless validation and geocoding both succeeded, and it never returns without a terminal status.

Setup

Requires Python 3.11+ and Poetry.

poetry install
cp .env.example .env

poetry install creates an in-project virtualenv (.venv/, see poetry.toml) with both runtime and dev dependencies. Run any command inside it with poetry run <command>, or poetry shell to activate it directly.

Edit .env and set ORS_API_KEY to a valid openrouteservice API key.

Environment variables
VariablePurposeDefault
ORS_API_KEYopenrouteservice API key(required for live routing)
ORS_BASE_URLopenrouteservice base URLhttps://api.openrouteservice.org
ORS_TIMEOUT_SORS request timeout (seconds)10.0
ORS_MAX_RETRIESRetries for timeouts/5xx2
GEOCODER_PROVIDERGeocoder backend: nominatim (public or self-hosted) or pelias (needs a Pelias API behind ORS_BASE_URL, see docs/geocoding.md; refuses the public ORS URL at startup)nominatim
GEOCODER_BASE_URLNominatim base URL (ignored when GEOCODER_PROVIDER=pelias)https://nominatim.openstreetmap.org
GEOCODER_TIMEOUT_SGeocoder request timeout5.0
GEOCODER_USER_AGENTRequired by Nominatim's usage policybike-routing-agent/0.1
GEOCODER_CACHE_TTL_SIn-memory geocode cache TTL3600
GEOCODER_AMBIGUITY_MARGINConfidence gap below which top-2 results are "ambiguous"0.05
GEOCODER_MIN_CONFIDENCEMinimum confidence to auto-accept a top result0.3
VALHALLA_BASE_URLBase URL of a self-hosted Valhalla (meili) serverhttp://localhost:8002
VALHALLA_TIMEOUT_SPer-request Valhalla timeout (seconds)30.0
VALHALLA_MAX_RETRIESRetries for Valhalla timeouts/5xx1
EXPORT_DIRLocal directory for GeoJSON/GPX artifactsexports
DATABASE_URLPostgreSQL/PostGIS URL: records route history, enables /v1/history/* (docs/persistence.md)unset
ARTIFACT_BACKENDlocal or database (artifacts stored in PostgreSQL)local
LOG_LEVELLog levelINFO

Running the API

poetry run uvicorn bike_routing_agent.api:app --reload

The app also serves a built-in web UI (map-based route planner) at http://localhost:8000/, with the interactive API docs at /docs.

Command-line interface

Installing the package exposes a bike-router command (also runnable as python -m bike_routing_agent.cli) organised into subcommand groups:

bike-router route plan --origin "Berlin Hbf" --destination "Potsdam" \
    --bike-type gravel --target-distance-km 30 --output plan.json
bike-router serve start --port 8000
bike-router docker up            # docker compose -f docker/compose.yaml up -d --build
bike-router docker logs -f
bike-router config show          # effective settings, secrets redacted
bike-router config check         # validate settings against the environment
bike-router providers list       # provider names and bike-type -> ORS profile map
bike-router route plan ... --format text --gpx best.gpx   # alternatives, sight stops, exports
bike-router route show plan.json # a saved plan again; `route export` writes any alternative
bike-router poi along --route plan.json   # sights near a route, ranked by fame
bike-router brouter needed|info|download  # BRouter map tiles (asks before downloading)
bike-router history list|show|stats       # past plans (needs DATABASE_URL)
bike-router status show          # readiness of every component and the active features
bike-router chat start           # plan routes by chatting (docs/chat.md)

Everything the API and the web form do is available from the CLI -- see docs/cli.md.

route plan --text "50 km gravel loop from Braunschweig" uses the optional LLM parser (docs/llm-parser.md).

route plan accepts lat,lon pairs or place text for --origin, --destination and repeated --via waypoints, prints the JSON result and exits 0 on ready, 1 on clarification/provider failure, 2 on usage errors. With DATABASE_URL set it also records the plan in the route history (best effort; plan_id in the output, --no-record to skip), a forecast along the route at --departure-time (docs/weather.md), and bike-router db migrate / db status manage the schema (docs/persistence.md).

Running with Docker

All container assets live in docker/:

cp .env.example .env        # set ORS_API_KEY
docker compose -f docker/compose.yaml up --build

Open http://localhost:8000/ for the web UI. Route artifacts are stored in the named pybikerouter-exports volume. See docs/self-hosted.md for the optional local routing / geocoding stack -- including what it needs and how to run just one half on a small machine.

Example request
curl -X POST http://localhost:8000/v1/route/plan \
  -H 'Content-Type: application/json' \
  -d '{
    "origin": "Braunschweig Hauptbahnhof",
    "destination": "Wolfenbüttel",
    "constraints": {
      "bike_type": "gravel",
      "avoid_high_traffic_roads": true,
      "max_ascent_m": 500
    }
  }'
Example response
{
  "status": "ready",
  "route": {
    "provider": "ors",
    "provider_profile": "cycling-regular",
    "geometry_geojson": {"type": "LineString", "coordinates": []},
    "metrics": {
      "distance_m": 12500,
      "duration_s": 2700,
      "ascent_m": 85,
      "descent_m": 90,
      "surface_coverage": {},
      "unknown_surface_fraction": null
    },
    "score": 0.91,
    "score_breakdown": {"distance_fit": 1.0, "elevation_fit": 1.0, "warning_penalty": 0.0},
    "warnings": [],
    "provenance": {"provider": "ors", "profile": "cycling-regular"}
  },
  "candidates": [
    {"provider": "ors", "provider_profile": "cycling-regular", "score": 0.91},
    {"provider": "brouter", "provider_profile": "trekking", "score": 0.84}
  ],
  "explanation": "...",
  "artifacts": {
    "geojson_url": "/v1/routes/<id>.geojson",
    "gpx_url": "/v1/routes/<id>.gpx"
  },
  "clarification": [],
  "errors": []
}

If origin/destination are ambiguous or unresolved, status is awaiting_clarification and clarification lists the candidate places to choose from -- no route is generated from a guess.

Bike types

constraints.bike_type selects the routing profile per engine and defaults to gravel. Supported values: road, gravel, touring, mountain, city, ebike, commuter (fast, low-traffic), recumbent. ORS ships four cycling profiles, so e.g. ebike uses cycling-electric while commuter rides on cycling-regular; the self-hosted BRouter provider differentiates further -- gravel, touring, mountain, city, commuter and recumbent each get their own profile, while road and ebike share fastbike (BRouter has no e-assist cost model) (see docs/configuration.md).

Make targets

make lists the everyday commands; they use the project's .venv (or the active environment):

make install       # poetry install
make hooks         # the git hooks (commit: lint + types; push: the whole suite)
make run           # the API with auto-reload (HOST=127.0.0.1 PORT=8000)
make check         # lint + types + tests without coverage, stops at the first failure
make ci            # what CI runs: lint, types, tests with the 80 % coverage floor
make test-fast ARGS="tests/nodes -k geocode"     # a part of the suite (no coverage floor)
make deploy        # build and restart the local container (bike-router deploy up)

Linting and type checking

poetry run ruff check .
poetry run mypy src

Both run in CI on every push and pull request (see .github/workflows/ci.yml).

Running tests

poetry run pytest

Coverage is collected on every run and the suite fails below 80% (--cov-fail-under=80 in tool.pytest.ini_options.addopts; currently 97% total, every module ≥84%).

Live tests that hit real external services are marked @pytest.mark.live and excluded by default (see tool.pytest.ini_options.addopts in pyproject.toml). Run them explicitly with:

poetry run pytest -m live

Pre-commit hooks

Quality gates run automatically before each commit/push via pre-commit. Install both git hook stages once after poetry install:

poetry run pre-commit install --hook-type pre-commit --hook-type pre-push

On commit it runs whitespace/EOF/YAML/TOML/merge-conflict/large-file checks, ruff check --fix and mypy src; the full pytest suite runs only in the pre-push stage on git push (mirroring CI). Run the commit stage manually with poetry run pre-commit run --all-files and the pre-push stage with poetry run pre-commit run --hook-stage pre-push --all-files.

Known limitations

  • OSM tag completeness varies by region; surface/access metadata is frequently missing and is represented as uncertainty, not treated as favorable.
  • Routing engines (ORS, Valhalla, BRouter) map bike categories to their own profiles differently; the mapping in config.py is an approximation, especially for gravel riding.
  • Elevation data (ascent/descent) varies in accuracy between providers and is not always available.
  • No route produced by this service is a safety guarantee. Explanations use hedged language ("better aligned with available map metadata") rather than claims like "safe route".
  • BRouter and Valhalla are wired up end to end but only against self-hosted deployments (ROUTING_PROVIDER=brouter / valhalla / all, BRouter via the compose profile in docker/brouter/README.md), and openrouteservice remains the default engine.
  • The Pelias geocoder option needs a Pelias API served at <ORS_BASE_URL>/pelias/v1: neither the public api.openrouteservice.org nor a stock self-hosted ORS provides one, and the adapter has only been tested against mocks (see docs/geocoding.md). For a local geocoder use the self-hosted Nominatim.

Repository layout

src/bike_routing_agent/
├── api.py            FastAPI app (POST /v1/route/plan)
├── config.py          Settings + provider profile mapping
├── models.py          Pydantic v2 domain models
├── state.py            LangGraph RouteAgentState
├── graph.py            LangGraph wiring
├── errors.py           Structured provider/geocoding errors
├── providers/         GeocodeProvider / RoutingProvider adapters
│                      (ors_client.py: full ORS client -- directions, export,
│                       isochrones, matrix, snap, POIs, Vroom, elevation,
│                       Pelias geocoding, health; ors.py: the RoutingProvider;
│                       geocoder.py: Nominatim; pelias.py: Pelias on
│                       self-hosted ORS)
├── nodes/              One module per graph node
├── scoring/            Deterministic route scoring
├── exporters/          GeoJSON / GPX export
├── frontend/           Static web UI served at /
└── cli/                bike-router command line (route/serve/docker/config/providers)
docker/                 Dockerfile + compose.yaml
tests/
├── fixtures/           Sample provider responses
├── nodes/               One test module per graph node
├── providers/          Adapter unit tests (mocked HTTP)
├── graph/              Graph branch tests
├── exporters/          Export format tests
└── live/                Tests marked @pytest.mark.live
docs/                   Documentation suite (see docs/README.md)

Licence

MIT, see LICENSE.

Third-party licences

BRouter (MIT) and the OpenStreetMap data (ODbL) this project builds on are credited, with the full BRouter licence text, in THIRD_PARTY_LICENSES.md.

Questions fréquentes

Qu'est-ce que Bike Router ?

Plan bike routes with a self-hosted OSM service: facts only, with weather and sights.

Comment installer Bike Router ?

Il s'exécute sur votre machine. Dans Claude Code : claude mcp add bike-router -e 'BIKE_ROUTER_API_URL=http://127.0.0.1:8000' -- uvx bike-router==0.3.0 mcp serve. Pour les autres clients, collez la configuration JSON de cette page dans leurs réglages MCP.

Faut-il une clé API pour Bike Router ?

Oui. Il attend BIKE_ROUTER_API_URL, à renseigner dans sa configuration.

Bike Router est-il open source ?

Oui. Son code est sur GitHub sous licence MIT.

Qui maintient Bike Router ?

Il est publié par JonasHeinickeBio. AI.tn le référence sans le maintenir ni le recommander.

Issu du registre MCP officiel. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.