Releases: dev-five-git/devup-mcp
Releases · dev-five-git/devup-mcp
Release list
devup-mcp-visual(crates/devup-mcp-visual/Cargo.toml)@0.2.1
devup-mcp-visual@0.2.0 → 0.2.1 - crates/devup-mcp-visual/Cargo.toml
Patch
- Ship one MCP Bundle per release so devup-mcp installs in a click on any operating system. The previous release attached six loose binaries, which left a user to work out which of them matches their machine and then wire stdio up by hand; there was no .mcpb at all. A new bundle job now collects the same three server binaries the build matrix already produces onto one runner and packs them into a single devup-mcp-.mcpb, whose manifest selects the matching command per host through server.mcp_config.platform_overrides keyed by darwin, win32 and linux, and whose one required user_config value is the workspace directory that becomes the server's only writable root. finalize now waits on the bundle as well as the binaries, so a published release can never be missing it. Three checks hold the bundle to what it claims: the manifest is schema-validated on every pull request rather than first on a release run, when the tag and the draft would already exist; every command the manifest is able to select is confirmed to exist inside the staged archive, because mcpb validate only ever inspects server.entry_point and would pass a bundle broken on exactly one operating system; and the packed archive is read back to confirm the Unix binaries kept their executable bit, which is why the job must run on Linux - mcpb writes permission bits into the zip only from a Unix host, and it stores them without the regular-file type bits, so zipinfo renders the type column as ? and a naive check on a leading - would have rejected every correctly packed archive instead. The version is read from [workspace.package] by one script shared with the release path so the manifest, the archive filename and the tag cannot drift apart. No crate behaviour changes; the version moves because the released artifacts do.
devup-mcp-visual(crates/devup-mcp-visual/Cargo.toml)@0.2.0
devup-mcp-visual@0.1.0 → 0.2.0 - crates/devup-mcp-visual/Cargo.toml
Minor
- Add a Rust-native private Figma Remote MCP with OAuth and verified official-host fallback; exact-node and full-theme one-call acquisition with JSON-string host normalization, atomic failed-root-only legacy fallback, observable fallback progress and a bounded shared upstream session with safe stage deadlines; bounded, ancestor-preserving linked-screen exploration with executable stress fixtures, refresh control, exact/related/superset cache reuse, compatible-request singleflight and current-versus-origin call diagnostics; cancellation-safe bounded memory-only artifact/resource reuse, URI-accurate native MCP resource links, independently readable binary assets and transactional file/resource delivery with verified rollback and retained recovery backups; transactional host-handoff acceptance with bounded consumed/expired tombstones so an invalid continuation call can no longer destroy a session; Section screen selection and deterministic batch export; syntax-validated DevupUI TSX, devup.json, raw snapshot, source maps, requested assets and strictly decoded bounded reference PNG; source-derived typed fidelity impact requiring zero approximated/lossy/failed impacts for strict compatibility, evidence-based absolute-layout fallback reporting, final-output provenance for non-default variants, one-to-one repeated text coverage and strict reports; a pure Rust PNG comparator plus renderer contract; loss-aware legacy large-field continuation; dirty-aware build identity with a local
--self-checkdiagnostic and a real-stdio smoke test; all ten WQUW-151 screens; 268/268 plugin snapshot parity and a zero-not-ported 978-entry ledger; focused server module boundaries; non-duplicating CI snapshot drift enforcement; Rust 1.88-compatible dependency updates; an opencode-compatible MCP tool schema fix replacing boolean JSON Schema with object schemas across all 7 tools plus a raw-stdio schema regression test; and a further oxc/rmcp dependency refresh with the pinned toolchain raised to Rust 1.98
Patch
- Make the direct Figma OAuth path work end to end, so a URL converts to DevupUI TSX without a host Figma MCP or an agent relay in the loop. Three defects each independently blocked it: Dynamic Client Registration always sent a client_name that Figma's catalog allowlist rejects with a plain-text 403, and the name is now configurable through --figma-client-name / DEVUP_FIGMA_CLIENT_NAME with doctor reporting the active value; the client_secret issued by registration was discarded even though Figma advertises only client_secret_basic/client_secret_post, so the token exchange answered a bare 400 after registration and browser consent had both succeeded, and the secret is now kept beside its client_id for the authorization-code exchange and refresh; and auth_network_error dropped the underlying transport error entirely, so every failure surfaced identically with no details, and it now carries kind/status/url/cause-chain with the URL reduced to scheme, host and path so a query string cannot carry a code or token into a log. Also tolerate a relay that re-serializes upstream results: get_metadata is no longer bare XML because Figma prepends a selected-nodes block and appends an instruction footer, and the fast envelope no longer requires integrity.utf8Bytes to equal the received byte length, since truncation is already caught by JSON parsing plus the node, resource-reference and resource-presence checks that read content rather than its serialized form. Every diagnostic and guidance string is now emitted in English, because these are returned to an LLM agent over MCP where Korean prose costs several times the tokens; Korean Figma fixture data is preserved where tests use it deliberately to exercise CJK handling. Consolidates CI and release into the single workflow the other org projects use, driven by changepacks/action: the action cuts draft releases and reports them through pending_releases, a build matrix compiles devup-mcp and devup-mcp-visual for Linux, Windows and a macOS universal binary and uploads them onto those drafts, and a finalize step publishes the drafts only after the uploads succeed, so a release is never visible without its binaries. A changepack-required gate fails any pull request that edits a crate without leaving a changepack log, since such a change never moves the version and therefore never releases. Also fixes output-root resolution for a root reached through a symlink: the root was canonicalised when the policy opened it while the requested outputPath was not, so a caller passing a path under the spelling it was given was refused with outputPath is outside the allowed root. On macOS that was the normal case rather than an edge case, because /tmp and the system temp directory both resolve through /var to /private/var. Fixes Section targets on the direct path: the fast snapshot script throws DEVUP_TARGET_IS_SECTION and MCP delivers a thrown error as a successful call carrying isError, which the direct path handed to accept and then failed with snapshot data not found, so a Section link had no way to reveal the screens inside it. It is now rejected exactly as the handoff path already did, so the collector switches to the section index and answers with selectable screens. Fixes SVG asset export, which failed for every request while PNG worked: Figma's remote MCP returns a written PNG as an image attachment but does not return a written .svg at all, so the bytes never reached devup-mcp. SVG is now exported as a string and carried inline beside the descriptor under a bounded size, and the payload search steps through the JSON encoding of a text block and accepts a text payload as well as base64. The missing-payload error now reports which content shapes and mime types the response actually carried, so an absent attachment, a wrong mime type and an unread field stay distinguishable. Adds server instructions covering that the generated component name and asset paths are starting points rather than contracts, that a fixed asset must be exported through assetRequests with an outputPath instead of referenced by a path that does not exist, and that resource delivery should be preferred over inlining bytes.
devup-mcp-figma(crates/devup-mcp-figma/Cargo.toml)@0.2.1
devup-mcp-figma@0.2.0 → 0.2.1 - crates/devup-mcp-figma/Cargo.toml
Patch
- Ship one MCP Bundle per release so devup-mcp installs in a click on any operating system. The previous release attached six loose binaries, which left a user to work out which of them matches their machine and then wire stdio up by hand; there was no .mcpb at all. A new bundle job now collects the same three server binaries the build matrix already produces onto one runner and packs them into a single devup-mcp-.mcpb, whose manifest selects the matching command per host through server.mcp_config.platform_overrides keyed by darwin, win32 and linux, and whose one required user_config value is the workspace directory that becomes the server's only writable root. finalize now waits on the bundle as well as the binaries, so a published release can never be missing it. Three checks hold the bundle to what it claims: the manifest is schema-validated on every pull request rather than first on a release run, when the tag and the draft would already exist; every command the manifest is able to select is confirmed to exist inside the staged archive, because mcpb validate only ever inspects server.entry_point and would pass a bundle broken on exactly one operating system; and the packed archive is read back to confirm the Unix binaries kept their executable bit, which is why the job must run on Linux - mcpb writes permission bits into the zip only from a Unix host, and it stores them without the regular-file type bits, so zipinfo renders the type column as ? and a naive check on a leading - would have rejected every correctly packed archive instead. The version is read from [workspace.package] by one script shared with the release path so the manifest, the archive filename and the tag cannot drift apart. No crate behaviour changes; the version moves because the released artifacts do.
devup-mcp-figma(crates/devup-mcp-figma/Cargo.toml)@0.2.0
devup-mcp-figma@0.1.0 → 0.2.0 - crates/devup-mcp-figma/Cargo.toml
Minor
- Make the direct Figma OAuth path work end to end, so a URL converts to DevupUI TSX without a host Figma MCP or an agent relay in the loop. Three defects each independently blocked it: Dynamic Client Registration always sent a client_name that Figma's catalog allowlist rejects with a plain-text 403, and the name is now configurable through --figma-client-name / DEVUP_FIGMA_CLIENT_NAME with doctor reporting the active value; the client_secret issued by registration was discarded even though Figma advertises only client_secret_basic/client_secret_post, so the token exchange answered a bare 400 after registration and browser consent had both succeeded, and the secret is now kept beside its client_id for the authorization-code exchange and refresh; and auth_network_error dropped the underlying transport error entirely, so every failure surfaced identically with no details, and it now carries kind/status/url/cause-chain with the URL reduced to scheme, host and path so a query string cannot carry a code or token into a log. Also tolerate a relay that re-serializes upstream results: get_metadata is no longer bare XML because Figma prepends a selected-nodes block and appends an instruction footer, and the fast envelope no longer requires integrity.utf8Bytes to equal the received byte length, since truncation is already caught by JSON parsing plus the node, resource-reference and resource-presence checks that read content rather than its serialized form. Every diagnostic and guidance string is now emitted in English, because these are returned to an LLM agent over MCP where Korean prose costs several times the tokens; Korean Figma fixture data is preserved where tests use it deliberately to exercise CJK handling. Consolidates CI and release into the single workflow the other org projects use, driven by changepacks/action: the action cuts draft releases and reports them through pending_releases, a build matrix compiles devup-mcp and devup-mcp-visual for Linux, Windows and a macOS universal binary and uploads them onto those drafts, and a finalize step publishes the drafts only after the uploads succeed, so a release is never visible without its binaries. A changepack-required gate fails any pull request that edits a crate without leaving a changepack log, since such a change never moves the version and therefore never releases. Also fixes output-root resolution for a root reached through a symlink: the root was canonicalised when the policy opened it while the requested outputPath was not, so a caller passing a path under the spelling it was given was refused with outputPath is outside the allowed root. On macOS that was the normal case rather than an edge case, because /tmp and the system temp directory both resolve through /var to /private/var. Fixes Section targets on the direct path: the fast snapshot script throws DEVUP_TARGET_IS_SECTION and MCP delivers a thrown error as a successful call carrying isError, which the direct path handed to accept and then failed with snapshot data not found, so a Section link had no way to reveal the screens inside it. It is now rejected exactly as the handoff path already did, so the collector switches to the section index and answers with selectable screens. Fixes SVG asset export, which failed for every request while PNG worked: Figma's remote MCP returns a written PNG as an image attachment but does not return a written .svg at all, so the bytes never reached devup-mcp. SVG is now exported as a string and carried inline beside the descriptor under a bounded size, and the payload search steps through the JSON encoding of a text block and accepts a text payload as well as base64. The missing-payload error now reports which content shapes and mime types the response actually carried, so an absent attachment, a wrong mime type and an unread field stay distinguishable. Adds server instructions covering that the generated component name and asset paths are starting points rather than contracts, that a fixed asset must be exported through assetRequests with an outputPath instead of referenced by a path that does not exist, and that resource delivery should be preferred over inlining bytes.
- Add a Rust-native private Figma Remote MCP with OAuth and verified official-host fallback; exact-node and full-theme one-call acquisition with JSON-string host normalization, atomic failed-root-only legacy fallback, observable fallback progress and a bounded shared upstream session with safe stage deadlines; bounded, ancestor-preserving linked-screen exploration with executable stress fixtures, refresh control, exact/related/superset cache reuse, compatible-request singleflight and current-versus-origin call diagnostics; cancellation-safe bounded memory-only artifact/resource reuse, URI-accurate native MCP resource links, independently readable binary assets and transactional file/resource delivery with verified rollback and retained recovery backups; transactional host-handoff acceptance with bounded consumed/expired tombstones so an invalid continuation call can no longer destroy a session; Section screen selection and deterministic batch export; syntax-validated DevupUI TSX, devup.json, raw snapshot, source maps, requested assets and strictly decoded bounded reference PNG; source-derived typed fidelity impact requiring zero approximated/lossy/failed impacts for strict compatibility, evidence-based absolute-layout fallback reporting, final-output provenance for non-default variants, one-to-one repeated text coverage and strict reports; a pure Rust PNG comparator plus renderer contract; loss-aware legacy large-field continuation; dirty-aware build identity with a local
--self-checkdiagnostic and a real-stdio smoke test; all ten WQUW-151 screens; 268/268 plugin snapshot parity and a zero-not-ported 978-entry ledger; focused server module boundaries; non-duplicating CI snapshot drift enforcement; Rust 1.88-compatible dependency updates; an opencode-compatible MCP tool schema fix replacing boolean JSON Schema with object schemas across all 7 tools plus a raw-stdio schema regression test; and a further oxc/rmcp dependency refresh with the pinned toolchain raised to Rust 1.98
Patch
- Render the generated screens against Figma's own PNGs and fix what that turned up. A render harness (harness/render) builds each acquired screen with devup-ui at the frame's own size and compares the capture with Figma's render of the same frame, each screen under its own node-scope theme and @devup-ui/reset-css, holding every screen to a recorded threshold so a change that moves it further from Figma fails the run. Measured against that, the notice screen went from 29% to 2% different at 1920, and every fix below keeps the plugin corpus at 268 of 268. devup_figma_export gains assetNamesPerNode, on by default: named after their layer, as the plugin names them, one file served every node sharing that name, so eight nodes on one screen claimed a file holding five different drawings and a photograph drawn at three widths kept whichever width was exported last and was stretched into the other two; named per node each gets its own file, while assetNamesPerNode false and the CodegenOptions library default keep the plugin's naming byte for byte. Where two different drawings still claim one file the first is written and the rest are reported as DEVUP_ASSET_NAME_SHARED rather than the export being refused. A layer name no file system takes, such as one carrying a colon, is made writable at delivery with the generated code and the manifest renamed together so the two cannot drift apart. The asset manifest now lists the image fills a layout container paints itself, which the code refers to as a background and nothing could previously deliver, and names them through image_fill_path; a fully transparent node is no longer advertised as exportable, since Figma refuses it as it refuses a hidden one; and a frame that binds no variable and uses no style now yields an empty theme instead of a refusal. A cropped image fill honours its imageTransform instead of being painted center/cover. Two layout facts CSS cannot reach on its own are now written: a node filling its parent's main axis while holding a positioned child, which CSS would otherwise hug to its text, and the pinned height of a positioned frame that fills its parent's width, which CSS would otherwise size by its children - the first put a hero picture 143px low, the second put a header row 18px high. Three invariants guard the result: a per-screen regression gate on rendered distance from Figma, a test that every picture the generated code points at is one the manifest lists, and a check that the words the page prints are the words the design says, which uses the browser as the oracle for JSX whitespace rather than reimplementing its rules.
devup-mcp-devup-ui(crates/devup-mcp-devup-ui/Cargo.toml)@0.2.1
devup-mcp-devup-ui@0.2.0 → 0.2.1 - crates/devup-mcp-devup-ui/Cargo.toml
Patch
- Ship one MCP Bundle per release so devup-mcp installs in a click on any operating system. The previous release attached six loose binaries, which left a user to work out which of them matches their machine and then wire stdio up by hand; there was no .mcpb at all. A new bundle job now collects the same three server binaries the build matrix already produces onto one runner and packs them into a single devup-mcp-.mcpb, whose manifest selects the matching command per host through server.mcp_config.platform_overrides keyed by darwin, win32 and linux, and whose one required user_config value is the workspace directory that becomes the server's only writable root. finalize now waits on the bundle as well as the binaries, so a published release can never be missing it. Three checks hold the bundle to what it claims: the manifest is schema-validated on every pull request rather than first on a release run, when the tag and the draft would already exist; every command the manifest is able to select is confirmed to exist inside the staged archive, because mcpb validate only ever inspects server.entry_point and would pass a bundle broken on exactly one operating system; and the packed archive is read back to confirm the Unix binaries kept their executable bit, which is why the job must run on Linux - mcpb writes permission bits into the zip only from a Unix host, and it stores them without the regular-file type bits, so zipinfo renders the type column as ? and a naive check on a leading - would have rejected every correctly packed archive instead. The version is read from [workspace.package] by one script shared with the release path so the manifest, the archive filename and the tag cannot drift apart. No crate behaviour changes; the version moves because the released artifacts do.
devup-mcp-devup-ui(crates/devup-mcp-devup-ui/Cargo.toml)@0.2.0
devup-mcp-devup-ui@0.1.0 → 0.2.0 - crates/devup-mcp-devup-ui/Cargo.toml
Minor
- Add a Rust-native private Figma Remote MCP with OAuth and verified official-host fallback; exact-node and full-theme one-call acquisition with JSON-string host normalization, atomic failed-root-only legacy fallback, observable fallback progress and a bounded shared upstream session with safe stage deadlines; bounded, ancestor-preserving linked-screen exploration with executable stress fixtures, refresh control, exact/related/superset cache reuse, compatible-request singleflight and current-versus-origin call diagnostics; cancellation-safe bounded memory-only artifact/resource reuse, URI-accurate native MCP resource links, independently readable binary assets and transactional file/resource delivery with verified rollback and retained recovery backups; transactional host-handoff acceptance with bounded consumed/expired tombstones so an invalid continuation call can no longer destroy a session; Section screen selection and deterministic batch export; syntax-validated DevupUI TSX, devup.json, raw snapshot, source maps, requested assets and strictly decoded bounded reference PNG; source-derived typed fidelity impact requiring zero approximated/lossy/failed impacts for strict compatibility, evidence-based absolute-layout fallback reporting, final-output provenance for non-default variants, one-to-one repeated text coverage and strict reports; a pure Rust PNG comparator plus renderer contract; loss-aware legacy large-field continuation; dirty-aware build identity with a local
--self-checkdiagnostic and a real-stdio smoke test; all ten WQUW-151 screens; 268/268 plugin snapshot parity and a zero-not-ported 978-entry ledger; focused server module boundaries; non-duplicating CI snapshot drift enforcement; Rust 1.88-compatible dependency updates; an opencode-compatible MCP tool schema fix replacing boolean JSON Schema with object schemas across all 7 tools plus a raw-stdio schema regression test; and a further oxc/rmcp dependency refresh with the pinned toolchain raised to Rust 1.98 - Add three read-only ground-truth tools so an agent can never fabricate a project identifier it never verified: devup_project_context reads a project's real devup.json theme tokens, openapi.json endpoints/schemas, or Vespertide models/*.json tables/columns/enums fresh on every call (no session cache), returning a shared {found:false,guardrail:{action:'stop-and-report',...}} envelope instead of guessing when the target file is missing; devup_ui_validate parses DevupUI TSX with the existing oxc_parser/oxc_allocator/oxc_span stack via a new oxc_ast_visit-based walker and flags unknown $token references (with edit-distance-suggested existing tokens), hardcoded hex colors/px lengths that match an existing token, unknown props on Box/Flex/Text/Center/Grid/Image (checked against the published devup-ui Style Props API reference, not invented), and non-static values inside css()/globalCss()/keyframes() calls specifically -- verified against devup-ui's own docs and css-utils-literal-only ESLint rule that plain JSX style props (bg={dynamic}) are valid devup-ui and must not be flagged; devup_stack_diff detects drift across vespertide model -> sea-orm entity -> vespera route -> openapi.json -> devup-api client with every finding carrying an explicit low/medium confidence since none of the checks is a real compiler front end. Regression-tested against the exact incident that motivated this work: three agents independently inventing a $gray100 color token, a 16px bubble radius, and a 36px avatar size that did not exist in the real project devup.json.
- Render the generated screens against Figma's own PNGs and fix what that turned up. A render harness (harness/render) builds each acquired screen with devup-ui at the frame's own size and compares the capture with Figma's render of the same frame, each screen under its own node-scope theme and @devup-ui/reset-css, holding every screen to a recorded threshold so a change that moves it further from Figma fails the run. Measured against that, the notice screen went from 29% to 2% different at 1920, and every fix below keeps the plugin corpus at 268 of 268. devup_figma_export gains assetNamesPerNode, on by default: named after their layer, as the plugin names them, one file served every node sharing that name, so eight nodes on one screen claimed a file holding five different drawings and a photograph drawn at three widths kept whichever width was exported last and was stretched into the other two; named per node each gets its own file, while assetNamesPerNode false and the CodegenOptions library default keep the plugin's naming byte for byte. Where two different drawings still claim one file the first is written and the rest are reported as DEVUP_ASSET_NAME_SHARED rather than the export being refused. A layer name no file system takes, such as one carrying a colon, is made writable at delivery with the generated code and the manifest renamed together so the two cannot drift apart. The asset manifest now lists the image fills a layout container paints itself, which the code refers to as a background and nothing could previously deliver, and names them through image_fill_path; a fully transparent node is no longer advertised as exportable, since Figma refuses it as it refuses a hidden one; and a frame that binds no variable and uses no style now yields an empty theme instead of a refusal. A cropped image fill honours its imageTransform instead of being painted center/cover. Two layout facts CSS cannot reach on its own are now written: a node filling its parent's main axis while holding a positioned child, which CSS would otherwise hug to its text, and the pinned height of a positioned frame that fills its parent's width, which CSS would otherwise size by its children - the first put a hero picture 143px low, the second put a header row 18px high. Three invariants guard the result: a per-screen regression gate on rendered distance from Figma, a test that every picture the generated code points at is one the manifest lists, and a check that the words the page prints are the words the design says, which uses the browser as the oracle for JSX whitespace rather than reimplementing its rules.
Patch
- Make the direct Figma OAuth path work end to end, so a URL converts to DevupUI TSX without a host Figma MCP or an agent relay in the loop. Three defects each independently blocked it: Dynamic Client Registration always sent a client_name that Figma's catalog allowlist rejects with a plain-text 403, and the name is now configurable through --figma-client-name / DEVUP_FIGMA_CLIENT_NAME with doctor reporting the active value; the client_secret issued by registration was discarded even though Figma advertises only client_secret_basic/client_secret_post, so the token exchange answered a bare 400 after registration and browser consent had both succeeded, and the secret is now kept beside its client_id for the authorization-code exchange and refresh; and auth_network_error dropped the underlying transport error entirely, so every failure surfaced identically with no details, and it now carries kind/status/url/cause-chain with the URL reduced to scheme, host and path so a query string cannot carry a code or token into a log. Also tolerate a relay that re-serializes upstream results: get_metadata is no longer bare XML because Figma prepends a selected-nodes block and appends an instruction footer, and the fast envelope no longer requires integrity.utf8Bytes to equal the received byte length, since truncation is already caught by JSON parsing plus the node, resource-reference and resource-presence checks that read content rather than its serialized form. Every diagnostic and guidance string is now emitted in English, because these are returned to an LLM agent over MCP where Korean prose costs several times the tokens; Korean Figma fixture data is preserved where tests use it deliberately to exercise CJK handling. Consolidates CI and release into the single workflow the other org projects use, driven by changepacks/action: the action cuts draft releases and reports them through pending_releases, a build matrix compiles devup-mcp and devup-mcp-visual for Linux, Windows and a macOS universal binary and uploads them onto those drafts, and a finalize step publishes the drafts only after the uploads succeed, so a release is never visible without its binaries. A changepack-required gate fails any pull request that edits a crate without leaving a changepack log, since such a change never moves the version and therefore never releases. Also fixes output-root resolution for a root reached through a symlink: the root was canonicalised when the policy opened it while the requested outputPath was not, so a caller passing a path under the spelling it was given was refused with outputPath is outside the allowed root. On macOS that was the normal case rather than an edge case, because /tmp and the system temp directory both resolve through /var to /private/var. Fixes Section targets on the direct path: the fast snapshot script throws DEVUP_TARGET_IS_SECTION and MCP delivers a thrown error as a successful call carrying isError, which the direct path handed to accept and then failed with snapshot data not found, so a Section link had no way to reveal the screens inside it. It is now rejected exactly as the handoff path already did, so the collector switches to the section index and answers with selectable screens. Fixes SVG asset export, which failed for every request while PNG worked: Figma's remote MCP returns a written PNG as an image attachment but does not return a written .svg at all, so the bytes never reached devup-mcp. SVG is now exported as a string and carried inline beside the descriptor under a bounded size, and the payload search steps through the JSON encoding of a text block and accepts a text payload as well as base64. The missing-payload...
devup-mcp(crates/devup-mcp/Cargo.toml)@0.3.2
devup-mcp@0.3.1 → 0.3.2 - crates/devup-mcp/Cargo.toml
Patch
- Stop a superseded run from deciding the release. The changepacks job runs after verify, which takes about ten minutes, so by the time it evaluates, main may already be several commits further on and it judges a state that no longer exists. On 2026-09-08 three merges landed inside one such window: each stale run opened a fresh Update Versions pull request instead of releasing, all three were merged, and the workspace went 0.2.1 to 0.3.0 to 0.3.1 while the newest release stayed 0.2.1. Nothing was tagged, and re-running could not recover it, because the action only cuts a release in the run that actually consumes the changepacks and by then there were none left - a version that moved without releasing cannot be un-moved. The job now compares its own commit with the tip of main and stands down when it has been replaced, leaving the decision to the run for the commit that replaced it.
devup-mcp(crates/devup-mcp/Cargo.toml)@0.2.1
devup-mcp@0.2.0 → 0.2.1 - crates/devup-mcp/Cargo.toml
Patch
- Ship one MCP Bundle per release so devup-mcp installs in a click on any operating system. The previous release attached six loose binaries, which left a user to work out which of them matches their machine and then wire stdio up by hand; there was no .mcpb at all. A new bundle job now collects the same three server binaries the build matrix already produces onto one runner and packs them into a single devup-mcp-.mcpb, whose manifest selects the matching command per host through server.mcp_config.platform_overrides keyed by darwin, win32 and linux, and whose one required user_config value is the workspace directory that becomes the server's only writable root. finalize now waits on the bundle as well as the binaries, so a published release can never be missing it. Three checks hold the bundle to what it claims: the manifest is schema-validated on every pull request rather than first on a release run, when the tag and the draft would already exist; every command the manifest is able to select is confirmed to exist inside the staged archive, because mcpb validate only ever inspects server.entry_point and would pass a bundle broken on exactly one operating system; and the packed archive is read back to confirm the Unix binaries kept their executable bit, which is why the job must run on Linux - mcpb writes permission bits into the zip only from a Unix host, and it stores them without the regular-file type bits, so zipinfo renders the type column as ? and a naive check on a leading - would have rejected every correctly packed archive instead. The version is read from [workspace.package] by one script shared with the release path so the manifest, the archive filename and the tag cannot drift apart. No crate behaviour changes; the version moves because the released artifacts do.
devup-mcp(crates/devup-mcp/Cargo.toml)@0.2.0
devup-mcp@0.1.0 → 0.2.0 - crates/devup-mcp/Cargo.toml
Minor
- Make the direct Figma OAuth path work end to end, so a URL converts to DevupUI TSX without a host Figma MCP or an agent relay in the loop. Three defects each independently blocked it: Dynamic Client Registration always sent a client_name that Figma's catalog allowlist rejects with a plain-text 403, and the name is now configurable through --figma-client-name / DEVUP_FIGMA_CLIENT_NAME with doctor reporting the active value; the client_secret issued by registration was discarded even though Figma advertises only client_secret_basic/client_secret_post, so the token exchange answered a bare 400 after registration and browser consent had both succeeded, and the secret is now kept beside its client_id for the authorization-code exchange and refresh; and auth_network_error dropped the underlying transport error entirely, so every failure surfaced identically with no details, and it now carries kind/status/url/cause-chain with the URL reduced to scheme, host and path so a query string cannot carry a code or token into a log. Also tolerate a relay that re-serializes upstream results: get_metadata is no longer bare XML because Figma prepends a selected-nodes block and appends an instruction footer, and the fast envelope no longer requires integrity.utf8Bytes to equal the received byte length, since truncation is already caught by JSON parsing plus the node, resource-reference and resource-presence checks that read content rather than its serialized form. Every diagnostic and guidance string is now emitted in English, because these are returned to an LLM agent over MCP where Korean prose costs several times the tokens; Korean Figma fixture data is preserved where tests use it deliberately to exercise CJK handling. Consolidates CI and release into the single workflow the other org projects use, driven by changepacks/action: the action cuts draft releases and reports them through pending_releases, a build matrix compiles devup-mcp and devup-mcp-visual for Linux, Windows and a macOS universal binary and uploads them onto those drafts, and a finalize step publishes the drafts only after the uploads succeed, so a release is never visible without its binaries. A changepack-required gate fails any pull request that edits a crate without leaving a changepack log, since such a change never moves the version and therefore never releases. Also fixes output-root resolution for a root reached through a symlink: the root was canonicalised when the policy opened it while the requested outputPath was not, so a caller passing a path under the spelling it was given was refused with outputPath is outside the allowed root. On macOS that was the normal case rather than an edge case, because /tmp and the system temp directory both resolve through /var to /private/var. Fixes Section targets on the direct path: the fast snapshot script throws DEVUP_TARGET_IS_SECTION and MCP delivers a thrown error as a successful call carrying isError, which the direct path handed to accept and then failed with snapshot data not found, so a Section link had no way to reveal the screens inside it. It is now rejected exactly as the handoff path already did, so the collector switches to the section index and answers with selectable screens. Fixes SVG asset export, which failed for every request while PNG worked: Figma's remote MCP returns a written PNG as an image attachment but does not return a written .svg at all, so the bytes never reached devup-mcp. SVG is now exported as a string and carried inline beside the descriptor under a bounded size, and the payload search steps through the JSON encoding of a text block and accepts a text payload as well as base64. The missing-payload error now reports which content shapes and mime types the response actually carried, so an absent attachment, a wrong mime type and an unread field stay distinguishable. Adds server instructions covering that the generated component name and asset paths are starting points rather than contracts, that a fixed asset must be exported through assetRequests with an outputPath instead of referenced by a path that does not exist, and that resource delivery should be preferred over inlining bytes.
- Add a Rust-native private Figma Remote MCP with OAuth and verified official-host fallback; exact-node and full-theme one-call acquisition with JSON-string host normalization, atomic failed-root-only legacy fallback, observable fallback progress and a bounded shared upstream session with safe stage deadlines; bounded, ancestor-preserving linked-screen exploration with executable stress fixtures, refresh control, exact/related/superset cache reuse, compatible-request singleflight and current-versus-origin call diagnostics; cancellation-safe bounded memory-only artifact/resource reuse, URI-accurate native MCP resource links, independently readable binary assets and transactional file/resource delivery with verified rollback and retained recovery backups; transactional host-handoff acceptance with bounded consumed/expired tombstones so an invalid continuation call can no longer destroy a session; Section screen selection and deterministic batch export; syntax-validated DevupUI TSX, devup.json, raw snapshot, source maps, requested assets and strictly decoded bounded reference PNG; source-derived typed fidelity impact requiring zero approximated/lossy/failed impacts for strict compatibility, evidence-based absolute-layout fallback reporting, final-output provenance for non-default variants, one-to-one repeated text coverage and strict reports; a pure Rust PNG comparator plus renderer contract; loss-aware legacy large-field continuation; dirty-aware build identity with a local
--self-checkdiagnostic and a real-stdio smoke test; all ten WQUW-151 screens; 268/268 plugin snapshot parity and a zero-not-ported 978-entry ledger; focused server module boundaries; non-duplicating CI snapshot drift enforcement; Rust 1.88-compatible dependency updates; an opencode-compatible MCP tool schema fix replacing boolean JSON Schema with object schemas across all 7 tools plus a raw-stdio schema regression test; and a further oxc/rmcp dependency refresh with the pinned toolchain raised to Rust 1.98 - Add three read-only ground-truth tools so an agent can never fabricate a project identifier it never verified: devup_project_context reads a project's real devup.json theme tokens, openapi.json endpoints/schemas, or Vespertide models/*.json tables/columns/enums fresh on every call (no session cache), returning a shared {found:false,guardrail:{action:'stop-and-report',...}} envelope instead of guessing when the target file is missing; devup_ui_validate parses DevupUI TSX with the existing oxc_parser/oxc_allocator/oxc_span stack via a new oxc_ast_visit-based walker and flags unknown $token references (with edit-distance-suggested existing tokens), hardcoded hex colors/px lengths that match an existing token, unknown props on Box/Flex/Text/Center/Grid/Image (checked against the published devup-ui Style Props API reference, not invented), and non-static values inside css()/globalCss()/keyframes() calls specifically -- verified against devup-ui's own docs and css-utils-literal-only ESLint rule that plain JSX style props (bg={dynamic}) are valid devup-ui and must not be flagged; devup_stack_diff detects drift across vespertide model -> sea-orm entity -> vespera route -> openapi.json -> devup-api client with every finding carrying an explicit low/medium confidence since none of the checks is a real compiler front end. Regression-tested against the exact incident that motivated this work: three agents independently inventing a $gray100 color token, a 16px bubble radius, and a 36px avatar size that did not exist in the real project devup.json.
- Render the generated screens against Figma's own PNGs and fix what that turned up. A render harness (harness/render) builds each acquired screen with devup-ui at the frame's own size and compares the capture with Figma's render of the same frame, each screen under its own node-scope theme and @devup-ui/reset-css, holding every screen to a recorded threshold so a change that moves it further from Figma fails the run. Measured against that, the notice screen went from 29% to 2% different at 1920, and every fix below keeps the plugin corpus at 268 of 268. devup_figma_export gains assetNamesPerNode, on by default: named after their layer, as the plugin names them, one file served every node sharing that name, so eight nodes on one screen claimed a file holding five different drawings and a photograph drawn at three widths kept whichever width was exported last and was stretched into the other two; named per node each gets its own file, while assetNamesPerNode false and the CodegenOptions library default keep the plugin's naming byte for byte. Where two different drawings still claim one file the first is written and the rest are reported as DEVUP_ASSET_NAME_SHARED rather than the export being refused. A layer name no file system takes, such as one carrying a colon, is made writable at delivery with the generated code and the manifest renamed together so the two cannot drift apart. The asset manifest now lists the image fills a layout container paints itself, which the code refers to as a background and nothing could previously deliver, and names them through image_fill_path; a fully transparent node is no longer advertised as exportable, since Figma refuses it as it refuses a hidden one; and a frame that binds no variable and uses no style now yields an empty theme instead of a refusal. A cropped image fill honours its imageTransform instead of being painted center/cover. Two layout facts CSS cannot reach on its own are now written: a node filling its parent's main axis while holding a positioned child, which CSS would otherwise hug to its text, and the pinned height of a positioned frame that fills its parent's width, which C...