Skip to content

feat(ci): ship one MCP Bundle per release, for every operating system - #6

Merged
owjs3901 merged 1 commit into
mainfrom
ci/mcpb-bundle
Sep 8, 2026
Merged

feat(ci): ship one MCP Bundle per release, for every operating system#6
owjs3901 merged 1 commit into
mainfrom
ci/mcpb-bundle

Conversation

@owjs3901

@owjs3901 owjs3901 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

feat(ci): ship one MCP Bundle per release, for every operating system

The 0.2.0 release attached six loose binaries and no .mcpb. That leaves
a user to work out which of the six matches their machine, put it
somewhere, and wire stdio up by hand — for a server whose whole point is
that a host launches it. The cross-builds were already there; nothing
combined them.

A 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-<version>.mcpb. One file installs on any of the three,
because server.mcp_config.platform_overrides — keyed by linux,
darwin and win32, which is Node's process.platform and so exactly
what the host looks itself up by — names the matching command at launch.
Its one required user_config value is the workspace directory, which
becomes the server's only writable root, so the install dialog asks for
the thing that would otherwise be an unpredictable process cwd. Only the
MCP server is carried: devup-mcp-visual is a comparison CLI a consumer
repository runs in its own CI, not something a host ever launches.

finalize waits on the bundle as well as the binaries, so a published
release cannot be missing it, exactly as it already could not be missing
a platform.

Three checks hold the bundle to what it claims, each for a failure that
is invisible until a user hits it.

The manifest is schema-validated on every pull request. It was otherwise
first exercised on a release run, where a malformed manifest is found
after the tag and the draft already exist.

Every command the manifest can select is confirmed to exist inside the
staged archive. mcpb validate only ever inspects server.entry_point,
so a typo in a platform_overrides path passes validation and ships a
bundle broken on exactly one operating system and healthy on the two the
runner can see.

The packed archive is read back to confirm the two Unix binaries kept
their executable bit. This is why the job must run on Linux: mcpb writes
Unix permission bits into the zip only from a Unix host — from Windows it
writes none at all — and the artifact round trip between jobs does not
carry file modes either, so the bit has to be restored and then proved.
Measured rather than assumed: mcpb stores (mode & 0o777) << 16, without
the regular-file type bits, so zipinfo renders the type column as ?
even for a correct archive, and a check on a leading - would have
rejected every good bundle instead of the bad ones. The assertion reads
the owner execute position, and was run against four archives — packed
Unix-executable, Unix-executable with type bits, Unix non-executable, and
Windows-packed — passing the first two and failing the last two.

The version comes from [workspace.package] through one script shared
with the release path, so the manifest, the archive filename and the tag
cannot drift apart. The mcpb CLI is pinned, since its packing behaviour
is load-bearing here.

No crate behaviour changes. The version moves because the released
artifacts do.

The 0.2.0 release attached six loose binaries and no `.mcpb`. That leaves
a user to work out which of the six matches their machine, put it
somewhere, and wire stdio up by hand — for a server whose whole point is
that a host launches it. The cross-builds were already there; nothing
combined them.

A `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-<version>.mcpb`. One file installs on any of the three,
because `server.mcp_config.platform_overrides` — keyed by `linux`,
`darwin` and `win32`, which is Node's `process.platform` and so exactly
what the host looks itself up by — names the matching command at launch.
Its one required `user_config` value is the workspace directory, which
becomes the server's only writable root, so the install dialog asks for
the thing that would otherwise be an unpredictable process cwd. Only the
MCP server is carried: devup-mcp-visual is a comparison CLI a consumer
repository runs in its own CI, not something a host ever launches.

`finalize` waits on the bundle as well as the binaries, so a published
release cannot be missing it, exactly as it already could not be missing
a platform.

Three checks hold the bundle to what it claims, each for a failure that
is invisible until a user hits it.

The manifest is schema-validated on every pull request. It was otherwise
first exercised on a release run, where a malformed manifest is found
after the tag and the draft already exist.

Every command the manifest can select is confirmed to exist inside the
staged archive. `mcpb validate` only ever inspects `server.entry_point`,
so a typo in a `platform_overrides` path passes validation and ships a
bundle broken on exactly one operating system and healthy on the two the
runner can see.

The packed archive is read back to confirm the two Unix binaries kept
their executable bit. This is why the job must run on Linux: mcpb writes
Unix permission bits into the zip only from a Unix host — from Windows it
writes none at all — and the artifact round trip between jobs does not
carry file modes either, so the bit has to be restored and then proved.
Measured rather than assumed: mcpb stores `(mode & 0o777) << 16`, without
the regular-file type bits, so zipinfo renders the type column as `?`
even for a correct archive, and a check on a leading `-` would have
rejected every good bundle instead of the bad ones. The assertion reads
the owner execute position, and was run against four archives — packed
Unix-executable, Unix-executable with type bits, Unix non-executable, and
Windows-packed — passing the first two and failing the last two.

The version comes from `[workspace.package]` through one script shared
with the release path, so the manifest, the archive filename and the tag
cannot drift apart. The mcpb CLI is pinned, since its packing behaviour
is load-bearing here.

No crate behaviour changes. The version moves because the released
artifacts do.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Changepacks

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-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-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-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.

@owjs3901
owjs3901 merged commit 74e1b1b into main Sep 8, 2026
8 checks passed
@owjs3901
owjs3901 deleted the ci/mcpb-bundle branch September 8, 2026 04:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant