npmswitch
by ApiCartOps
README.md
<p align="center"><img src="https://raw.githubusercontent.com/ApiCartOps/npmswitch/main/assets/logo.svg" width="96" alt="npmswitch logo"></p>
<h1 align="center">npmswitch</h1>
<p align="center"><b>One MCP server, every npm account.</b><br>Routes each call to the right token, for any AI agent.</p>
<p align="center"><img alt="license" src="https://img.shields.io/badge/license-MIT-f59e0b"> <img alt="node" src="https://img.shields.io/badge/node-%3E%3D20-f59e0b"> <img alt="MCP" src="https://img.shields.io/badge/MCP-stdio-f59e0b"></p>
An MCP server and CLI for the npm registry that manages **multiple accounts** (personal, org, scoped) and routes each call to the right token. It speaks plain MCP over stdio, so it works with any MCP-capable agent: Claude Code and Desktop, Cursor, GitHub Copilot (VS Code), Windsurf, Cline, Gemini CLI, Codex CLI, opencode, Continue, Hermes Agent, Zed and others.
Sibling of [Octoswitch](https://github.com/ApiCartOps/github-multi-mcp), the same idea for GitHub accounts. Not affiliated with or endorsed by npm, Inc. or GitHub.
## Why
The npm CLI keeps one login at a time. This keeps several named accounts and picks one per call:
1. an explicit `account` argument (name or npm username), else
2. the account whose username or `--scopes` match the package scope (`@acme/tool` uses the account that owns `@acme`), else
3. the default account.
## Setup
```bash
git clone https://github.com/ApiCartOps/npmswitch.git
cd npmswitch
npm install
npm link # puts the `npmswitch` command on your PATH
npmswitch add personal --default
npmswitch add work --scopes=@acme,@tools
npmswitch list
```
```text
NAME LOGIN SCOPES
* personal alice
work bob @acme @tools
```
Without `npm link`, run `node src/cli.js` in place of `npmswitch`. From npm, `npm install -g @apicartops/npmswitch` installs the same `npmswitch` command (the package is scoped because npm rejects `npmswitch` as too similar to an existing package).
`add` asks for the token at a hidden prompt (or reads it from an env var with `--token-env=VAR`), checks it against the registry and stores the username. Tokens are never passed on the command line, never printed, and never returned by any tool. They are stored in `~/.npmswitch/accounts.json` (file mode 600).
**Tokens.** Create one per account at npmjs.com under Access Tokens. A read-only token is enough for lookups. For deprecate, dist-tags and publish, use a granular token with read and write on the packages, or an automation token. Accounts that require a one-time code work too: pass it as `otp`.
## Connect it to your agent
```bash
npmswitch agents # what is supported, and what looks installed
npmswitch install cursor # or: claude-code, vscode, codex, gemini-cli, ... or: all
npmswitch install cursor --dry-run
npmswitch config hermes # print the snippet instead of editing anything
```
`install` merges one entry into the agent's user-level MCP config and leaves everything else in the file alone. It refuses to touch a file it cannot parse (for example JSON with comments) and prints the snippet instead. `uninstall <agent>` removes it. Restart the agent afterwards.
| Agent | Id | What `install` does |
|---|---|---|
| Claude Code | `claude-code` | `mcpServers` in `~/.claude.json`, plus the skill and slash commands below |
| Claude Desktop | `claude-desktop` | `mcpServers` in `claude_desktop_config.json` |
| Cursor | `cursor` | `mcpServers` in `~/.cursor/mcp.json` |
| VS Code / GitHub Copilot | `vscode` | `servers` in the user `mcp.json` |
| Windsurf | `windsurf` | `~/.codeium/windsurf/mcp_config.json` |
| Cline | `cline` | `cline_mcp_settings.json` |
| Gemini CLI | `gemini-cli` | `mcpServers` in `~/.gemini/settings.json` |
| opencode | `opencode` | `mcp` in `~/.config/opencode/opencode.json` |
| Codex CLI | `codex` | `[mcp_servers.npmswitch]` in `~/.codex/config.toml` |
| Continue | `continue` | `~/.continue/mcpServers/npmswitch.yaml` |
| Hermes Agent | `hermes` | prints a YAML snippet for `~/.hermes/config.yaml` |
| Zed | `zed` | prints a snippet for `settings.json` |
| anything else | `generic` | prints the standard `mcpServers` JSON |
Config locations differ between versions of these tools, so check the printed path if an agent does not pick it up. Use `--npx` to register `npx -y @apicartops/npmswitch mcp` instead of a path to this checkout (once published), and `--name=NAME` to use a different server name.
### Project instructions
```bash
npmswitch init # AGENTS.md, plus any of CLAUDE.md / GEMINI.md / copilot / cursor rule that already exist
npmswitch init --all # write all five
```
This adds a short, marked block telling the agent to use the `npm_*` tools and to confirm before any write. Re-running updates the block in place.
## What it exposes
**Tools** (all agents):
| Read | Write (change a public registry; agents are told to confirm first) |
|---|---|
| `npm_list_accounts`, `npm_whoami`, `npm_search`, `npm_get_package`, `npm_downloads`, `npm_list_packages` | `npm_deprecate`, `npm_set_dist_tag`, `npm_remove_dist_tag`, `npm_publish` |
**Publishing is safe by default.** `npm_publish` is a dry run unless `dry_run: false` is passed, and its description tells agents to show you the account, name, version and file list and to wait for a yes before publishing for real. A published version can never be reused. The token reaches `npm` through a temporary config file (mode 600) that is deleted afterwards, never on the command line, and is scrubbed from the output. Packages marked `"private": true` are refused. The package's own lifecycle scripts run, as with `npm publish`; set `ignore_scripts` to skip them.
**Server instructions**: the same guidance is sent in the MCP `initialize` response, so clients that surface it need no extra setup.
**Prompts** (commands, for clients that support MCP prompts): `npm_accounts`, `npm_package_health`, `npm_release_check`, `npm_deprecate`. They take one optional argument such as a package name or a directory.
**Resource**: `npmswitch://accounts` (names, usernames and scopes only).
**Claude Code extras** (installed by `install claude-code`): the `npmswitch` skill in `~/.claude/skills/` and the slash commands `/npm-accounts`, `/npm-package-health`, `/npm-release-check` and `/npm-deprecate` in `~/.claude/commands/`. The sources live in [`content/`](content), including a standard `SKILL.md` you can copy into any agent that supports Agent Skills.
## CLI reference
| Command | Description |
|---|---|
| `add <name> [--token-env=VAR] [--scopes=@a,@b] [--default]` | Add or replace an account |
| `list [--json]` | Show accounts (no tokens) |
| `default <name>` | Set the default account |
| `remove <name>` | Remove an account |
| `agents` | List supported agents |
| `install <agent\|all> [--name=N] [--npx] [--dry-run]` | Register the server with an agent |
| `uninstall <agent>` | Remove it |
| `config <agent>` | Print the snippet |
| `init [--dir=.] [--only=a,b] [--all]` | Write project instruction files |
| `mcp` | Run as an MCP server over stdio |
## Test
```bash
npm test
```
Runs against a fake registry with temporary config and home folders, including a real `npm publish` to the fake registry; no real accounts or agent configs are touched.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues