Skip to content

feat(install): register Claude Code MCP server at user scope#270

Draft
gbrlcustodio wants to merge 4 commits into
feat/234-release-wheel-urlsfrom
feat/mcp-install-user-scope-override
Draft

feat(install): register Claude Code MCP server at user scope#270
gbrlcustodio wants to merge 4 commits into
feat/234-release-wheel-urlsfrom
feat/mcp-install-user-scope-override

Conversation

@gbrlcustodio

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #260. Removes the double materialization of the Pipefy MCP server on the Claude Code path and makes the installer the single owner of the Claude Code registration.

Previously /pipefy:install installed pipefy_mcp_server as a uv tool that the plugin's bundled .mcp.json (uvx) never referenced, so a plugin user who ran it ended up with two copies (the uvx-cached env plus an unused tool venv).

What changed

  • install.sh: the --client claude-code branch now registers the server via claude mcp add pipefy --scope user -- pipefy-mcp-server (idempotent: removes any prior user-scope entry first). By Claude Code's name precedence (local > project > user > plugin) the user-scope entry shadows the plugin's bundled entry, so only the installed binary spawns, on the same system Python the CLI is pinned to. require_claude errors cleanly if the claude CLI is absent.
  • commands/install.md: /pipefy:install switches from --client none to --client claude-code, and the prose explains the override + required reload.
  • .claude-plugin/plugin.json + hooks/check-server-version.sh: a SessionStart hook that nudges the user to re-run /pipefy:install when the installed server version drifts from the plugin's. No-ops for users on the pure plugin/uvx path (no installed binary).

Design notes

  • The committed .mcp.json (uvx) is unchanged and remains the zero-config default, so /plugin install pipefy@pipefy still works with no prior step. The override only kicks in once a user runs /pipefy:install.
  • Why user scope, not editing the plugin: there's no supported way to disable just one plugin's MCP server; name-shadowing at a higher scope is the only lever, and it's deterministic per the documented precedence.
  • Why the drift hook: shadowing means plugin auto-updates stop reaching the running server. The hook is the accepted mitigation for that staleness. It reads the plugin version (cheap sed on plugin.json) before spawning pipefy-mcp-server --version, so the Python cold start only happens for users who actually have the override installed.
  • The server only reads the keychain (load_session -> keyring.get_password); the documented macOS -25244 issue is a non-deterministic write during pipefy auth login. So this is about consistency with the CLI's system-Python install, not a known server-side failure.

Test plan

  • sh -n install.sh and sh -n hooks/check-server-version.sh
  • .claude-plugin/plugin.json parses as valid JSON
  • install.sh --dry-run --client claude-code previews the claude mcp add registration
  • Hook verified in all three states: no binary (silent), version drift (nudge), version match (silent)
  • uv run pytest (2955 passed, 46 skipped)
  • On a machine with Claude Code: after /pipefy:install + reload, confirm claude mcp list shows the user-scope pipefy and the plugin's uvx entry is suppressed

Release sequencing

Targets #260's branch to keep the stack; retarget to main once #260 merges.

`/pipefy:install` previously installed `pipefy_mcp_server` as a uv tool that
the Claude Code plugin's bundled `.mcp.json` (uvx) never referenced, so a
plugin user who ran it ended up materializing the server twice.

Make the installer the single owner of the Claude Code server registration
instead: `install.sh --client claude-code` now runs `claude mcp add pipefy
--scope user -- pipefy-mcp-server`. By Claude Code's name precedence
(local > project > user > plugin) the user-scope entry shadows the plugin's
bundled entry, so only the installed binary spawns, on the same system Python
the CLI uses. The committed `.mcp.json` (uvx) stays as the zero-config default
for the marketplace one-liner.

Shadowing means plugin auto-updates no longer reach the running server, so add
a SessionStart hook (hooks/check-server-version.sh) that nudges the user to
re-run `/pipefy:install` when the installed server version drifts from the
plugin's. It no-ops for users on the pure plugin/uvx path.

`/pipefy:install` switches from `--client none` to `--client claude-code`.
@gbrlcustodio gbrlcustodio self-assigned this Jun 2, 2026
@gbrlcustodio gbrlcustodio requested a review from adriannoes June 2, 2026 18:56
@gbrlcustodio gbrlcustodio added the enhancement New feature or request label Jun 2, 2026

@adriannoes adriannoes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

Reviewed the install/plugin diff locally (sh -n, install.sh --dry-run --client claude-code). The user-scope registration cleanly fixes double materialization (uv tool + plugin uvx) and the hook’s “cheap checks first” ordering is sensible. One version-sync gap is worth addressing before this ships broadly (inline on the hook).

Also noted

  • F2 — install ordering: main() installs both uv tools before require_claude, so a missing claude CLI leaves pipefy / pipefy-mcp-server on disk but exits before claude mcp add. Since /pipefy:install now passes --client claude-code, consider checking for claude before install_tool, or finishing with a clear warning instead of err after wheels are already installed.

Comment thread hooks/check-server-version.sh
…flicts

Adopt the org rename (pipefy/ai-toolkit) and the install.sh-delegation form
from the updated base, while keeping this branch's additions: the plugin.json
SessionStart version-check hook and the install.sh --client claude-code
(user-scope MCP registration) behavior.
The SessionStart hook (hooks/check-server-version.sh) raw-string compares the
installed server's --version against plugin.json's version, but bump_version.py
never wrote or verified that file. Releases left the manifest behind, so users
on the current release got a drift nudge every session. Add .claude-plugin/
plugin.json to the write and verify paths and sync it to the package version.
…atterns

Use numbered groups and re-emit the closing quote in the replacement, matching
VERSION_ASSIGN_RE and ROOT_PROJECT_VERSION_RE in the same file, instead of the
named-group prefix/value/suffix form. Verify reads the version via group(2).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants