Skip to main content
Glama
borovkov-d

BuildWindow

by borovkov-d

BuildWindow

MCP-Labprojekt: Ein Agent plant Bauarbeiten anhand einer echten Wettervorhersage – mit zwei MCP-Servern.

Dies ist eine Kursaufgabe für die KSE AI Agentic School (eine MCP-Integrationsaufgabe): Baue einen eigenen MCP-Server für ein echtes Domänenproblem und verbinde ihn dann – neben einem bestehenden MCP-Server eines Drittanbieters – mit einem Agenten, der beide gemeinsam nutzt, um etwas zu erreichen, was ein einzelnes Tool nicht könnte.

Überblick

BuildWindow ist ein MCP-Labprojekt (Model Context Protocol), das um ein konkretes Planungsproblem herum aufgebaut ist: Gegeben sind eine Liste von Bauarbeiten mit Abhängigkeiten zwischen ihnen sowie eine Wettervorhersage für eine Stadt – gesucht ist ein Zeitplan, der beides berücksichtigt. Der Agent, der diese Planung durchführt, hält gleichzeitig zwei getrennte MCP-Verbindungen. Die erste ist der externe, auf Go basierende OpenWeather-MCP-Server (github.com/mschneider82/mcp-openweather), den der Agent bei jedem Lauf einmal aufruft, um aktuelle Live-Bedingungen und eine 5-Tage-Vorhersage für die angeforderte Stadt zu erhalten – das ist die einzige Stelle im gesamten Projekt, an der ein Netzwerkaufruf stattfindet. Die zweite ist der eigene BuildWindow-MCP-Server dieses Repositorys: ein lokaler, vollständig deterministischer Server ohne Netzwerkaufrufe zur Laufzeit, gestützt auf einen lokalen JSON-Datensatz von Bauarbeitstypen und deren Wettergrenzen. Er stellt vier Tools bereit, die die Regeln der Bau-Domäne abbilden (Wettertauglichkeits-Urteile, Schätzung der Aushärtungszeit und die Planung mehrerer Arbeiten).

Die beiden Server teilen sich die Verantwortlichkeiten bewusst nicht. OpenWeather-MCP ist die einzige Quelle für alles, was sich von Tag zu Tag ändert – das Wetter selbst. BuildWindow-MCP übernimmt dagegen alles, was eine feste Regel ist: welche Temperatur, welcher Wind, welche Luftfeuchtigkeit und welcher Niederschlag für einen bestimmten Arbeitstyp tolerierbar sind, wie lange Beton bei einer gegebenen Temperatur zum Aushärten braucht und wie mehrere abhängige Arbeiten in die frühesten nicht verbotenen Fenster einer Mehrtagevorhersage gelegt werden. Der BuildWindow-Server ist mit dem offiziellen Python-MCP-SDK (Paket mcp, v2.0.0+) erstellt und nutzt die Klasse MCPServer – beachte: In älteren SDK-Versionen hieß diese Klasse FastMCP und wurde mit SDK v2.0.0 in MCPServer umbenannt. Der Agent, der die beiden Verbindungen nutzt, ist mit dem Claude Agent SDK (Paket claude-agent-sdk auf PyPI) gebaut.

Der zeitplankritische OpenWeather-Aufruf wird bewusst nicht vom LLM durchgeführt. Die tatsächliche Ausgabe des Upstream-Tools (durch Lesen seines Quellcodes bestätigt – siehe docs/tool-contracts.md) ist ein reiner Textbericht und kein JSON; das einzige Signal, das es bei jedem Fehler liefert (ungültiger Schlüssel, unbekannte Stadt, nicht erreichbarer Anbieter), ist eine syntaktisch erfolgreiche, aber leere Antwort – es gibt keinen Fehlertext, auf den man reagieren könnte. Deshalb ruft agent/main.py es direkt über einen Low-Level-MCP-Client auf, parst es mit einer kleinen, unit-getesteten Funktion (agent/normalize.py) und startet erst danach eine LLM-Sitzung – das Modell erhält also bereits saubere Tageswerte, anstatt gebeten zu werden, einen rohen Anbietertext zu interpretieren. Die LLM-Sitzung ist mit beiden MCP-Servern verbunden (die Erkennung get_mcp_status() zeigt beide Verbindungen), und das Modell darf das Wetter-Tool durchaus auch selbst aufrufen (allowed_tools führt es explizit auf) – aber nur für einen Satz über die aktuellen Bedingungen in seinem Abschlussbericht, wie im System-Prompt festgelegt; die Tagesvorhersage, die plan_work_schedule steuert, stammt immer aus dem deterministischen Vorabruf vor Sitzungsbeginn, niemals aus dem eigenen Aufruf des Modells. Beide Server werden also tatsächlich in der eigenen Ablauf des Agents verwendet, nicht nur sichtbar gemacht.

