Skip to content

Add disable, enable, completions, and alias commands#2

Merged
m1ngshum merged 4 commits into
mainfrom
claude/quick-win-features-1bEvo
Apr 5, 2026
Merged

Add disable, enable, completions, and alias commands#2
m1ngshum merged 4 commits into
mainfrom
claude/quick-win-features-1bEvo

Conversation

@m1ngshum

@m1ngshum m1ngshum commented Apr 5, 2026

Copy link
Copy Markdown
Member

What does this PR do?

This PR adds four new commands to mcpm:

  1. disable <name> – Disables an MCP server across all (or a specific) client config files by setting "disabled": true on the entry. The server remains in config but won't be loaded by the client.

  2. enable <name> – Re-enables a previously disabled MCP server by removing the "disabled": true flag.

  3. completions <shell> – Generates shell completion scripts for bash, zsh, and fish. Users can pipe the output into their shell config to enable tab-completion for mcpm commands and arguments.

  4. alias [args...] – Creates short aliases for long MCP server names (e.g., mcpm alias fs io.github.domdomegg/filesystem-mcp). Aliases are stored in ~/.mcpm/aliases.json and can be resolved automatically in install, info, remove, enable, and disable commands.

The implementation includes:

  • New setServerDisabled() method in BaseAdapter to toggle the disabled flag on server entries
  • A new disabled?: boolean field in McpServerEntry interface
  • Comprehensive unit tests for all new commands and the aliases store
  • Dependency injection pattern for testability
  • Updated list command to display server status (enabled/disabled)

How to test

  • pnpm test passes
  • pnpm typecheck passes

All new functionality is covered by unit tests:

  • src/__tests__/commands/disable.test.ts – Tests for disable command
  • src/__tests__/commands/enable.test.ts – Tests for enable command
  • src/__tests__/commands/completions.test.ts – Tests for completions command
  • src/__tests__/commands/alias.test.ts – Tests for alias command
  • src/__tests__/store/aliases.test.ts – Tests for aliases store

Checklist

https://claude.ai/code/session_01HVQiPLMxn8TFFcKbSyt42s

claude and others added 4 commits April 4, 2026 12:17
- `mcpm disable/enable <name>`: toggle servers on/off without removing
  them by setting `disabled: true` in config entries. Supports --client filter.
- `mcpm completions <shell>`: generate shell completion scripts for
  bash, zsh, and fish with full subcommand and option completions.
- `mcpm alias <shortname> <server>`: create short aliases for long
  server names, stored in ~/.mcpm/aliases.json. Supports --list and --remove.
- `mcpm list` now shows disabled/active status column.
- Added setServerDisabled() to ConfigAdapter interface and BaseAdapter.
- 33 new tests (730 total), all passing.

https://claude.ai/code/session_01HVQiPLMxn8TFFcKbSyt42s
- Extract shared handleToggleServer() into toggle.ts, reducing
  disable.ts and enable.ts from ~124 lines each to ~55 line wrappers
- Single-pass config read per client (was reading twice)
- Validate --client against CLIENT_IDS before unsafe as ClientId cast
- Replace loose alias validation (only blocked / and .) with strict
  regex /^[\w-]+$/ plus length cap, rejecting shell metacharacters
- Block __proto__/constructor/prototype as server names
- Validate alias name on --remove path before calling removeAlias()
- Add tests: mixed-state toggle, invalid client, empty alias, shell
  metacharacters, max length, __proto__ server name, remove validation
@m1ngshum m1ngshum marked this pull request as ready for review April 5, 2026 06:07
@m1ngshum m1ngshum merged commit 7abbb4e into main Apr 5, 2026
4 checks passed
@m1ngshum m1ngshum deleted the claude/quick-win-features-1bEvo branch April 5, 2026 06:07
m1ngshum added a commit that referenced this pull request Jun 1, 2026
…-disable

Applies review findings #1#5 from the multi-perspective review:

- #1/#5: extract the secret→placeholder swap into a single
  `applyKeychainSecrets()` in keychain.ts (also now the home of
  `SecretsMode`). install.ts and up.ts both delegate to it, so the
  "no plaintext secret in config" invariant lives in one place and
  can't diverge. Drops the duplicated loop + the `storedSecretKeys`
  array (now a returned count).
- #2: guard disable's placeholder warning is now a static import,
  never throws (advisory scan can't break a successful disable), and
  surfaces non-ENOENT config-read errors instead of swallowing them.
- #3: `up --secrets keychain` only prints the "run mcpm guard enable"
  notice when a secret was actually stored (threaded via storedCount).
- #4: add tests for applyKeychainSecrets, the slash-name keychain-id
  round-trip, and placeholderEnvKeys (the disable-warning detector).

1088 tests pass; typecheck + build clean.
m1ngshum added a commit that referenced this pull request Jun 1, 2026
* feat: add `--secrets keychain` to install/up + guard-disable safety

Phase 2 of encrypted secrets. Opt-in, default unchanged (plaintext), so
no existing flow regresses.

- install/up `--secrets keychain`: secret-flagged env vars are stored
  AES-GCM-encrypted in ~/.mcpm and written into client configs as
  `mcpm:keychain:server/KEY` placeholders instead of plaintext.
  Non-secret vars stay inline. mcpm guard resolves the placeholders at
  launch (Phase 1). Both commands print a "run `mcpm guard enable`" note.
- `up --secrets keychain` is rejected under `--ci` (never persist
  secrets to a CI runner's keychain).
- keychain.ts: add deriveKeychainId() — maps slash-containing registry
  ids (io.github.owner/repo) to a placeholder-safe keychain id.
- guard disable: read-only post-disable scan warns when an unwrapped
  config still references `mcpm:keychain:` placeholders (they only
  resolve under guard). Never silently rewrites plaintext (security-first).

Tests: 9 new (deriveKeychainId, install/up keychain placeholder writes,
plaintext no-regression, missing-dep + --ci guards). 1082 pass;
coverage 80.85% lines / 87.57% branches. typecheck + build clean.

* refactor: address PR review — consolidate keychain swap, harden guard-disable

Applies review findings #1#5 from the multi-perspective review:

- #1/#5: extract the secret→placeholder swap into a single
  `applyKeychainSecrets()` in keychain.ts (also now the home of
  `SecretsMode`). install.ts and up.ts both delegate to it, so the
  "no plaintext secret in config" invariant lives in one place and
  can't diverge. Drops the duplicated loop + the `storedSecretKeys`
  array (now a returned count).
- #2: guard disable's placeholder warning is now a static import,
  never throws (advisory scan can't break a successful disable), and
  surfaces non-ENOENT config-read errors instead of swallowing them.
- #3: `up --secrets keychain` only prints the "run mcpm guard enable"
  notice when a secret was actually stored (threaded via storedCount).
- #4: add tests for applyKeychainSecrets, the slash-name keychain-id
  round-trip, and placeholderEnvKeys (the disable-warning detector).

1088 tests pass; typecheck + build clean.

* fix(security): injective keychain id + atomic secret store + honest messaging

Addresses the dedicated security audit of the secrets feature:

- CRIT-1: deriveKeychainId is now injective — sanitized prefix + a
  sha256 suffix — so names differing only in unsafe chars
  ("owner/repo" vs "owner_repo") can no longer collide into one secret
  namespace (cross-server secret confusion/exposure).
- MED-1: applyKeychainSecrets persists all of a server's secrets in a
  single atomic batch (new keychain.setSecrets) BEFORE returning the env
  written to config — no orphaned half-written secrets if one fails.
  install/up deps switch from setSecret to setSecrets accordingly.
- MED-4: install/up notices no longer overclaim — they state secrets are
  "encrypted at rest (protects against other-user/offline access, not
  same-user processes)", matching keychain.ts's documented posture.

Tests updated to the batch API + an explicit collision-injectivity test.
1088 pass; typecheck + build clean.

Pre-existing guard/keychain findings (full-env forward, event-log
sanitization, store write lock, log rotation) are out of scope here and
tracked separately.
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