Windmill
SkillDatabases & dataUse when working with GOAT's Windmill instance — running or syncing analytics tools, inspecting job execution, adding a new tool, or checking which f/goat/tools/* scripts exist. Use the Windmill MCP tools for API calls.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Windmill skill
What this skill tells your AI
The instructions your AI receives, as published by plan4better/goat in .claude/skills/windmill/SKILL.md and read by ahel’s review.
Context for GOAT's Windmill instance. Use the Windmill MCP tools for API calls; this skill provides domain knowledge.
Setup
- URL / Web UI:
WINDMILL_URLin.env(local dev: http://localhost:8110) - Workspace:
goat - Token:
WINDMILL_TOKENin.env— never hardcode it
Folder Structure
All scripts live under f/goat/:
f/goat/tools/— Analytics tools (synced from goatlib, run by theprocessesservice)f/goat/tasks/— Background tasks (thumbnails, S3 sync, etc.)
Tools — do not hardcode the list
The tool set changes often. The single source of truth is the registry:
packages/python/goatlib/src/goatlib/tools/registry.py (TOOL_REGISTRY). Scripts under
f/goat/tools/ are auto-generated from it via python -m goatlib.tools.sync_windmill.
To see the current tools (grouped by category, with hidden/beta/worker flags), run from repo root:
uv run python -c "
from itertools import groupby
from goatlib.tools.registry import TOOL_REGISTRY
for cat, items in groupby(sorted(TOOL_REGISTRY, key=lambda t: t.category), key=lambda t: t.category):
print(f'## {cat}')
for t in items:
flags = [f for f, on in (('hidden', t.toolbox_hidden), ('beta', t.beta), (f'worker={t.worker_tag}', t.worker_tag != 'tools')) if on]
print(f\" {t.windmill_path}{' [' + ', '.join(flags) + ']' if flags else ''} — {t.description}\")
"
Categories: geoprocessing, data_management, geoanalysis, accessibility_indicators, data
(the data category is toolbox-hidden internal tools: layer import/update/delete/export/create,
project export/import, print report). toolbox_hidden tools stay callable via the API but don't
appear in the toolbox UI; beta tools render in a Beta sub-section; worker_tag routes the job to a
specific Windmill worker (e.g. print).
How Tools Execute
- Frontend calls the
processesservice API to start a tool processescreates a job incustomer.joband submits it to Windmill- Windmill runs the generated Python script, which imports from
goatlib.tools - The tool class (
BaseToolRunnersubclass) reads input from DuckLake, processes, writes output to DuckLake - Job status is tracked in
customer.job(pending → running → finished/failed)
Adding / Changing a Tool
- Add or edit the tool class in
goatlib/tools/<name>.py(inheritBaseToolRunner[TParams]) - Register it in
goatlib/tools/registry.py(TOOL_REGISTRY) — this is what makes it appear everywhere - Sync to Windmill (reads
WINDMILL_URL/WINDMILL_TOKENfrom the environment):
set -a && source .env && set +a
uv run python -m goatlib.tools.sync_windmill
Checking Jobs
Via Windmill MCP, or via the database (see the db skill for connection):
SELECT id, type, status, created_at, payload->>'tool_type' AS tool
FROM customer.job ORDER BY created_at DESC LIMIT 10;
Key Code Paths
- Tool registry (source of truth):
packages/python/goatlib/src/goatlib/tools/registry.py - Tool base class:
packages/python/goatlib/src/goatlib/tools/base.py - Individual tools:
packages/python/goatlib/src/goatlib/tools/<name>.py - Windmill sync:
packages/python/goatlib/src/goatlib/tools/sync_windmill.py - Code generation:
packages/python/goatlib/src/goatlib/tools/codegen.py - Processes API:
apps/processes/(OGC API Processes service)
Signals
- GitHub stars
- 165
- Forks
- 68
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
windmill- Source
- github.com/plan4better/goat