engineer input (city + work list)
  -> agent/main.py calls OpenWeather MCP directly (not via the LLM) for
     the schedule-critical daily forecast
  -> agent/normalize.py parses the plain-text response into daily figures
  -> (if no usable forecast: report plainly, stop -- no LLM session started)
  -> LLM session starts, connected to BOTH MCP servers; may itself call
     the weather tool once for current-conditions color commentary only
  -> given the daily forecast + works as plain JSON (the only input that
     ever drives scheduling)
  -> BuildWindow MCP  (plan_work_schedule, validate_work_window,
     estimate_curing_time, ...)
  -> schedule + explanation

Related MCP server: Weather MCP Server

Voraussetzungen

  • Python 3.12+ – dieses Repository wurde gegen 3.12.3 gebaut und getestet.

  • uv – wird als Dependency-Manager für dieses Projekt verwendet.

  • Go 1.24+ – nur nötig, wenn du den OpenWeather-MCP-Server selbst bauen möchtest (hier installiert via winget install --id GoLang.Go, derzeit Go 1.26.7). Nicht erforderlich, um den BuildWindow-Server zu nutzen oder seine Tests auszuführen.

  • Ein OpenWeather-API-Schlüssel – nur für einen Live-Agent-Lauf gegen echtes Wetter nötig. Kostenlos erhältlich unter openweathermap.org/api.

Installation

Aus dem Stammverzeichnis des Repos:

uv sync

Dadurch wird ein .venv erstellt und sowohl die Laufzeit-Abhängigkeiten (mcp, pydantic, claude-agent-sdk, python-dotenv) als auch die Entwicklung-Abhängigkeiten (pytest, ruff, black) installiert.

Konfiguration

Kopiere die Beispiel-Umgebungsdatei und trag deinen Schlüssel ein:

Copy-Item .env.example .env

bash: cp .env.example .env

Bearbeite dann .env und setze OWM_API_KEY auf einen echten Schlüssel von openweathermap.org/api (kostenloser Tarif). .env ist gitignored – Sie wird nie eingecheckt.

agent/mcp_config.json ist die alleinige Quelle der Wahrheit für beide MCP-Server-Konfigurationen. Sein openweather-Eintrag referenziert ${OWM_API_KEY} als Platzhalter, den agent/main.py beim Start aus der Prozessumgebung ersetzt. Beachte: agent/main.py liest nicht selbst .env – seine main()-Methode ruft zuerst load_dotenv() aus python-dotenv auf, und erst dieser Awruf bringt die Werte.

Sein openweather.command-Feld ist selbst ein Platzhalter – ${MCP_OPENWEATHER_PATH}. agent/main.py löst ihn aus der UmgebungsvariablenMCP_OPENWEATHER_PATH auf, falls gesetzt; andernfalls fällt er auf den bloßen Befehl mcp-openweather zurück (dem PATH entsprechend). Setze MCP_OPENWEATHER_PATH in .env (siehe .env.example) auf den absoluten Pfad zur »-Binärdatei, falls du ihr Verzeichnis nicht zu PATH hinzufügen möchtest. Beides wurde heißt.

Den OpenWeather-MCP-Server bauen (nur nötig, wenn du einen Live-Lauf gegen echtes Wetter machen möchtest). Das sind die exakten Befehle zum Bau und zur Verifizierung in der eigenen Entwicklungsumgebung des Repos:

winget install --id GoLang.Go -e --accept-source-agreements --accept-package-agreements
# open a new shell so PATH picks up the Go toolchain, then:
go install github.com/mschneider82/mcp-openweather@main

Dies installiert in $(go env GOPATH)\bin\mcp-openweather.exe – unter Windows ist das normalerweise %USERPROFILE%\go\bin\mcp-openweather.exe. Wichtig: Der Go-MSI-Installer fügt die Go-Toolchain (C:\Program Files\Go\bin) zu PATH hinzu, jedoch nicht %USERPROFILE%\go\bin – den Ort, an dem go install die gebauten Binärdateien tatsächlich ablegt. Füge entweder dieses Verzeichnis selbst zu PATH hinzu oder setze MCP_OPENWEATHER_PATH auf den vollständigen Pfad der Binärdatei (siehe oben). Die Einstellung dieses Repos verwendet die zweite Variante.

