Skip to main content
Glama
borovkov-d

BuildWindow

by borovkov-d

BuildWindow

BuildWindow is a deterministic engine for planning work that depends on the weather, served as an MCP (Model Context Protocol) server. You give it the weather limits for your activities: minimum and maximum temperature, maximum wind, maximum humidity, and whether precipitation is allowed. You also give it a daily forecast. Four tools then tell an AI agent whether each activity may go ahead on each day, which limit is breached and by how much, and the earliest schedule that respects both the weather and the dependencies between activities. The same tools work for any activity with a weather window, for example concrete pours, roofing, exterior painting, drone flights or crop spraying.

The verdict is computed by code, not by the model. Every verdict, schedule and curing estimate comes from pure functions in server/rules.py. The same limits and the same weather always give the same answer, down to the exact margin of each breach. The agent only passes data in and reports what comes back. It never decides for itself whether 4 m/s of wind is acceptable.

You bring the thresholds. The limits live in a JSON dataset that you point the server at with BUILDWINDOW_DATASET. A construction dataset with 12 work types is built in, but it is an illustrative example only, not a normative source. Its numbers are plausible, not taken from any standard. Use your own limits from your standards, product data sheets, equipment manuals or regulations.

your limits (JSON)  ─┐
                     ├─> BuildWindow MCP server ──> verdicts, violations, schedule
daily forecast  ─────┘   (deterministic, offline)
     ▲
any weather source: a weather MCP server, an API, a file, the user

Quick start

Requirements: uv, which fetches a suitable Python by itself, and git.

1. Start the server once by hand, in a terminal. On first use, uvx downloads the tagged release from GitHub and about 30 small packages (mcp, pydantic and their dependencies; the reference agent's Claude Agent SDK is not included). On a clean cache this took 34 seconds, and it takes longer on a slow connection or if uv must download Python first. Claude Code's connection check gives up after about 30 seconds. So if you skip this step, the first connection fails with Failed to connect (measured, see DECISIONS.md, phase 14).

uvx --from git+https://github.com/borovkov-d/buildwindow-mcp@v0.2.2 buildwindow-server

You will see download lines such as Downloading pydantic-core, Built buildwindow @ git+https://github.com/... and Installed 30 packages, then:

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

The server then waits silently on stdio for a client, which is normal. Press Ctrl+C. If the "ready" line never appears, the error above it says why (usually git missing or no network).

2. Register it with Claude Code. This is quick now, because everything is cached:

claude mcp add buildwindow -- uvx --from git+https://github.com/borovkov-d/buildwindow-mcp@v0.2.2 buildwindow-server

With your own limits:

claude mcp add buildwindow -e BUILDWINDOW_DATASET="/full/path/to/your_limits.json" -- uvx --from git+https://github.com/borovkov-d/buildwindow-mcp@v0.2.2 buildwindow-server

claude mcp get buildwindow should show Status: Connected. Leave out -e BUILDWINDOW_DATASET=… to start with the built-in example. Any other MCP client takes the same command (uvx, arguments --from git+https://github.com/borovkov-d/buildwindow-mcp@v0.2.2 buildwindow-server) and the same environment variable in its stdio server config.

3. Restart the Claude Code session so the tools appear.

To check which dataset is loaded, run step 1's command with BUILDWINDOW_DATASET="/full/path/to/your_limits.json" set. The ready line must end with your file's path, for example … 5 work types loaded from /full/path/to/your_limits.json.

The server makes no network calls of its own, reads only the one dataset file, and needs no API keys. uvx downloads it on first use, and on each later start contacts GitHub briefly to resolve the v0.2.2 tag. The server does not fetch weather itself; the agent skill has the agent obtain a forecast and always name its source.

From a clone (to edit the code)

Requirements: Python 3.12+ and uv.

git clone https://github.com/borovkov-d/buildwindow-mcp.git
cd buildwindow-mcp
uv sync
uv run python -m server.main

uv sync also installs the dev tools and the reference agent's dependencies (dependency groups dev and agent). To register a clone instead of the released package:

claude mcp add buildwindow -e BUILDWINDOW_DATASET="/full/path/to/your_limits.json" -- uv --directory "/full/path/to/buildwindow-mcp" run python -m server.main

Related MCP server: condensation-mcp

Bring your own dataset

Set BUILDWINDOW_DATASET to the path of a JSON file with this shape:

{
  "schema_version": "1.0",
  "work_types": [
    {
      "code": "crop_spraying",
      "name": "Crop spraying by drone",
      "category": "agriculture",
      "temp_min_c": 5,
      "temp_max_c": 28,
      "wind_max_ms": 4,
      "humidity_max_pct": 95,
      "precipitation_allowed": false,
      "base_curing_hours": null,
      "reference_note": "Product label section 4 and our spray-drift procedure."
    }
  ]
}

Field

Type and range

Meaning

code

snake_case string, unique

The work_code used in every tool call

name

string

Human-readable name

category

snake_case string

Group for lookup_work_requirements(category=…)

temp_min_c, temp_max_c

number, −30…50, min < max

Allowed air temperature band, °C

wind_max_ms

number, 0…40

Highest allowed wind speed, m/s

humidity_max_pct

integer, 0…100

Highest allowed relative humidity, %

precipitation_allowed

boolean

false: any precipitation is a hard violation

base_curing_hours

positive integer or null

Nominal curing time at 20 °C, for estimate_curing_time. null means no curing stage.

reference_note

string

Where the limits come from. It is shown to the user, so cite your source.

All fields are required (base_curing_hours may be null), and no extra fields are accepted.

  • Unset or empty BUILDWINDOW_DATASET → the built-in example (server/data/work_types.json).

  • Prefer an absolute path. A relative one is resolved against the server's working directory.

  • The file is read once at startup. Restart the server after changing it.

  • The tools' input schemas list the loaded dataset's codes and categories as an enum, so the agent sees your activities, not the example's.

An invalid file stops the server with every problem listed:

[dataset_error] Dataset file …/limits.json is invalid. 2 problems found:
  - work_types[0] (code 'windy_flight').wind_max_ms: Input should be less than or equal to 40 (got 55)
  - work_types[1] (code 'Bad Code').code: String should match pattern '^[a-z][a-z0-9_]*$' -- use lowercase snake_case such as 'survey_mapping' (got 'Bad Code')

A different domain, same engine: examples/drone_operations.json defines five drone missions: aerial photography, LiDAR survey, thermal inspection, crop spraying and parcel delivery. Each has its own wind, temperature, humidity and rain limits. Run the server on it with no code changes:

BUILDWINDOW_DATASET=examples/drone_operations.json uv run python -m server.main

These limits are illustrative too. Each record's reference_note says what to check them against.

Tools

Tool

Summary

lookup_work_requirements

Look up the limits of one work type, or of every work type in a category.

validate_work_window

Check one work type against one day of weather. Returns allowed, conditional or prohibited, with every breached limit, its value and its margin.

plan_work_schedule

Place several works, with dependencies, across a forecast of up to 14 days in one call. Works that can't be placed come back with a reason.

estimate_curing_time

For work types with a curing stage: when the work is actually ready, given the daily temperatures (Nurse-Saul maturity method).

A breach within a small tolerance band of the limit is reported as a warning, and the verdict is conditional. The bands are 2 °C, 1 m/s and 5 % humidity. A breach beyond the band is a violation, and the verdict is prohibited. Errors come back as [code] message (unknown_work_code, unknown_category, cyclic_dependency, no_curing_stage, …). For unknown values, the message lists the valid ones, so the agent can correct itself. lookup_work_requirements also reports whether the limits come from the built-in example or from your own file (dataset_is_builtin_example, dataset_file). Full contracts, with real captured examples, are in docs/tool-contracts.md.

Use as an agent skill

skill/buildwindow/ is a ready SKILL.md package. It tells an agent when to reach for these tools and how to use them. It covers the call order, how to read verdicts, how to react to each error, and the rule that verdicts are never the model's own judgement. It also says where the forecast comes from, in order: the user's own figures, a weather connector, the agent's web search, and finally asking the user. The agent always names the source, and it asks for a missing value rather than invent it or treat it as zero. Its references/dataset-format.md documents the dataset format for the agent. To install it, zip the skill/buildwindow folder for a skill marketplace, or copy it into your agent's skills directory. The skill expects the MCP server above to be registered.

Reference agent: OpenWeather + Claude Agent SDK

The repository also contains a complete example client in agent/. It connects to two MCP servers: BuildWindow, and the external Go-based OpenWeather MCP server (mschneider82/mcp-openweather). It then plans a list of works for a city. The agent is optional: the BuildWindow server does not depend on it, and you can feed the server forecasts from any other source.

It works with your own limits too. The BuildWindow server subprocess inherits BUILDWINDOW_DATASET from the agent's environment, so pass work codes from your dataset as the agent's work list.

Prerequisites (agent only)

  • Go 1.24+, only to build the OpenWeather MCP server yourself.

  • An OpenWeather API key, only for a live run against real weather. A free tier is available at openweathermap.org/api.

  • A logged-in Claude Code / Claude Agent SDK session: the agent drives the model through claude-agent-sdk.

Replay mode (--forecast-from-file, below) needs neither Go nor an API key.

How the agent uses the two servers

The schedule-critical OpenWeather call is deliberately not made by the LLM. The upstream tool's actual output (confirmed by reading its source — see docs/tool-contracts.md) is a plain-text report, not JSON, and the only signal it gives for any failure (bad key, unrecognized city, unreachable provider) is a syntactically successful but empty response — there is no error text to react to. So agent/main.py calls it directly via a low-level MCP client, parses it with a small, unit-tested function (agent/normalize.py), and only then starts an LLM session — handing the model already-clean daily figures instead of asking it to interpret raw provider text. The LLM session is connected to both MCP servers (get_mcp_status() discovery shows both connections), and the model genuinely is allowed to call the weather tool itself (allowed_tools lists it explicitly) — but only for one current- conditions sentence in its final report, per the system prompt; the daily forecast that drives plan_work_schedule always comes from the deterministic pre-session fetch, never from the model's own call.

user 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

Configuration

Copy the example environment file and fill in your key:

Copy-Item .env.example .env

bash: cp .env.example .env

Then edit .env and set OWM_API_KEY to a real key from openweathermap.org/api (free tier). .env is gitignored — it is never committed.

agent/mcp_config.json is the single source of truth for both MCP server configurations. Its openweather entry references ${OWM_API_KEY} as a placeholder, which agent/main.py substitutes from the process environment at startup. Note that agent/main.py does not read .env itself — its main() calls python-dotenv's load_dotenv() first, and that call is what actually makes the values in .env reach the process environment before the substitution happens.

Its openweather.command field is itself a placeholder, ${MCP_OPENWEATHER_PATH} — agent/main.py resolves it from the MCP_OPENWEATHER_PATH environment variable if set, or falls back to the bare command mcp-openweather (relying on PATH) if not. Set MCP_OPENWEATHER_PATH in .env (see .env.example) to the binary's absolute path if you don't want to add its directory to PATH — both were verified to work live.

Building the OpenWeather MCP server (only needed if you want a live run against real weather). These are the exact commands used to build and verify it during development:

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

This installs to $(go env GOPATH)\bin\mcp-openweather.exe — on Windows that's typically %USERPROFILE%\go\bin\mcp-openweather.exe. Important: the Go MSI installer adds the Go toolchain (C:\Program Files\Go\bin) to PATH, but does not add %USERPROFILE%\go\bin — where go install actually places built binaries. Either add that directory to PATH yourself, or set MCP_OPENWEATHER_PATH to the binary's full path (see above) — this repo's own setup uses the latter.

Why @main and not @latest: go install ...@latest resolves to tag v1.0.0, which is one real commit ("Fix #5") behind the repository's main branch. Both were built and compared live: v1.0.0 reads the optional units/lang arguments with no fallback when they're omitted entirely, so an omitted lang fails with language unavailable even though the tool's own schema declares a default; main's "Fix #5" commit adds defensive handling and the same call succeeds. The forecast template itself is otherwise identical between the two (confirmed by reading both versions' source) — building from main doesn't add per-day wind/humidity/precipitation, it only fixes the argument bug. agent/main.py always passes city, units="c", and lang="en" explicitly regardless, so this bug can't actually surface through this project either way — but main is the more robust binary to depend on if you ever call the tool a different way.

Do not use the -o mcp-weather flag shown in some of the upstream README's own examples — that produces a binary name inconsistent with its own config example. Build with the default name, mcp-openweather.

Running the agent

uv run python -m agent.main

With no arguments, this uses a default city ("Kyiv") and a default work list from the built-in example dataset: excavation, then concrete_pour (depending on it), then concrete_finishing (depending on that).

Both can be overridden:

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

The optional second argument is a JSON array of works in the same shape as the default list. With your own dataset, use its codes:

$env:BUILDWINDOW_DATASET = "examples/drone_operations.json"
uv run python -m agent.main "Longyearbyen" '[{"work_code": "lidar_survey", "duration_days": 1, "depends_on": []}, {"work_code": "crop_spraying", "duration_days": 2, "depends_on": []}]' --forecast-from-file fixtures/weather_longyearbyen.txt

Replay mode — --forecast-from-file <path> is fully offline: it replaces both the daily forecast and the current-conditions note with data read from a recorded file, via the same deterministic parsers (normalize_forecast, parse_current_conditions) used for a live call. openweather is not connected at all in this mode (confirmed via get_mcp_status() — only buildwindow appears), so a run needs no network access and no API key whatsoever — verified live with a deliberately broken OWM_API_KEY and an unreachable MCP_OPENWEATHER_PATH at the same time; the run still completed normally:

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 and fixtures/weather_longyearbyen.txt are real responses captured live (trimmed to 3 full real calendar days each, no key inside either) — not synthetic, not fabricated examples. Useful for a reproducible run that doesn't depend on today's weather, or when there's no network at all.

A full live run against real weather requires a genuinely valid OWM_API_KEY (see Configuration above) — confirmed working in this repo's own development environment: uv run python -m agent.main "Kyiv" produces a real schedule from a real forecast, and uv run python -m agent.main "Longyearbyen" '[...]' demonstrates real weather actually forcing a reschedule (see docs/demo-checklist.md step 4). Without a working key, agent/main.py fetches the forecast directly (not via the LLM), gets an empty result, prints Forecast unavailable for '<city>' (...), and exits before starting any LLM session — no wasted model call, no fabricated schedule. This was verified with three real failure modes: the mcp-openweather binary unreachable at all, an invalid OWM_API_KEY, and an invalid city name — the last two are actually indistinguishable through this upstream tool (see docs/tool-contracts.md for why) and were both confirmed to fail the same clean way, even with a genuinely valid key active elsewhere in the same environment.

OpenWeather rate limits: one successful live run makes exactly two real calls to the weather tool (confirmed live, by counting) — the deterministic pre-session fetch, plus the model's own single current-conditions call (see Overview above). A failed live run (no usable forecast) makes exactly one, since the LLM session never starts. Replay mode (--forecast-from-file) makes zero — both the daily forecast and the current-conditions note come from the recorded file, and openweather isn't connected at all in this mode (confirmed live: get_mcp_status() shows only buildwindow). OpenWeather's free tier is documented at 60 calls/minute and 1,000,000 calls/month — comfortably enough for any number of manual runs; this project doesn't stress-test that published figure itself.

Project structure

.
├── README.md, DECISIONS.md, LICENSE, pyproject.toml, uv.lock, .env.example, .gitignore
├── server/                 # the engine: MCP server, no network, no I/O beyond the dataset
│   ├── main.py             # entry point, registers the 4 tools
│   ├── schemas.py          # Pydantic input/output models
│   ├── rules.py            # deterministic verdict/curing/planner logic
│   ├── dataset.py          # resolves BUILDWINDOW_DATASET, loads and validates the dataset
│   ├── errors.py           # domain exceptions and error codes
│   └── data/work_types.json  # built-in construction example dataset
├── examples/
│   └── drone_operations.json # example dataset from another domain
├── skill/buildwindow/      # SKILL.md package for agents
│   ├── SKILL.md
│   └── references/dataset-format.md
├── agent/                  # optional reference agent (OpenWeather + Claude Agent SDK)
│   ├── main.py
│   ├── normalize.py        # deterministic OpenWeather text -> daily figures
│   └── mcp_config.json     # config for both MCP servers
├── fixtures/               # real captured OpenWeather responses, for replay mode
├── scripts/
│   └── list_tools.py       # lists both MCP connections' tools, no weather call
├── docs/
│   ├── tool-contracts.md
│   ├── design-rationale.md
│   ├── demo-checklist.md   # step-by-step end-to-end verification walkthrough
│   └── sample-runs/        # real captured agent and skill-session transcripts
└── tests/
    ├── conftest.py
    ├── test_dataset.py, test_custom_dataset.py, test_lookup.py, test_validate.py
    ├── test_curing.py, test_planner.py, test_errors.py
    └── test_normalization.py

Testing

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

All 63 tests pass. They cover the rules, the four tools, every error code, custom datasets (loading, readable validation errors, the BUILDWINDOW_DATASET fallback, and the schema enum), and the reference agent's weather parsing. ruff and black report no issues.

Limitations

The engine:

  • Thresholds are yours. The engine applies whatever limits the dataset contains and does not check them against any standard. The built-in construction dataset and the drone example are illustrative.

  • Five fixed weather dimensions: temperature band, wind, humidity and precipitation, one value each per day. There is no hourly resolution, and no gusts, visibility, UV or other dimensions.

  • Tolerance bands are global, not set per work type: 2 °C, 1 m/s, 5 % humidity, none for precipitation.

  • The planner is greedy. Each work goes into the earliest feasible window, in dependency order. There are no resource or crew limits (works may overlap), no optimisation, and one work occupies one continuous block. The forecast is limited to 14 days per call.

  • Curing uses a simplified Nurse-Saul maturity model, meant for cementitious materials. It applies only to work types with base_curing_hours set.

The reference agent's weather source. These limitations belong to the specific external OpenWeather MCP tool, not to the engine. If you connect another forecast source, you won't have them:

  • That tool returns only temperature per 3-hour forecast entry. Wind and humidity come from a single current-conditions snapshot, which the agent applies as a constant across every forecast day. Precipitation is not exposed at all, so precipitation_mm is always 0.0 through that integration, and a precipitation rule can never trigger in a live run. The engine itself evaluates precipitation fully, as the unit tests show (tests/test_validate.py).

  • Its forecast covers about 5 days, so live runs are capped there. The engine accepts up to 14.

  • It cannot distinguish a bad API key, an unrecognised city and an unreachable provider. All three return the same empty response, so the agent can only report "no forecast available". It never invents a schedule.

See docs/design-rationale.md and docs/tool-contracts.md for the full, source-verified detail.

Documentation

  • docs/tool-contracts.md: the exact contracts for all four tools, and for the external OpenWeather weather tool as the reference agent uses it, each with a real captured example.

  • docs/design-rationale.md: why each tool exists, the component boundaries, and the trade-offs.

  • docs/demo-checklist.md: a step-by-step end-to-end walkthrough of the server and the reference agent, including failure scenarios.

  • docs/sample-runs/: real captured output: the reference agent planning drone missions from examples/drone_operations.json, and a fresh Claude Code session in which the installed skill was picked up from its description alone.

  • DECISIONS.md: a dated log of implementation decisions, each with its rationale and the rejected alternative.

Origin

BuildWindow started as a course project for the KSE AI Agentic School, an MCP integration assignment. The task was to build a custom MCP server for a real domain problem and connect it, alongside an existing third-party MCP server, to an agent. The first version was a construction scheduler with its work types hard-coded. It was later generalised into the dataset-agnostic engine described above. DECISIONS.md records each step of that history.

License

MIT.

Available Tools

4 tools
estimate_curing_timeA

Estimate how long a concrete or coating work type needs to cure under a given sequence of daily temperatures, using the Nurse-Saul maturity method. Use this when a work type has a curing stage and cold weather may extend it beyond the nominal duration. Returns the ready date, or capped=true when the supplied temperature sequence is too short to reach the required maturity. Fails with no_curing_stage for work types that have no curing stage at all.

ParametersJSON Schema
NameRequiredDescriptionDefault
requestYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
cappedYesTrue if the supplied temperature sequence was too short to reach required_maturity.
work_codeYesThe work type curing was estimated for.
ready_dateYesDate the work becomes ready, or null if not reached within the supplied days.
daily_breakdownYesPer-day maturity accounting. Truncated at the ready day if one was found; includes every supplied day if capped is true.
effective_hoursYesElapsed hours (in whole days) needed to reach the ready date, or covered by the supplied days if capped.
required_maturityYesTarget Nurse-Saul maturity in degree-Celsius-hours.
accumulated_maturityYesMaturity actually accumulated over the supplied days.

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and clearly discloses return behavior (ready date vs capped=true when the sequence is too short) and error behavior (no_curing_stage). It also names the calculation method, which informs the agent of the model type.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and information-dense, with the main action, method, use trigger, return modes, and error case all front-loaded. There is no filler; every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description explains the output conditions and failure mode, and an output schema exists, so the remaining gaps are acceptable. A brief note about which sibling tool to use instead would make it fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Top-level schema coverage is 0%, so the description must compensate; it does mention daily temperatures and work types in prose but does not clarify start_date semantics or the work_code enum/units. The nested schema does contain field descriptions, so this is adequate but not a strong contribution beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific action (estimate curing time), the resource class (concrete/coating work types), the input (daily temperature sequence), and the method (Nurse-Saul), which clearly distinguishes it from planning/lookup siblings. The use-case sentence also reinforces that this tool estimates curing duration rather than performing scheduling, validation, or requirements lookup.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says to use this when a work type has a curing stage and cold weather may extend duration beyond nominal, and notes that it fails with no_curing_stage for work types without one. It does not name sibling alternatives or provide a full when-not-to-use list, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

lookup_work_requirementsA

Return the normative weather constraints for construction work types. Call this first when you need to know the temperature, wind, humidity or precipitation limits that apply to a work type, or to enumerate all work types in a category. Provide either a single work_code or a category, never both. Returns an empty list with matched_count=0 when the category contains no work types; this is a successful result, not an error.

ParametersJSON Schema
NameRequiredDescriptionDefault
requestYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
requirementsYesMatching work type requirements. Empty if none matched.
matched_countYesNumber of items in requirements.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must disclose behavior. It does: it notes that an empty category returns an empty list with matched_count=0 and is a successful result, not an error. It also states the exclusive parameter requirement. It does not go into depth about authorization, reversible side effects, or detailed response structure, but for a read-only lookup this is adequate. The empty-result behavior is a valuable disclosure beyond what a schema would imply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four sentences, front-loaded with the core purpose, then usage guidance, then a constraint, then a behavioral nuance. Every sentence contributes information; there is no fluff. The structure guides the agent from 'what' to 'how' to 'edge case' efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a straightforward lookup tool with two optional parameters and an output schema (not shown). The description covers the trigger conditions, the parameter rule, and the successful empty-result case. Since an output schema exists, defining the return format is not necessary. All information needed to invoke the tool correctly and interpret a common edge case is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already provides rich descriptions for both parameters (enums with names and descriptions), so the baseline is 3 given high schema coverage. The description adds the critical mutual-exclusivity rule ('never both') which is also in the schema description, but the tool description reinforces it in a clearer, action-oriented way. It also mentions the two parameter names, which helps an agent map the rule to the fields. Slight added value over the schema, hence 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Return the normative weather constraints for construction work types.' It also explains the two call patterns (by work_code or category) and clearly distinguishes this tool from siblings like validate_work_window and estimate_curing_time, which address different concerns. The phrase 'Call this first' reinforces its role as a primary lookup.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use it: 'Call this first when you need to know the temperature, wind, humidity or precipitation limits...' and also covers the alternate use case 'to enumerate all work types in a category.' It adds a strict usage constraint ('Provide either a single work_code or a category, never both') and clarifies the handling of empty results. No ambiguity remains about when to call this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

plan_work_scheduleA

Produce a feasible day-by-day schedule for several construction works across a weather forecast, honouring declared dependencies between works. Each work is placed in the earliest window where no day is prohibited. Returns both the scheduled works and the works that could not be placed, each with an explicit reason. Use this instead of repeated validate_work_window calls when planning more than one work.

ParametersJSON Schema
NameRequiredDescriptionDefault
requestYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
scheduledYesWorks successfully placed.
horizon_endYesLast date covered by the supplied forecast.
horizon_startYesFirst date covered by the supplied forecast.
unschedulableYesWorks that could not be placed, each with a reason.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It explains the scheduling algorithm ('earliest window where no day is prohibited'), the dependency handling, and what the return value contains (scheduled works and unplaced works, each with an explicit reason). It does not explicitly state side-effect behavior, but the tool's planning nature and return-focused wording make non-mutation reasonably clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four sentences and every sentence carries useful information: purpose, algorithm, return value, and usage guidance. It is not bloated, though the algorithm and return-value sentences could potentially be tightened without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with a relatively complex input (multiple works, dependencies, weather windows) and an output schema, the description covers the essential behaviors: feasibility, dependency ordering, earliest placement, and the split between scheduled and unscheduled works with reasons. It does not mention input limits or detailed weather constraint logic, but those are already present in the schema and would not affect tool selection.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0% at the top level, and the description does not directly explain the `request` parameter structure. However, it does reference the core semantic pieces: 'several construction works', 'weather forecast', and 'dependencies', which map to `works`, `forecast`, and `depends_on`. The nested schema definitions are rich, so the description adds modest context without fully compensating for the top-level coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Produce a feasible day-by-day schedule for several construction works across a weather forecast' while also naming the key constraint ('honouring declared dependencies'). It clearly distinguishes this tool from the sibling validate_work_window by framing it as the multi-work planning alternative, so an agent can identify its purpose immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool instead of an alternative: 'Use this instead of repeated validate_work_window calls when planning more than one work.' This provides a clear condition and names the sibling tool it replaces, giving the agent actionable selection guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_work_windowA

Evaluate whether one construction work type may proceed during one specific day of weather. Returns a verdict of allowed, conditional or prohibited together with the exact constraints that were breached and by how much. Use this to check a single work-day; use plan_work_schedule when you need to place several works across a forecast. The verdict is computed by deterministic rules, not estimated — treat it as authoritative.

ParametersJSON Schema
NameRequiredDescriptionDefault
requestYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
verdictYesOverall verdict for this work-day.
warningsYesNear-boundary breaches within the conditional tolerance band. Any entry here (with violations empty) forces verdict=conditional.
work_codeYesThe work type that was evaluated.
violationsYesHard constraint breaches. Any entry here forces verdict=prohibited.
window_dateYesThe date that was evaluated.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses the output format (allowed, conditional, or prohibited verdict) and that it includes constraints breached and by how much. It also states the tool is deterministic and authoritative, setting clear expectations for behavior. It does not detail the exact conditions for each verdict, but for a validation tool this is adequate and is not a major gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, consisting of two clear sentences with no redundant phrases. It efficiently conveys purpose, usage, and behavior without fluff, making it easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description fully covers the necessary context: what the tool does, when to use it, what it returns, and how it differs from a sibling. The existence of an output schema (even if not shown) is not a barrier because the description already specifies the expected output shape. It is self-contained and sufficient for an agent to decide when and how to invoke it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides rich descriptions for the nested fields (work_code, window, and weather properties), so the parameter semantics are already well-defined. The description text does not add explicit parameter details beyond implicitly referencing 'work type' and 'day of weather.' Since the schema coverage is strong, the description's minimal contribution is acceptable, but it does not elevate the score beyond the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: evaluating whether a work type may proceed under given weather. It explicitly distinguishes it from the sibling tool plan_work_schedule, making its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool versus the alternative: 'Use this to check a single work-day; use plan_work_schedule when you need to place several works across a forecast.' This leaves no ambiguity about appropriate invocation scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedestimate_curing_time
    • First observedlookup_work_requirements
    • First observedplan_work_schedule
    • First observedvalidate_work_window

TDQS

A4.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool serves a distinct purpose: querying requirements, validating a single day, estimating curing, and planning schedules. No overlap in functionality; an agent can easily select the right tool for the task.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern: lookup_work_requirements, validate_work_window, estimate_curing_time, plan_work_schedule. The naming style is uniform and predictable.

Tool Count5/5

With only 4 tools, the server is well-scoped for its construction weather planning domain. Each tool is essential and there is no bloat or missing core functionality.

Completeness5/5

The server covers the full workflow from looking up constraints to validating individual days, estimating curing, and planning multi-work schedules. It also handles edge cases like capping and failures gracefully, indicating thorough domain coverage.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

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
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables users to assess condensation and mold risk for building surfaces using physics-based calculations (dew point, surface temperature, critical thresholds), with support for real-time weather data and prescription generation.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables users to check whether an Australian field-service job can be worked at a location on a given day for a specific trade, using live weather forecasts, daylight hours, and public holiday data.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables construction teams to create local projects, record sourced field evidence, run deterministic quantity calculations, and draft daily logs, estimates, and change orders with visible assumptions and human approval gates. Keeps all data local and portable with no external side effects.
    10
    Apache 2.0