* fix: let the MCP createScript tool deploy without a parent hash
The tool advertised creating a new script with `parent_hash` left unset, but
`parent_hash` was one of its declared arguments — and a client that requires
every declared argument to be filled has no way to leave it unset. The values
such a caller invents (`""`, `"0"`, a zero hash) are all rejected by
`/scripts/create`, so no script was ever created.
`parent_hash` is now gone from the tool, and the MCP layer sends `auto_parent`
in its place: the server resolves the lineage from the path, creating the script
when the path is free and deploying a new version of it when it is not. That is
what the tool already claimed to do, and it no longer asks the caller to track a
hash to do it.
`x-mcp-tool-fixed-fields` is the general mechanism behind this — body fields the
MCP layer fills in itself, absent from the tool schema. A null argument is also
dropped from the assembled body now, for the same reason the placeholder hashes
were a problem: it is how a caller with no value to give says so, and the API
rejects it rather than falling back to the field's default.
Fixes GIT-973
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: hash a script version once auto_parent has resolved its parent
`create_script` hashed the incoming script before the `auto_parent` block filled
in `parent_hash`, and the version hash covers that field. A deploy that let the
server resolve the parent was therefore hashed as if the path had no history, so
redeploying content the path had held before collided with that archived version
and returned "A script with same hash ... already exists!" instead of becoming a
new version of the lineage. Reverting a script to an earlier state was impossible
for any caller relying on auto_parent alone, which is now every MCP caller.
The hash and the duplicate-hash check move below the resolution, so an
auto_parent deploy hashes the lineage it will actually be attached to. Callers
passing an explicit `parent_hash` are unaffected: the resolution block leaves
their `ns` untouched, so they hash exactly as before.
The CLI masked this by sending `parent_hash` and `auto_parent` together, using
auto_parent only as a stale-hash fallback.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: state the constraint that pins the script hash site
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject a fixed-fields spec the MCP layer would not honour
`validate_fixed_fields` ran only for an operation that declares a request body,
and passed any body whose properties it could not see. Two shapes reached the
generated tool with fixed fields that are dropped at call time: an operation with
no `requestBody`, where the body builder returns before reading them, and a
pass-through body, which carries the runnable's own arguments and never receives
a key of ours. Both are now generation-time errors, so the only specs that get
the extension are the ones where it means something.
Also name the folder-derived `on_behalf_of` alongside `parent_hash` at the hash
site: both are written to `ns` before it, and a reader who knows about only one
could reintroduce the early hash.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: keep fixed fields internal and catch a misspelled one
`EndpointTool` is what `list_tools` publishes as the tool catalogue, so deriving
`body_fixed_fields` into it put a field in the caller's view that is by definition
not the caller's to set, and that the OpenAPI schema does not declare. It is no
longer serialized.
The generator also only checked a fixed key against the exposed subset of the body
properties, which cannot tell a field deliberately left out of
`x-mcp-tool-include-fields` from a misspelling of one. A key the API does not
declare is now a generation-time error rather than one serde discards in silence,
and the extension must be a non-empty mapping — an empty list previously slipped
through the type check on its way to being ignored.
Narrow the hash-site comment to the ordering it actually constrains.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat: split the MCP script tools into createScript and updateScript
Scripts were the only entity in the MCP surface without the create/update pair
every other one has, because the REST API has no update route for them: a script
is immutably versioned, so `POST /scripts/create` is also its update, and one
tool had to infer which the caller meant from the state of the path.
That inference is what GIT-973 is. `parent_hash` told the two apart, and an MCP
client that requires every declared argument to be filled has no way to leave it
unset, so no script could be created: `""` is a 422, `"0"` is a 422, and
`"0000000000000000"` is a 400.
Naming the intent removes the field instead of the guard. `createScript` means
the path should be free and keeps refusing an occupied one; `updateScript` names
the version it supersedes in its URL, so the body carries no hash either. Picking
the wrong one now fails loudly rather than succeeding on the wrong script.
- New `POST /w/{workspace}/scripts/update/{path}`, deploying a new version of
the script the URL names. Its body `path` is the destination, defaulting to the
URL's, so setting a different one moves the script and keeps its history —
which no MCP client could ask for while `createScript` was the only tool.
- New `x-mcp-tool-optional-fields`, dropping a body field from the tool's
`required` where the handler defaults it. `updateScript` uses it for that
destination path: required, an agent has to restate the path on every edit, and
a value that drifts from the URL's silently moves the script.
- `assemble_request_body` drops null-valued arguments, matching what the
pass-through branch already did. A client that must fill in every argument says
"no value" with `null`, and the API rejects that for a bare `String` field
rather than falling back to its default.
Fixes GIT-973
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* fix: confine updateScript to the token's script paths
`endpoint_path_policy` is what applies an `mcp:scripts:<pattern>` token's path
patterns to an endpoint tool, and a tool it does not name is not confined at all.
`updateScript` was not named, so a path-scoped token could deploy over, and move,
any script in the workspace: the proxy mints a bare `scripts:write` for a caller
whose only scopes are `mcp:`-prefixed, and nothing downstream held a pattern.
The destination path has to bind only when supplied — omitting it is how a caller
updates in place — so `PathArgs` grows `optional_fields`, checked when present and
never required. Empty reads as absent, matching the handler, which now takes an
empty body `path` for "leave it where it is" rather than moving the script to the
empty path: a caller obliged to fill in every field sends `""` as readily as null.
That shape also fixes `updateFlow`, whose entry named `path__path` for the URL
argument. The generator gives the URL path the plain name, so the lookup never
matched and every confined call failed closed on a missing argument.
Both sides now have a drift guard: a script/flow tool the URL addresses by path
must have a policy. The backend one lives in windmill-api, where the generated
catalogue is, since the policy is in windmill-mcp and neither crate sees both.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* fix: address the review round on the script tool split
Four findings, three of them one bug: a destination path the caller left empty.
`update_script` read it as "leave it where it is", the confinement check skipped
it on the strength of that, and `update_flow` did neither — it takes the empty
string literally and moves the flow there, so the skipped check was the only
thing standing in front of that move. A database constraint refuses the empty
path, so nothing was reachable through it, but the confinement was relying on a
property of one handler that its sibling did not have.
The MCP layer now strips an empty optional destination from the arguments, so no
handler receives one and there is nothing left for the check to skip. Neither
tool depends on the other's reading of it any more.
`update_script` also resolved the head before opening the deploying transaction.
A version landing in between is caught — it leaves a child behind, and the
linear-lineage check refuses that — but an archive leaves none, and the hash of
an archived version still exists, so the deploy would have chained onto it and
revived the script the archive had just retired. The resolution moves into the
transaction.
The scope check on the URL path moves ahead of that resolution, so a path outside
the token's scope answers the same whether or not a script is there, rather than
telling the two apart through 404 against 403.
`x-mcp-tool-optional-fields` goes: the generator already strips a body field that
collides with a same-named path parameter from `required`, so the extension
regenerated byte-for-byte identical output. The test that pinned the destination
as optional stays — it pins the behavior, which is now the collision handling's.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* fix: lock the head an updateScript supersedes
Moving the resolution into the deploying transaction narrowed the archive race
without closing it. The plain SELECT took no row lock, so an archive could still
land between it and the parent-existence check below, which finds the parent by
hash and never looks at `archived` — the deploy then chained onto the archived
version and inserted a live child, reviving the script the archive had retired.
`FOR UPDATE` on the resolution is what makes the row the head rather than a head
it once was: the archive either waits for the deploy, or wins and leaves the row
failing the `archived` qualifier on re-check, so no version resolves at all.
The regression test stages that interleaving rather than approximating it. It
holds the head row from a second connection so the deploy parks on it, waits for
a backend to actually be blocked before archiving — without that wait the request
loses to a local UPDATE and never reaches its resolution, which is the sequential
case the neighbouring test already covers — then asserts the update is refused.
It returns 201 and revives the script with the lock removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* fix: have the MCP layer name the path an update keeps
The tool lets a caller omit the destination, and the endpoint was absorbing that
by accepting a body without a `path` and defaulting it from the URL. The OpenAPI
schema says `path` is required, so the two disagreed and a generated REST client
could not follow the contract the description promised.
The MCP layer fills the destination in instead, from the path the item is already
at, since that is what omitting it means. The endpoint then always receives a body
naming its own path and matches its schema, `update_script` takes a `NewScript`
rather than picking a JSON object apart to inject a default, and the empty string
stops being a value any handler has to interpret — `update_flow` reads one as the
empty path, which is why it was stripped a commit ago.
The alternative, an `EditScript` schema differing from `NewScript` only in whether
`path` is required, was measured and rejected: openapi-ts drops the `required` of
an `allOf` branch, so `NewScript` came out with every field optional and broke 15
frontend types. Loosening a schema every API consumer shares, to make one field
optional on one route, is the worse trade.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* fix: tell a superseded update apart from a missing script
Locking the head made the loser of two concurrent deploys answer 404 "Script not
found" for a path the caller can see holds a script: its lock re-check finds the
row archived and filtered, and nothing looked further. It now looks — a live
version at the path means this deploy lost to one that superseded the version it
set out to supersede, which is a conflict to retry, not a script to go find.
The regression test stages that interleaving the way the archive one does, with
the winner leaving a live head behind rather than an archived path. It answers
404 with the branch removed.
The rationale for the lock also sat in two places; it stays at the query, which
is where dropping it would do the damage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: drop the path default update_script no longer applies
The handler stopped defaulting the body's path when the MCP layer took the job
over; its doc comment still described the old contract.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: sync the deref YAML with the update route's path contract
The dereferenced bundle rewraps prose at its own width, so the edit that updated
the canonical spec and the JSON bundle matched nothing here and left the served
YAML still offering a default the endpoint no longer applies.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: stop the script tools describing a parent_hash they cannot take
`description` is read by two audiences: it documents the route, and it opens the
MCP tool's text. Written for the first, it told an agent that createScript
"does it too when given that version's `parent_hash`" — a field neither tool
exposes, and inviting exactly the call this branch exists to make impossible.
updateScript's told the agent to repeat the URL's path while its own instructions
say to omit it; both work, since the MCP layer fills it in, but only one of them
can be the advice.
Both now describe what the operation does and leave the mechanics to the text
that belongs to each caller: the request body's own description for REST, the
tool instructions for an agent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: give the create route's two audiences their own description
Removing the `parent_hash` sentence took a true fact out of the REST
documentation: the create route does still deploy a new version, and still
rename, when the body names the version it supersedes. Nothing replaced the
explanation, and the field carried no description of its own.
`description` cannot serve both readers — it documents an endpoint whose schema
has `parent_hash`, and it opens a tool whose filtered schema deliberately does
not. `x-mcp-tool-description` stands in for it on the tool, the way
`x-mcp-tool-name` already does for the name, so the route keeps its full
contract and the agent is not told to send a field it has no way to send.
What `parent_hash` does now sits on the field, where a REST caller looks for it
and where `x-mcp-tool-include-fields` drops it before an agent sees it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: tell an agent a new version is not runnable the instant it deploys
A deploy returns before its lockfile exists, so a script run straight after one
can still execute the previous version.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: say why a new version is not runnable the instant it deploys
Its lock is generated asynchronously, so a script run straight after a deploy
can still execute the previous version. On both script tools: a freshly created
script is no more immediately runnable than a freshly updated one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
* docs: bound the wait after a deploy instead of naming a signal for it
`getScriptByPath` reports the new hash the instant the version exists, while its
lock is still null, so the previous version is what a run by path executes. There
is no signal that fixes this: the deploy evicts DEPLOYED_SCRIPT_HASH_CACHE, but
anything resolving the path before the lock lands re-populates it with the old
hash, and the lock landing evicts nothing. Waiting for a non-null lock is
necessary and not sufficient, so pointing at one would have been a second wrong
answer.
Measured: a run right after the lock lands still gets the previous version, and
the same run 65s later gets the new one, which is the cache's 60s TTL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CEWFHpmTBauDBi93MnsT7s
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* fix: refuse an MCP endpoint call whose required request body is empty
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: state the required-body rationale once
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix: tell MCP clients which tool parameters may be omitted
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: make the mcp property-key rename testable and shorten the hint
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the mcp omission hint from calling flow inputs optional
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: skip the mcp omission hint on a parameterless tool
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: carry the validated token endpoint with MCP OAuth credentials
get_or_refresh_mcp_client already resolved and checked the token endpoint on
both its cached and freshly-registered paths, then dropped the result. Keeping
it on McpClientCredentials lets the callers that post the client_secret there
connect to the address that was checked, and removes a second lookup they were
each doing on their own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor: hand out the token URL with the client pinned to it
Makes the pin unrepresentable-if-wrong rather than documented: the validated
target is private and reachable only through token_request, which returns the
URL together with the client pinned to the address it was checked against, so
a caller cannot pin one host and post to another.
Adds the test that was missing under the whole guard: that the pinned client
really does connect to the pinned address instead of resolving the host. The
accept loop is bounded, so a pin that stops working fails in seconds.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep the token endpoint private behind token_request
Leaving the URL public still allowed posting the client_secret to it on an
unpinned client, so the invariant was only documented. Both the URL and its
validated target are now private and reachable together, and the pinning test
resets the accepted socket to blocking so it does not read empty on macOS.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: drop the non-blocking reset from the pinning test
Linux hands back a blocking socket from accept regardless of the listener's
flag, and no runner here builds this crate for a platform that does otherwise.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to f8d523195e40fd1d740595dcab6ce5cdc1bdbf09
This commit updates the EE repository reference after PR #718 was merged in windmill-ee-private.
Previous ee-repo-ref: 729df45314c6f2168b44eddb6edea401b0495d6d
New ee-repo-ref: f8d523195e40fd1d740595dcab6ce5cdc1bdbf09
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 5 (1M context) <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>
* feat(mcp): serve the 2026-07-28 spec alongside the legacy protocol
* fix(mcp): keep oauth discovery strict and preserve request limits
* fix(mcp): allow the protocol's own headers through CORS
* fix(mcp): expose the auth challenge to browser clients
* chore: update ee-repo-ref to c1665a881b61616f96ffe7702b44840905304660
This commit updates the EE repository reference after PR #711 was merged in windmill-ee-private.
Previous ee-repo-ref: bc1c001e3e386342415dfb8ac31c6b97f6629320
New ee-repo-ref: c1665a881b61616f96ffe7702b44840905304660
Automated by sync-ee-ref workflow.
---------
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: show which MCP endpoint tools a token scope will actually expose
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: keep a wildcard endpoint scope when pruning the MCP endpoint selection
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: drop orphaned matcher comment and name the wildcard remedy in the MCP scope warning
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* [ee] fix: pin validated DNS address to close SSRF DNS-rebinding TOCTOU
validate_url_for_ssrf resolved the host, checked every address was
public, then discarded them. Callers re-used the hostname and let stock
reqwest re-resolve at connect time, so a TTL-0 DNS rebinder that answered
a public IP at check-time and an internal one (e.g. 169.254.169.254) at
connect-time slipped straight through the guard.
Return the resolved addresses as a ValidatedTarget and pin them onto the
client that connects, so validate-time and connect-time target the same
address. Covers the AI proxy and worker AI-agent base_url (the primary
readable-SSRF sink), AI OAuth token_url, MCP server + OAuth
registration/discovery/token endpoints, SAML metadata, and the WebSocket
trigger connect.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 22abd6d4e229f1206a13ebee8a6a9b808cd82a0d
This commit updates the EE repository reference after PR #684 was merged in windmill-ee-private.
Previous ee-repo-ref: 700feb02ef1b96758ba9425358dbebc83bc02c61
New ee-repo-ref: 22abd6d4e229f1206a13ebee8a6a9b808cd82a0d
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
#10176 removed `path_field_renames` from `EndpointTool`, while #10162 added a
new `ep()` test constructor in runner.rs that still set it. Each passed CI
against its own base, but together on main the test build fails with E0560.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(mcp): apply token scopes consistently across mcp endpoint tools
Endpoint-tool authorization is now shared between single- and
multi-workspace modes, honors the token's script/flow path patterns for
every path-taking tool, and the JWT minted for proxied endpoint calls
carries scopes derived from the caller's own.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(mcp): review nits: restore create_http_request doc, reword comments
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(mcp): default a body field to its same-named path param so updateFlow works
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: trim mcp path-param fallback helper comment
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(mcp): keep path params un-mangled so update tools take plain `path`
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(flows): default update_flow body path from URL via EditFlow
Harmonizes updateFlow with the EditVariable/EditResource/EditApp convention: the
flow to update is identified by the URL, so the body path is optional and only
needed to rename. Fixes the 422 at the API layer for every client (MCP, the
in-app AI chat, raw HTTP), not just the MCP tool schema.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(mcp): drop redundant body-path fallback now that the server defaults it
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: fix stale generator comment after removing mcp body-path fallback
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(flows): mark updateFlow body path optional in the openapi contract
Adds an `EditFlow` schema (path optional) for the update route so the public
contract matches the server; createFlow keeps `OpenFlowWPath` (path required).
Also trims two test comments to record constraints rather than history.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(mcp): push granular scope patterns into SQL so scoped scripts/flows aren't truncated
MCP `list_tools` fetched scripts/flows capped at the 100 newest by
`created_at` and only *then* filtered by the token's granular
folder/custom scope in Rust. In a workspace with more than 100
scripts/flows, in-scope items outside that newest-100 window were
truncated before the scope filter ran, so a folder- or custom-scoped
token could see zero tools even though matching items existed.
Push the scope patterns into the query via a new `PathFilter::Patterns`
(mirroring `is_resource_allowed`: `*` disables filtering, exact paths
match by equality, `x/*` matches the folder or its subtree, empty grants
nothing) so the filter applies before the `ITEMS_FETCH_MAX_LIMIT` cap.
The existing hashed-name resolution path keeps its prefix behavior via
`PathFilter::Prefix`, and the Rust post-filter stays as defense in depth.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(mcp): warn in scope selector when a scope exceeds the MCP tool cap
The server exposes at most ITEMS_FETCH_MAX_LIMIT (100) scripts and 100
flows per token; a scope matching more silently drops the overflow, which
bloats the assistant's context with a partial, arbitrary tool set.
McpScopeSelector now computes how many scripts/flows the current scope
would expose (per type, mirroring the backend's is_resource_allowed) and
shows a warning Alert when either exceeds the cap, so the user can narrow
the scope before generating the URL/token. An async sequence guard keeps
rapid scope changes from applying stale counts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): address review — dedup count fetches, boundary-aware folder counting, fix copy
Follow-up to the MCP scope-selector truncation warning:
- Reuse a single per-type (scripts/flows) cache for both the preview list and
the exposed count, instead of a second concurrent fetch of the same rows.
- Count a folder scope against the `f/{folder}/*` subtree (via the same
boundary-aware matcher), so a folder like `team` no longer over-counts a
sibling like `team2` and falsely warns.
- Custom-mode counts are derived synchronously from the already-loaded
scripts/flows — no fetch.
- Reword the warning to "most recent" (flows are ordered by edited_at, not
created_at).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): anchor folder count fetch at the folder boundary
Follow-up to review: the folder count fetched the unbounded prefix
`f/{folder}` (backend `path LIKE 'f/{folder}%'`), so a prefix-sharing
sibling like `f/team2` shared the page. With a page limit, enough newer
sibling rows could fill the first page ahead of the target folder's older
rows; the client-side boundary filter then dropped them all, wrongly
suppressing the warning and emptying the preview.
Fetch `f/{folder}/` instead so the backend prefix (`LIKE 'f/{folder}/%'`)
is anchored at the folder boundary and never returns siblings. The
client-side matcher stays as a backstop for folder names whose LIKE
wildcards (`_`, `%`) can still let the backend prefix over-match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Flow input schemas omit `required` (they carry an `order` key instead),
which made `serde_json::from_str::<SchemaType>` fail in
`convert_schema_to_schema_type`. The error was swallowed and callers fell
back to an empty `SchemaType::default()`, so MCP flow tools advertised no
inputs. Add `#[serde(default)]` to `type`, `properties`, and `required` on
`SchemaType` so these schemas deserialize correctly. Scripts always include
`required` and were unaffected.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(mcp): add multi-workspace MCP tokens via the gateway endpoint
A single MCP token with no bound workspace (workspace_id NULL + mcp scope)
now works across every workspace the token owner can access, served through
the existing /api/mcp/gateway endpoint. This avoids having to register one
MCP server entry per workspace in clients like Claude/Cursor.
In multi-workspace mode the runner exposes a synthetic `list_workspaces`
tool plus the generic API endpoint tools, each workspace-scoped one gaining
a required `workspace_id` argument (mirroring the proxy pattern users built
externally). Per-workspace scripts/flows are not enumerated to avoid
flooding the tool list — they are run via runScriptByPath/runFlowByPath
with an explicit workspace_id.
Auth is resolved per tool call: the gateway middleware detects a
workspace-less mcp token and marks the request MultiWorkspaceMcp, and the
runner resolves a per-workspace ApiAuthed from the raw token via the
AuthCache (validating membership; superadmins may act in any workspace).
Single-workspace tokens are unchanged.
Frontend: the MCP token creation flow gains an "All workspaces" option that
produces a workspace-less token and the gateway URL.
Fixes WIN-2153
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(mcp): cover multi-workspace endpoint tool transformation
Unit tests for endpoint_tool_to_mcp_tool_multi and list_workspaces_tool:
workspace-scoped tools gain a required workspace_id arg, global tools are
left unchanged, workspace_id is not duplicated, and list_workspaces takes
no arguments.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): forward script/flow args for runScriptByPath/runFlowByPath
These endpoints have an additionalProperties body (no declared properties),
so build_request_body previously returned an empty body and dropped every
script/flow argument. This was latent for the per-path run endpoints and
became load-bearing in multi-workspace mode, where scripts/flows can only be
run via runScriptByPath/runFlowByPath — parameterized runs silently lost
their arguments.
build_request_body now forwards all arguments not consumed by a path/query
parameter for pass-through (additionalProperties) bodies, keeping the strict
declared-only behavior for endpoints with explicit properties. The runner
strips the synthetic workspace_id argument before dispatch so it can't leak
into the forwarded body.
Reported by Codex review on #10043.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(mcp): note workspace_id requirement in multi-workspace tool descriptions
Workspace-scoped tools already gain a required workspace_id parameter (with
its own schema description) in multi-workspace mode, but the tool's prose
description was unchanged. Append a note so models/clients that read the
description text know to pass workspace_id (and to call list_workspaces
first). Global tool descriptions are left untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(mcp): trim multi-workspace tool/arg descriptions
The workspace_id note repeats across every workspace-scoped tool in each
tools/list, so keep it terse: description suffix "Requires `workspace_id`."
and arg description "Target workspace id (from list_workspaces)." to avoid
spending tokens on repeated boilerplate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): enforce script/flow path scopes for multi-workspace run-by-path
In multi-workspace mode runScriptByPath/runFlowByPath are the only way to run
scripts/flows, but they were authorized against the endpoint scope only — never
the caller's mcp:scripts:/mcp:flows: path scopes. A granular token could run
items outside its allowed paths (e.g. mcp:scripts:f/team/* + mcp:endpoints:*
running f/other/secret), and a mcp:endpoints:* token could run arbitrary
scripts.
Now these two endpoints are authorized by the script/flow scope of the
requested path (matching single-workspace mode's per-item tools): exposed in
list_tools only when the token grants some script/flow (McpScopeConfig::has_any),
and at call time the path is checked via is_allowed("script"/"flow", path).
Verified e2e: mcp:scripts:f/team/* runs f/team/* but is denied f/other/*;
mcp:endpoints:* alone no longer exposes or runs run-by-path.
Reported by Codex + Pi review on #10043.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): deny run-by-path for mcp:favorites multi-workspace tokens
mcp:favorites sets granular=false, so the previous run-by-path scope check
(gated on `granular`) was skipped entirely — a default "Favorites only"
all-workspaces token could run any script/flow by naming its path, bypassing
the favorites restriction.
Favorites are an enumerated set reachable only through per-item tools, not by
arbitrary path, so they grant nothing for run-by-path. has_any() now returns
true only for mcp:all (not favorites), and the call-time check drops the
`granular` gate and relies on is_allowed() directly (already false for
favorites, true for mcp:all, pattern-matched for granular).
Verified e2e: mcp:favorites no longer exposes or runs run-by-path; mcp:all
still runs; granular script scopes still path-enforced.
Reported by Codex review on #10043.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: re-enforce per-path token scope on store rename, delete and interpolation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: enforce token scope on workspace export and resume-url minting
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: enforce per-item and runnable scope on trigger create paths
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: enforce app write scope before persistence and on rename
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: enforce scope containment on mcp oauth approval
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: scope mcp endpoint-proxy jwt to the proxied route
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: treat resource-linked variables and resources as covered by resource scope
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: only require variables:read for plaintext-secret workspace export
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: handle singlestepflow resume, reject empty mcp grant, scope var-skipped tarball
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop empty/unknown `type` values (Windmill emits `type: ""` for untyped fields) and infer `type: array` for nodes carrying `items`, so generated MCP tool schemas validate against JSON Schema draft 2020-12. Anthropic's tool registration rejected the whole tool list otherwise.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): resolve MCP resource token via caller RLS + SSRF-guard url
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): clone user_db for oauth2 refresh and drop advisory ids from comments
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): disable redirects on MCP client to prevent SSRF bypass
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: read-only flag on API tokens, orthogonal to scopes
Add a per-token `read_only` boolean set at creation time. When true, the
token can only call HTTP methods classified as Read (GET/HEAD/OPTIONS).
Mutating methods and job-run actions are rejected with 403, regardless of
which scopes are attached. Surfaced as a prominent toggle in the standard
token-creation flow and a discreet `2xs` toggle in MCP mode (where users
often want write access, so we don't bias them toward enabling it).
MCP enforcement: read-only tokens hide all script/flow/hub tools from
`list_tools` and only see endpoint tools whose method is GET, and the
runner rejects `call_tool` on anything mutating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: review fixes for read-only token flag
- Exempt /api/mcp/* and /mcp/* paths from the read-only middleware check.
MCP transport runs over POST (streamable HTTP / SSE), so otherwise the
middleware would 403 every MCP request before the runner could enforce
read-only at the tool-call level.
- Tighten is_endpoint_read_only to GET only, matching the read_only_hint
that create_endpoint_annotations actually emits.
- Add unit test for check_read_only_for_route covering GET/HEAD/OPTIONS,
mutating methods, and run paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump ee-repo-ref to read-only-trigger-toggle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): make read-only toggle discreet in both modes
Match the MCP-mode treatment in standard mode: text-tertiary, 2xs, shared
"Read-only" label. The tooltip switches per mode so the explanation still
fits the context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): gate read-only toggle behind Limit token permissions
The read-only toggle now only shows when the user has limited the token's
scopes (standard mode) or in MCP mode (which always picks an MCP scope).
Turning the limit off also resets read-only so it doesn't silently stick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(frontend): hide incompatible MCP tools when read-only is on
When the read-only toggle is on in MCP mode:
- Endpoint badges and the custom-mode endpoint MultiSelect filter to GET.
- Already-selected non-GET endpoints are pruned from the scope.
- The scripts/flows preview is replaced with a note explaining they're
hidden (the runner already rejects script/flow runs for read-only).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): place read-only toggle at top of limited scope area
The previous gate required at least one scope to be picked before the
read-only toggle appeared, which made it look missing while the user was
still building their scope list. Move the toggle inside ScopesPicker:
- Standard mode: sits directly under the "Limit token permissions" toggle
whenever Limit is on, before the scope selector.
- MCP mode: sits at the top of the MCP scope block.
readOnly is now $bindable on ScopesPicker so CreateToken still owns the
value. The auto-reset on un-limit moves into ScopesPicker too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): nest read-only toggle inside the scope list card
Place the read-only toggle at the top of the scope list (between the
Selected Scopes summary and the bordered domain list) via a new optional
topSlot snippet on ScopeSelector. Keeps ScopeSelector decoupled from
read-only specifics; ScopesPicker fills the slot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a
This commit updates the EE repository reference after PR #571 was merged in windmill-ee-private.
Previous ee-repo-ref: f53d26e6685dfd60bfa67686fbd7358169cfd130
New ee-repo-ref: 9bc8160be50b3e57a60daf4e1b71c389a6e02b8a
Automated by sync-ee-ref workflow.
* fix: address CI review for read-only token flag
- P1 (Codex): narrow the MCP middleware exemption from "any /api/mcp/*"
to just the streamable HTTP transport endpoints
(/api/mcp/gateway, /api/mcp/w/{ws}/{mcp,sse,list_tools}). Without this,
a read-only token could POST /api/mcp/gateway/oauth/server/approve and
mint a follow-on non-read-only MCP token via the OAuth code/token
exchange.
- P2 (Claude/cubic): fix test comment/assertion mismatch — the run-path
assertion now exercises GET (which is what the RUN_PATH_ACTIONS
elevation comment describes) in addition to POST. Add a regression
assertion for /api/mcp/gateway/oauth/server/approve.
- P2 (cubic): short-circuit script/flow/hub-script/resource fetches in
MCP list_tools when read_only is on — they would only be discarded
below, so skipping the DB and resource fan-out is pure win.
- P2 (cubic): when scopes are pre-supplied via the CreateToken prop, the
ScopesPicker isn't rendered, which previously hid the read-only
toggle entirely. Render it next to the pre-supplied scopes display.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* refactor: replace SELECT * with explicit column lists
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: update sqlx offline query cache
* chore: update sqlx offline query cache
* chore: update sqlx offline query cache with EE support
* chore: update sqlx offline query cache, no deletions
* chore: update sqlx offline query cache after rebase
* fix: correct column names in explicit script query lists
- concurrency_limit → concurrent_limit (matches DB column name)
- runnable_settings → runnable_settings_handle (matches DB column name)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add missing delete_after_secs column to script queries
Also add integration test covering all explicit-column export queries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add workspace export integration test covering all explicit-column queries
Covers tarball_workspace (folder, script, resource, resource_type, variable,
schedule, usr, group_) and the mcp_oauth_client SELECT query from windmill-mcp.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add tarball export integration test covering all explicit-column queries
Single test creates one of each entity type and exercises every runtime-checked
explicit-column query in tarball_workspace. Uses archive_type=tar to avoid
zip feature-gate in CI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: fix stale verification step and CI contradiction in update-sqlx skill
- Regenerate current_files.txt after EE cache restoration so step 4 reports accurate diff
- Scope "Never use SQLX_OFFLINE=true" to local prepare (CI legitimately uses it)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: remove Co-Authored-By from commit skill template
* refactor: extract SCRIPT_COLUMNS const to single source of truth
Replaces 5 duplicated 44-column lists with a shared const in windmill-types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract load helpers from reload_setting family
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: convert atomic primitive globals to AtomicBool/AtomicI64
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: convert CRITICAL_*/HUB_API_SECRET/INSTANCE_EVENTS_WEBHOOK/JWT_SECRET to ArcSwap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: pin ee-repo-ref to arcswap-refactor EE branch commit
* refactor: convert BASE_URL/HUB_BASE_URL/MIN_VERSION/LICENSE_KEY*/LICENSE_KEY_ID to ArcSwap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: convert worker hot-path globals to ArcSwap (WORKER_CONFIG et al)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: pin ee-repo-ref to combined arcswap-urls+worker EE commit
* chore: update ee-repo-ref to d8be8f88cb8898c8f6b27421989d53528223815d
This commit updates the EE repository reference after PR #532 was merged in windmill-ee-private.
Previous ee-repo-ref: c375aaaac9ec0fc0480993627d0defc8054c31a4
New ee-repo-ref: d8be8f88cb8898c8f6b27421989d53528223815d
Automated by sync-ee-ref workflow.
* fix: cleanup unused imports + fix 2 missed WORKER_CONFIG readers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to ce0f8fbbbde09c4a858312d2d8716d224e99042c
This commit updates the EE repository reference after PR #534 was merged in windmill-ee-private.
Previous ee-repo-ref: 450b601b5aba0ca0b2045f4b5071aa8701b4bfb7
New ee-repo-ref: ce0f8fbbbde09c4a858312d2d8716d224e99042c
Automated by sync-ee-ref workflow.
* fix: secret_backend_integration test — BASE_URL.write().await → .store()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: convert APP_WORKSPACED_ROUTE to AtomicBool for symmetry with HTTP_ROUTE_WORKSPACED_ROUTE
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to e587df8 (post-#535 merge)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* fix: add SSRF protection to SAML and MCP OAuth endpoints
- Add shared SSRF URL validation utility (windmill-common/ssrf.rs) that blocks private/loopback/link-local IPs and validates DNS resolution
- Move test_metadata to authed service requiring superadmin access
- Strip response body from SAML metadata parsing errors
- Add SSRF blocklist to MCP OAuth discover, start, and client registration endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref.txt for SSRF fix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref.txt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update ee-repo-ref to 563877bf1c8b4184f638bab51be89b1c0aec6dad
This commit updates the EE repository reference after PR #471 was merged in windmill-ee-private.
Previous ee-repo-ref: a600fe1807ea267f87a57360f4b48bf917776723
New ee-repo-ref: 563877bf1c8b4184f638bab51be89b1c0aec6dad
Automated by sync-ee-ref workflow.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
* feat: MCP server readiness for Anthropic integrations directory
- Add CORS layer to MCP streamable HTTP endpoint for browser clients
- Add tool result truncation (25K token limit) to prevent oversized responses
- Add HEAD method support on OAuth authorize endpoint
- Skip workspace selection redirect during MCP OAuth flow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR #8438 review feedback
- Add DELETE to CORS allowed methods (MCP spec requires DELETE for session termination)
- Add CORS layer to deprecated /sse endpoint for completeness
- Remove redundant .head() on OAuth authorize (axum auto-handles HEAD via GET)
- Fix comment: "chars/token" → "bytes/token" since len() returns bytes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: replace _TRUNC with hash-based MCP tool names (50 char limit)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: reduce MCP tool name limit from 50 to 40 chars
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: use path prefix filtering instead of separate DB query for hashed name resolution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove long path warning from MCP token creation (hashing handles long names)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: unify tool prefix parsing and fix extract_path_prefix_from_hashed for Hs- names
- Replace `is_hashed_name` + `parse_hashed_name` with unified `parse_tool_prefix`
that returns `(type_str, is_hub, is_hashed)` in one call
- Fix `extract_path_prefix_from_hashed` to dynamically determine prefix length
(3 for `Hs-`, 2 for `S-`/`F-`) instead of hardcoding index 2
- Simplify `reverse_transform` to reuse `parse_tool_prefix`
- Add tests for invalid prefixes and `Hs-` prefix handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: escape LIKE wildcards in MCP hashed name path prefix query
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: respect favorites scope in hashed tool name resolution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: deduplicate MCP tool name resolution and rename get_path_or_id
- Extract `unescape_path` helper in transform.rs to deduplicate the
3-step placeholder unescape logic
- Extract `find_matching_path` helper in runner.rs to deduplicate
script/flow candidate matching via ToolableItem trait
- Remove verbose tracing::info! logs from hashed tool resolution hot path
- Fix doc comment referencing nonexistent `is_hashed_name` function
- Rename `get_path_or_id` to `get_transformed_path` for clarity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update stale doc comments to reflect MAX_PATH_LENGTH=40
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* add endpoints
* feat: add MCP tools for script/flow/app CRUD and run endpoints with field filtering
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: convert enum arrays to description text in MCP tool schemas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: auto-detect and rename conflicting parameter names across MCP tool schemas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use two-pass approach in convert_enums_to_descriptions to preserve dict ordering
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add MCP instructions to createScript, runScriptByPath, and runFlowByPath
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add query param exclusion for MCP tools, slim down run endpoints
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: preserve additional top-level keys in allOf schema flattening
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: replace x-mcp-tool-exclude-query-params with x-mcp-tool-include-query-params
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: replace empty {} schemas with valid JSON Schema draft 2020-12 equivalents
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: revert openapi value:{} changes, sanitize empty schemas in generator instead
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
* refactor: extract windmill-api into 4 subcrates (api-auth, store, api-sse, api-jobs)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: eliminate refresh_token OnceLock bridge in windmill-store
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: eliminate FromRequestParts OnceLock bridge in windmill-api-auth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: wire subcrates into workspace and clean up unused re-exports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve cargo check --all-features errors in subcrate wiring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* sqlx
* all
* chore: update ee-repo-ref for warning fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract windmill-trigger crate and expand windmill-api-jobs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-kafka crate from windmill-api
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-postgres crate from windmill-api
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-websocket and windmill-trigger-mqtt crates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-nats, sqs, gcp, and email crates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-trigger-http crate from windmill-api
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: move token creation and permission helpers to windmill-api-auth
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract windmill-native-triggers crate from windmill-api
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* sqlx
* all
* refactor: extract windmill-api-embeddings crate and fix CI warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: resolve type mismatch in oauth2_oss and remaining warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use correct HTTP_CLIENT config in embeddings crate (30s timeout, cert override)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* all
* fix: gate oauth_refresh_ee on oauth2 feature to fix warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* all
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Renamed deprecated type aliases following rmcp library update:
- CallToolRequestParam → CallToolRequestParams
- InitializeRequestParam → InitializeRequestParams
- PaginatedRequestParam → PaginatedRequestParams
Also added required `meta` field to ClientInfo and CallToolRequestParams.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>