Warum @main und nicht @latest: go install ...@latest löst auf den Tag v1.0.0 auf, einen echten Commit ("Fix #5") hinter der main-Branch des Repos. Beide wurden live gebaut und verglichen: v1.0.0 liest die optionalen units/lang-Argumente ohne Fallback, wenn sie vollständig weggelassen werden, sodass ein weggelassenes lang mit language unavailable scheitert – obwohl das Schema des Tools selbst einen Standardwert deklariert; der Commit "Fix #5" in main fügt defensive Fehlerbehandlung hinzu, und derselbe Aufruf gelingt. Das Vorhersage-Template ist otherwise otherwise between the versions otherwise identical (per source reading of the bestätigt) – ein Build von main fügt keine tagesweisen Wind-/Feuchte-/Niederschlagswerte hinzu, sondern behebt nur den Argument-Bug. agent/main.py übergibt unabhängig von der gewählten Version immer city, units="c" und lang="en" explizit, sodass dieser Bug durch dieses Projekt in jedem Fall nicht auftreten kann – aber main ist die robustere Binärdatei, auf die man sich verlassen kann, wenn man das Tool jemals anders aufruft.

Verwende nicht die in einigen Beispielen des Upstream-READMEs gezeigte Option -o mcp-weather – die erzeugt einen Binärnamen, der nicht zum eigenen Beispiel der README passt. Baue mit dem Standardnamen mcp-openweather.

MCP-Server ausführen

uv run python -m server.main

So lässt sich der BuildWindow-MCP-Server über stdio aus, unabhängig vom Agenten-Prozess – er kann vollständig für sich gestartet und geübt werden. Bei Erfolg gibt er genau diese Zeile auf stderr aus:

BuildWindow MCP server ready: 4 tools, 12 work types loaded

Agent ausführen

uv run python -m agent.main

