--- name: setup-interns version: 0.4.0 description: Installs and configures the local `interns` MCP server — clones the repo to `~/projects/.common/lib/interns-mcp/` (or uses an existing clone), `pip install -e` it, writes `~/.config/projects-secrets/interns.env` with endpoint API keys, and registers `mcpServers.interns` in `~/.claude.json`. Use this skill when the user says "install interns", "set up interns", "configure interns", "настрой интернов", "установи интернов", "interns не работает", "interns isn't working", or whenever the `mcp__interns__*` tools are missing in a session that needs delegation. Cross-platform — Windows / Linux / macOS. Mutates user-level config and writes secrets; pauses for confirmation before every write. --- # setup-interns > One-time skill that gets the local `interns` MCP server running with the user's endpoint API keys and a registered `mcpServers.interns` entry in `~/.claude.json`. Stops at confirmation gates because the procedure runs `pip install`, writes a secret-bearing `.env` file, and edits user-level config. Reference: full design lives in this repo at `.wiki/concepts/interns-design.md` (Layer 1/2/3 architecture, MVP catalog, always-ask paths, routing hints). ## When to use - User explicitly asks: install / set up / configure interns. - A `using-interns`-driven task fails because `mcp__interns__*` tools aren't available in the session. - New machine in the user's multi-machine fleet — install once per machine that wants delegation. - Migrating a stale install (endpoint key rotated, server source moved, broken `dist/` artifact) — same procedure, Phase 1 detects what's already in place. ## Out of scope - Issuing or rotating endpoint API keys (Ollama Cloud, OpenRouter, etc.). The skill *uses* keys the user already has; if there is none, it points at the provider's settings page and stops. - Running `interns-mcp` itself — the Claude Code harness spawns it on session start. - Authoring new interns or editing `~/projects/.common/config/interns/config.yaml` — that's a content task, not a setup task. - Any other MCP server. - Setting up the entire `common` monorepo beyond what `interns-mcp` needs. The clone step gets the full monorepo (it also contains `projects-meta-mcp` and other shared tools), but this skill only verifies the `lib/interns-mcp/` subdirectory. ## Hard rule: don't auto-mutate config The procedure runs `pip install`, writes `~/.config/projects-secrets/interns.env` (carries endpoint API keys), and edits `~/.claude.json`. **Always pause for explicit confirmation between Phase 1 (discovery, read-only) and Phase 2 (plan), and again before Phase 3 (backup + writes).** A trigger phrase grants permission to inspect, not to install or write secrets. ## Procedure ### Phase 0 — Environment sanity - Confirm Claude Code is the current harness (need `mcpServers` registration in `~/.claude.json`). - Confirm `python` ≥ 3.11 and `pip` are on `PATH`. Report the resolved interpreter path (`python -c "import sys; print(sys.executable)"`); the skill will pin this exact path in the MCP registration so a later `python` shadowed by another env doesn't silently take over. - Confirm `node` is on `PATH` (`node --version`). The `repo_read` intern runs `npx repomix@latest` as a subprocess. If `node` is absent, the user must install Node.js 20+ before proceeding — `repo_read` calls will fail at runtime with a clear "node not found" error. - (Optional, recommended) Pre-warm the repomix binary: `npx --yes repomix@latest --version`. This caches the package so the first real `repo_read` call is instant (5–10s first-run penalty avoided). Skip silently on failure — the call will just be slower the first time. - Confirm network reachability to the configured endpoints (default: `https://ollama.com/v1`). On HTTP 401 / 403 later, the API key is dead — stop and ask for a new one. - Confirm `git` is on `PATH` (needed for clone-fallback in Phase 4 if the source isn't already present). - Pick paths: `~/projects/.common/lib/interns-mcp/` (source), `~/projects/.common/config/interns/config.yaml` (catalog), `~/.config/projects-secrets/interns.env` (keys, outside any git tree), `~/.claude.json` (MCP registration). POSIX-style paths resolve correctly under git-bash on Windows. ### Phase 1 — Discovery (read-only) Search, in order. Report only "found at ", never echo key values. **Server source.** Check whether `~/projects/.common/lib/interns-mcp/pyproject.toml` exists. - If present → report "found at ~/projects/.common/lib/interns-mcp/". Phase 4 will `pip install -e` (or skip if already importable). - If absent → check whether `~/projects/.common/.git` exists (the `common` monorepo). - If the monorepo exists but the subdirectory is missing → report "source subdir absent — will git pull". Phase 4 will `git -C ~/projects/.common pull --ff-only` to fetch the latest content, then verify the subdirectory appeared. - If the monorepo is absent → report "source not found — will clone common monorepo from gitea". Phase 4 will `git clone https://git.kzntsv.site/OpeItcLoc03/common.git ~/projects/.common`. If clone or pull fails (no network, no Gitea PAT), stop with a clear message: ``` Failed to clone/pull the common monorepo. Options: 1. Check network access to https://git.kzntsv.site 2. If you need a Gitea PAT: https://git.kzntsv.site/user/settings/applications (scope: read:repository is sufficient) 3. If Gitea is unreachable, re-run setup-interns when it's back. ``` **Build artifact.** Run `python -c "import interns_mcp" 2>&1` against the candidate interpreter. If it fails with `ModuleNotFoundError`, Phase 4 will run `pip install -e ~/projects/.common/lib/interns-mcp/`. If it succeeds, capture the installed location and skip the install in Phase 4. **Config catalog.** Read `~/projects/.common/config/interns/config.yaml`. Extract the unique set of `endpoints..api_key_env` values — these are the env var names the runtime expects to find. Capture for Phase 2. **Existing endpoint keys.** Look in priority order, per `api_key_env` name from the config: 1. `~/.config/projects-secrets/interns.env` (`=...` lines). 2. Process env (`os.environ[]`). 3. `~/.config/projects-mcp/auth.toml` — only if the user has explicitly noted the key is shared with another local MCP server (rare). The first hit wins per key. **Never echo key values in chat.** **MCP registration.** Read `~/.claude.json` and check `mcpServers.interns`. Note the `command` and `args`. If args point at a stale interpreter, Phase 6 will fix it. ### Phase 2 — Plan + confirm Present a single-block plan to the user: ``` Source: Module: Config: — endpoints: Missing keys: not yet present in interns.env | none> MCP entry: Backups: ~/.claude.json.bak-, ~/.config/projects-secrets/interns.env.bak- (if exists) ``` Wait for explicit confirmation ("ok", "go", "поехали"). Anything else → stop. For each missing key, ask: "Paste a value for `` (endpoint: `` — provider settings page: ``), or skip and we'll leave the entry blank for you to fill later." Don't proceed past Phase 2 without resolving every required key — the runtime won't start with a missing API key. ### Phase 3 — Backup Copy each file we will modify to `.bak-YYYYMMDD-HHMMSS`: ```bash TS=$(date +%Y%m%d-%H%M%S) [ -f ~/.claude.json ] && cp ~/.claude.json ~/.claude.json.bak-$TS [ -f ~/.config/projects-secrets/interns.env ] && cp ~/.config/projects-secrets/interns.env ~/.config/projects-secrets/interns.env.bak-$TS ``` Confirm both backups exist (when their source existed) before any further edit. ### Phase 4 — Clone (if needed) + Install Python module **Clone or pull.** If Phase 1 found the source absent: ```bash if [ -d ~/projects/.common/.git ]; then # Monorepo exists but subdirectory missing — pull latest git -C ~/projects/.common pull --ff-only else # Fresh clone of the common monorepo mkdir -p ~/projects git clone https://git.kzntsv.site/OpeItcLoc03/common.git ~/projects/.common fi ``` Verify `~/projects/.common/lib/interns-mcp/pyproject.toml` exists after clone/pull. If the subdirectory is still missing after pull — the interns-mcp module may not have been pushed to the monorepo yet. Abort with a clear message. If the source was already present and Phase 1 found `interns_mcp` importable, skip both clone/pull and install. **Install.** If Phase 1 found `interns_mcp` not importable: ```bash python -m pip install -e ~/projects/.common/lib/interns-mcp/ ``` Capture the resolved `python` from Phase 0; use the same interpreter for both `pip install` and the later MCP `command:` field. Verify post-install: ```bash python -c "import interns_mcp; print(interns_mcp.__file__)" ``` If import fails — abort. Ask the user to paste the `pip install` output so the failure mode is visible. ### Phase 5 — Write `interns.env` Write `~/.config/projects-secrets/interns.env` (parent dir `mkdir -p ~/.config/projects-secrets` if missing). The file lives outside any git tree, so no `.gitignore` rule is needed. Per-key behavior: - Existing key in the file with a non-empty value — leave it alone. - Missing key — append `=` if the user pasted one, or `=` (blank) if the user skipped. A blank entry will fail at runtime with a clear `KeyError`; that's acceptable for the "I'll fill it later" path. Permissions: on Linux / macOS run `chmod 600 ~/.config/projects-secrets/interns.env`. On Windows the default ACL is per-user, no extra step. ### Phase 6 — Register in `~/.claude.json` Edit `~/.claude.json`. Add or update the `mcpServers.interns` block: ```json { "mcpServers": { "interns": { "command": "", "args": ["-m", "interns_mcp.server"], "cwd": "~/projects" } } } ``` `cwd` is set so the runtime resolves `.common/config/interns/config.yaml` relative to the `~/projects` base directory regardless of where Claude Code was launched. Secrets are loaded from `~/.config/projects-secrets/interns.env` (overridable via `INTERNS_SECRETS_PATH` env var) — independent of `cwd`. On Windows, expand `~/projects` to the absolute path (e.g. `C:/Users//projects`). Absolute interpreter path comes from Phase 0 (`sys.executable`). Forward slashes work in JSON on Windows without escaping. After each edit, validate JSON: ```bash # Windows (git-bash) powershell.exe -NoProfile -c "Get-Content '' -Raw | ConvertFrom-Json | Out-Null" # Linux / macOS jq empty # fallback python -c "import json; json.load(open(''))" ``` If validation fails → restore from `.bak-*` and abort. ### Phase 7 — Smoke test (best-effort) Best-effort: ask the user to call `mcp__interns__bulk_text_read` against a tiny benign input (e.g. read this file's `Phase 7` section, ask "what is this section about?"). A response that is structurally valid (text + usage) means the server is reachable. **Important caveat to relay to the user:** in the *same* session that just ran setup, the active MCP connection was bound at session start. So a passing in-session smoke test only proves "an interns server is alive" — not "the registration we just wrote is what's serving it". The real test is after Claude Code restart. If `mcp__interns__*` tools aren't registered in this session at all, skip the smoke test and rely on Phase 8. ### Phase 8 — Restart guidance + final report Tell the user: ``` Setup complete. Restart Claude Code so the new mcpServers.interns registration binds to a fresh stdio session. After restart: • mcp__interns__* tools serve from -m interns_mcp.server • Source at ~/projects/.common/lib/interns-mcp/ • Endpoint keys live in ~/.config/projects-secrets/interns.env (outside any git tree) • Config catalog at ~/projects/.common/config/interns/config.yaml • Backups saved at ~/.claude.json.bak- (and interns.env.bak- if it existed before) Runtime policy lives in `using-interns`. CLAUDE.md trigger: delegate to interns when allowed project-bootstrap 1.6.0+ adds it to new projects automatically. If something breaks after restart: • Restore from .bak-* and tell me — we'll roll back together. ``` ## Rollback procedure If a problem surfaces (now or after restart): 1. Stop. Don't try to fix forward. 2. Find the most recent `.bak-YYYYMMDD-HHMMSS` next to `~/.claude.json` (and `~/.config/projects-secrets/interns.env` if applicable). 3. `cp .bak- ` for each. 4. Optional: `pip uninstall interns-mcp` if you want to remove the editable install. 5. Optional: `rm -rf ~/projects/.common/lib/interns-mcp` if you want to remove the cloned source. 6. Restart Claude Code. 7. Confirm `mcp__interns__*` is gone (or back to its pre-existing version). 8. Report what went wrong so we can fix the procedure. ## Cross-platform notes The procedure is platform-agnostic. Only auxiliary tooling differs: | | JSON validate | Backup | Permissions on `.env` | |---|---|---|---| | Windows (git-bash) | `powershell.exe -NoProfile -c "Get-Content '' -Raw \| ConvertFrom-Json \| Out-Null"` | `cp` | per-user ACL by default | | Linux | `jq empty ` (or `python -c "import json; json.load(open(''))"`) | `cp` | `chmod 600` | | macOS | same as Linux | `cp` | `chmod 600` | Path forms (`~/projects/.common/...`, `~/.config/...`, `~/.claude.json`) are identical on all three. ## Common mistakes - **Skipping Phase 1.** "User just said 'install interns' — let's go." No — find existing source / module / keys first; clobbering an existing `.env` over a working one loses keys you can't recover. - **Echoing endpoint keys.** They're secrets. Edit / Write tool calls inevitably contain them (that's how they land in `.env`), but no chat output should. - **Pinning `python` instead of ``.** A bare `python` in the MCP `command:` resolves to whatever interpreter is first on `PATH` at session start — often a different env without the `interns_mcp` module. Always use the absolute interpreter path captured in Phase 0. - **Forgetting `cwd: ~/projects`.** Without it the runtime can't find `.common/config/interns/config.yaml` and bombs at startup with a config-not-found error that looks like a Claude Code bug. - **Writing `.env` with `0644` perms on Linux/macOS.** Token leak. Always `chmod 600` after write. - **Treating in-session smoke test as proof.** Same as the context7 / projects-meta caveat — the active MCP connection was bound at session start. Real verification happens after restart. - **Auto-running on every "use interns".** This skill is intrusive. Trigger only on explicit "install / set up / configure interns", or when MCP tools are missing and the user is blocked. - **Cloning over an existing source directory.** If `~/projects/.common/lib/interns-mcp/` already exists with `pyproject.toml`, don't clone — use what's there. Clone is only for the "source not found" case. - **Cloning the wrong repo.** `interns-mcp` lives inside the `common` monorepo at `OpeItcLoc03/common.git`, not as a separate `interns-mcp` repo. Always clone `common.git` and verify the subdirectory.