* feat: add a workspace mcp server with the ai session toolset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep workspace mcp tool calls on their own routes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep a token's job tag fence on workspace mcp tool calls Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: state the workspace mcp token rule in its refusal message Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: let the workspace mcp server span workspaces chosen per token Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: let the mcp url form choose between the tools and workspace servers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: serve the workspace mcp server as windmill mcp v2 at /api/mcp/v2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: add read-and-run access to windmill mcp v2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: serve the full windmill mcp experience at /api/mcp, sign-in first Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: align the mcp url form with the brand guidelines Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: serve every mcp token from /api/mcp, with one access picker Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: rename mcp access levels and keep selections to scripts and flows on /api/mcp Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: name the mcp access levels in the url caption Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: name the mcp server windmill Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: lead the mcp access picker with build and run, and spell out what a selection excludes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: show the tools each mcp access level gives Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: list the mcp tools in a collapsible section under the access row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: offer all scripts and flows as typed mcp tools again, as wildcard scopes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: label the mcp selection access as scripts and flows as tools Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: link mcp run, app draft and deploy results to their windmill pages Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: mirror the session tool changes from main in the mcp server Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep an ai agent's identity when the ai chat deploys its resource Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: call the api in-process from the mcp session tools Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep a draft that rebase_draft could not return whole Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: let read-and-run mcp tokens run scripts and flows that take secrets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: ask mcp clients to open windmill links in a browser the user sees Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: send mcp clients condensed instructions and serve the full rules on demand Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: put the core rules first in the mcp instructions instead of condensing them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: go straight to the mcp sign-in page after login, without the workspace picker Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: no workspace error toast on the mcp sign-in page while logged out Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: open raw app previews on the preview alone, from mcp links and in sessions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: link mcp script, flow and agent drafts to their editors, and agents to their page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep the session preview's saved sidebar choice, fold the build log in preview-only views Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: hold mcp tokens below full access to their level on the per-workspace urls Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: revoke tokens with the new mcp scopes when the migration is reverted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep an mcp token's scopes when its editor is saved without changing access Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: mint one jwt per mcp tool call, accept the old approve body, list the new scopes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: reset the mcp token editor's picker state for each token Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: mark only mcp selections, read levels imply no endpoint tools Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: update ee-repo-ref to b3fd7d49f2ba58f5080fabc3a5686c34a0b2684c This commit updates the EE repository reference after PR #877 was merged in windmill-ee-private. Previous ee-repo-ref: 9e960246d51401a2b0487dcffcadd0b43cd67d39 New ee-repo-ref: b3fd7d49f2ba58f5080fabc3a5686c34a0b2684c Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
System Prompts
The single source of the AI guidance Windmill ships: what an agent needs to know about Windmill itself to write flows, scripts, apps, pipelines, resources, triggers and schedules. Two consumers are built from it:
- the chat (the frontend AI chat: flow, script, app and global modes), through the
$system_promptsalias toauto-generated/; - the CLI (
wmill init/wmill refresh prompts), as the skills embedded incli/src/guidance/skills.gen.ts, next to theAGENTS.wmill.mdtemplate incli/src/guidance/core.ts.
The workspace MCP server (backend/windmill-api/src/mcp/session/) is a third reader, one step
removed: it serves the global chat's own get_instructions texts and system prompt, rendered for
an external client by npm run gen:mcp-session in frontend/. Rerun that after generate.py.
Guidance written in one consumer only is guidance the other never gets. Write it here.
Structure
system_prompts/
├── base/ # Hand-written guidance, one file per topic (flow-base.md, raw-app.md, …)
│ # plus CLI-only intros (flow-cli.md, script-cli.md, raw-app-cli.md, …)
├── languages/ # Hand-written per-language guidance (bun.md, python3.md, …)
├── generate.py # Builds everything under auto-generated/ and cli/src/guidance/skills.gen.ts
├── utils.py # Shared helpers, including the fence renderer
└── auto-generated/ # Generated — never edit
├── prompts.ts # One export per base/ and languages/ file (chat render), SDK docs, schema
├── index.ts # Chat helpers (getFlowPrompt, getScriptPrompt, …)
├── skills/ # One SKILL.md per CLI skill (cli render)
├── sdks/ # SDK reference extracted from the TypeScript and Python clients
└── cli/ # CLI command reference extracted from cli/src/commands/
How a topic is assembled
TOPICS in generate.py lists, for each topic, the parts both consumers concatenate in order:
a file under base/ or languages/, or a token filled per consumer ({lang}, {sdk},
{wac_sdk}, {openflow_schema}, {cli_commands}). The same entry produces the topic's chat
helper in index.ts and its CLI skill, so a part added there reaches both. A part tagged
('chat', …) or ('cli', …) goes to that consumer only, and cli_intro heads the CLI skill with
a CLI-only workflow file (flow-cli.md, script-cli.md, raw-app-cli.md).
| Topic | Chat helper | CLI skill | Shared parts |
|---|---|---|---|
| script | getScriptPrompt(lang) |
write-script-<lang> |
script-base.md, the language file, its SDK |
| flow | getFlowPrompt() |
write-flow |
flow-base.md, the OpenFlow schema |
| raw app | getRawAppPrompt(lang) |
raw-app |
raw-app.md (chat adds the SDK) |
| resources | getResourcePrompt() |
resources |
resources.md |
| workflow-as-code | getWorkflowAsCodePrompt(lang) |
write-workflow-as-code |
workflow-as-code.md, the WAC SDK |
| pipeline | getPipelinePrompt() |
write-pipeline |
pipeline-base.md |
| data tables | getDatatablePrompt() |
datatables |
datatable.md |
| workspace settings | getWorkspaceSettingsPrompt() |
workspace-settings |
workspace-settings.md |
| triggers, schedules, preview, CLI commands | — | same names | CLI only |
The app chat embeds RAW_APP_BASE directly (chat/app/core.ts).
Consumer fences
A sentence that only one consumer should see stays in the shared file, fenced:
A tool name outside that character set fails every run of the flow.
<!-- chat-only -->
The flow write tools refuse such a name.
<!-- /chat-only -->
<!-- cli-only -->
`wmill lint <flow folder>` reports it before anything runs.
<!-- /cli-only -->
render_for (utils.py) renders every file once per consumer: prompts.ts gets the chat render,
the skills get the cli render, and the fence lines reach neither. Fences sit on their own lines and
cannot nest; an unbalanced or misspelled fence fails generation (an HTML comment opening with cli
or chat, or ending in only, is read as a fence attempt).
Use a fence for a sentence or a section. When most of a topic differs per consumer, a CLI-only
file used as cli_intro reads better than a file that is mostly fences.
What goes where
- Here: anything true of Windmill regardless of who is asking — module shapes, data flow rules, what the editor accepts, how data tables, secrets or app access work.
- In the chat's TypeScript prompt builders (
frontend/src/lib/components/copilot/chat/*/core.ts): only tool plumbing (which tool to call, its arguments) and values known at run time (the user's name, this app's data-table policy, session capabilities). - In
cli/src/guidance/core.ts(AGENTS.wmill.md): project-level CLI workflow — which skill to use, deploying, debugging jobs.
A file in TOPICS names no chat tool, not even inside a chat-only fence: it reaches every chat
mode (flow mode's system prompt, global mode's get_instructions), each with its own tool names,
and global/sessionToolset.test.ts fails a prompt that names a tool its session lacks. Describe the
action instead ("the flow write tools refuse such a name") and name the tool in TypeScript. The one
exception is flow-chat-special-modules.md, which only flow mode reads.
Regenerating
After editing anything here, or when SDK methods, the OpenFlow schema or CLI commands change:
python system_prompts/generate.py
CI (.github/workflows/check-system-prompts.yml) runs system_prompts/check-freshness.sh, which
fails when the committed output is stale.
To also refresh the standalone skills in a Claude plugin checkout:
python system_prompts/generate.py --plugin-dir ~/windmill-claude-plugin
--plugin-dir accepts:
- the
windmill-claude-pluginrepo root - a plugin root such as
plugins/windmill - a direct
skills/directory
To regenerate the public docs repo (consumed by context7):
python system_prompts/generate.py --context7-dir ~/windmill-cli-docs
--context7-dir writes a fully-rendered snapshot (AGENTS.md,
cli-commands.md, skills/<name>/SKILL.md, README.md, manifest.json
with the Windmill version) with all template placeholders resolved —
suitable for ingestion by docs aggregators. In CI this runs from
.github/workflows/publish-cli-docs.yml on every release tag. The
generator refuses to wipe the target directory unless it's empty or has
a context7 marker (context7.json, manifest.json, or a
windmill-cli-docs git remote), so a typo can't delete unrelated files.