How I Run Multiple Claude Code Accounts From One Terminal
Directory-Scoped AI Workspaces: Isolating Claude Code Profiles with direnv
Current Situation Analysis
The modern developer workstation is no longer a single-tenant environment. Engineers routinely juggle personal projects, employer repositories, and client engagements, each carrying distinct authentication tokens, service integrations, and compliance requirements. When AI coding assistants transition from chat interfaces to terminal-based agents with filesystem and network access, the assumption of a single global configuration becomes a liability.
Claude Code operates as a stateful runtime. It maintains authentication sessions, conversation history, tool permissions, and MCP (Model Context Protocol) server configurations. Treating it like a stateless CLI utility creates immediate friction: context bleeding across projects, accidental billing attribution to the wrong account, and credential leakage between isolated environments.
This problem is frequently overlooked because AI tooling is still maturing within the developer workflow. Most teams apply legacy environment management patterns (shell aliases, manual export commands, or Docker containers) to a tool that requires persistent state isolation. The result is fragile switching logic that breaks under routine directory navigation.
The security implications are documented. Community reports highlight billing confusion and accidental cross-context tool execution. More critically, supply-chain incidents involving MCP servers demonstrate the risk of unbounded tool permissions. Postmark published a security advisory regarding a malicious postmark-mcp npm package that exfiltrated outgoing emails. Koi Security and OX Security subsequently documented how MCP servers inherit the exact permissions granted by the host agent, with command injection vulnerabilities capable of escalating to remote code execution. A single global AI profile turns every connected service into a unified attack surface. Directory-scoped isolation does not eliminate malicious package risks, but it strictly contains the blast radius when a tool behaves unexpectedly.
WOW Moment: Key Findings
The architectural shift from global state to directory-scoped profiles fundamentally changes how AI agents interact with your workspace. By anchoring configuration to filesystem boundaries, you convert manual context switching into an automatic, zero-overhead operation.
| Approach | Credential Isolation | Context Leakage Risk | Setup Overhead | Security Blast Radius |
|---|---|---|---|---|
| Global Single Profile | None | High | Low | Entire workstation |
| Shell Alias Switching | Manual/Partial | Medium | High (per-session) | Per-terminal session |
| Directory-Scoped Profiles | Automatic/Strict | Near Zero | Low (one-time) | Per-trust-boundary |
This finding matters because it decouples identity management from terminal sessions. Instead of remembering to toggle accounts before opening a repository, the workspace itself dictates the agent's permissions, history, and toolchain. The pattern scales predictably: add a new client or product line, create a boundary, and the AI agent automatically inherits the correct operational context.
Core Solution
The mechanism relies on two established components: direnv for directory-aware environment injection, and CLAUDE_CONFIG_DIR for redirecting Claude Code's state directory. The architecture treats each profile as a trust boundary rather than a per-repository artifact.
Step 1: Define Trust Boundaries
Do not create a profile for every repository. Create a profile for every identity, credential set, or compliance domain. Group repositories that share the same GitHub organization, Slack workspace, Linear team, or customer data access under a single boundary.
~/.ai-workspaces/
personal/
employer/
engineering/
clients/
acme-corp/
nova-fintech/
sandbox/
Step 2: Initialize Profile Storage
Create the directory structure that will hold isolated state, conversation logs, and MCP configurations.
mkdir -p ~/.ai-workspaces/personal
mkdir -p ~/.ai-workspaces/employer/engineering
mkdir -p ~/.ai-workspaces/clients/acme-corp
mkdir -p ~/.ai-workspaces/clients/nova-fintech
mkdir -p ~/.ai-workspaces/sandbox
Step 3: Configure Directory-Aware Injection
Place .envrc files at the root of each trust boundary. direnv will automatically load these variables when entering the directory and unload them when leaving.
# ~/projects/personal/.envrc
export CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/personal"
# ~/projects/employer/engineering/.envrc
export CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/employer/engineering"
# ~/projects/clients/acme-corp/.envrc
export CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/clients/acme-corp"
Step 4: Authorize and Initialize
Navigate to a boundary root and authorize the environment file. The first invocation of Claude Code will initialize the profile directory with default configurations.
cd ~/projects/clients/acme-corp
direnv allow
claude
Execute /login to authenticate the account for this boundary. Configure MCP servers using /mcp. All subsequent repositories under ~/projects/clients/acme-corp will automatically inherit this isolated state.
Step 5: Decouple Shared Definitions from Private Credentials
MCP server definitions can be standardized across teams, but authentication tokens must remain scoped. Store shared server manifests in a version-controlled .mcp.json at the repository level, while keeping OAuth tokens, API keys, and session cookies inside the directory-scoped profile.
// .mcp.json (committed to repo)
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
},
"linear": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-linear"]
}
}
}
Authentication state remains isolated within ~/.ai-workspaces/clients/acme-corp/.claude/. This prevents credential cross-contamination while allowing team-wide tool standardization.
Architecture Rationale
- Why
direnv? It is the industry standard for directory-scoped environment management. It automatically unloads variables oncd, preventing state leakage between terminals. It supports team-wide approval workflows and integrates cleanly with existing shell configurations. - Why folder-level boundaries? Developers already organize workspaces by trust domain. Aligning AI state with filesystem hierarchy eliminates cognitive overhead. Per-repo profiles create unnecessary duplication and violate the principle of least privilege at the boundary level.
- Why
CLAUDE_CONFIG_DIR? It is the official environment variable for redirecting Claude Code's configuration root. It cleanly separates authentication, history, permissions, and MCP state without requiring wrapper scripts or Docker overhead.
Pitfall Guide
1. Over-Partitioning Profiles
Explanation: Creating a unique profile for every repository leads to configuration drift, duplicated MCP setups, and fragmented conversation history. Fix: Group repositories by shared credential sets and compliance requirements. Only split further when a specific project requires distinct OAuth scopes or isolated data access.
2. Committing .envrc or Profile Paths to Version Control
Explanation: Environment files containing CLAUDE_CONFIG_DIR paths or local directory references should never enter shared repositories. They expose local filesystem structure and can cause path collisions on other machines.
Fix: Add .envrc to .gitignore. Distribute boundary definitions via internal documentation or team onboarding scripts, not through source control.
3. Mixing Global MCP Auth with Scoped Credentials
Explanation: Storing API keys or OAuth tokens in global shell profiles (~/.bashrc, ~/.zshrc) while using directory-scoped AI workspaces creates unpredictable authentication behavior. The agent may fall back to global credentials when scoped ones are missing.
Fix: Remove AI-related credentials from global shell configurations. Rely exclusively on CLAUDE_CONFIG_DIR and local .envrc files for authentication state.
4. Assuming Isolation Neutralizes Malicious MCP Servers
Explanation: Directory scoping limits which credentials a compromised tool can access, but it does not prevent command injection or data exfiltration within that boundary.
Fix: Treat MCP servers as untrusted by default. Audit server source code, pin package versions, use --no-install flags where supported, and implement runtime permission prompts for sensitive operations.
5. Inconsistent direnv Approval Across Team Members
Explanation: New team members or CI environments may fail to load scoped profiles if .envrc files are not explicitly allowed, causing silent fallback to global state.
Fix: Document the direnv allow requirement in onboarding guides. Consider using direnv's allow command in setup scripts, or leverage DIRENV_WARN_TIMEOUT to surface missing approvals during development.
6. State Corruption from Overlapping Environment Variables
Explanation: Running multiple terminals in the same directory while manually overriding CLAUDE_CONFIG_DIR can cause concurrent writes to the same profile directory, corrupting conversation history or MCP state.
Fix: Avoid manual export overrides when direnv is active. If debugging requires temporary state redirection, use explicit temporary directories and clean them up immediately after testing.
7. Neglecting Offboarding Procedures
Explanation: When a client engagement ends or an employee leaves, archived profiles may retain active OAuth tokens, cached credentials, and conversation logs containing sensitive context. Fix: Implement a profile retirement checklist: revoke OAuth access at the provider level, archive the directory-scoped state, and securely delete local conversation logs. Automate this via internal tooling where possible.
Production Bundle
Action Checklist
- Audit existing workspace structure and identify natural trust boundaries (personal, employer, clients, sandbox)
- Install and configure
direnvin your shell environment (eval "$(direnv hook zsh)"or equivalent) - Create centralized profile storage directory (
~/.ai-workspaces/or equivalent) - Place
.envrcfiles at boundary roots withCLAUDE_CONFIG_DIRexports - Authorize each
.envrcwithdirenv allowand verify automatic loading/unloading - Initialize each profile with
/loginand configure MCP servers via/mcp - Standardize MCP server definitions in repository-level
.mcp.jsonfiles - Document offboarding procedures for profile archival and credential revocation
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|---|---|---|
| Solo developer with personal + side projects | Single boundary with two profiles (personal, sandbox) | Minimal overhead, clean separation of experimental vs production state | Zero |
| Agency/Freelancer managing 5+ clients | Directory-scoped profiles per client | Prevents credential leakage, simplifies compliance, enables clean offboarding | Low (setup time) |
| Enterprise engineering team | Shared MCP definitions + scoped auth boundaries | Standardizes tooling while isolating org credentials and conversation history | Medium (team coordination) |
| Security-first / regulated workloads | Strict directory isolation + runtime permission prompts | Limits blast radius of MCP supply-chain incidents, enforces least privilege | High (operational overhead) |
Configuration Template
# ~/.config/direnv/direnv.toml (optional: customize warning behavior)
[global]
warn_timeout = "5s"
# ~/projects/.envrc (example boundary root)
export CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/personal"
# ~/projects/employer/.envrc
export CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/employer/engineering"
# ~/projects/clients/acme/.envrc
export CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/clients/acme"
# Repository-level .mcp.json (shared tool definitions)
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github@1.0.2"]
},
"sentry": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sentry@0.5.1"]
}
}
}
Quick Start Guide
- Install
direnv: Runbrew install direnv(macOS) orsudo apt install direnv(Linux). Addeval "$(direnv hook zsh)"to your shell config and reload. - Create Profile Storage: Execute
mkdir -p ~/.ai-workspaces/{personal,work,clients/sandbox}to establish isolated state directories. - Define Boundaries: Place
.envrcfiles in your project roots withexport CLAUDE_CONFIG_DIR="$HOME/.ai-workspaces/<boundary>". Rundirenv allowin each directory. - Initialize & Authenticate: Open a terminal in a boundary directory, run
claude, execute/loginto authenticate, and configure tools via/mcp. - Verify Isolation: Navigate between boundaries and confirm that conversation history, tool permissions, and authentication state remain strictly scoped to each directory.
