New Forecasts and four MCP tools for every station

Every dock in Chicago,
read every fifteen minutes.

DivvyAI archives Divvy's public station feeds byte for byte, models them in BigQuery with dbt, forecasts the next hour and answers your questions through read-only MCP tools.

Try
  • Cloud Scheduler
  • Cloud Run Jobs
  • Cloud Storage
  • BigQuery
  • dbt Core
  • Cloud Monitoring
  • Model Context Protocol
  • Python 3.12
  • GBFS 1.1
96

captures a day, one every fifteen minutes

2,074

stations in every capture

4,148

records loaded into BigQuery per run

0

duplicate rows after a replay

Product

One public feed in.
Clear answers out.

Four layers, each small enough to understand and each tested on its own.

A capture ZIP holding both feed responses and a manifest

01 · Archive

Original bytes, every time.

Each run fetches station_information and station_status, then writes both responses and a manifest to one ZIP in Cloud Storage before anything is parsed. Replays reuse the original load job, so nothing is counted twice.

dbt lineage from raw records to five views

02 · Model

Five dbt views, all tested.

Staging views type the raw JSON. A snapshot fact joins status to station details from the same capture. Marts roll the snapshots up to hourly availability and measure the time between captures.

A question answered by the rank_stations tool

03 · Ask

Questions, answered over MCP.

A read-only MCP server gives Claude, or any MCP client, four tools: availability, capture gaps, station rankings and forecasts. Every query is capped at 256 MiB billed and nothing is ever written.

Observed bikes and a dashed forecast with an interval

04 · Forecast

The next hour, before it happens.

A gradient-boosted model predicts bikes and docks one to three hours ahead, with intervals. It trains only on data that was available at prediction time and beats both baselines.

Pipeline

From feed to answer
in under a minute.

Select a step to see how it is configured. Packets show a capture moving through the system.

      Dashboard

      A station's day,
      four samples an hour.

      Hourly averages from the warehouse, straight from mart_station_hourly_availability. Hover the chart to read each hour.

      Station a3ad8072 46 docks · Wednesday, October 21, 2026
      Peak bikes–
      Fewest free docks–
      Hours averaging zero bikes–overnight, before the commute
      Samples–4 per hour, none missing

      Network

      Watch downtown fill up.

      Every dot is a station sized by capacity. Scrub through a weekday and watch bikes flow into the Loop for the morning commute, then back out at night.

      8:00 AM America/Chicago · weekday
      –with bikes and docks
      –out of bikes
      –out of docks

      Forecasts

      Know it's full
      before you ride there.

      One to three hours ahead for every station, with an interval and a probability of running out.

      Canal St & Adams St 47 docks · Monday 7:00 AM forecast
      P(full by 9:00) 0.81

      Model card

      Beats both baselines.

      Model, +1 hourMAE (bikes)Brier, empty
      Persistence (last value)3.920.112
      Same hour last week3.110.094
      DivvyAI gradient boosting2.180.067
      • Chronological split: six weeks train, one week validation, one untouched test week
      • Features use only receipts available at prediction time
      • Stale or missing future observations are unknown labels, never negatives

      Monday 8:00 AM watchlist

      Likely out of bikes

      1. Wells St & Concord Ln0.64
      2. Clark St & Elm St0.58
      3. Larrabee St & Division St0.52

      Likely out of docks

      1. Canal St & Adams St0.88
      2. Clinton St & Washington Blvd0.79
      3. Kingsbury St & Kinzie St0.61

      MCP

      Ask the warehouse.

      Connect the DivvyAI MCP server to Claude and ask in plain language. Each answer comes from a typed, read-only tool call you can inspect.

      DivvyAI read-only analytics
      stdio · 4 tools
      Ready
      get_station_availabilityread-only

      One station's hourly bike and dock averages with sample counts. Up to seven days, 168 hours at most.

      station_id, start, end

      find_capture_gapsread-only

      Per-feed intervals between observed captures since yesterday, largest first.

      min_minutes = 30, limit = 20

      rank_stationsread-only

      Rank stations by how often they ran empty or full at one Chicago hour, by weekday or weekend and area.

      metric, hour, limit, start_date, end_date, day_type, area

      forecast_availabilityread-only

      Next one to three hours of bikes and docks, with an 80% interval and the chance of running out.

      station_id, horizon_hours = 3

      Add it to Claude Code

      claude mcp add divvyai -- uv run --directory ai python server.py

      Reliability

      Built to notice
      when it misses.

      Every scheduled slot for the last seven days. One square per capture, one row per UTC day.

      671 of 672 slots captured October 18 – 24, 2026 · UTC
      Passed ·

      Receipt freshness

      Warns when the newest receipt is 30 minutes old and fails at 60. Runs hourly at minute 10.

      Passed ·

      Snapshot quality

      Keys present and unique per capture and station; bike and dock counts present and never negative.

      Verified · Oct 24

      Failure email

      Any failed collector or checker run opens an incident in Cloud Monitoring and emails the on-call inbox.

      0 duplicates

      Byte-exact replay

      Replays verify the saved archive and reuse the original load job instead of appending rows again.

      Incident · October 18, 2026 · Resolved

      One missed run, traced in an afternoon.

      The 16:45 UTC launch failed before the container started: Artifact Registry returned HTTP 401 during image import. Both feeds show a 29.98-minute gap from 16:31 to 17:01. The 17:00 run used the same image and loaded 4,148 records.

      Read the report

      Developers

      Run it yourself.

      Python 3.12 with uv, Ruff, mypy and pytest. The default suite uses synthetic fixtures, so it never calls a live feed or the cloud.

      • Locked dependencies with uv sync --locked
      • Separate uv project for dbt, with its own lockfile
      • No service-account keys anywhere: attached identities only
      
              

      See Chicago's docks
      right now.

      Live availability, tomorrow's crunch points and every capture since launch.

      A docked bike beside a station kiosk