Skip to content

feat: downloadable .mcpb bundles for local stdio servers - #109

Merged
pacnpal merged 8 commits into
mainfrom
claude/auto-generate-mcpb-files-119goz
Aug 2, 2026
Merged

feat: downloadable .mcpb bundles for local stdio servers#109
pacnpal merged 8 commits into
mainfrom
claude/auto-generate-mcpb-files-119goz

Conversation

@pacnpal

@pacnpal pacnpal commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Every local (npx/uvx/command/docker) server can now be exported as an MCPB bundle so users can install it in Claude Desktop and run the same server on their own machine.

What's in the change

  • backend/app/mcpb.py — a small pure module: manifest(server) builds the MCPB manifest.json from the row through the same runners.build_spec the bridge launches (single source of truth — the bundle always carries the exact command/args/env the elevator runs, nothing is stored so nothing can drift), and bundle(server) zips it. Deterministic output: fixed zip timestamp, so the same row yields byte-identical bytes. Non-stdio (remote) specs raise ValueError — there's nothing to run locally.
  • GET /api/servers/{id}/mcpb — streams the bundle as an attachment named <slug>.mcpb. Control-plane gated like every server route (env values in the manifest are visible to exactly the principals who can already read them on the detail endpoint); remote servers get a clean 400.
  • SPA — a "Download .mcpb" row in the server detail Endpoints card (hidden for remote servers). Fetched via a new downloadMcpb helper that carries the bearer header a plain <a href> can't, then saved through an object URL.
  • README — one paragraph under "Adding a server" documenting the button and endpoint.

Notes

  • manifest_version is pinned to 0.2 (the baseline every bundle-aware client accepts); server.type is "binary" since the bundle ships no code — its mcp_config invokes the host's own npx/uvx/docker/executable.
  • cwd and setup_script have no MCPB equivalent and are not represented (noted in the module docstring).

Testing

  • backend/tests/test_mcpb.py: HTTP round trip (zip contents, Content-Disposition, exact mcp_config mirror of the launch spec) and the remote-server 400.
  • Full backend suite: 739 passed. Frontend: svelte-check 0 errors, vitest 67 passed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD


Generated by Claude Code

Every local (npx/uvx/command/docker) server can now be exported as an MCPB
bundle so users can run the same server directly in Claude Desktop:

- app/mcpb.py generates manifest.json + zip on the fly from the Server row
  via the same runners.build_spec the bridge launches (SSOT, nothing stored)
- GET /api/servers/{id}/mcpb streams the bundle (control-plane gated,
  400 for remote servers, which have nothing to run locally)
- SPA: a Download .mcpb button in the server detail Endpoints card,
  fetched with the bearer header and saved via an object URL

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@pacnpal, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 91d4c1f3-3862-47ec-b147-48ee5af93a4f

📥 Commits

Reviewing files that changed from the base of the PR and between eb42c74 and 42cde0c.

📒 Files selected for processing (7)
  • backend/app/api/schemas.py
  • backend/app/api/servers.py
  • backend/app/mcpb.py
  • backend/tests/test_mcpb.py
  • frontend/src/lib/api.ts
  • frontend/src/lib/types.ts
  • frontend/src/routes/server/[id]/+page.svelte
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a Download .mcpb option for local stdio servers.
    • Downloads include the server’s configured command, arguments, and environment settings.
    • Download progress is protected against duplicate requests, with clear error notifications.
  • Bug Fixes

    • Remote servers correctly do not offer .mcpb downloads.
    • Invalid download configurations now return an appropriate error.

Walkthrough

The PR adds MCPB bundle generation for local stdio servers, an authenticated backend download endpoint, frontend download handling, and a server-detail control. Tests validate bundle contents and reject remote servers.

Changes

MCPB download flow

Layer / File(s) Summary
MCPB manifest and archive generation
backend/app/mcpb.py
The backend builds an MCPB 0.2 manifest from the server launch specification and packages manifest.json into a deterministic ZIP archive.
Authenticated bundle endpoint and validation
backend/app/api/servers.py, backend/tests/test_mcpb.py, README.md
The API validates server visibility and transport, returns the bundle as a named attachment, and maps invalid configurations to HTTP 400. Tests inspect the archive and validate remote-server rejection. The README documents the endpoint and local-server restriction.
Frontend download control
frontend/src/lib/api.ts, frontend/src/routes/server/[id]/+page.svelte
The frontend fetches the authenticated bundle, saves it as ${server.slug}.mcpb, prevents concurrent downloads, reports errors, and hides the control for remote servers.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ServerDetailPage
  participant ServersAPI
  participant mcpb
  User->>ServerDetailPage: Select Download .mcpb
  ServerDetailPage->>ServersAPI: Authenticated GET /servers/{server_id}/mcpb
  ServersAPI->>mcpb: Generate local server bundle
  mcpb-->>ServersAPI: Return manifest.json ZIP
  ServersAPI-->>ServerDetailPage: Return downloadable attachment
  ServerDetailPage-->>User: Save ${server.slug}.mcpb
Loading

Possibly related PRs

  • pacnpal/mcpelevator#1: Its server authentication changes support the server visibility and launch configuration used by this download flow.

Poem

A manifest blooms in a ZIP so neat,
With command and environment sweet.
Local servers lift their MCPB wings,
The browser saves what the backend brings.
Remote servers wait beyond the gate.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the addition of downloadable .mcpb bundles for local stdio servers.
Description check ✅ Passed The description accurately covers the backend endpoint, bundle generation, frontend control, documentation, and testing changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/auto-generate-mcpb-files-119goz

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/mcpb.py`:
- Around line 45-50: The MCPB export declares spec.command as server.entry_point
without packaging that executable, producing invalid bundles for npx, uvx,
command, and Docker launches. Update bundle() and the server configuration
around mcp_config to either include a valid bundled launch file and point
entry_point to it, or reject non-bundled commands instead of emitting an invalid
MCPB; add coverage that validates generated bundle bytes with the MCPB
validator.

In `@frontend/src/lib/api.ts`:
- Around line 243-250: Update the error handling in downloadMcpb to detect HTTP
401 before reading the response body, and reuse request’s existing
unauthorized-response behavior to clear the stored token and redirect to /login.
Preserve the current ApiError construction for all other unsuccessful responses.

In `@frontend/src/routes/server/`[id]/+page.svelte:
- Around line 118-128: Update saveMcpb to capture the current server id and slug
before awaiting downloadMcpb, then use those captured values for the request and
generated filename so navigation during the download cannot change the target
bundle name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d52d8016-cb4a-458f-83db-210d96769141

📥 Commits

Reviewing files that changed from the base of the PR and between 2a957c7 and eb42c74.

📒 Files selected for processing (6)
  • README.md
  • backend/app/api/servers.py
  • backend/app/mcpb.py
  • backend/tests/test_mcpb.py
  • frontend/src/lib/api.ts
  • frontend/src/routes/server/[id]/+page.svelte

Comment thread backend/app/mcpb.py
Comment thread frontend/src/lib/api.ts
Comment thread frontend/src/routes/server/[id]/+page.svelte
- downloadMcpb now clears the stale token and redirects to /login on 401,
  matching request()'s behavior
- saveMcpb captures the target id/slug before the await so a same-route
  navigation mid-download can't mislabel the saved file

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb42c747fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/mcpb.py
Comment on lines +48 to +49
"type": "binary",
"entry_point": spec.command,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Package the declared binary entry point

Every generated archive declares a binary server whose entry_point is an external host command such as npx, uvx, or docker, while bundle() writes only manifest.json. MCPB binary entry points must identify an executable included in the bundle, so validation or installation cannot resolve this path and the downloaded bundle will not launch. Include the executable and its supporting files, or generate a supported packaged-server layout instead of classifying an external launcher as the bundled binary.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining — this was verified empirically and CodeRabbit withdrew the same finding above (see the resolved thread on this file). The official @anthropic-ai/mcpb CLI accepts the wrapper-style bundle: validate passes, clean (unpack → re-validate → repack) passes, info reads it. The entry-point-in-archive check exists only in mcpb pack (authoring from a source directory); consumers launch via server.mcp_config, which is exactly what we emit. Shipping no code is the design: the bundle invokes the host's own npx/uvx/docker/executable with the same argv the elevator runs.


Generated by Claude Code

Comment thread backend/app/mcpb.py Outdated
Comment on lines +25 to +28
Raises ``ValueError`` for a non-stdio (remote) server. ``env`` is embedded
verbatim — the download is control-plane-gated, and the same principal
already reads those values on the server detail endpoint. ``cwd`` and
``setup_script`` have no MCPB equivalent and are not represented.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject launch specs whose required context cannot be exported

For a command configured with cwd or a nonempty setup_script, the running bridge depends on context that this export silently discards. Relative arguments/files will resolve from a different directory in Claude Desktop, and prerequisites created by setup will be absent, so the UI still offers a supposedly ready-to-install bundle that cannot reproduce the server. Since these fields have no MCPB representation, either package/rewrite their dependencies or reject and hide downloads for such configurations.

Useful? React with 👍 / 👎.

Comment thread backend/app/mcpb.py Outdated
"manifest_version": "0.2",
"name": server.slug,
"display_name": server.name,
"version": "1.0.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive the bundle version instead of hardcoding it

All exports are permanently labeled 1.0.0, even after the deployed release or a same-slug server configuration changes. MCPB clients use this package version to distinguish updates, so a later download can be treated as the already-installed version rather than an upgrade; derive it from the repository's existing app.__version__ source (or another deterministic version that changes with the exported config) instead of adding a stale constant.

AGENTS.md reference: AGENTS.md:L69-L74

Useful? React with 👍 / 👎.

Comment thread backend/app/mcpb.py
Comment on lines +33 to +35
mcp_config: dict = {"command": spec.command, "args": list(spec.args)}
if spec.env:
mcp_config["env"] = dict(spec.env)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve disabled-tool enforcement in exported servers

When server.disabled_tools is nonempty, the normal bridge installs middleware that hides and rejects those tools, but the generated bundle launches the upstream command directly and serializes only its command, arguments, and environment. Installing the bundle therefore silently re-enables operations the operator deliberately disabled. Either package a filtering wrapper that applies spec.disabled_tools or refuse the export with a clear explanation when this policy is configured.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining as intended behavior, now documented in the module docstring (4ca69a4). disabled_tools filters the elevator's exposed surfaces (MCP proxy, REST, group hub). The .mcpb download is control-plane-gated: the only people who can fetch it are admins/owners who can already read the full launch spec (command, args, env) on the detail endpoint and could reproduce the local run by hand — so no policy is escapable via this export that wasn't already. A packaged filtering wrapper would mean shipping code and a drift surface, which this export deliberately avoids; refusing would block the legitimate "run my own server locally" case for the policy's own author.

The other two findings are fixed in 4ca69a4: export now returns 400 (and the UI hides the button) when the spec depends on cwd/setup_script, and the manifest version derives from app.__version__ instead of a hardcoded constant.


Generated by Claude Code

- reject .mcpb export (400) when the launch spec depends on cwd or a
  setup_script — a bundle can't carry either, so exporting would hand out
  a file that can't reproduce the server; the UI hides the download row
  for those configs
- manifest version now derives from app.__version__ (release-tag SSOT)
  instead of a hardcoded 1.0.0, so re-exports after an upgrade register
  as updates in MCPB clients

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ca69a4aca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/mcpb.py Outdated
"display_name": server.name,
# The elevator's own version (release-tag-derived, never hardcoded — see
# app.__init__), so re-exports after an upgrade register as updates.
"version": __version__.lstrip("v"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Change the bundle version when its launch config changes

When an operator edits a server's command, arguments, or environment and re-downloads it during the same mcpelevator release, this still emits the identical package version, so an MCPB client can treat the changed bundle as the already-installed version rather than an update. Fresh evidence after the prior review is that the follow-up now derives the value solely from __version__, leaving same-release configuration changes unrepresented; retain the release-derived base but include a valid, changing server-configuration revision.

AGENTS.md reference: AGENTS.md:L69-L74

Useful? React with 👍 / 👎.

A same-release config edit now yields a distinguishable version string:
<release>+<config_hash> (hex + dots are valid semver build metadata),
so MCPB clients can tell a re-export of a changed launch config apart
from the already-installed bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1d5433f53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/mcpb.py
# (the "0.0.0+unknown" fallback) — semver allows only one "+".
version = __version__.lstrip("v").split("+", 1)[0]
if server.config_hash:
version = f"{version}+{server.config_hash}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Put the config revision in SemVer precedence

When command, arguments, or environment change within one mcpelevator release, the only version difference is now placed after +; SemVer §10 requires build metadata to be ignored when determining precedence, so an MCPB installer using semantic version comparison can consider the old and new bundles equal and skip the replacement. Fresh evidence after the earlier review is that this follow-up places config_hash specifically in build metadata; keep the release-tag-derived base but use a client-supported revision or a monotonically increasing, precedence-bearing version component instead.

AGENTS.md reference: AGENTS.md:L69-L74

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining the precedence escalation — build metadata is the deliberate stopping point.

A precedence-bearing revision needs a monotonically increasing counter per config change. The Server row has none (config_hash is a hash, not a sequence; updated_at can't be embedded in a valid three-component SemVer), so honoring this would mean adding and persisting a revision column purely for this export. That's real schema surface for a speculative client behavior: manually downloaded .mcpb files are installed by user action, where the installer replaces on confirmation rather than short-circuiting on version precedence — there's no known consumer that semver-compares a hand-installed bundle and skips it. The build-metadata suffix already makes changed configs distinguishable (string-unequal, visible in the UI), which is the concrete need.

If a real client is ever shown to skip on precedence, a revision column is the fix — noted, but not worth the schema change today. The name-stability finding is fixed in 449c70b (name = immutable server id, human name in display_name).


Generated by Claude Code

Comment thread backend/app/mcpb.py Outdated
# 0.2 is the MCPB baseline every bundle-aware client accepts; nothing
# here needs a newer manifest feature.
"manifest_version": "0.2",
"name": server.slug,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the MCPB package name stable across slug renames

When an operator uses the supported slug-rename flow and downloads the bundle again, using the mutable routing slug as the manifest name changes the package identity, so the client can install a second extension instead of updating the first and leave the stale server enabled. The same common slug can also collide with a bundle exported from another mcpelevator instance; derive name from an immutable, instance-unique identifier and keep the mutable human-facing value in display_name.

Useful? React with 👍 / 👎.

manifest name now uses server.id instead of the operator-renameable slug,
so a slug rename (or a same-slug server on another instance) updates the
installed extension instead of forking a second one. display_name keeps
the human-facing name; the download filename stays <slug>.mcpb.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 449c70bee8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/mcpb.py
spec = build_spec(server)
if spec.transport != "stdio":
raise ValueError("only local stdio servers can be exported as .mcpb")
if spec.cwd or spec.setup_script:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject bundles that rely on implicit relative paths

When a command runner uses a relative executable or file argument without setting cwd—for example, python server.py or ./server—this guard permits the export even though the archive contains only manifest.json. The elevator resolves that path from its backend working directory, while Claude Desktop launches it from a different directory where the referenced file is absent, so the downloaded server fails or may run an unintended file. Fresh evidence after the prior launch-context review is that the follow-up rejects only explicit cwd/setup_script dependencies; reject or package filesystem-relative launch inputs as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially fixed in a6877ec: a relative command path (./server, bin/server) is now rejected with a 400 — that's precisely detectable and genuinely non-portable. Relative arguments stay exportable by design: whether server.py is a file reference or an opaque token (-y, @scope/pkg, a subcommand) is undecidable without executing the tool, and a heuristic would reject working configs. The trade-off is noted in a code comment at the guard.


Generated by Claude Code

A relative command (./server, bin/server) resolves against the
elevator's working directory and cannot exist where a client launches
the bundle. Bare names PATH-resolve and absolute paths are well-defined,
so both stay exportable; args are not analyzed (whether 'server.py' is
a file or a token is undecidable).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6877ec344

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

</div>
{/if}
<!-- cwd/setup_script have no MCPB equivalent (the backend refuses those too). -->
{#if server.runner !== 'remote' && !server.cwd && !server.setup_script}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide downloads that the backend will reject

For a command server whose executable is a relative path such as ./server, this condition still displays the download action because it checks only runner, cwd, and setup_script, while the new backend guard in mcpb.manifest() rejects that server with a 400. The advertised button therefore always fails for this supported configuration; mirror the backend eligibility check here or expose an mcpb_exportable field from the API.

Useful? React with 👍 / 👎.

…rule

The detail response now carries the backend's own verdict
(app.mcpb.exportable — 'would manifest() accept this row'), and the
download button renders on that flag alone. Removes the UI's duplicated
runner/cwd/setup_script checks, which had already drifted from the new
relative-path guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb34e00d18

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/mcpb.py Outdated
# the bundle. Bare names (npx, python) PATH-resolve and absolute paths are
# well-defined, so both stay exportable. Args are not analyzed: whether
# "server.py" is a file or a token is undecidable here.
if "/" in spec.command and not spec.command.startswith("/"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject Windows-style relative command paths

When an imported command uses Windows syntax such as .\server.exe or bin\server.exe, this check sees no /, marks the server exportable, and emits a bundle containing only manifest.json; Claude Desktop then resolves that executable relative to its own working directory, where the file is not packaged. Fresh evidence after the prior relative-path review is that the new guard handles only POSIX separators, even though the repository's path classification elsewhere normalizes backslashes too; reject Windows-relative paths while continuing to allow drive-absolute paths.

Useful? React with 👍 / 👎.

The relative-path guard now catches both separator flavors
(.\server.exe, bin\server.exe) while drive-absolute (C:\...) and
UNC (\\host\share) paths stay exportable, matching the POSIX rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxd3iyJwW8Jean5nkABtsD
@pacnpal
pacnpal merged commit ddcc151 into main Aug 2, 2026
8 checks passed
@pacnpal
pacnpal deleted the claude/auto-generate-mcpb-files-119goz branch August 2, 2026 09:59
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.

2 participants