Skip to content

feat(mcpb): companion Desktop Extension — Claude Desktop config form#38

Merged
kouko merged 6 commits into
mainfrom
feat/companion-mcpb
Jun 12, 2026
Merged

feat(mcpb): companion Desktop Extension — Claude Desktop config form#38
kouko merged 6 commits into
mainfrom
feat/companion-mcpb

Conversation

@kouko

@kouko kouko commented Jun 12, 2026

Copy link
Copy Markdown
Owner

What

Adds a companion .mcpb Desktop Extension (alongside the existing Claude Code plugin) so Claude Desktop / Cowork users get a Tableau-style connection config form in the Connectors panel — no terminal, no JSON, password → OS keychain.

Server package code is unchanged — the bundle reuses the server's existing inline mode via a thin adapter.

Why

The existing Claude Code plugin's userConfig does not render as a form in the Claude Desktop Connectors panel (empirically confirmed — it shows raw config). Only a .mcpb Desktop Extension (like Tableau MCP) renders the form there. plugin and .mcpb are separate artifacts (a .mcpb can't ship skills), so this is a companion — users install one of: the plugin (for skills) or the .mcpb (for the desktop form).

How it works (spike-validated end-to-end on real Claude Desktop)

  • server.type: "uv" → no dependency bundling (deps from PyPI via uv); requires uv on the user's machine.
  • The host honors mcp_config.env (ignores command/args for uv-type), so the 5 form fields inject as REDSHIFT_* env via ${user_config.X}.
  • mcpb/server/main.py (adapter) maps those env vars → the server's inline-mode CLI flags; password stays in REDSHIFT_PASSWORD env (never in argv).
  • Spike confirmed: form renders ✅, injection ✅, list_schemas connects ✅.

Changes

  • mcpb/server/main.py — env→argv adapter (build_argv), lazy server import.
  • scripts/generate_mcpb_manifest.py — generates mcpb/manifest.json (manifest_version 0.4, server.type uv, user_config form, env injection) + bundle pyproject.toml (pins redshift-comment-mcp==<root version>) + .mcpbignore. Version single-sourced from pyproject; generated files gitignored (CI regenerates before pack).
  • tests/test_mcpb_adapter.py, tests/test_mcpb_manifest.py — TDD coverage.
  • tests/test_repo_invariants.py — invariant: generated manifest version == pyproject == plugin.json (no drift).
  • .github/workflows/publish.yml — new pack-mcpb job (needs: publish): generate → mcpb pack → attach .mcpb to the GitHub Release.
  • READMEs ×3 — document the .mcpb install path + uv prereq + "install one of".

Verification

  • ✅ Unit suite: 316 passed, 2 skipped.
  • ✅ Whole-branch review: PASS (env-var names match across adapter/manifest/server/plugin; password never in argv).
  • ✅ Real-Desktop spike: form + injection + connection all confirmed.

Post-merge (separate, manual)

After merge, cut a v0.9.0 GitHub Release → fires publish.yml (publishes 0.9.0 to PyPI — fixing the standing gap where PyPI is stuck at 0.7.1) → the new pack-mcpb job packs + attaches the .mcpb. Bundle-pin ↔ PyPI consistency holds because publish runs before pack.

🤖 Generated with Claude Code

kouko and others added 6 commits June 12, 2026 17:43
Thin bundle entry point: build_argv() maps the form-injected REDSHIFT_* env vars into the server's inline-mode CLI flags; password stays in REDSHIFT_PASSWORD env. Server package unchanged (adapter lives in the bundle). Part of the companion .mcpb (server.type uv).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generates mcpb/manifest.json (manifest_version 0.4, server.type uv, user_config form + ${user_config.*} env injection) + bundle pyproject (pins redshift-comment-mcp==<root version>) + .mcpbignore. Version single-sourced from pyproject; generated files gitignored (CI regenerates before mcpb pack).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…angs)

Adds the Claude Desktop .mcpb path (download from Releases → install → fill form; needs uv) alongside the plugin path; clarifies install ONE of plugin (skills) OR .mcpb (desktop form).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runs the generator into a tmp dir and asserts manifest version == pyproject fallback_version == plugin.json version. The .mcpb is a 4th version surface but auto-derived, so this guards against drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New pack-mcpb job (needs: publish, prerelease==false): generate manifest → npx @anthropic-ai/mcpb pack → softprops upload to the release. Runs after PyPI publish so the bundle-pinned version exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
brainstorm→plan for the Claude Desktop .mcpb (server.type uv); spike PASSED (form+injection+connect); decision: assume uv prereq, no bundling/signing. See also the surface-difference finding (plugin userConfig ≠ desktop form).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kouko kouko self-assigned this Jun 12, 2026
@kouko kouko merged commit 6968e39 into main Jun 12, 2026
3 checks passed
@kouko kouko deleted the feat/companion-mcpb branch June 12, 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.

1 participant