Ohne Argumente wird eine eingebaute Demo-Stadt („Kyiv") und eine eingebaute Demo-Arbeitsliste verwendet: excavation, dann concrete_pour (abhängig davon), dann concrete_finishing (abhängend davon).

Beide lassen sich überschreiben:

uv run python -m agent.main "CityName"
uv run python -m agent.main "CityName" '[{"work_code": "excavation", "duration_days": 1, "depends_on": []}]'

Das optionale zweite Argument ist ein JSON-Array von Arbeiten in derselben Form wie die Demo-Liste.

Replay-Modus--forecast-from-file <path> ist vollständig offline: Er ersetzt sowohl die Tagesvorhersage als auch die Notiz zu aktuellen Bedingungen durch Daten aus einer aufgezeichneten Datei – über dieselben deterministischen Parser (normalize_forecast, parse_current_conditions wie. auch ein Live-Aufruf. opOpenWeather ist in diesem Modus überhaupt nicht verbunden (per get_mcp_status() bestätigt – nur buildwindow ersetzt), ein Lauf braucht also kein Netzwerk und keinen API-Schlüssel – live verifiziert mit einem absichtlich unbrauchbar OWM_API_KEY und gleichzeitig unerreichbaren MCP_OPENWEATHER_PATH; der Lauf ist trotzdem normal durchgelaufen.

uv run python -m agent.main "Longyearbyen" '[{"work_code": "excavation", "duration_days": 1, "depends_on": []}, {"work_code": "exterior_painting", "duration_days": 2, "depends_on": []}]' --forecast-from-file fixtures/weather_longyearbyen.txt

fixtures/weather_kyiv.txt und fixtures/weather_longyearbyen.txt sind reale, live aufgezeichnete Antworten aus diesem Projekt (jeweils auf, 3 volle echte Kalendertage gekürzt, beide ohne Schlüssel) – keine synthetischen oder erfundenen Beispiele. Nützlich, wenn sich das echte Wetter in einer Demo-Stadt zwischenzeitlich geändert hat oder wenn zum Demo-Zeitpunkt gar kein Netz verfügbar ist.

Ein voller Live-Lauf gegen echtes Wetter erfordert einen ich wirklich gültigen OWM_API_KEY (siehe Konfiguration oben) – in der eigenen Entwicklungsumgebung dieses Repos bestätigt: uv run python -m agent.main "Kyiv" erzeugt einen echten Zeitplan aus einer echten Vorhersage, und uv run python -m agent.main "Longyearbyen" '[...]' zeigt echtes Wetter, das tatsächlich eine Neuplanung erzwingt (siehe docs/demo-checklist.md Schritt 4). Ohne funktionierenden Schlüssel ruft agent/main.py die Vorhersage direkt ab (nicht über das LLM), bekommt ein leeres Ergebnis, gibt Forecast unavailable for '<city>' (...) aus und beendet sich, bevor eine LLM-Sitzung startet – kein verschwendeter Modell-Arm, kein Platz-Betrug. Das wurde mit drei realen Fehlerarten verifiziert: Die Binärdatei mcp-openweather überhaupt nicht erreichbar, ein ungültiger OWM_API_KEY und ungültigen Stadtname – Die letzen beiden sind über das Upstream-Tool tatsächlich nicht unterscheidbar (siehe docs/Tool- contracts.md), und beide wurden bestätigt, dass sie auf , dieselbe saubere Weise scheitern, selbst wenn in derselben Umgebung ein nachweislich gültiger Schlüssel aktiv ist.

OpenWeather-Rate-Grenzen: Ein erfolgreicher Live-Lauf erzeugt genau zwei Aufrufe an das weather-Tool (live durch Nachzählen bestätigt) – den deterministischen Vorabruf vor der Sitzung und dazu den einzigen Aufrufs des Modells zu den aktuellen Bedingungen (siehe oben). Ein fehlerhafter Live-Lauf (keine brauchbare Vorhersage) erzeugt genau einen, da die LLM- Sitzung nie startet. Der Replay-Modus (--forecast-from-file) erzeugt null – die Tagesvorhersage und der Hinweis auf aktuelle Bedingungen kommen aus der aufgezeichneten Datei, und openweather ist in diesem Modus gar nicht verbunden (live bestätigt: get_mcp_status() zeigt nur buildwindow). Der kostenlose Tarif von OpenWeather ist mit 60 Aufrufen pro Minute und 1.000.000 Aufrufen pro Monat dokumentiert – eine angemessene Linie sollte für manuelle Demo-Läufe reichen; dieses Projekt hat den veröffentlichten Wert nicht selbst getestet.

Projektstruktur

.
├── README.md, DECISIONS.md, pyproject.toml, uv.lock, .env.example, .gitignore
├── docs/
│   ├── tool-contracts.md
│   ├── design-rationale.md
│   └── demo-checklist.md
├── scripts/
│   └── list_tools.py      # proves both MCP connections discover fine offline
├── server/
│   ├── main.py            # MCP server entry point, registers the 4 tools
│   ├── schemas.py         # Pydantic input/output models
│   ├── rules.py           # deterministic verdict/curing/planner logic
│   ├── dataset.py         # loads and validates work_types.json
│   ├── errors.py          # domain exceptions and error codes
│   └── data/work_types.json
├── agent/
│   ├── main.py             # agent entry point (Claude Agent SDK)
│   ├── normalize.py        # deterministic OpenWeather text -> daily figures
│   └── mcp_config.json     # config for both MCP servers
├── fixtures/
│   ├── weather_kyiv.txt      # real captured response, for --forecast-from-file
│   └── weather_longyearbyen.txt  # real captured response, for --forecast-from-file
└── tests/
    ├── conftest.py
    ├── test_dataset.py, test_lookup.py, test_validate.py
    ├── test_curing.py, test_planner.py, test_errors.py
    └── test_normalization.py

Tool-Übersicht

Tool

Beschreibung

lookup_work_requirements

Ruft die Wettergrenzen eines Arbeitstyps – oder einer gesamten Kategorie – ab.

validate_work_window

Prüft einen Arbeitstyp gegen einen einzelnen Tag Wetter und liefert ein detaillierendes "Urteil zurück.

estimate_curing_time

Schätzt, wann ein aushärtender Arbeitstyp fertig sein wird – gegeben eine Sequenz von Tagesstempeln.

plan_work_schedule

Plant mehrere arbeiten mit Abhängigkeiten in einer einzigen "Mehrtagesvorhersage"-Ausgabe.

Die vollständige Toolverträge – exakte JSON-Schemas und echte aufgezeichnete Beispiele für jedes Tool, einschließlich des externen weather-Tools in diesem Projekt – stehen in docs/tool-contracts.md.

Testing

uv run pytest -v
uv run ruff check .
uv run black --check .

Alle drei laufen derzeit sauber in diesem Repository durch: 51 Tests bestehen (die 38 spezifikationsgeforderten Fälle, einige ergänzende Assertions und 8 Tests für das Wetter-Parsing-Modul agent/normalize.py, einschließlich der beiden Fälle mit echten Fixtures und der beiden Fälle für aktuelle Bedingungen), und sowohl ruff als auch black melden keine Probleme.

Einschränkungen

Die vollständige Begründung für jede dieser Einschränkungen findet sich in docs/design-rationale.md – diese Liste ist bewusst kurz gehalten:

  • Die Datensatz-Schwellenwerte sind illustrativ und nicht aus echten ДБН/ДСТУ- Normen abgeleitet.

  • Der Planer hat keine Ressourcen-/Crew-Beschränkungen – Arbeiten können sich zeitlich überschneiden.

  • Der reale Planungshorizont ist durch den OpenWeather- Anbieter auf 5 Tage begrenzt.

  • Die Aushärtezeit verwendet ein vereinfachtes Nurse-Saul-Reifemodell.

  • Eine Arbeit belegt einen durchgehenden Block – es gibt keine geteilte Terminplanung.

  • Das weather-Tool des OpenWeather-MCP-Servers (durch Lesen seines Quellcodes bestätigt, nicht angenommen) stellt nur die Temperatur pro 3-Stunden-Vorhersageeintrag bereit – Windgeschwindigkeit und Luftfeuchtigkeit sind nur in einer einzigen Momentaufnahme der aktuellen Bedingungen verfügbar, die hier als Konstante über jeden Vorhersagetag angewendet wird, und Niederschlag wird überhaupt nicht bereitgestellt, sodass precipitation_mm über diese Integration immer 0.0 ist. Das bedeutet, dass die Niederschlagsregel von BuildWindow (eine Arbeit mit precipitation_allowed=false erhält eine harte Verletzung, wenn precipitation_mm > 0 ist) bei einem Live-Lauf über diese Integration tatsächlich nie ausgelöst werden kann – es ist echter, korrekter Code, der durch Unit- Tests gegen konstruierte Daten abgedeckt ist (tests/test_validate.py, Spezifikationsfälle #16-17), aber nicht etwas, das eine Live-Demo zeigen kann, da es keinen Live- Pfad zu Niederschlagswerten ungleich null gibt. Dieses Projekt simuliert oder injiziert keine gefälschten Regendaten, um diese Demo zu erzeugen. Dasselbe Upstream-Tool kann außerdem einen ungültigen API-Schlüssel nicht von einer unbekannten Stadt von einem nicht erreichbaren Anbieter unterscheiden – alle drei Fälle kommen als dieselbe syntaktisch erfolgreiche, aber leere Antwort zurück, weshalb agent/main.py für jeden dieser drei Fälle nur „keine Vorhersage verfügbar" melden kann, nicht eine spezifische Ursache. Siehe docs/tool-contracts.md für die vollständigen, quellverifizierten Details.

  • Ein wirklich gültiger OWM_API_KEY ist nun nachweislich funktionsfähig: Ein vollständiger Live- Lauf gegen echtes Kiewer Wetter erzeugt einen echten Zeitplan von Anfang bis Ende, und eine echte Kaltwetterstadt (Longyearbyen) wurde gefunden, in der die Live- Vorhersage tatsächlich eine Arbeit als unschedulable erzwingt und validate_work_window mit echten Zahlen ausgelöst wird – siehe docs/demo-checklist.md Schritt 4. Alles, was in dieser README beschrieben wird, wurde nun gegen einen echten, funktionierenden Schlüssel verifiziert, nicht nur gegen einen fehlenden; siehe DECISIONS.md für das, was dieser Live-Lauf gegen echte Daten im Code geändert hat und was nicht.

Dokumentation

  • docs/tool-contracts.md – die exakten JSON- Schema-Verträge für alle vier BuildWindow-Tools und für das externe OpenWeather-weather-Tool, wie es von diesem Projekt verwendet wird, jeweils mit einem echten erfassten Beispiel.

  • docs/design-rationale.md – warum jedes Tool existiert, wie die Tool-Menge auf den Workflow abgebildet wird, die Grenzen zwischen Komponenten, die eingegangenen Kompromisse und die Einschränkungen des Projekts im Detail.

  • docs/demo-checklist.md – eine Schritt-für-Schritt- Checkliste für die Durchführung einer Live-Demo des Projekts.

  • DECISIONS.md – ein datiertes Protokoll der Implementierungs- entscheidungen, jeweils mit Begründung und der abgelehnten Alternative.

Install Server
F
license - not found
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    Enables users to get weather alerts and forecasts through natural language interactions. Provides real-time weather information and alert notifications via MCP tools.
    2
  • A
    license
    Not graded
    quality
    D
    maintenance
    Global weather intelligence for AI assistants providing 10 weather tools — forecasts, historical data, air quality, marine, geocoding, elevation, and climate projections at 1km resolution with 80+ years of archive.
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes Swiss weather forecast data as MCP tools, including rainfall, sunshine, temperature, wind, and more, with local caching.
    1
    Apache 2.0

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/borovkov-d/buildwindow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server