More than one network
Two servers on one machine, without the credentials mixing.
One machine often has reason to reach two networks: a self-hosted deployment and a shared one, a client's server and your own, work and personal. Two things have to hold for that not to hurt.
Credentials never mix
Tokens are filed under the server that issued them, so a token cannot be read for a server that did not mint it. The failure is Not logged in to <url>, before anything is sent, rather than a 401 from the far end after you thought it worked.
One workspace slug on two servers is two identities, not one slot.
constellation login --server https://work.example
constellation login --server http://localhost:4000
Registrations never collide
An MCP client resolves a server by name. Two installs sharing a name means the second silently rebinds the first, and nothing tells you. Give the second one its own:
constellation install mcp --name constellation-dev --server http://localhost:4000
Confining one to a single checkout
If a network belongs to one project rather than the whole machine:
constellation install mcp --scope local --name constellation-dev
Claude Code gets it under projects[<repo>] in ~/.claude.json. Cursor gets a .cursor/mcp.json inside the repository, gitignored because it carries absolute paths. Claude Desktop is skipped — it keeps one global config and cannot express the scope at all, and writing a machine-wide entry instead is exactly the failure the flag exists to prevent.
local is Claude Code's own word. Its project scope means a committed .mcp.json shared with the team, which is a different thing.
Claude Desktop can still hold both, just not scoped — one config takes as many differently-named entries as you like:
constellation install mcp --name constellation-dev --client claude-desktop
There is no containment there, though: both toolsets are live in every conversation, and the model has to pick the right one.
Keeping two identities apart
CONSTELLATION_HOME moves the whole state directory, so a second identity is written beside the first instead of over it:
CONSTELLATION_HOME=~/.constellation-work constellation login --server https://work.example
An MCP registration does not inherit the environment its client was started with, so install mcp writes the current CONSTELLATION_HOME into the entry whenever it has been moved off the default. Without that, the hooks and the MCP server read different credentials while both look correctly configured.
Hooks work the other way round
This is the one place the pattern inverts, and it is worth reading twice.
An MCP server is resolved by name, so two names coexist happily. Hooks are not. Claude Code merges hooks from every settings file and offers no override — install a global hook and a second one inside a checkout, and both fire there. One session, opened on two networks, two digests.
So there is one hook install, and a directory retargets it rather than adding its own. The front door for that is the config command, which writes the same settings layers every command and hook reads:
constellation config list # every key, and which layer said so
constellation config set server https://staging.example # this machine
constellation config set server http://localhost:4000 --local # this checkout only
constellation config set hooks off --local # a repo that should not post at all
CONSTELLATION_HOOKS=off in the environment silences a one-off shell or CI job; the --local form is the durable per-checkout answer. The hook reads that and exits before doing anything — it exists because the harness has no way to turn a merged hook off, and the CLI is the process, so the override has to come from this side.