One-time setup for engineers to use AI-assisted workflows.
- Node.js >= 18
- Git
- Cursor IDE (or another AI editor: VS Code + Copilot, Claude Code, Antigravity, etc.)
- Access to your organization's GitHub repos
Keep all repos under a single root directory:
~/dev/<your-org>/
βββ workspace/ # this repo
βββ <project-a>/ # your app, service, etc.
βββ <project-b>/
βββ ...
This lets AI tools search across repos, reference files consistently, and share conventions.
mkdir -p ~/dev/<your-org>
cd ~/dev/<your-org>
git clone <your-teams-workspace-repo> workspace
cd workspace && npm installOpen ~/dev/<your-org> in Cursor as your workspace.
Your team's workspace repo is a fork of the aiworkspace template. Each team owns their copy β customize skills, docs, and root-config/ freely. See Upgrading for how to pull template updates.
Useful for PRs, issues, and repo operations from the terminal:
brew install gh
gh auth login # GitHub.com β HTTPS β web browser
gh auth status # verifynpm install mirrors MCP configs from root-config/ to the parent workspace root automatically. No manual Cursor Settings setup needed.
The workspace ships configs for all major tools β Cursor, Claude Code, Codex, and VS Code. Every developer gets all of them; there is no per-tool opt-out. Unused symlinks are harmless. npm install and git hooks will recreate parent-root tool dirs if removed.
The workspace ships with context7 β up-to-date library and framework documentation (HTTP OAuth β sign in via Settings β MCP).
After install:
- Open
~/dev/<your-org>/in Cursor (or your AI editor) - Restart the editor if MCP servers don't appear immediately
- For context7: Settings β MCP β Authenticate (OAuth)
Configs are symlinked from root-config/:
| Parent root | Points to |
|---|---|
.agents/mcp.json |
workspace/root-config/.agents/mcp.json (canonical) |
.mcp.json |
.agents/mcp.json (Claude Code) |
.cursor/mcp.json |
workspace/root-config/.cursor/mcp.json (Cursor twin, ${env:VAR} syntax) |
.codex/config.toml |
workspace/root-config/.codex/config.toml (Codex) |
.vscode/mcp.json |
workspace/root-config/.vscode/mcp.json (VS Code) |
To override MCP for a single project, add <project>/.cursor/mcp.json β nearest-wins.
To add more servers later, edit root-config/.agents/mcp.json only β npm run sync regenerates the Codex and VS Code twins from canonical. See root-config/AGENTS.md.
MCP servers that need tokens load them from .env.local at the parent workspace root. Open the parent directory (not a single project repo) in your editor so MCP paths resolve correctly.
One-time setup (from parent workspace root):
cp .env.example .env.local # fill in your tokensThen restart your editor (MCP reads config at startup).
.env.local is gitignored. .env.example lives in root-config/ and is symlinked to the parent root. OAuth HTTP servers (e.g. Slack) use the editor's sign-in flow β no .env.local entry needed.
Prefix secret keys when loading into a shell. Bearer tokens for Cursor are often loaded via mcp:install-shell or a manual source of .env.local in ~/.zshrc / ~/.bashrc. Those variables then live in your login shell (and on macOS, may be pushed to launchctl for Dock-launched apps). Use a workspace-specific prefix in .env.example and matching ${env:VAR} / ${VAR} placeholders in mcp.json β for example ACME_SONAR_TOKEN rather than SONAR_TOKEN β to avoid clashing with other projects, CLI tools, or generic names. Keep the same names in .env.local, mcp.json, and .env.example.
npm run sync wraps secret-bearing stdio servers that use ${VAR} placeholders with a built-in env loader (mcp-load-env.mjs). Those read .env.local directly β no shell profile changes, no direnv, no extra packages.
Cursor also supports envFile: "${workspaceFolder}/.env.local" on stdio servers natively; the env loader is the workspace default so secrets work the same in Claude Code and other tools.
Some remote MCP servers have no OAuth endpoint and need a Bearer token in headers, for example:
"headers": {
"Authorization": "Bearer ${SONAR_TOKEN}"
}Write canonical mcp.json with bare ${NAME} β that's the syntax Claude Code resolves natively via its symlink. Cursor's MCP client only resolves ${env:NAME} for headers, not bare ${NAME}, so npm run sync regenerates .cursor/mcp.json as its own twin with the placeholder translated automatically. Never hand-edit ${env:NAME} into canonical β that breaks Claude Code, which reads canonical directly and does not understand the env: prefix.
| Editor | How the token is loaded |
|---|---|
| VS Code | Sync adds envFile: "${workspaceFolder}/.env.local" to the MCP twin |
| Codex | bearer_token_env_var in .codex/config.toml β set the var in your shell |
| Cursor | Twin's ${env:NAME} reads from the process environment at Cursor startup β not from envFile (stdio only) |
| Claude Code | Canonical's bare ${NAME} (via the .mcp.json symlink) reads from the real process environment at connection time β same requirement as Cursor, just different placeholder syntax; not from .env.local directly |
So for both Cursor and Claude Code, load Bearer tokens into the process environment before the editor/CLI starts.
Recommended (all platforms) β from your workspace repo:
cd path/to/your-workspace-repo
npm run mcp:install-shellThis appends a marked block to your shell profile (~/.zshrc, ~/.bashrc, and/or PowerShell $PROFILE). Every new shell exports the Bearer vars directly β which is what a terminal-launched claude inherits β and on macOS it also runs launchctl setenv for Bearer keys so Dock-launched Cursor inherits them too. Re-run after moving the repo or changing Bearer vars in mcp.json. Remove with npm run mcp:uninstall-shell.
Multiple clones on one machine. Each clone gets its own independently identified block (a per-clone id in local/.mcp-env.id), so running mcp:install-shell/mcp:uninstall-shell from one workspace never touches another workspace's block in the same shell profile. Moving a clone (mv) preserves its identity as long as local/.mcp-env.id travels with it, so re-running mcp:install-shell still updates that clone's own block in place instead of adding a duplicate. Marker isolation doesn't cover same-named Bearer keys, though β two clones both exporting e.g. GITHUB_PAT still collide at the OS-env level, so prefix your key names as described above.
Windows GUI apps: add --persist to write User environment variables from .env.local:
npm run mcp:install-shell -- --persistWithout a login profile β vars stay in one terminal session only (Dock-launched Cursor won't see them):
cd ~/dev/<your-org>
set -a && source .env.local && set +a && cursor .direnv with dotenv .env.local works the same if you use it. Prefer OAuth MCP servers when you can; VS Code loads Bearer tokens from .env.local via envFile without a shell step.
Manual fallback (macOS / Linux) β add to ~/.zshrc or ~/.bashrc (adjust the path to your parent workspace root):
[ -f "$HOME/dev/<your-org>/.env.local" ] && set -a && source "$HOME/dev/<your-org>/.env.local" && set +aManual fallback (Windows PowerShell) β add to your PowerShell profile, or run before starting Cursor:
$envFile = "$env:USERPROFILE\dev\<your-org>\.env.local"
if (Test-Path $envFile) { Get-Content $envFile | ForEach-Object { if ($_ -match '^\s*([^#=]+)=(.*)$') { Set-Item -Path "env:$($matches[1].Trim())" -Value $matches[2].Trim() } } }Alternatively, set User environment variables manually (Settings β System β Environment variables) from the keys in .env.local.
Restart Cursor / relaunch claude after changes. If Bearer headers still send the literal ${env:VAR} / ${VAR} string, the editor did not inherit the variables β check with claude mcp get <server> for Claude Code, or Cursor's MCP settings panel for Cursor:
- macOS: launch Cursor from Terminal after
source ~/.zshrc, or set session vars withlaunchctl setenvbefore opening from Dock;claudeinherits whatever shell launched it, so open a fresh terminal after installing the profile block - Linux: add vars to
/etc/environmentor~/.pam_environment, or always launch from a login shell - Windows: use User/System environment variables and fully restart Cursor (not just reload window)
Prefer OAuth HTTP servers ({ "type": "http", "url": "..." } with no Bearer header) whenever the provider supports it.
Run npm run mcp:check-secrets for a non-fatal hint if tokens are missing, .env.local is absent, placeholders are still empty, or HTTP Bearer servers need the shell step above (also runs on every postinstall).
Codex + OAuth HTTP servers: the generated .codex/config.toml sets experimental_use_rmcp_client = true and emits a url for each HTTP server. For OAuth servers, run the one-time codex mcp login <name> (the sync output lists the exact commands). Note: GitHub's Copilot MCP (api.githubcopilot.com/mcp/) only supports OAuth for first-party clients (Cursor, VS Code, Claude); in Codex it requires a PAT via a Bearer ${env:VAR} header instead β or just use Cursor/VS Code for GitHub.
npm install sets up everything automatically:
- Restores skills from
skills-lock.json - Mirrors
root-config/to parent root (symlinks files and directories) - Creates per-skill symlinks for each AI tool
- Installs git hooks (post-merge, post-checkout) for auto-sync
cd ~/dev/<your-org>/workspace
npm install
npm run skills:list # verifynpm run skills:add -- <source> [--project <repo>] # add
npm run skills:remove -- [<skill>] [--project <repo>] # remove
npm run skills:list # list
npm run skills:update # update all
npm run skills:create -- --name my-skill # create manually
npm run skills:setup # re-syncWithout --project: workspace-wide (installs to root-config/.agents/skills/). With --project: project-only (installs to <repo>/.agents/skills/).
- Cursor:
@workspace/.agents/skills/<name>/SKILL.mdin chat - Codex, Amp, Gemini CLI: auto-discovered from
.agents/skills/
npm install mirrors team settings.json to the parent root. For personal MCP enable toggles, copy the example once (sync never overwrites your live file):
cp <workspace-repo>/root-config/.claude/settings.local.json.example .claude/settings.local.jsonEdit locally β remove servers you do not use. MCP definitions stay in .agents/mcp.json; enabledMcpjsonServers only controls which servers Claude Code activates for you.
If you already have a local copy at the parent root, the first sync backs it up when it differs from team canonical β see root-config/README.md β Migrating mirrored settings.json.
npm install mirrors team .cursor/settings.json to the parent root. Personal editor preferences and MCP enable/disable stay in Cursor User settings and Settings β MCP.
If you already have a local copy at the parent root, the first sync seeds or backs it up β see root-config/README.md β Migrating mirrored settings.json.
| Tool | Team (mirrored on sync) | Personal (never mirrored) |
|---|---|---|
| All | AGENTS.md, .agents/mcp.json |
.env.local (secrets) |
| Claude | .claude/settings.json |
.claude/settings.local.json |
| Cursor | .cursor/settings.json, .cursor/rules/, .cursor/mcp.json |
Cursor User settings, MCP enable in Settings β MCP |
| VS Code | .vscode/settings.json, extensions.json, mcp.json |
User settings, MCP UI |
| Codex | config.toml preamble, .codex/rules/ |
~/.codex/config.toml, codex mcp login |
Use Cursor's @Docs > Add new doc for built-in indexing. For non-Cursor tools, create a docs-3rdparty/ sibling repo.
The workspace uses two git remotes:
| Remote | Points to | Purpose |
|---|---|---|
origin |
Your team's repo | Where your team pushes changes (skills, docs, configs) |
upstream |
The aiworkspace template | Source for script updates and bug fixes |
npx aiworkspace init sets up upstream automatically. Team members who clone from origin only have origin β npm run upgrade adds upstream on first run.
Initial setup (person who ran init):
git remote add origin <your-teams-workspace-repo>
git push -u origin mainPulling template updates (anyone on the team):
npm run upgrade # npm update aiworkspace + copy scripts/ (or git upstream fallback)
git diff --cached # review what changed (both paths stage scripts/)
git commit -m "upgrade scripts from aiworkspace"Syncing config changes (after editing root-config/, especially .agents/mcp.json):
npm run sync # regenerate MCP twins + mirror to parent root (no template bump)
git diff --cached # review what changed
git commit -m "sync mcp configs"New workspaces include aiworkspace in devDependencies so npm outdated shows when a newer template is on npm. Your team's own version in package.json stays independent.
npm run upgrade also chains workspace sync (MCP merge + parent-root symlinks). For MCP-only edits you do not need upgrade β use sync.
Only scripts/ is updated from the template package (and lockfile if npm changed the devDep). MCP files are merged into your root-config/ without overwriting your custom servers. Your other root-config/ files (AGENTS.md, rules) and skills stay yours.
If you have no aiworkspace devDependency (older layout), upgrade uses git fetch upstream and checks out scripts/ from upstream/main instead.
| Problem | Fix |
|---|---|
| context7 MCP not working | Complete OAuth in Settings β MCP, restart Cursor |
| MCP configs missing at parent root | cd workspace && npm run sync (or npm run skills:setup), verify ls -la ../.agents/mcp.json |
| Skills not showing up | cd workspace && npm run skills:setup, verify ls root-config/.agents/skills/ |
| MCP server red/error | Click server name in Cursor Settings -> MCP for details, restart Cursor |
| HTTP MCP Bearer auth fails in Cursor or Claude Code | See <workspace-repo>/setup.md Β§4.1 β Unix: ~/.zshrc source line; Windows: User env vars or PowerShell profile. For Claude Code specifically, run claude mcp get <server> β if the header shows the literal ${VAR} string unexpanded, the shell that launched claude didn't have the var (open a fresh terminal after mcp:install-shell) |
npm install fails on postinstall |
Run node scripts/skills/setup-skills.mjs manually to see errors |
npm run upgrade fails |
With aiworkspace in devDependencies: run npm install then retry. Without it: git remote -v, add upstream https://github.com/a-tokyo/aiworkspace.git |