@bridge_gpt/mcp-server 0.2.51 → 0.2.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +24 -8
  2. package/build/agent-capabilities/probe-context.js +15 -7
  3. package/build/agent-capabilities/probes.js +42 -6
  4. package/build/agent-launchers/claude-executor-adapter.js +98 -14
  5. package/build/commands.generated.js +1 -1
  6. package/build/conduct-epic/cut-protocol.js +17 -3
  7. package/build/conductor/bridge-api-client.js +171 -5
  8. package/build/conductor/deny-enforcement-preflight.js +107 -10
  9. package/build/conductor/local-merge.js +170 -11
  10. package/build/conductor-bin.js +2 -2
  11. package/build/connect-bitbucket-api.js +370 -0
  12. package/build/connect-bitbucket.js +437 -0
  13. package/build/docs.generated.js +1 -1
  14. package/build/doctor.js +40 -1
  15. package/build/drive-epic.js +423 -11
  16. package/build/env-file-link.js +164 -0
  17. package/build/epic-integration-pr.js +10 -0
  18. package/build/executor/cli.js +41 -6
  19. package/build/executor/deps.js +5 -1
  20. package/build/executor/env-file-guard.js +113 -0
  21. package/build/executor/env.js +78 -1
  22. package/build/executor/heartbeat.js +9 -0
  23. package/build/executor/http-client.js +90 -22
  24. package/build/executor/job-errors.js +43 -2
  25. package/build/executor/job-runner.js +130 -28
  26. package/build/executor/merge-job.js +67 -16
  27. package/build/executor/permissions.js +106 -0
  28. package/build/executor/preflight.js +38 -13
  29. package/build/executor/resume-pre-spawn.js +2 -1
  30. package/build/executor/runner.js +175 -4
  31. package/build/executor/service-unit.js +15 -0
  32. package/build/executor/terminal-mutation.js +22 -1
  33. package/build/executor/types.js +86 -0
  34. package/build/executor/worker-command.js +21 -5
  35. package/build/executor/worker-guard-hook.js +939 -0
  36. package/build/executor/worker-log.js +56 -0
  37. package/build/executor/worktree.js +11 -0
  38. package/build/git-reachability.js +147 -0
  39. package/build/index.js +514 -121
  40. package/build/install-bridge.js +95 -0
  41. package/build/pipelines.generated.js +5 -3
  42. package/build/plan-epic-conductor-eligibility.js +37 -7
  43. package/build/plane/cli.js +78 -15
  44. package/build/plane/defaults.js +165 -0
  45. package/build/plane/manifest.js +63 -8
  46. package/build/plane/member-logs.js +6 -0
  47. package/build/plane/member-roster.js +195 -11
  48. package/build/plane/preflight.js +43 -0
  49. package/build/plane/shutdown.js +25 -3
  50. package/build/plane/status.js +11 -0
  51. package/build/plane/supervisor.js +343 -14
  52. package/build/plane/test-fakes.js +43 -0
  53. package/build/plane/types.js +82 -11
  54. package/build/pr-base-contract.js +20 -0
  55. package/build/readme.generated.js +1 -1
  56. package/build/review-synthesis-config.js +60 -0
  57. package/build/scripts/executor-protocol-contract-driver.js +311 -0
  58. package/build/setup-epic.js +560 -139
  59. package/build/sfcc/log-query.js +2 -1
  60. package/build/start-tickets-conductor.js +11 -2
  61. package/build/start-tickets.js +69 -2
  62. package/build/version.generated.js +3 -3
  63. package/build/worker-containment-diagnostic.js +97 -0
  64. package/build/worker-guard-hook-bin.js +6 -0
  65. package/docs/CONDUCTOR.md +27 -0
  66. package/docs/install/mcp-tool-integrations.md +3 -2
  67. package/package.json +3 -2
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
2
  // This file is produced by scripts/bundle-readme.js
3
- export const README = "# @bridge_gpt/mcp-server\n\nThe Bridge MCP is an end-to-end accelerator for shipping code within SFCC, powered by [Bridge API](https://bridgegpt-api.com). Works with Claude Code, Github Copilot, Cursor, Windsurf, and OpenAI Codex.\n\n## Install\n\nFrom your **project root**, run:\n\n```bash\nnpx -y @bridge_gpt/mcp-server install\n```\n\nThat is the whole setup command. It works whether or not you already have a Bridge\naccount — it will ask.\n\n> We recommend **the command above** instead of the `npm i @bridge_gpt/mcp-server`\n> one in npm's sidebar, because it **will make set up much easier**.\n\n**What it will do**\n\n- **Bootstraps the Bridge MCP for you** — one command and your editor's agent can\n use Bridge's tools and slash commands on this project.\n- Registers a `bridge` MCP server in your editor's MCP config, leaving any\n other servers you have configured untouched.\n- Creates and updates the files it needs inside your project root: slash commands\n and agent definitions for your editor (`.claude/commands/`, `.cursor/commands/`,\n and the equivalents your editor uses), your editor's MCP config, and `.bridge/`\n for your project manifest and pipeline definitions.\n- Stores your Bridge credential outside the project, so the MCP server and the\n tooling that spawns its own shells can find it without you configuring anything.\n Re-running `install` still asks for the credential unless you supply it through\n `--api-key` or `BAPI_API_KEY` — the installer writes that store, it does not read\n it back.\n- Writes outside your project root only when you pick a host whose configuration is\n global: OpenAI Codex (`~/.codex/config.toml`) and GitHub Copilot CLI\n (`~/.copilot/mcp-config.json`).\n\n**Prerequisites**\n\n- **Node.js 18 or newer** (`node --version`), which is what provides `npx`.\n- **A project directory** — run the command from the folder your editor opens: your\n repository root, the one containing `.git`. No `package.json` is required — SFCC\n cartridge repos, Python, Go, Rust, and other non-Node projects work the same way.\n- **An MCP-capable editor or CLI**: Claude Code, GitHub Copilot in VS Code, GitHub\n Copilot CLI, Cursor, Windsurf, or OpenAI Codex.\n- **No Bridge account needed.** The installer can create one for you from just an\n email address.\n\n## Contents\n\n- [Install](#install)\n- [Installation details](#installation-details)\n - [Installing, step by step](#installing-step-by-step)\n - [What to expect](#what-to-expect)\n - [Troubleshooting](#troubleshooting)\n- [Usage Documentation](#usage-documentation)\n - [Regularly useful](#regularly-useful)\n - [Occasionally useful](#occasionally-useful)\n - [Now and then](#now-and-then)\n - [Workflow commands](#workflow-commands)\n - [Operational commands](#operational-commands)\n - [Extra Capabilities](#extra-capabilities)\n- [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools)\n- [CLI Subcommands](#cli-subcommands)\n- [Custom Pipelines](#custom-pipelines)\n- [Environment Variables](#environment-variables)\n- [Worktree credentials and the `mcp-invoke` shim](#worktree-credentials-and-the-mcp-invoke-shim)\n- [Reference](#reference)\n\nFor advanced epic/multi-agent orchestration, see [CONDUCTOR.md](./docs/CONDUCTOR.md).\n\n## Installation details\n\n### Installing, step by step\n\n**1. Open a terminal in your project root.** This matters: the installer writes\nyour slash commands and MCP config relative to the directory you run it from. If\nyou run it in your home directory, your editor will not find any of it.\n\n**2. Run the command.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install\n```\n\n**3. Answer the sign-in question.** On a first run it asks whether you already have\na token:\n\n```\n1. Yes, I have received a token\n2. No, I need one\n```\n\n- Choose **2** if you have nothing yet. It asks for your email address and a name\n for your new Bridge project, then creates both for you.\n- Choose **1** if someone gave you a token — either a Bridge API key or an invite\n code. Paste it at the hidden prompt; you do not have to say which kind it is,\n because the installer recognizes it. Nothing is echoed as you type.\n\nThere is no default answer, so pressing Enter alone selects nothing. If you would\nrather not be asked, pass the answer up front instead — see\n[Choosing how you sign in](#choosing-how-you-sign-in).\n\n**4. Pick which editors to configure.** The installer detects the MCP hosts on your\nmachine and asks which ones to set up. Pick every editor you actually use for this\nproject; you can re-run the command later to add another.\n\n**5. Reload your MCP host.** Editors read their MCP configuration at startup, so a\nfreshly written config is not live until you reload. Restart the editor, or use its\n\"reload MCP servers\" action. In Claude Code you will also be asked to trust the\nproject's `.mcp.json` the first time.\n\n**6. Finish in the agent session the installer opens — when it opens one.** The\nlast thing the installer does is offer to open a fresh agent session running\n`/install-bridge`, which reads your codebase, fills in the remaining project\nsettings, and prints a short report of what Bridge can help with. Let it finish.\n\nThree things all have to hold for that session to open: your selection has to\ninclude a host the installer can launch, the run has to be on an interactive\nterminal, and you have to accept the consent prompt (*\"Bridge can configure and set\nup this project for you automatically. Open a `<tool>` session to do that now?\n(Y/n)\"*). Claude Code is the only selection that launches on its own. A\nCursor-only, Copilot, Copilot CLI, Codex, or Windsurf selection, a non-interactive\nrun, or a declined prompt all print the command to continue by hand instead. Pass\n`--agent claude` or `--agent cursor-agent` to override the decision outright.\n\n**7. Follow the next step the session shows you, if it shows one.** The installer\nasks the server what should happen next and shows that command only when there is\none to show — most often `/learn-repository`, which it recommends when the project\nstill needs its architecture, testing, review, and correctness standards documented\nand your key can run it. The installer deliberately does not run it for you. Those\nstandards are what make every later plan, critique, and review match how your\nproject actually works, and they only need to be gathered once per project — the\nresult is shared with everyone on the team. If the session shows no next step,\nthere is nothing for you to run.\n\nWant to see what would happen without changing anything? Add `--dry-run`.\n\n<details>\n<summary id=\"what-to-expect\"><strong>What to expect</strong></summary>\n\n**Files that appear in your project**\n\n| Path | What it is | Commit it? |\n|---|---|---|\n| `.claude/commands/`, `.cursor/commands/` | The slash commands your editor runs | Yes |\n| `.claude/agents/` and editor equivalents | Agent definitions used by those commands | Yes |\n| `.bridge/config` | Your project manifest — the repository name and which MCP targets to provision. Deliberately secret-free | Yes |\n| `.bridge/pipelines/`, `.bridge/instructions/` | Editable pipeline definitions | Yes |\n| `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json` | MCP registrations for your editor. These can carry your key, so the installer git-ignores them | No |\n\nThe installer tells you which of these are safe to commit and never recommends\ncommitting anything that can hold a credential.\n\n**Prompts you will see.** More than the sign-in question, in three groups:\n\n- *Always on a first bare interactive run:* the sign-in question, a hidden prompt\n for a token (or a visible one for an email), a project name for a brand-new\n project, a picker for which editors to configure, and an offer to connect GitHub\n (`Connect GitHub? [y/N]:`).\n- *Conditional on your situation:* a confirmation when the directory has no `.git`\n (default **No**, and declining aborts); a *\"Which tool should open? [1-N]\"*\n chooser when your selection contains more than one launchable tool; and the\n consent prompt before the final agent session.\n- *Overwrite confirmations, each default **No** and each skippable with `--force`:*\n a saved key for this project already exists; a host config already contains a\n `BAPI_API_KEY`; a **git-tracked** config would receive your real key; a saved but\n expired self-serve signup would be discarded.\n\n**A fresh agent session opens at the end — if your selection can launch one.** See\nstep 6 above for the three conditions. Use `--agent cursor-agent` if you want\nCursor's agent instead of Claude Code.\n\n**Selecting Windsurf prints instructions instead of writing config.** Windsurf's\nglobal `mcp_config.json` is never modified automatically; the installer reports the\nentry for you to paste yourself. Codex and Copilot CLI *are* written automatically,\neven though their files are global too.\n\n**Your key is stored for the tools that read the store.** The MCP server and the\nshell-spawned tooling (`start-tickets` and its model routing) resolve it from\n`~/.config/bridge/credentials.json` on their own. The **installer** does not: a\nrepeat `install` prompts for the credential again unless you pass `--api-key` or\nset `BAPI_API_KEY` in the environment.\n\n**A next step, when the project needs one.** The session closes with whatever\ncommand the server says comes next, and stays quiet when there is nothing to\nrecommend. `/learn-repository` is the usual one: it is recommended when the project\nstill needs its conventions documented and your key can run it. It is never\nautomatic — until someone runs it, Bridge's agents work from your code alone rather\nthan from your project's documented conventions.\n\n**Indexing happens on its own.** There is no \"index my repository?\" question. Once\nyour project has the settings it needs, indexing starts server-side. You never have\nto ask for it.\n\n</details>\n\n<details>\n<summary id=\"troubleshooting\"><strong>Troubleshooting</strong></summary>\n\n**\"My editor doesn't see any Bridge tools.\"** Two usual causes. First, the config\nwas written somewhere your editor is not looking — re-run the installer from the\ndirectory your editor actually opens, and check that a `bridge` entry exists in\nthat project's MCP config. Second, the editor has not been reloaded since the file\nwas written; restart it. In Claude Code, also confirm you accepted the trust prompt\nfor the project's `.mcp.json`.\n\n**\"I ran it in the wrong folder.\"** Nothing is broken. Depending on which editors\nwere detected, a run can leave `.bridge/`, `.bridge/install-state.json`, `.claude/`,\n`.cursor/commands/`, `.cursor/mcp.json`, `.vscode/mcp.json`, `.github/agents/`,\n`.mcp.json`, and appended `.gitignore` lines. Remove only what that run created and\nre-run the command from the right directory — if you already had a `.vscode/`,\n`.cursor/`, or `.gitignore` there, keep the parts you had before.\n\n**\"It seems to hang with no output.\"** If you ran the bare command\n(`npx -y @bridge_gpt/mcp-server`) with no subcommand, you started the MCP *server*,\nnot the installer. It is waiting for an editor to connect over stdio, which is\nexactly what it should do when your editor launches it — but from a terminal it\nlooks like a hang. It prints a line saying so. Press Ctrl-C and run\n`npx -y @bridge_gpt/mcp-server install` instead. The explicit spelling\n`npx -y @bridge_gpt/mcp-server serve` starts the server on purpose.\n\n**\"It can't reach Bridge\" or \"my key was rejected.\"** The installer checks\nconnectivity before it saves your **credential** anywhere, so a failure here has not\nwritten your key into a config or stored it for later. It has already\nscaffolded the project files by then — slash commands, agents, pipelines,\n`.bridge/config`, and secret-free per-host MCP placeholders — so expect those to\nexist; re-running is safe and refreshes them. A\nrejected key means the credential is not valid for that project — check the project\nname you gave, and generate a fresh key on the Bridge web UI's **Security** page if\nneeded. A network failure usually means a proxy or VPN is in the way.\n\n**\"Which repository name should I use?\"** The one registered with Bridge. If you\nhave an existing key, the installer usually resolves it for you; when it cannot, it\nasks, and `--repo <name>` answers it up front.\n\n**Still stuck? Ask the installer to diagnose itself.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server doctor\n```\n\n`doctor` is strictly read-only. It reports what it found — configs, registrations,\ncredential availability, prerequisites — and changes nothing.\n\n</details>\n\n<details>\n<summary id=\"choosing-how-you-sign-in\"><strong>Choosing how you sign in</strong></summary>\n\nThree routes lead to the same place. The interactive question above picks one for\nyou; these flags pick it up front and skip the question entirely.\n\n**No account yet — sign up with an email.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install --email you@example.com\n```\n\nCreates a brand-new Bridge project for that address and your first admin key in one\ncommand. No account, no key, and no invite needed beforehand. The address labels\nyour new workspace and may receive a setup message; delivery is best-effort, so\nnothing waits on it. The email is visible as you type (it is not a secret) and is\nnever written to a log. This is the same route as answering **2** at the prompt.\n\n**You were sent an invite code.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install --invite\n```\n\nRedeems the invite, creates your project, and mints your first admin key. Run it\n*without* a value, as shown: the installer then asks for the code at a hidden\nprompt, so the code never lands in your shell history. `--invite <code>` and the\n`BAPI_INVITE` environment variable exist for scripting, but both expose the code to\nyour shell history and to the process list.\n\n**Your team already has a project and gave you an API key.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install --api-key <key>\n```\n\nOr omit the flag and paste the key at the hidden prompt. Generate a key on the\nBridge web UI's **Security** page (**Create New Key**, role **Admin**) and copy it\nimmediately — it is shown once. `BAPI_API_KEY` works too.\n\nIf you paste an invite code where a key was expected, or a key where an invite was\nexpected, the installer recognizes the mismatch and tells you before anything is\ncreated or spent.\n\n</details>\n\n<details>\n<summary><strong>Installer flags</strong></summary>\n\n| Flag | What it does |\n|---|---|\n| `--email <addr>` | Sign up for a new Bridge project with just an email address |\n| `--invite [code]` | Redeem an invite code. Omit the value for the hidden prompt (recommended) |\n| `--api-key <key>` | Use an existing Bridge API key |\n| `--repo <name>` | Name the registered repository instead of resolving or asking for it |\n| `--tools <list>` | Configure specific MCP hosts without the picker. Accepted IDs are exactly `claude-code`, `cursor`, `copilot-vscode`, `copilot-cli`, `codex`, and `windsurf` (e.g. `claude-code,cursor`); any other value is a parse error |\n| `--agent claude\\|cursor-agent` | Which agent to open for the final configuration step. **No default** — without this flag the agent is derived from the hosts you selected, and an explicit value always wins, including for a host you did not select |\n| `--dry-run` | Preview every step without writing, contacting Bridge, resolving or prompting for a credential, or opening anything. Genuinely inert: it returns before the project-root prompt, before the repository is resolved, and before any tool-selection prompt, so a value it cannot know locally (an unresolved repository name, an unselected tool) is shown as **not yet known** rather than guessed |\n| `--force` | Overwrite an existing stored key without asking |\n| `-h`, `--help` | Full usage |\n\n`--email`, `--invite`, and `--api-key` are mutually exclusive — each names a\ndifferent way to arrive, and the installer will not guess between them.\n\n</details>\n\n<details>\n<summary><strong>Setting up an MCP host by hand</strong></summary>\n\nThe installer configures your editors for you. Do this only if you would rather\nwrite the config yourself, or if you use a host it cannot write automatically.\n\nScaffold the project files and write a secret-free MCP registration. Run it from\nthe same project root `install` uses — your repository root, the one containing\n`.git`. No `package.json` is required:\n\n```bash\nnpx -y @bridge_gpt/mcp-server --init\n```\n\n`--init` always creates `.mcp.json`, and adds `.vscode/mcp.json` or\n`.cursor/mcp.json` when it detects those editors. Each generated entry carries\n`BAPI_BASE_URL`, `BAPI_REPO_NAME`, `BAPI_DOCS_DIR`, and `BAPI_PROJECT_ROOT`, and\n**never** `BAPI_API_KEY` — the server resolves the credential itself at runtime.\n\nSo the manual work left after `--init` is narrower than writing an entry from\nscratch: correct `BAPI_REPO_NAME` if it was written as the `YOUR_REPO_NAME`\nplaceholder, and supply your credential through a supported source (`BAPI_API_KEY`\nin the entry's `env` block, `BAPI_API_KEY` in the server's environment, or the\n`~/.config/bridge/credentials.json` store).\n\nWrite the entry yourself instead — for a host `--init` does not touch, or because\nyou would rather — using the shapes below. Add `\"serve\"` as the last launcher\nargument, as shown: it is the explicit way to say \"start the MCP server.\" Pin the\npackage to an exact version and pass `--prefer-offline`, which is what the\ngenerated entries do and what keeps npx from resolving a different build on some\nlater boot.\n\n<!-- BEGIN GENERATED: mcp-host-examples (managed by mcp_server/scripts/sync-readme-host-examples.js — DO NOT EDIT BY HAND) -->\n<details>\n<summary><strong>Claude Code (.mcp.json)</strong></summary>\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.51\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>VS Code / Copilot (.vscode/mcp.json)</strong></summary>\n\n```json\n{\n \"servers\": {\n \"bridge\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.51\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>Cursor (.cursor/mcp.json)</strong></summary>\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.51\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>GitHub Copilot CLI (~/.copilot/mcp-config.json)</strong></summary>\n\nCopilot CLI reads a single global file. The installer writes this one for you when\nyou select `copilot-cli`; the shape below is what it produces.\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"type\": \"local\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.51\", \"serve\"],\n \"tools\": [\"*\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>Windsurf (~/.codeium/windsurf/mcp_config.json)</strong></summary>\n\nWindsurf only supports global MCP configuration.\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.51\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>OpenAI Codex (~/.codex/config.toml)</strong></summary>\n\n```toml\n[mcp_servers.bridge]\ncommand = \"npx\"\nargs = [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.51\", \"serve\"]\n\n[mcp_servers.bridge.env]\nBAPI_BASE_URL = \"https://bridgegpt-api.com\"\nBAPI_REPO_NAME = \"your-repo\"\nBAPI_API_KEY = \"your-api-key\"\nBAPI_DOCS_DIR = \"docs/tmp\"\n```\n\n> Codex users: set `BAPI_PROJECT_ROOT` manually in your config (see [Environment Variables](#environment-variables)).\n</details>\n<!-- END GENERATED: mcp-host-examples -->\n\nAfter saving, reload your editor and ask your assistant to call the `ping` tool to\nconfirm the connection.\n\nAn entry with no trailing `serve` still starts the server — bare invocation means\n\"server\" permanently, and nothing rewrites an existing config to add the token.\n\n</details>\n\n<details>\n<summary><strong>Upgrading Bridge</strong></summary>\n\n```bash\nnpx -y @bridge_gpt/mcp-server@latest --upgrade\n```\n\n`upgrade` fetches the latest published version, refreshes your scaffolded slash\ncommands, agents, and pipelines, updates the version pin in your MCP config, and\nopens a session so you can reconnect. It is also available as the\n`/upgrade-bridge` slash command.\n\nUse the `@latest` form. It applies to the short-lived *upgrader* process: without\nit, npx may reuse a cached older copy of the package and \"upgrade\" you with the\nbuild you are trying to replace. The exact `MAJOR.MINOR.PATCH` pin the upgrader\nwrites into your MCP config is deliberately different — host configs stay pinned\nto an exact release so a project's server is reproducible.\n\n`upgrade` reports **per config file**, because a project can have several\n(`.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`) and they can disagree:\n\n```\nLauncher pins:\n .mcp.json: 0.2.16 -> 0.2.36\n .cursor/mcp.json: already 0.2.36\n```\n\nWhen every applicable launcher pin was already at the target, it prints\n`Already up-to-date.` — that status comes from comparing your configs, not from\nthe version of the CLI process. A non-zero exit means the upgrade did **not**\nconverge, and nothing is reported as complete in that case. The causes:\n\n- the npm registry lookup failed **and** this process was not started from\n `@latest`, so the target version could not be confirmed — the likeliest one\n offline, and why the canonical command uses `@latest`;\n- a launcher pin is already **newer** than the target, which an automated repin\n must never downgrade;\n- an unreadable or unparseable config, a launcher carrying a version range or a\n dist-tag rather than an exact release, or two Bridge registrations in one file;\n- a competing local install it could not remove, or a pin that failed post-write\n verification;\n- the upgrade finished but left an **unconfigured** MCP entry — one that would\n authenticate as nobody.\n\nThe server checks for updates on startup. The check is cached for a day and never\nblocks startup. When a newer version is known, it surfaces in two places you do\nnot have to go looking for: a one-line warning on the server's **stderr**, and a\nshort advisory attached to the ordinary `tools/list` response so the agent in the\nsession can see that some tools may be missing or renamed in the older build.\nNeither requires calling `ping` or `doctor`.\n\nRe-running `install` on an already-configured project is safe: it refreshes the\nscaffolded files without overwriting your stored credential unless you pass\n`--force`.\n\n</details>\n\n## Usage Documentation\n\nThis is the Bridge API tooling worth knowing about as a software engineer — the things you'd ask an agent to do — grouped by how often you would use them. Each entry covers **what it does**, **when it's useful**, **how to use it**, and its **flags**. The behind-the-scenes plumbing is summarized at the end under [Extra Capabilities](#extra-capabilities), and a full enumeration lives in [Reference](#reference).\n\nWorking in a Salesforce B2C Commerce codebase? Bridge also ships SFCC platform tools — read-only introspection under the `sfcc` profile, and nine destructive writes under the separate `sfcc-write` opt-in. See [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools).\n\nFor invocation, prefer the slash command — it's deterministic. A free-text example is shown only where natural-language phrasing reliably maps to the right automation; high-consequence or easily-misread automations show only the slash command on purpose.\n\n<!-- The three tier sections below are GENERATED from TWO catalogs by\n scripts/sync_mcp_server_readme.py: api/library/config/mcp_tool_catalog.json,\n the authoritative MCP tool catalog, and api/library/config/workflow_catalog_lib.py,\n the immutable catalog of slash-command workflows (which have no MCP registration\n and therefore cannot live in the JSON artifact). Edit the curated tool metadata in\n scripts/sync_mcp_tool_catalog.py and the workflow definitions in\n workflow_catalog_lib.py — never the JSON artifact and never the text between the\n markers. Generation order is: sync_mcp_tool_catalog.py, then\n sync_mcp_server_readme.py, then `cd mcp_server && npm run build` (which bundles this\n file into readme.generated.ts, served as the MCP resource bridge://readme).\n Everything outside the marker pair — including the sections below it — is hand-written. -->\n\n<!-- BEGIN GENERATED: mcp-tool-documentation (managed by scripts/sync_mcp_server_readme.py — DO NOT EDIT BY HAND) -->\n### Regularly useful\n\nThe tools worth knowing for most tickets.\n\n**1. Review Ticket**\n- **What it does:** Runs a full quality review of a ticket: clarifying questions and a critique plus an alternate-model second opinion, then evaluates the findings and produces a decision page for accepting or rejecting them.\n- **When it's useful:** (Refinement) Right after a ticket is drafted, before anyone starts building — to surface gaps and tighten it.\n- **How to use it:** `/review-ticket BAPI-123` (command only — \"review\" as free text is easily mistaken for a freehand agent review). For several tickets at once, `/review-tickets BAPI-123 BAPI-456` opens one terminal tab per ticket and reviews them in parallel with no worktrees; every `/review-ticket` flag applies, and `--review KEY=auto,rounds=N` sets per-ticket overrides.\n- **Flags:** `--auto` auto-accept findings and skip the approval gates · `--rounds=1` a cheaper single-pass review that still evaluates findings and captures decisions · `--rounds=2` force the full second-opinion review · omit `--rounds` to let the difficulty-adaptive review policy decide.\n\n**2. Start Tickets**\n- **What it does:** Creates one git worktree per ticket and spawns an agent session in each to implement them in parallel.\n- **When it's useful:** (Implementation | Automation) When you're ready to start building one or more refined tickets concurrently.\n- **How to use it:** `/start-tickets BAPI-248 BAPI-250` (see [CLI Subcommands](#cli-subcommands) for the full flag table and cross-platform behavior).\n- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default · `--workflow implement|review-and-implement` selects which slash command each spawned worktree runs · `--rounds=1|2` forwarded to the review phase, valid only with `--workflow review-and-implement` · `--tier cheap|basic|premium` coarse model-routing override.\n\n**3. Review and Start**\n- **What it does:** Spawns one worktree per ticket; each session reviews the ticket inline and, after a per-ticket proceed/halt gate, hands off to a **fresh implementation session** that reuses the same worktree — review and implementation run in two separate agent contexts, not one shared session.\n- **When it's useful:** (Refinement | Implementation | Automation) The **recommended front door** for \"review these tickets, then implement the ones that pass,\" starting from existing ticket keys (unlike `/full-automation`, which only accepts an idea).\n- **How to use it:** `/review-and-start BAPI-248 BAPI-250` (single or multiple keys flow through the identical code path).\n- **Flags:** `--auto` auto-approves both the review and the implementation phase of every spawned session · `--rounds=1|2` forwarded to the review phase · `--agent`, `--base-branch`, `--max-parallel`, `--dry-run` mirror `/start-tickets`.\n\n**4. Explore Ticket**\n- **What it does:** Maps the code paths, dependencies, and project conventions a task would touch, settles its acceptance criteria with you on a decision page, then compares the viable implementation approaches and their trade-offs and writes up a proposed design. Along the way it surfaces the ambiguities that still need deciding and can pull in optional web or deep research where the answer is not in the code.\n- **When it's useful:** (Architecture | Refinement) Before writing a ticket or a plan, when you're unsure how a change would fit the existing code and want the open questions and the realistic options laid out first.\n- **How to use it:** `/explore-ticket <task>` — *\"Explore the codebase for how we'd add a Mistral LLM provider and recommend 2–3 implementation options.\"*\n- **Flags:** None.\n\n**5. Council**\n- **What it does:** Fans your problem out to two different models and returns their approaches, in technical, design, discovery, or general mode.\n- **When it's useful:** (Architecture | Refinement) Early, when you want a spread of approaches — technical for how to build it, design for how it should look, discovery for what still needs figuring out before a real ticket exists, general for a quick brief-driven pass before the repository is indexed.\n- **How to use it:** `/council <question>`\n- **Flags:** `--mode` selects one of four modes, passed to the underlying `request_council` tool as e.g. `mode: \"discovery\"`: `technical` (the default — implementation/architecture approaches), `design` (UI/UX and visual direction), `discovery` (stakeholder discovery questions, grouped into `Technical Discovery Questions` and `Business / Stakeholder Discovery Questions` and tagged `[HUMAN]`/`[CODE]`/`[TICKET]`), and `general` (brief-driven ideation from your task description alone). `technical` and `discovery` are codebase-grounded and need an indexed repository; `general` needs no code index at all, so it works immediately after install. The legacy boolean `design=true` still works and maps to `mode: \"design\"`.\n\n**6. Deep Research**\n- **What it does:** Runs multi-source, fact-checked web research on a technical topic and returns a cited report.\n- **When it's useful:** (Architecture | Refinement) When a decision hinges on outside knowledge — libraries, best practices, standards — that you do not already have.\n- **How to use it:** `/bridge-research <question>`\n- **Flags:** None.\n\n### Occasionally useful\n\nGood to know, but not needed every day.\n\n**1. Upload Ticket**\n- **What it does:** Creates a real Jira issue from a drafted ticket, including child tickets under an epic; your agent should confirm with you before creating it.\n- **When it's useful:** (Refinement) The final step after drafting — to get the ticket into your tracker so it can be tracked and worked.\n- **How to use it:** Ask your agent to create the ticket; it should confirm with you before creating the live issue.\n- **Flags:** Name the issue type (Bug / Story / Task / Epic) and, for a child ticket under an epic, the parent key.\n\n**2. Plan Ticket**\n- **What it does:** Generates a step-by-step implementation plan for a ticket that references real files in your codebase.\n- **When it's useful:** (Refinement | Implementation) Once a ticket is solid and you want a concrete build plan before — or instead of — auto-implementing it.\n- **How to use it:** `/plan-ticket BAPI-123`\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check the plan with a second provider.\n\n**3. Create Doc**\n- **What it does:** Generates a design document for a ticket — a technical design document, a functional spec, or a product requirements document.\n- **When it's useful:** (Architecture | Refinement) When a ticket needs a fuller design write-up before planning or implementation, in the shape that fits your audience.\n- **How to use it:** `/create-doc BAPI-123 --doc-type tdd` (or `fsd` / `prd`)\n- **Flags:** `--doc-type tdd|fsd|prd` which document to generate (required) · `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**4. Second Opinion**\n- **What it does:** Gets an immediate critique of any text from a different model family, without saving an artifact.\n- **When it's useful:** (Architecture | Refinement | Implementation) Any time you want a quick sanity check on a plan, draft, or decision from a fresh perspective.\n- **How to use it:** Ask your agent — \"Get a second opinion from Gemini on whether the BAPI-123 plan's migration step is safe to run against production.\"\n- **Flags:** Pick the provider (anthropic / openai / gemini) and the tier (cheap / basic / premium).\n\n**5. Generate Image**\n- **What it does:** Generates an image from a text prompt using a provider image model, spending provider credits on every call.\n- **When it's useful:** (Architecture | Refinement) When you want a quick visual — a UI mockup, diagram, or illustration — to anchor a design discussion or attach to a ticket.\n- **How to use it:** Ask your agent — \"Generate an image of a dashboard showing SOC2 evidence freshness as a traffic-light grid.\"\n- **Flags:** `provider` openai (`gpt-image-2`) / gemini (Imagen, which adds an invisible SynthID watermark) · `quality` low (default, cheapest) / medium / high · `size` 1024x1024 / 1024x1536 / 1536x1024. The image is saved to `BAPI_DOCS_DIR/images/` and also returned inline.\n\n### Now and then\n\nUseful once in a while.\n\n**1. Reimplement Ticket**\n- **What it does:** Gathers the context and attachments added since the last pass so a targeted follow-up change can be made.\n- **When it's useful:** (Implementation) After review feedback or new screenshots, when you need a targeted second pass rather than a fresh build.\n- **How to use it:** `/reimplement-ticket BAPI-123`\n- **Flags:** None.\n\n**2. Update Ticket**\n- **What it does:** Rewrites a ticket's description, fully replacing what is there today.\n- **When it's useful:** (Refinement) After review, to fold the resolved questions and fixes back into the ticket itself.\n- **How to use it:** Ask your agent — \"Replace BAPI-123's description with: <new text>.\" It fully overwrites the live description, which is hard to reverse.\n- **Flags:** None.\n\n**3. Get Ticket**\n- **What it does:** Retrieves the full details of a ticket, including its summary, status, and description, and optionally the model tier its difficulty routes to.\n- **When it's useful:** (Refinement | Implementation) Any time you want the agent to read a ticket before acting on it.\n- **How to use it:** Ask your agent — \"Pull up BAPI-123 and show me its description, status, and acceptance criteria.\"\n- **Flags:** `include_model_tier` adds the model tier the ticket's difficulty routes to; leave it off for an ordinary read.\n\n**4. Search Tickets**\n- **What it does:** Searches across the tickets in your project.\n- **When it's useful:** (Refinement) When you need to find tickets by project, status, or wording rather than by key.\n- **How to use it:** Ask your agent — \"Search our project for open tickets mentioning rate limiting.\"\n- **Flags:** Narrow the search by project, status, issue type, or free text.\n\n**5. Write Comment**\n- **What it does:** Posts a comment on a ticket.\n- **When it's useful:** (Refinement | Implementation) To leave context, status, or a decision trail on the ticket.\n- **How to use it:** Ask your agent — \"Post a comment on BAPI-123: blocked on the expired Atlassian token — will retry after it is rotated.\"\n- **Flags:** A long comment can be attached as a file instead of inlined.\n\n**6. Read Comments**\n- **What it does:** Reads the comment thread on a ticket.\n- **When it's useful:** (Refinement | Implementation) When the discussion on a ticket matters and you want the agent to read it before acting.\n- **How to use it:** Ask your agent — \"Read the comments on BAPI-123 and summarize what was decided.\"\n- **Flags:** None.\n\n**7. Ticket Attachments**\n- **What it does:** Downloads files from a ticket to your disk, or attaches a local file to a ticket.\n- **When it's useful:** (Refinement | Implementation) When a ticket has design files or logs you need locally, or you want to attach output back to it.\n- **How to use it:** Ask your agent — \"Download the design mockups attached to BAPI-123 into my docs folder,\" or \"Attach build-log.txt to BAPI-123.\"\n- **Flags:** Choose the direction (download from the ticket, or upload to it) and, for a download, where the files should land.\n\n**8. Estimate Epic**\n- **What it does:** Estimates an epic, or an explicit group of tickets you name.\n- **When it's useful:** (Architecture | Refinement) When you need a sizing pass across an epic, or across a set of tickets you name explicitly.\n- **How to use it:** `/estimate-epic BAPI-123`\n- **Flags:** Pass an epic key, or an explicit list of ticket keys to estimate as one group.\n<!-- END GENERATED: mcp-tool-documentation -->\n\n### Workflow commands\n\nSlash commands that drive several tools at once. Start Tickets, Review and Start, and Explore Ticket are documented above under [Regularly useful](#regularly-useful) — the rest live here.\n\n**1. Implement Ticket**\n- **What it does:** Full build for one ticket: generate a plan, write the code, commit, open a PR, and monitor CI.\n- **When it's useful:** (Implementation) When a ticket is ready and you want it taken from plan to open PR in one go.\n- **How to use it:** `/implement-ticket BAPI-123` (command only — \"implement X\" as free text almost always triggers a freehand build instead of the Bridge plan→code→PR→CI pipeline).\n- **Flags:** `--auto` skip the approval gates (e.g. auto-commit/push).\n\n**2. Jira Ticket Writer**\n- **What it does:** An agent that drafts a well-structured Jira ticket from a plain description, applying your project's standards.\n- **When it's useful:** (Refinement) When you have an idea in your head and want a properly-formatted ticket draft without writing it by hand.\n- **How to use it:** Ask your agent, *\"Use the jira ticket writer to turn our conversation into a ticket.\"* The other ticket commands draft through it automatically.\n- **Flags:** None — name a specific standards file in your request to have it applied when drafting.\n\n**3. Idea to Ticket**\n- **What it does:** Turns a one-line idea into a Jira Task/Spike (or an Epic plus child tickets), with research, duplicate detection, and a critique pass built in.\n- **When it's useful:** (Refinement | Automation) When you have a rough idea and want a fully-formed, uploaded ticket without the manual draft-and-refine loop.\n- **How to use it:** `/idea-to-ticket <idea>`\n- **Flags:** None.\n\n**4. Plan Epic**\n- **What it does:** Decomposes a large epic into sub-tasks with a structured exploration doc for each.\n- **When it's useful:** (Architecture | Refinement) When a feature is too big for one ticket and you need it broken down and scoped.\n- **How to use it:** `/plan-epic <epic>` — *\"Decompose the epic 'migrate PayPal token storage off Custom Objects' into sub-tasks with an exploration doc for each.\"*\n- **Flags:** None.\n\n#### Ticket-authoring posture\n\n`/explore-ticket`, `/idea-to-ticket`, and `/plan-epic` all decide ticket shape\nthe same way, as does the `jira-ticket-writer` agent they draft through. A fresh install inherits this with no configuration\nstep and no server call; the full rationale and the closed exception list ship as\n`docs/bridge-ticket-authoring.md`.\n\n- **Drafted by the writer.** Every ticket body — epic parent, epic child, and\n ordinary sibling alike — goes through the `jira-ticket-writer` agent. Nothing\n composes a ticket description inline.\n- **Sized toward L, overflowing upward.** `L` (target) → `XL` (when the work\n does not fit in `L`) → `M` (third choice) → `S` (only when unavoidable). A\n slice that outgrows `L` becomes one `XL` ticket rather than two `L` ones —\n splitting a coherent slice to fit a band buys another worktree, another PR, and\n another rebase for nothing. This binds a standalone ticket and an epic child\n alike. Past roughly 40 files or ~3000 LOC it splits anyway, into the largest\n coherent pieces available.\n- **Grouped at three.** Three or more tickets is an epic: an epic parent plus an\n ordered child manifest, shown in full at an approval gate before anything is\n created. One or two are ordinary siblings — no epic parent, no manifest. The\n threshold is exactly three.\n- **Decomposed once, rendered many.** One pass freezes the split; body drafting\n then fans out one writer invocation per entry against that frozen manifest. A\n rendering invocation never re-splits, merges, reorders, or rescopes.\n- **Handed off once.** An epic handoff names exactly one entry point,\n [`drive-epic`](#drive-epic) — never a choice between conductors.\n\n**5. Run Tests**\n- **What it does:** Runs the unit and E2E suites and autonomously triages/fixes failures (via the test-correction agent).\n- **When it's useful:** (Implementation) After making changes, to confirm everything passes and auto-fix straightforward breakages.\n- **How to use it:** `/run-tests`\n- **Flags:** `--unit-only` skip the E2E suite · `--skip-e2e` same, phrased the other way.\n\n**6. Learn Repository**\n- **What it does:** Researches and documents the repo's architecture, testing, review, and correctness standards, then saves them to Bridge for future agents.\n- **When it's useful:** (Setup/Learning) When onboarding a new repo, or after big changes, so Bridge's agents follow your conventions.\n- **How to use it:** `/learn-repository`\n- **Flags:** None.\n\n**7. Teach Bridge**\n- **What it does:** Takes a plain-English instruction, figures out which standards field it belongs to, and merges it in (admin only).\n- **When it's useful:** (Setup/Learning) When you notice the agents missing a convention and want to correct it in one sentence.\n- **How to use it:** `/teach-bridge <teaching>` — *\"Teach Bridge: always use data-testid selectors in E2E tests.\"*\n- **Flags:** None.\n\n### Operational commands\n\nRepository and ticket housekeeping commands, beyond the tiers above. Implementation, commit/push, PR creation, and CI monitoring are steps of the `implement-ticket` recipe rather than standalone commands:\n\n| Command | What it does |\n|---|---|\n| `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |\n| `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |\n\n> Commands are designed for Claude Code. Other editors may support slash commands differently — check your editor's documentation for how to invoke prompt files.\n\n### Extra Capabilities\n\nBehind-the-scenes capabilities an agent gains from the MCP tools — mostly invoked automatically by the commands above, rarely requested by name:\n\n- **Ship a PR end-to-end:** commit & push, open a pull request, transition the Jira status, and discover/poll CI checks — the commit-and-push, PR-creation, and CI-monitoring steps of the `implement-ticket` recipe, driven by its bundled instruction assets.\n- **Design documents** for a ticket (TDD / FSD / PRD, generated and retrieved through `create_doc` / `get_doc`).\n- **Index the codebase** so Bridge's agents can reason about it: queue/parse the repo and check parse status (`parse_repository`), regenerate the directory map.\n- **Read & tune project config/standards:** list/read/update config fields, fetch project standards, and the per-topic `learn-*` commands that populate them.\n- **Ticket lifecycle bookkeeping:** track tickets and backfill workflow-state timestamps (`scan-tickets`), search across tickets, read comments, list attachments.\n- **Pipeline machinery:** fetch a resolved pipeline recipe (the step list behind the orchestration commands) and execute its steps agent-side.\n- **Decision page** generation for capturing human review decisions as structured data.\n- **Connectivity check:** ping Bridge to confirm the API key and repository resolve.\n- **Retrieve any generated artifact** (`get_*` for plans, critiques, questions, councils, research, design docs) without regenerating it.\n- **Tiered-section execution telemetry** recording (internal measurement).\n\n## Salesforce B2C Commerce (SFCC) Tools\n\nSalesforce's official MCP server, `@salesforce/b2c-dx-mcp`, covers developer-experience tasks — cartridge deploy, step debugging, MRT bundle push, and SCAPI Custom API scaffolding. **As of `@salesforce/b2c-dx-mcp` 1.1.2 (published 2026-05-20)** it has no way to *read* the platform's object model, custom objects, or site configuration — exactly the context an AI coding agent needs to write correct Commerce code and to inspect a sandbox before touching it. That comparison is dated on purpose: its basis is this repository's hand-maintained [vendor manual](../docs/mcp/b2c-commerce-developer.md), pinned to the same version, so a new Salesforce toolset ages the claim visibly instead of rotting silently. Bridge's SFCC tools install side-by-side with `b2c-dx-mcp` (they don't duplicate its surface) and close that gap with **OCAPI Data API introspection** of system objects, custom object definitions, and site preferences — plus, behind a separate opt-in, a set of sandbox-bounded writes.\n\n**Every SFCC tool is restricted to a developer sandbox, and the restriction is checked at invocation time against the hostname your credentials actually resolve to** — not against anything the caller passes in. If `dw.json` or `SFCC_HOSTNAME` names a host Bridge does not recognize as a developer sandbox, every SFCC tool refuses with a `403` before contacting it. See [Sandbox enforcement](#sandbox-enforcement).\n\n**Credentials stay local** — in `dw.json` or `SFCC_*` env vars — and are never sent to Bridge. The `sfcc` profile registers read-only tools; the nine destructive write tools require the separate `sfcc-write` opt-in (see [Read and write profiles](#read-and-write-profiles)).\n\nFor a step-by-step OCAPI client setup guide (including the Business Manager permissions grant), see [docs/install/sfcc-integration.md](./docs/install/sfcc-integration.md).\n\n<details>\n<summary><strong>Setup</strong></summary>\n\nEvery SFCC tool must be enabled with a profile (step 3) — including the two diagnostics, `sfcc_setup_status` and `check_permissions`, which belong to the `sfcc` profile. A session on the default `core` profile registers no SFCC tool at all. Changing `BRIDGE_MCP_PROFILE` requires an MCP client restart.\n\n**Prerequisites:** a running SFCC **sandbox** and its hostname, plus an Account Manager API client (`client-id` + `client-secret`).\n\n**1. Set the repo `version` config field** to your SFCC project type — one of `sfra | pwakit | sitegenesis | storefrontnext | hybrid`. The call-time gate reads this; a non-SFCC value blocks the OCAPI tools — the eight reads, the nine writes, and `check_permissions`. It does **not** block `sfcc_setup_status`, and it does not block `sfcc_log_query`: log query runs on its own gate, which reads neither the `version` field nor `dw.json` and instead probes the backend log capability (log access is WebDAV Basic auth, a different boundary from OCAPI's OAuth). Set the field via your normal config path, the `config_field` MCP tool (operation `update`, field `version`), or the `/teach-bridge` skill.\n\n**2. Provide SFCC credentials.** Create a `dw.json` in your project root:\n\n```json\n{\n \"hostname\": \"zzzz-001.sandbox.us01.dx.commercecloud.salesforce.com\",\n \"client-id\": \"<account-manager-client-id>\",\n \"client-secret\": \"<account-manager-client-secret>\"\n}\n```\n\nAccepted key spellings: `hostname`/`host`, `client-id`/`clientId`/`client_id`, `client-secret`/`clientSecret`/`client_secret`. `dw.json` is auto-added to git exclude and must never be committed.\n\nCredentials resolve in **three tiers, highest first** — the environment wins over `dw.json`, not the other way round:\n\n1. An explicit dotted `instance` argument on the call, **plus** `SFCC_CLIENT_ID` and `SFCC_CLIENT_SECRET` in the environment. Secrets are never read from `dw.json` on this tier, so an explicit instance without those two env values is an error.\n2. `SFCC_HOSTNAME` **and** `SFCC_CLIENT_ID` **and** `SFCC_CLIENT_SECRET`, all three set.\n3. `dw.json`.\n\nBecause tier 2 outranks tier 3, a stale `SFCC_HOSTNAME` left in the environment silently wins over the `dw.json` you are looking at. Check both when a tool reports an unexpected host.\n\n**Use a single-config `dw.json`, or set all three `SFCC_*` variables.** A multi-entry `configs[]` array is **rejected outright** — it is not a working setup that merely requires an explicit `instance` on every call. Two things make that workaround unavailable: an explicit `instance` takes tier 1, which needs the client id and secret in the environment anyway, and most tools cannot accept a hostname at all — the value must contain a dot, and the site-preference tools constrain `instance` to `staging | development | sandbox | production`, none of which is a hostname.\n\n**3. Enable the tools you want.** Add the groups to `BRIDGE_MCP_PROFILE` in the MCP server `env` block (it is comma-separated), then **restart the MCP client**:\n\n```json\n\"env\": { \"BRIDGE_MCP_PROFILE\": \"sfcc\" }\n```\n\n`sfcc` gives the eight read tools plus `sfcc_log_query`. For the nine destructive write tools as well, use `\"sfcc,sfcc-write\"`; `full` expands to every group and is therefore write-capable. Without any of these, only the diagnostic tools are registered.\n\n**4. Verify and grant OCAPI access.** With `sfcc` selected in step 3 (both diagnostics live in that profile), ask your agent to run `sfcc_setup_status` (expect all checks ✓), then `check_permissions`. If it reports HTTP 401/403, it prints the exact OCAPI Settings JSON to paste in Business Manager (**Administration → Site Development → Open Commerce API Settings → Data API** tab); save it there and re-run. Restart the MCP client after any credential or env change — a running session does not pick them up.\n\nRead what it prints before pasting it. The output is **two labelled blocks**, and they are not equivalent:\n\n- **READ/SEARCH TOOL GRANTS** — what the `sfcc` read tools need: `get` on `/system_object_definitions`, and `get` + `post` on `/system_object_definitions/**`, `/site_preferences/**`, and `/custom_object_definitions/**`. The `post` is OCAPI's convention for its `*_search` endpoints, not a mutation — but it is a grant you are pasting, so it is labelled for what it is rather than as \"read-only\".\n- **MUTATION GRANTS** — required by the nine `sfcc-write` tools and by nothing else: `put`/`patch` on `/system_object_definitions/**` and `/custom_object_definitions/**`, and `patch` on `/site_preferences/**`. Paste this block only if you intend to enable `sfcc-write`.\n\nNeither block grants `delete`, and neither pastes the global `resource_id: \"/**\"` that would cover every Data API resource. Each entry names one resource family — `/system_object_definitions`, `/custom_object_definitions/**`, `/site_preferences/**` — so the wildcard is scoped to the family, not to the API. Within a family it is still broad, and `write_attributes` is `(**)`, so a throwaway sandbox is the right place for these.\n\n</details>\n\n### Sandbox enforcement\n\nEvery SFCC tool — all twenty, reads and writes alike, including the diagnostics —\npasses through one check before its own logic runs: **the hostname your\ncredentials actually resolve to must be a recognized developer sandbox.**\n\n- The check reads `credentials.hostname`, the value that goes into the OCAPI\n URL. It does not read the `instance` tool argument. Omitting `instance`, or\n passing `instance: \"sandbox\"`, has no effect on the decision — neither one\n selects or proves anything about the target. A dotted `instance` still\n *selects* a host through the documented credential precedence, but the host it\n selects is then validated like any other, so `check_permissions` cannot be\n aimed at a named production instance.\n- It **fails closed.** An unrecognized, malformed, or unparseable hostname is\n refused with HTTP `403`, `error.code: \"TARGET_NOT_SANDBOX\"`, and\n `error.details.failure_class: \"target-not-sandbox\"`, before any request leaves\n your machine.\n\nThe accepted hostname forms are:\n\n| Form | Example |\n|---|---|\n| `<realm>-<nnn>.sandbox.<region>.dx.commercecloud.salesforce.com` | `zzzz-001.sandbox.us01.dx.commercecloud.salesforce.com` |\n| `<realm>-<nnn>.sandbox.dx.commercecloud.salesforce.com` | `zzzz-001.sandbox.dx.commercecloud.salesforce.com` |\n| `<realm>-<nnn>.dx.commercecloud.salesforce.com` | `zyis-001.dx.commercecloud.salesforce.com` |\n\nAnything else is refused. In particular, hosts whose leading label names an\nenvironment (`production-…`, `staging-…`, `development-…`) are rejected even\nwhen they otherwise fit a form above, and the legacy `*.demandware.net` domain\nis not accepted at all — sandbox, staging, and production instances share that\ndomain with no suffix that separates them.\n\n### Read and write profiles\n\n`sfcc` and `sfcc-write` are **independent** groups. Neither implies the other.\n\n| `BRIDGE_MCP_PROFILE` | SFCC tools registered |\n|---|---|\n| unset / `core` | none |\n| `estimation` | none — this group adds only `estimate_epic`, which is not an SFCC tool |\n| `sfcc` | `sfcc_setup_status`, `check_permissions`, the 8 OCAPI read tools, and `sfcc_log_query` |\n| `sfcc-write` | the 9 destructive write tools **only** — it does not imply `sfcc`, so neither diagnostic and no read tool is registered |\n| `sfcc,sfcc-write` | all 20 |\n| `full` | all 20 — `full` includes `sfcc-write` and is therefore write-capable, and it also includes `estimation` |\n\n**`check_permissions` needs `sfcc`, even when you are diagnosing writes.** It is\nthe tool that prints the mutation-grant OCAPI Settings JSON on a 401/403, but it\nis registered by the `sfcc` group, not `sfcc-write`. An `sfcc-write`-only session\ntherefore cannot run it. Use `BRIDGE_MCP_PROFILE=sfcc,sfcc-write` whenever you\nneed to diagnose write grants.\n\n**Diagnostics moved into `sfcc`.** `sfcc_setup_status` and `check_permissions`\nused to register unconditionally, on every session including non-SFCC ones. They\nno longer do. The accepted consequence is that a session without `sfcc` can no\nlonger ask the server why SFCC tools are missing — run `npx -y @bridge_gpt/mcp-server doctor`\nfor that instead, which reports the resolved profile without needing any tool.\n\n**Migration.** Enabling `sfcc` used to register the nine write tools as well. It\nno longer does. If you were relying on SFCC writes through\n`BRIDGE_MCP_PROFILE=sfcc`, change it to `BRIDGE_MCP_PROFILE=sfcc,sfcc-write`.\nUsers of `BRIDGE_MCP_PROFILE=full` keep write access and need no change.\n\n### Tools\n\nTwenty tools in total, none of them registered unconditionally: the `sfcc` profile's **read-only** surface (the two diagnostics, eight OCAPI reads, and `sfcc_log_query`), plus the nine destructive writes that only the separate `sfcc-write` profile registers — see [Read and write profiles](#read-and-write-profiles). Every one of them is bounded to a developer sandbox by the same invocation-time check. All twenty are enumerated below.\n\nAn oversized response is saved in full to `BAPI_DOCS_DIR/sfcc/` and replaced by a parseable JSON descriptor — `truncated: true`, the `saved_path` it was written to, and the `page` metadata (`returned`, `total` when OCAPI supplied one, `has_more`) — so the collection metadata survives even though the data itself is on disk. If that save fails, the complete payload is returned inline instead, still as parseable JSON.\n\nAttribute-definition reads and writes can return an attribute's `default_value` at `projection: \"full\"`, and Bridge withholds it — every key is preserved except that one, whose value becomes `[REDACTED_BY_BRIDGE]` — from the inline response, the saved file, and a successful write echo alike. Attribute defaults are intentionally unavailable through this MCP surface; Business Manager is the supported path to read one.\n\n**Diagnostics** (registered by the `sfcc` profile)\n- `sfcc_setup_status` — report on every prerequisite: Bridge API key, repo name, `version` config, `dw.json` presence/uniqueness, AM (OCAPI) token acquisition, and the independent **SFCC Log Query (WebDAV)** capability that gates `sfcc_log_query`.\n- `check_permissions` — probe OCAPI access via `GET /system_object_definitions`; on 401/403, print the exact OCAPI Settings JSON to paste in Business Manager (read/search grants for the `sfcc` tools, mutation grants for the `sfcc-write` tools). An explicit `instance` hostname is still subject to the sandbox check below.\n\n**System object model — reads** (needs the `sfcc` profile)\n- `system_object_list` — list system object types (Product, Order, Customer, …).\n- `system_object_get` — fetch one system object type's definition.\n- `system_object_attribute_search` — search a type's attribute definitions; prefer this over a full dump when hunting a specific `c_` custom attribute.\n\n**System object model — writes** (needs the `sfcc-write` profile; every one is a destructive write, sandbox only)\n- `system_object_attribute_definition_create` — create an attribute definition via `PUT /system_object_definitions/{type}/attribute_definitions/{id}`.\n- `system_object_attribute_definition_update` — update one via `PATCH` on the same path.\n- `system_object_attribute_group_create` — create an attribute group via `PUT /system_object_definitions/{type}/attribute_groups/{id}`.\n- `system_object_attribute_group_update` — update one via `PATCH` on the same path.\n- `system_object_attribute_assign_to_group` — assign an existing attribute definition into a group via `PUT …/attribute_groups/{group}/attribute_definitions/{def}`.\n- `custom_preference_definition_create` — define a custom site or organization preference via `PUT /system_object_definitions/{SitePreferences|OrganizationPreferences}/attribute_definitions/{id}`.\n\n**Custom object definitions** (reads need `sfcc`; the two writes need `sfcc-write`)\n- `custom_object_definition_attributes_get` — fetch attribute definitions for a known custom object type (`default_value` withheld). OCAPI cannot enumerate custom object type *IDs* directly, so `object_type` must be known — but it is discoverable: call `system_object_list` at `projection: \"full\"` for each custom type's `display_name` and `attribute_definition_count`, derive a candidate id (e.g. strip spaces from `\"Product Quality Result\"` → `ProductQualityResult`), and confirm it by checking that this tool's returned attribute count matches that row's `attribute_definition_count`.\n- `custom_object_definition_attribute_search` — search attribute definitions within a known custom object type (`default_value` withheld). Read-only — creating a custom object *type* isn't possible via OCAPI; that's a future v2 metadata-import capability. Same discovery path as above applies to `object_type`.\n- `custom_object_definition_attribute_create` — **write** (sandbox only): create an attribute definition on a known custom object type via `PUT /custom_object_definitions/{type}/attribute_definitions/{id}`. TYPE creation is never attempted (the type must pre-exist). Echoes paste-ready OCAPI grant JSON on 403.\n- `custom_object_definition_attribute_update` — **write** (sandbox only): update an attribute definition via an ETag-conditional `PATCH …/attribute_definitions/{id}`; surfaces 409/412 conflicts and echoes grant JSON on 403.\n\n**Site preferences** (reads need `sfcc`; the write needs `sfcc-write`; sandbox only)\n- `site_preference_group_list` — list the preference groups on a site. This is the discovery tool the other two reads depend on: both take a group, and this is how you find one.\n- `site_preference_get` — list the preference **identifiers** in a group.\n- `site_preference_search` — search/filter preference identifiers within a group.\n- `site_preference_values_set` — **write** (sandbox only): set custom preference values via `PATCH /site_preferences/preference_groups/{group}/sandbox` with a flat map of `c_`-prefixed ids to string/number/boolean/string[] values. A bad group returns 404 `CustomPreferenceGroupNotFoundException`; echoes grant JSON on 403.\n\n> **Site preference values are write-only through this surface.** `site_preference_get` and `site_preference_search` return **ids only, never values** — an unset preference and one set to the empty string are indistinguishable. So you can *set* a value with `site_preference_values_set` and have no way to read it back through an MCP tool. Business Manager is the supported path to read a preference value.\n\n**On-demand log query** (needs the `sfcc` profile)\n- `sfcc_log_query` — query redacted, filtered SFCC logs on demand. `environment` and `time_range` (`start`/`end`) are **required** — production, \"all environments\", and an open-ended period are never inferred. The tool calls a Bridge backend endpoint that runs the pull → redaction → filter pipeline server-side and returns scoped, redacted findings; **WebDAV credentials, retrieval, redaction, and filtering all stay server-side and single-sourced.** It holds no credentials of its own.\n - **Guardrails.** Selection is bounded by log-file `prefixes` (max 5), the time range, a scanned-entry cap (`max_entries`, ≤ 2000), a finding cap, and a per-snippet length cap. High-volume prefix classes (`info`, `jobs`, `debug`, `customdebug`) impose a **stricter 6-hour** max range (vs. 24h for the error class) because `info-*` runs ~1 MB/day versus `error-*` at ~13 KB median — a wide window over a high-volume prefix is **rejected**, never silently narrowed.\n - **Response order.** Resolved scope (`environment`, `time_range`, `applied_prefixes`) and cap `status` first, redacted `findings` second, retrieval/truncation `metadata` last.\n - **Statuses & errors.** `ready`, `no_matching_findings`, `results_truncated`; plus `VALIDATION_ERROR` (bad/oversized scope, caught before any network call), `NOT_CONFIGURED` (503 — the log capability isn't set up; run `sfcc_setup_status`, whose step 6 reports it), and `BAD_GATEWAY`/`SERVICE_UNAVAILABLE` on a retrieval/backend failure.\n - **Auth is separate from OCAPI.** Log retrieval uses **HTTP Basic auth** — a Business Manager username + a **40-character WebDAV access key** — *not* the OCAPI Account Manager OAuth token the other SFCC tools use. A valid AM bearer token 401s on `/Logs`. `sfcc_setup_status` step 5 (AM/OCAPI token) and step 6 (WebDAV log access) are independent: one can be green while the other is not.\n - **Local / air-gapped fallback.** The primary path above is the only path this tool takes. For air-gapped development, the documented fallback is Salesforce's own **`@salesforce/b2c-cli`**, shelled out to directly: `b2c logs get --since <window> --search <q> --json`. There is no MCP alternative to reach for — `@salesforce/b2c-dx-mcp` ships **no `logs_*` tool** as of 1.1.2, and every log workflow in the vendor toolkit goes through the CLI anyway (see the [vendor manual](../docs/mcp/b2c-commerce-developer.md)). It is **not** the primary path because its credentials live client-side and its output has **not** passed Bridge's redaction/filter. If you use it, its output must be treated as raw: route it back through the same server-side Python `LogSource` composition and `RedactionPort`/T3 filter workflow — never paste or relay unredacted CLI output to an LLM.\n\n## CLI Subcommands\n\nBeyond `--init` / `--upgrade`, the package ships operational subcommands of the **single `bridge-api-mcp-server` bin** (not separate binaries) — so they travel with the package to every consumer. See [Usage Documentation → Start Tickets](#regularly-useful) for *when* to use `start-tickets`; this section is the full CLI reference.\n\n### `start-tickets`\n\nSpawns one Worktrunk worktree + selected-agent session per Jira ticket and backs the `/start-tickets` slash command. The agent defaults to **Claude Code** (`claude`) and is configurable via `--agent`. For existing ticket keys, `/review-and-start` (see [Usage Documentation → Review and Start](#regularly-useful)) is the recommended enriched front door over this CLI's `--workflow review-and-implement` seam; using the CLI directly (below) remains the advanced/lower-level path.\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]\n```\n\n| Flag | Default | Meaning |\n|---|---|---|\n| `--agent claude\\|cursor-agent` | `claude` | Agent command to launch in each worktree |\n| `--workflow implement\\|review-and-implement` | `implement` | Slash command each spawned worktree runs. `implement` is byte-identical to today's `/implement-ticket <KEY> [--auto]`. `review-and-implement` spawns `/review-and-implement <KEY> [--auto] [--rounds=<n>]`, which runs `/review-ticket` then, after a per-ticket halt gate, `/implement-ticket` in the same session. `--auto` applies to the selected workflow as a whole. |\n| `--rounds 1\\|2` | unset | Review round count forwarded to the review phase. Review-only — valid only with `--workflow review-and-implement`. |\n| `--tier cheap\\|basic\\|premium` | unset (difficulty routing) | Coarse model-routing override. Bypasses **only** the per-ticket difficulty/tier lookup (`GET /jira/tickets/{KEY}/model-tier`) and applies this one tier to every ticket; the tier is still mapped to a model through the centralized agent registry and any configured `difficulty_model_tier_overrides`, then validated. It is **not** a raw `--model` alias and never carries an API key or credential. A malformed value fails open to premium routing. Used by the `/review-and-implement` handoff to reuse the review-time tier. |\n| `--terminal terminal\\|iterm` | auto-detect via `$TERM_PROGRAM` | Override the macOS terminal app (honored on macOS only) |\n| `--dry-run` | off | Print intended actions; create no worktrees, open no tabs (any OS) |\n| `--branch KEY=BRANCH` | `feature/<KEY>` | Use a custom branch for that ticket (repeatable) |\n| `--base-branch <BRANCH>` | `main` | Cut new worktrees from `<BRANCH>` and refresh `origin/<BRANCH>` instead of `main` |\n| `--no-refresh-main` | off (the configured base branch is refreshed) | Skip refresh of the configured base branch (default `main`). Historical flag name preserved for backward compatibility — despite the name, it now skips refresh of whatever `--base-branch` resolves to. |\n| `--max-parallel N` | `3` | Max worktrees created concurrently |\n| `--conductor` | off | Opt into the Conductor system (per-worker `BAPI_CONDUCTOR_*` env + Claude hook injection, lifecycle ledger events, and a supervisor peer tab). **Default off** — a plain run spawns `cd <worktree> && <agent> '/implement-ticket <KEY>'`. |\n| `-h`, `--help` | — | Show usage |\n\nEach `KEY` must match `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). The CLI creates/switches each worktree up front (throttled by `--max-parallel`), then opens one tab/session per successful worktree running the selected agent's `'/implement-ticket <KEY>'` — `claude '/implement-ticket <KEY>'` by default, or `cursor-agent '/implement-ticket <KEY>'` with `--agent cursor-agent`. The `/implement-ticket <KEY>` prompt is unchanged for both agents. To launch Cursor Agent instead of Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\n**Difficulty-based model routing.** Before launching each agent, the CLI selects an implementation **model tier** from the ticket's `difficulty` (1-2 → cheap, 3-5 → basic, 6+ → premium) and injects it as a `--model` flag at the spawn boundary. The Python backend returns only the coarse tier (`GET /jira/tickets/{KEY}/model-tier`, computing + caching difficulty on demand); this CLI alone maps a tier to the agent-specific alias (`claude`: `haiku`/`sonnet`/`opus`; `cursor-agent`: version-suffixed strings validated against `cursor-agent --list-models`). It is gated per repo by `difficulty_model_routing_enabled` (default **ON**) with an optional `difficulty_model_tier_overrides` JSON map (tier → alias). Routing is **fail-open**: missing credentials, an evaluation failure/timeout, a backend `fallback`, an invalid/unavailable alias, an unadvertised Cursor model, or an agent without `--model` support all default to the **premium** (Opus) model — and when even the premium alias cannot be resolved/validated for the agent, `--model` is omitted (the agent uses its default) — each surfaced as a per-ticket warning rather than failing the spawn. `--dry-run` does **not** create worktrees or open tabs, but it **does** resolve routing read-only to preview the `--model` each tab would use.\n\n**Coarse `--tier` override.** Passing `--tier cheap|basic|premium` bypasses **only** the per-ticket difficulty lookup above and applies that one tier to every ticket; the tier is still resolved to an alias through the same agent registry + `difficulty_model_tier_overrides` and validated the same way (including the live `cursor-agent --list-models` check). It is never treated as a raw `--model` alias, and no API key or credential belongs in the spawned command (the CLI resolves credentials itself via `resolveBapiCredentials`). A malformed/unrecognized `--tier` value is **fail-open**: the CLI logs one concise warning and routes every ticket on the premium (Opus) fallback rather than aborting. `/review-and-implement` uses this flag to hand its review-time tier snapshot to the fresh implementation session.\n\n**Conductor observability (opt-in via `--conductor`, BAPI-394).** Conductor is **off by default**. With `--conductor`, a run mints a conductor `run_id`, emits events into the local ledger (`~/.config/bridge/events.db`), opens a supervisor peer tab, and (for Claude Code) injects a secret-free lifecycle hook into each worktree; it also sets `BRIDGE_MCP_PROFILE=conductor` so workers get the conductor MCP tools. Observability is best-effort — a conductor failure never blocks a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally.) See **[CONDUCTOR.md](./docs/CONDUCTOR.md)** for the full observability, git-hook, done-gate, and auto-merge reference.\n\n**Cross-platform spawning.** The CLI routes spawning per platform; `--dry-run` previews the platform-correct command form on any OS. An unsupported `process.platform` (not `darwin`/`win32`/`linux`) fails fast with a clear \"unsupported platform\" message.\n\n- **macOS** — opens a Terminal.app or iTerm tab via `osascript`.\n- **Windows** — creates worktrees with **`git-wt`** (Worktrunk's winget alias) and opens a tab via **Windows Terminal (`wt.exe new-tab`)**, falling back to **`Start-Process powershell.exe`** when Windows Terminal is absent. Requires **Git for Windows / Git Bash** (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash). The Worktrunk binary (`git-wt`) and the tab launcher (`wt.exe`) are resolved independently and never conflated.\n- **Linux** — creates one detached **tmux** session per ticket (pane kept open after the agent exits); attach with `tmux attach -t <session>`. A missing `tmux` produces a clear, actionable error.\n\nPer-OS prerequisites: macOS `wt`, `git`, `osascript`; Windows `git-wt`, Git for Windows / Git Bash, Windows Terminal or PowerShell; Linux `wt`, `git`, `tmux`. Set `BAPI_WORKTRUNK_BIN` to override the Worktrunk executable name/path for nonstandard installs (`doctor` honors it too). The read-only `doctor` subcommand (below) additionally surfaces a missing `uv` — Worktrunk's `pre-start` hook runs `uv`, but live preflight does not check it — and the selected agent's command; run `doctor --agent cursor-agent` to also check `cursor-agent` (it prints `cursor-agent login` as an informational auth reminder).\n\n### `doctor`\n\nThe package also ships a strictly **read-only** `doctor` subcommand — titled **`bridge doctor — read-only diagnostics`** — that diagnoses your whole Bridge install without changing anything:\n\n```\nnpx -y @bridge_gpt/mcp-server doctor [--agent <name>]\n```\n\nThe report always leads with the advisory **`Install status`** section (repo identity, credential resolution, server connectivity, bootstrap-field completeness, integration credentials, indexing state) **before** the `start-tickets` prerequisite diagnostics; the launcher-cache and MCP tool-surface sections follow. `Install status` is read-only GETs only and never affects the exit code.\n\nThe report also includes a **Claude login** advisory: whether the host's own\n`~/.claude.json` carries a login marker. This is informational only — it never\nblocks the doctor run and cannot guarantee the next worker spawn will\nauthenticate. See\n[Claude login for conductor workers](#claude-login-for-conductor-workers).\n\nIt is **read-only**: it never installs anything, modifies your system, adds an npm `postinstall`, spawns a terminal, or starts the MCP server, and there is no `--fix`. For each prerequisite it prints found/missing and, when missing, the exact per-OS install command **as a manual instruction you run yourself**. The checked set is the `start-tickets` preflight prerequisites **plus `uv`** **plus the selected agent's command** (`claude` by default, or `cursor-agent` with `--agent cursor-agent`). The Worktrunk binary is probed via the resolved name (honoring `BAPI_WORKTRUNK_BIN`), not a hard-coded one. **Exit code:** `0` when all required prerequisites are present, non-zero when any is missing or the platform is unsupported. A failing `start-tickets` preflight now hints you to run `doctor` for an actionable diagnostics report.\n\n**`claude-review` workflow lineage (advisory, read-only).** `doctor` also reports\nwhether the current branch's `.github/workflows/claude-review.yml` matches the\nrepository default branch's copy. This matters because\n`anthropics/claude-code-action` refuses to run when the two differ — so a\nlong-lived base branch that has fallen behind silently blinds automated review for\n**every pull request based on it**, even though those pull requests change no\nworkflow file at all. The probe runs `git symbolic-ref`, `git rev-parse`, and two\n`git show` reads against the local object database: it **fetches nothing, spawns\nnothing, and creates no worktree**, and an unresolvable ref simply reports the\ncomparison as unavailable. It is **advisory only** — it is registered as a\ndoctor-only diagnostic and **never changes `doctor`'s required-prerequisite exit\ncode**, whether it finds drift or cannot complete the comparison. Spawning a\nworker against a stale base prints the same advisory and **still spawns the\nworker**. Remedy and full diagnosis:\n`docs/claude/runbooks/claude-review-base-branch-drift.md`.\n\n### `drive-epic`\n\nThe one conductor entry point every Bridge surface names. Give it an epic key and\nit reads conductor readiness for your repository and routes to the single path\nyour project can actually run:\n\n```\nnpx -y @bridge_gpt/mcp-server drive-epic <EPIC>\n```\n\nYou are never asked to choose. Bridge currently has two conductors and a standing\nrule that they must never operate on the same epic — two transition authorities on\none epic wedge it permanently — so the choice is made structurally rather than by\njudgement. Readiness green routes to the v2 bootstrap below (pass `--plan-file`\nand `drive-epic` runs it for you); readiness not green prints the interactive\npilot instruction instead. If readiness is **unknown** — unreachable,\nunauthorized, or malformed — it escalates and prints no conductor invocation at\nall, because an unknown owner is not the same as a not-ready one. No branch,\nincluding every error path, ever offers you two paths.\n\nTwo conductors is a transitional state. When one is eliminated, `drive-epic` is\nthe only thing that changes.\n\n### `setup-epic`\n\nBootstraps an Epic Conductor v2 run in one command — creates the epic run, stores the plan DAG, and approves it:\n\n```\nnpx -y @bridge_gpt/mcp-server setup-epic --epic-key <KEY> --plan-file <path-to-epic-plan.dag.json>\n```\n\nThe plan sidecar is produced by the `decompose-epic` pipeline step. `setup-epic` validates it locally (unique ticket keys, resolvable dependency references, acyclicity) before sending anything, so a malformed plan fails legibly instead of as a bare HTTP 400. It is **idempotent**: re-running it on an epic that already has a live run reuses that run rather than minting a second one. `--dry-run` validates and previews the calls without mutating anything; `--json` emits a single machine-readable result object.\n\nOnce the plan is approved, the **server-side reconciler** picks the run up within ~30s. To execute claimed jobs on your machine, run `executor`:\n\n```\nnpx -y @bridge_gpt/mcp-server executor --repo <name>\n```\n\n### `conduct-epic`\n\nThe deterministic half of the `/conduct-epic` loop: it owns the epic branch, a\nversioned local checkpoint, a per-epic lock, and the read-only probes the loop\ndecides on. It never creates or mutates an `epic_run` — that is the server-side\nv2 reconciler's job, and `init` refuses to start when one is already active.\n\n```\nnpx -y @bridge_gpt/mcp-server conduct-epic <verb> [flags]\n```\n\n**Verbs**\n\n| Verb | Flags |\n| --- | --- |\n| `init <EPIC>` | `--tickets K1,K2,…` (required), `--base-branch <b>`, `--checkpoint-path <p>`, `--dry-run`, `--json` |\n| `status <EPIC>` | `--json` (required), `--checkpoint-path <p>` |\n| `checkpoint set <EPIC>` | `--ticket <KEY>` (required), `--field <name> <value>` (repeatable), `--journal \"<line>\"`, `--checkpoint-path <p>` |\n| `finish <EPIC>` | `--checkpoint-path <p>`, `--json` |\n| `spawn <EPIC>` | `--ticket <KEY>` and `--prompt-file <path>` (required), `--agent claude\\|cursor-agent`, `--checkpoint-path <p>`, `--json` |\n\n**Local state.** Everything lives *outside* the repository, under\n`~/.config/bridge/conduct/<repo>/` (honoring `XDG_CONFIG_HOME`), so it resolves\nidentically from the main checkout and from any worktree and can never be\ncommitted by an agent running `git add`:\n\n| Path | Purpose |\n| --- | --- |\n| `<EPIC>.json` | the version-1 checkpoint (file `0600`, directory `0700`) |\n| `<EPIC>.json.prev` | the previous valid checkpoint, retained on every write |\n| `<EPIC>.lock` | the per-epic lock |\n| `<EPIC>/prompts/<KEY>-<kind>-<n>.md` | prompt files the caller writes for `spawn` |\n\n`status` prints the resolved `checkpoint_path`. To unpark a run a human edits the\ncheckpoint (`needs_human` → `null`, plus the ticket's `status`/counters);\n`last_seen_head`, `ci_last_poll`, and `lock` are observational and are never\nhand-edited.\n\n**`init` runs ONE preflight** that reports *every* failure in a single pass and\nwrites nothing unless all of them pass: `gh auth status`; Worktrunk resolves\n(honoring `BAPI_WORKTRUNK_BIN`); Bridge credentials resolve; `auto_merge_enabled`\nis on — or is turned on by PUTting the *complete* effective config back with just\nthat flag flipped, which prints a line beginning `announced:`; at least one\nrequired CI check exists (an empty required set would make the done gate pass\nvacuously); no active server-side `epic_run` for the key; the lock is free or its\nowner is provably dead; the base branch exists on `origin` after `git fetch`; and\nthe indexed-branch override is either absent or this epic's own — a re-`init`\nafter a crash is accepted and its `original_base_branch` becomes the default base,\nwhile a *foreign* override is refused by name. `resolve-ci-checks` is called\nexactly once either way, because that call is what warms the `poll-ci-checks`\ncache the first `status` depends on. Only then does `init` push\n`epic/<EPIC>` to `origin` at the fetched base tip (no local checkout), repoint the\nindex, write the checkpoint, and take the lock. `--dry-run` prints the validated\nplan and mutates nothing. A second `init` refuses with `already initialized`.\n\n**Failure posture is split on purpose.** In `status`, each probe fails *open*: a\n`gh`, CI, review, or parse failure leaves that sub-object `null`, adds an entry to\n`probe_errors`, and the command still exits `0` with a complete object — the loop\nmust be able to read its own checkpoint during a GitHub outage. Everything else\nfails *closed*: a corrupt or wrong-version checkpoint makes every verb but `init`\nexit non-zero **without rewriting it**, and `checkpoint set`, `spawn`, and\n`finish` refuse a lock held by another live process. `status` never takes the lock.\n\n**Exit codes.** `0` on success — including a missing checkpoint\n(`checkpoint_exists: false`) and an idempotent second `finish`. Non-zero on any\nother failure, with a one-line reason on stderr. With `--json`, stdout is exactly\none JSON object carrying `ok`.\n\n**Credentials** resolve only from `BAPI_API_KEY` or the user-scoped\n`bapi:<repo>` credential target, travel only in the `X-API-Key` header, and never\nappear in a command argument, in stdout/stderr, or in a journal line.\n\n### Conductor (epic & multi-agent orchestration)\n\nConductor is an **opt-in, off-by-default** layer for epic supervision, inter-agent messaging, done-gate evaluation, local git-hook event producers, and conditional auto-merge. Its full reference — the v2 architecture (server-side reconciler + local executor), `setup-epic`, `conductor install-git-hooks`, the supervisor `done_gate_config` / `auto_merge_enabled` settings, and the observability stream — lives in **[CONDUCTOR.md](./docs/CONDUCTOR.md)**.\n\n> The v1 `conductor epic-tick` command is **frozen** — it throws on every invocation. There is nothing to schedule locally, and `conductor doctor` flags any epic-tick schedule left over from an earlier release so you can cancel it.\n\n## Custom Pipelines\n\nYou can create your own pipelines by adding JSON files to `.bridge/pipelines/`. Running `--init` scaffolds this directory with a `README.md` and an example pipeline to get you started.\n\nThe easiest way to write a custom pipeline is to describe what you want to automate to your AI coding agent and have it draft the JSON for you. The schema is straightforward, and agents like Claude Code understand it well — just describe the steps you want, and the agent will produce a working pipeline file.\n\n**What you can build:**\n\n- Any sequence of Bridge MCP tool calls and free-form agent tasks\n- Parameterized workflows using variables (e.g., `{ticket_key}`)\n- Approval gates that pause for user confirmation before sensitive steps\n- Per-step error handling — halt immediately or warn and continue\n\n**Ideas for custom pipelines:**\n\n- A standup pipeline that fetches your open tickets and summarizes their status\n- A ticket triage pipeline that runs critiques on a batch of new tickets\n- A pre-merge checklist that runs tests, checks linting, and posts a summary comment\n\n**Step types:**\n\n| Type | What it does |\n|---|---|\n| `mcp_call` | Calls an MCP tool with the given params |\n| `agent_task` | Gives the AI a free-form instruction (inline or from a file in `.bridge/instructions/`) |\n\nVariables are declared in the `variables` array and referenced as `{variable_name}` in params and instructions. Each step supports `on_error: \"halt\"` (default) or `\"warn_and_continue\"`, and `requires_approval: true` to pause before execution.\n\n**System variables:**\n\nTwo variables are automatically available in every pipeline without declaring them:\n\n| Variable | What it does |\n|---|---|\n| `{provider}` | Routes AI generation to a specific LLM provider (`openai`, `anthropic`, or `gemini`). Pass it through to any `request_*` tool param to control which provider handles that step. Omit it (or leave it empty) to use the project default. |\n| `{second_opinion}` | Runs AI generation through a different provider than the default, acting as a cross-check. Set to `\"auto\"` to let Bridge pick the second provider automatically. When set, it takes precedence over `{provider}`. Use this when you want two independent AI perspectives on the same task — for example, running clarifying questions and a critique twice (once with each provider) produces better results than a single pass. |\n\nSee `.bridge/pipelines/README.md` for the full schema reference.\n\nIf a custom pipeline has the same key as a built-in pipeline, the custom version takes precedence (a warning is logged at startup).\n\n## Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `BAPI_BASE_URL` (server, installer) | No | `https://bridgegpt-api.com` | Bridge API base URL. The MCP server and the `install` CLI both fall back to the production default |\n| `BAPI_BASE_URL` (`executor` subcommand) | **Yes** | _(none)_ | The `executor` deliberately has **no** production fallback — it refuses to start rather than guess a target |\n| `BAPI_REPO_NAME` | Yes | _(none)_ | Jira project/repository identifier configured in Bridge API |\n| `BAPI_API_KEY` | No | _(none)_ | A Bridge credential **is** required; this environment variable is only the first place the server looks for it. When it is unset the server resolves the credential from the user-scoped store (`~/.config/bridge/credentials.json`, target `bapi:<repo>`), which is why generated MCP registrations are secret-free |\n| `BAPI_PROJECT_ROOT` | No | _(see fallback order)_ | Absolute path to project root. Anchors `BAPI_DOCS_DIR` and `BAPI_PIPELINES_DIR` resolution. Resolved once, in order: `BAPI_PROJECT_ROOT` → the connected client's MCP `roots/list` → `CLAUDE_PROJECT_DIR` → `process.cwd()`. Several paths *write* it into a generated registration (`--init`, host-config provisioning, the worktree `mcp-invoke` shim) — that is provenance, not a runtime default |\n| `SFCC_HOSTNAME` | No | _(none)_ | SFCC sandbox hostname. Part of the environment credential tier — `SFCC_HOSTNAME`, `SFCC_CLIENT_ID`, and `SFCC_CLIENT_SECRET` must **all three** be set for that tier to apply, and a complete tier takes precedence over `dw.json` |\n| `SFCC_CLIENT_ID` | No | _(none)_ | Account Manager API client id. See `SFCC_HOSTNAME` — all three are needed together. Also required on its own when a tool is called with an explicit dotted `instance` |\n| `SFCC_CLIENT_SECRET` | No | _(none)_ | Account Manager API client secret. See `SFCC_HOSTNAME` — all three are needed together. Never sent to Bridge; it goes only to the Account Manager token endpoint |\n| `CLAUDE_CODE_OAUTH_TOKEN` | No | _(none)_ | The supported headless authentication input for conductor workers. Export it into the **executor process's own** environment; Bridge forwards it unchanged into the worker and stores it nowhere — no credential-store entry, no disk, never sent to Bridge. See [Claude login for conductor workers](#claude-login-for-conductor-workers) |\n| `BAPI_INSTALL_DEBUG` | No | _(unset)_ | Set to any non-empty value to unlock raw diagnostics in `install` and the `apply_install_manifest` path — the underlying error message and stack behind an `unexpected error` summary. The installer's own failure text tells you to set it |\n| `BAPI_SIGNUP_EMAIL` | No | _(none)_ | Selects the self-serve signup route without `--email`. Precedence: `--email` first, then this variable, then the visible interactive prompt |\n| `BAPI_INVITE` | No | _(none)_ | Invite code for `install`, for scripting. Like `--invite <code>`, it exposes the code to your shell history and the process list — prefer bare `--invite` and the hidden prompt |\n| `BAPI_PLANE_PYTHON` | No | `python` | Executable used for the Python members of `plane up`. Point it at a venv interpreter when `python` on `PATH` is not the one you want |\n| `BAPI_PLANE_UVICORN` | No | `uvicorn` | Executable used for the server member of `plane up` |\n| `BAPI_DOCS_DIR` | No | `docs/tmp` | Local directory for saving plans, critiques, and research reports |\n| `BAPI_PIPELINES_DIR` | No | `.bridge/pipelines` | Directory for user-defined custom pipeline JSON files |\n| `BAPI_WORKTRUNK_BIN` | No | `wt` (`git-wt` on Windows) | Override the Worktrunk executable name/path used by `start-tickets` for nonstandard installs |\n| `BAPI_TMUX_SESSION` | No | `bridge-start-tickets` | Override the tmux session-name prefix used by `start-tickets` on Linux |\n| `BAPI_MCP_UPGRADE_ADVICE_ENABLED` | No | _(enabled)_ | MCP-local opt-out for proactively surfacing upgrade advice in pipeline recipe preambles. Set to `false`/`0`/`no`/`off`/`disabled` to suppress. Disabling it does **not** change the `/jira/ping` response or server-side upgrade computation — it only gates the recipe-preamble convention |\n| `BAPI_MCP_TOOL_SURFACE_GATING_ENABLED` | No | _(enabled)_ | MCP-local kill switch for **dynamic tool-surface capability gating** (see [Dynamic tool-surface gating](#dynamic-tool-surface-gating-capability-availability)). Default-on; set to `false`/`0`/`no`/`off`/`disabled` to skip the startup probe, the recurring poll, and the custom `tools/list` handler entirely, restoring the SDK's previous full profile-derived surface. Fail-open: any probe timeout, unreachable backend, non-2xx, malformed payload, incomplete evaluation, or unsupported schema advertises the full profile |\n| `BAPI_MCP_TOOL_SURFACE_POLL_ENABLED` | No | _(disabled)_ | Opt IN to the recurring tool-surface **poll**. Default-**off**: a session gates once via the startup probe and never re-probes. Set to `true`/`1`/`yes`/`on`/`enabled` to restore the jittered 12–18 s heartbeat that pushes `notifications/tools/list_changed` on mid-session capability changes. No effect when gating itself is disabled |\n| `BAPI_MCP_UPDATE_CHECK_ENABLED` | No | _(enabled)_ | MCP-local kill switch for the cached update check run at startup. Default-on; set to `false`/`0`/`no`/`off`/`disabled` to skip it entirely — no npm registry request, no update-cache read/write, no stderr advice, and no `tools/list` advisory decoration. Any other value is treated as enabled. Useful behind a firewall/registry-restricted network, and used by the payload-measurement harness so a stale local cache can never perturb a capture |\n| `CURSOR_API_KEY` | No | _(none)_ | API key used to authenticate `cursor-agent` when launching `start-tickets`/scheduled runs with `--agent cursor-agent`. Not needed for the default Claude Code agent |\n| `BRIDGE_MCP_PROFILE` | No | `core` | Startup-time tool registration profile — a **comma-separated** list of groups controlling which tool groups are registered when the server starts. `core` is always implicitly included. Valid groups: `core` (default — normal coding tools only), `conductor` (+ 8 conductor/event/supervisor tools), `pipeline-authoring` (+ 5 pipeline run/admin tools — `get_pipeline_recipe` is NOT gated; it stays in `core` because the recipe-driven slash commands depend on it), `estimation` (+ `estimate_epic`, which is **hidden by default** — `/estimate-epic` needs this group), `sfcc` (+ the 8 heavy SFCC read tools, `sfcc_log_query`, and the `sfcc_setup_status`/`check_permissions` diagnostics — read-only, see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools)), `sfcc-write` (+ the 9 destructive SFCC write tools — independent of `sfcc`, which does not enable them; see [Read and write profiles](#read-and-write-profiles)), and `full` (shortcut that expands to every group, **including `estimation` and `sfcc-write`**). Example: `sfcc,conductor`; use `sfcc,sfcc-write` for reads plus writes. Unknown, blank, or malformed tokens are dropped (falling back to `core`). Dynamic mid-session switching via `tools/list_changed` is unsupported — groups are resolved once at process startup, so **changing this variable requires restarting the MCP server**. That is separate from the learn/onboarding tools, which are gated per session and can appear mid-session without a restart (see [Learn/onboarding tool availability](#learnonboarding-tool-availability)). Conductor/epic sessions launched via `start-tickets --conductor` **merge** `conductor` into the parent process's already-resolved groups at the spawn boundary rather than replacing them — a project running on `sfcc` spawns workers on `core,sfcc,conductor`. A normal `start-tickets` run stays on `core`. |\n\nEnvironment values are **trimmed**, and only a non-empty result wins. A\nwhitespace-only `BAPI_API_KEY` therefore does not override anything: it falls\nthrough to credential-store resolution exactly as an unset variable would.\n\n## Learn/onboarding tool availability\n\nThree tools are **gated per session** rather than by profile:\n\n- `apply_install_manifest`\n- `invite_member`\n- `regenerate_directory_map`\n\nThey are registered in the `core` profile like any other core tool, but the\nserver starts them **disabled**, so a client that honors mid-session tool-list\nchanges does not see them in its first `tools/list`. They are revealed — all\nthree together, once per session — by either of two successful requests:\n\n1. a successful **`get_install_manifest`** call, or\n2. a **`get_pipeline_recipe`** request for the **`learn-repository`** pipeline.\n\nOn reveal the server emits `notifications/tools/list_changed`, so a client that\nhonors it picks the three tools up **without a restart**.\n\n`get_install_manifest` and `persist_routing_credential` stay ungated in `core` at\nall times, so the install and `start-tickets` flows always have their entry\npoints. Only the first of those two is a trigger — `persist_routing_credential`\nnever reveals anything; it is listed here because `/start-tickets` calls it and\nit therefore must never be gated.\n\n**Unrecognized clients get all three at startup.** The hide-then-reveal behavior\napplies only to clients verified to honor mid-session `tools/list_changed`. Any\nother client — including one that sends no name, a blank name, or a name the\nserver does not recognize — is enabled at session initialization instead and\ntherefore sees all three tools in its **first** `tools/list`. This is deliberate:\nthe failure mode of guessing wrong should be an extra tool, never a permanently\nmissing one.\n\n**A failed request changes nothing.** Only a genuinely successful manifest\nreveals the tools — the response must parse as JSON and carry a nonblank\n`snapshot_token`. An error response, a non-JSON body, or a manifest without a\ntoken leaves the three tools hidden, and the response you receive is unchanged by\nthe check either way. Likewise, only the exact `learn-repository` pipeline is a\ntrigger; other recipes and failed lookups reveal nothing.\n\n**Capability filtering still applies independently.** Revealing these tools does\nnot override the backend capability gate described below. The effective surface\nis the *intersection* of the two, so a tool can be enabled for the session and\nstill be absent from `tools/list` because this repository lacks the integration\nit needs. That is expected behavior, not a bug — check the integration first.\n\n## Dynamic tool-surface gating (capability availability)\n\nThe **effective advertised tool surface** is the intersection of three things:\n\n1. **Startup profile registration** — which tool groups `BRIDGE_MCP_PROFILE`\n registered at process start (see above).\n2. **Current SDK-enabled state** — a tool the server has disabled for another\n reason (e.g. `poll_ci_checks` when `ci_check_config` is unset) stays hidden.\n3. **Backend capability availability** — the set of tool IDs the backend would\n currently hard-block for this repo, reported by `GET /jira/mcp/tool-surface`.\n\nOn startup the server issues one bounded probe to that endpoint and installs a\ncustom `tools/list` handler that subtracts the backend-blocked IDs (intersected\nwith the locally advertised surface) from what it advertises. That single startup\nprobe is the default: the surface is gated once per session and the server does\nnot re-probe. Installed integrations change rarely and MCP clients re-list on\nreconnect, so a permanent per-session heartbeat — multiplied across every\nconcurrent worktree/agent session — was pure request noise against the backend.\nOpt in with `BAPI_MCP_TOOL_SURFACE_POLL_ENABLED=true` to restore the jittered\n12–18 s re-probe that emits `notifications/tools/list_changed` whenever the\neffective visible set actually changes, so a connected client converges to the\ncurrent surface mid-session without a reconnect.\n\n**Fail-open by design.** A probe timeout, unreachable backend, non-2xx response,\nmalformed payload, incomplete evaluation, or unsupported schema version all\nadvertise the **full** existing profile baseline — gating never removes a tool on\na doubtful signal.\n\n**Hidden ≠ disabled.** A capability-hidden tool remains **registered and\ncallable**, including through in-process pipelines. Hiding affects `tools/list`\nprojection only; it never calls `.disable()` or mutates the SDK `enabled` flag,\nbecause doing so would also block `tools/call` and the in-process dispatch path —\nthe backend remains the authoritative enforcement boundary, returning its own\nrefusal for a stale call rather than a local \"disabled\" error.\n\n**Kill switch.** Set `BAPI_MCP_TOOL_SURFACE_GATING_ENABLED` to an accepted false\ntoken (`false`/`0`/`no`/`off`/`disabled`) to skip the probe, the poll, and the\ncustom handler entirely, restoring the SDK's previous full profile-derived\nsurface.\n\n**Client convergence and the reconnect escape hatch.** Clients that honor\n`notifications/tools/list_changed` converge automatically. A client that does not\nhonor the notification must **reconnect or start a new MCP server session** to\nobserve the current surface; no project MCP configuration change is required.\n\n**Diagnosing the surface.** Run `doctor` (its advisory \"MCP tool surface\"\nsection reports the kill-switch state, reachability, decision reason, blocked\ncount, physical tool IDs, and catalog revision via a single read-only GET), or\nread the server's stderr gating decision lines (`tool-surface gating: reason=…\nhidden=… revision=… hidden_tools=[…]`).\n\n## Worktree credentials and the `mcp-invoke` shim\n\nWhen `start-tickets` creates a git worktree, it provisions a Bridge API MCP\nregistration into that worktree so Claude Code (`.mcp.json`) and Cursor\n(`.cursor/mcp.json`) can reach the server immediately. These registrations are\n**secret-free**: they contain no `env` block and no API key. Instead they point\nat an internal subcommand of the published single CLI bin, `mcp-invoke`:\n\n```bash\nnpx -y @bridge_gpt/mcp-server@<VERSION> mcp-invoke --target bapi --project-root <ABS_WORKTREE_PATH>\n```\n\n`mcp-invoke` is not a separate binary — it is a positional subcommand of\n`bridge-api-mcp-server`. It resolves the repo identity from the absolute\n`--project-root` (the committed `.bridge/config` manifest, falling back to the\ngit common dir), resolves credentials from the home-directory credential store,\nand then spawns the real MCP server with that environment.\n\n### Credential store\n\nCredentials live outside the repository, keyed by `bapi:<repo_name>`:\n\n```json\n{\n \"bapi:<repo_name>\": {\n \"BAPI_API_KEY\": \"...\"\n }\n}\n```\n\nResolution order:\n\n1. `BAPI_API_KEY` in the parent environment (overrides the file entirely).\n2. `$XDG_CONFIG_HOME/bridge/credentials.json`, else `~/.config/bridge/credentials.json`.\n3. `~/.bridge/credentials.json` (only when the primary path is absent).\n\nOn POSIX systems, lock the file down so only you can read it:\n\n```bash\nchmod 600 ~/.config/bridge/credentials.json\n```\n\n`mcp-invoke` warns (but continues) if the file is group/world-readable, and it\nnever creates or initializes the credential file for you.\n\n### Populating the credential store\n\nThe same store also backs the shell-spawned `start-tickets` CLI (its\ndifficulty→model routing runs in a Bash process that cannot see an `env` block in\n`.mcp.json` / `.cursor/mcp.json`), so the store must hold the key for routing to\nwork. Two supported paths write it for you:\n\n- **Install-time upsert.** `/install-bridge`'s final stage persists the validated\n routing credential into `~/.config/bridge/credentials.json` (target\n `bapi:<repo>`) via the `persist_routing_credential` MCP tool — the tool resolves\n the key inside the MCP process and writes the store, so no secret crosses the\n wire.\n- **One-shot migration.** If a key currently lives only in `.mcp.json` /\n `.cursor/mcp.json`, migrate it into the user-scoped store with the consent-gated\n command (a compatibility aid, not a live fallback):\n\n ```bash\n npx -y @bridge_gpt/mcp-server credentials migrate-agent-config [--write-credentials]\n ```\n\n Run it without `--write-credentials` to preview; add the flag to write the store.\n\n### Claude login for conductor workers\n\nConductor workers are **not** isolated into a private Claude configuration\ndirectory — they run with the executor host's own `HOME`, so a worker\nauthenticates the same way any interactive `claude` invocation on that host\ndoes. The prerequisite is simple: run\n\n```bash\nclaude login\n```\n\non the executor host, once, the normal way. Bridge never stores, resolves, mints,\nrotates, validates, or diagnoses this credential — it is entirely the operator's\nown Claude CLI state, exactly as if you were running `claude` at the terminal\nyourself.\n\n**Headless hosts.** If the executor host has no interactive login session\navailable (a service-launched executor, a CI-style runner), export\n`CLAUDE_CODE_OAUTH_TOKEN` into the **executor process's own environment**\nyourself before starting it:\n\n```bash\nexport CLAUDE_CODE_OAUTH_TOKEN=\"$(claude setup-token)\" # run once, wherever you can browser-login\n```\n\nBridge forwards that value **unchanged**, byte-for-byte, into the direct worker\nprocess environment — nothing else. It is never written to disk, never placed in\na generated launchd/systemd service unit, never placed in project configuration\n(`.mcp.json` / `.cursor/mcp.json`), and never sent to Bridge servers. There is no\ncredential store entry for it and no lifecycle tracking: expiry, rotation, and\nvalidity are entirely the operator's own responsibility, the same as any other\nvalue you choose to export into a process environment.\n\n**`ANTHROPIC_API_KEY` is never forwarded to a worker**, under any circumstance —\nthere is no fallback path for it.\n\n`mcp-server doctor` reports a single advisory **Claude login** line — whether\n`~/.claude.json` on the host it runs on carries a login marker. This is\ninformational only: it cannot confirm the next worker spawn will authenticate,\nand it never blocks the doctor run or changes its exit code.\n\n## Reference\n\nThe full surface, for when you need the complete enumeration. Day-to-day, use [Usage Documentation](#usage-documentation) instead — you don't call MCP tools directly; you ask your AI assistant to perform a task, or compose tools into a pipeline.\n\n### MCP tools\n\nThe authoritative tool catalog covers **70 tools** (enumerated below). What's actually registered in a session depends on `BRIDGE_MCP_PROFILE`: the default `core` profile loads a trimmed subset, and the conductor/pipeline-authoring/SFCC tools are added only under their respective profiles (see [Environment Variables](#environment-variables)). Async AI tools follow a request/get pattern: call the `request_*` tool to kick off generation, then the matching `get_*` tool to retrieve the result (or pass `wait_for_result: true` to poll automatically).\n\n- **Connectivity & identity** — `ping` (its JSON also carries `docs_dir`, `role`, and `customer_type`)\n- **Team & access** — `invite_member` (admin-only; mints a scoped access key for a teammate on an already-configured project — the plaintext key is shown exactly once)\n- **Jira tickets** — `get_tickets`, `get_ticket`, `create_ticket`, `update_ticket_description`, `add_comment`, `get_comments`\n- **Attachments** — `attachment` (operations: `upload`, `download`, `list`)\n- **AI generation (request/get)** — `request_plan_generation`/`get_plan`, `create_doc`/`get_doc` (design docs by `doc_type`: tdd/architecture/fsd/prd, where `architecture` is an alias of `tdd`), `request_ticket_review` (writes both `get_clarifying_questions` and `get_ticket_critique`), `request_reimplement_context`/`get_reimplement_context`, `request_council`/`get_council`, `request_deep_research`/`get_deep_research`\n- **Other AI** — `second_opinion`, `generate_image`, `generate_decision_page`, `visual_diff` (deterministic pixel diff of a rendered URL vs a design comp)\n- **Ticket lifecycle** — `track_ticket`, `update_ticket_state`, `get_ticket_state`, `get_ticket_state_tree` (live repo-wide lifecycle + dependency tree; read-only, no mutation parameter)\n- **Jira status** — `update_jira_status` (pass `status: \"auto\"` to resolve the configured post-PR status server-side)\n- **Repository & CI** — `parse_repository` (`action`: `start`, `status`), `regenerate_directory_map`, `create_pull_request`, `merge_pull_request`, `resolve_ci_checks`, `poll_ci_checks`\n- **Pipelines & automation** — `get_pipeline_recipe` (returns a fully resolved recipe the agent executes step-by-step)\n- **Config** — `get_project_standards`, `config_field` (operations: `get`, `update`, `list`)\n\n### Bundled pipelines\n\nPipelines are declarative, multi-step workflows your AI agent executes step-by-step — each a JSON recipe chaining MCP tool calls and free-form agent tasks, with variable substitution, per-step error handling, and optional approval gates. You can also write your own (see [Custom Pipelines](#custom-pipelines)).\n\n| Pipeline | Description | Invoke with |\n|---|---|---|\n| `implement-ticket` | Generate a plan, execute the implementation, commit, open a PR, and monitor CI | `/implement-ticket PROJ-123` |\n| `review-ticket` | Full ticket quality review: clarifying questions + critique plus an automatic second-opinion pass, then evaluation and decision capture. The backend owns round orchestration; pass `--rounds=1` for a single-pass review or `--rounds=2` to force the full second-opinion review, or omit `--rounds` to let the backend decide adaptively. | `/review-ticket PROJ-123` |\n| `idea-to-ticket` | Turn an idea into a Jira Task/Spike (or Epic + children) with research, dedup, and critique | `/idea-to-ticket \"<idea>\"` |\n| `plan-epic` | Decompose an epic into sub-tasks with a structured exploration doc for each | `/plan-epic \"<epic>\"` |\n| `full-automation` | Client-side wrapper: runs the `idea-to-ticket` recipe, then hands the created keys to `/review-and-start --auto` | `/full-automation \"<idea>\"` |\n| `learn-repository` | Analyze codebase architecture, testing, review, and documentation standards, then upload to Bridge | `/learn-repository` |\n\n### Pipeline response envelope\n\n`get_pipeline_recipe` returns a fully resolved recipe: an ordered `steps` array\nplus `total_steps`, `auto_approve`, and `execution_mode`. Each step is either an\n`mcp_call` (a tool name with resolved params) or an `agent_task` (instruction\ntext). `execution_mode` is `inline` — the calling agent executes every step\nitself and keeps its own durable record through `record_phase_result` /\n`record_checkpoint`. There is no server-side orchestrator and no run to resume.\n";
3
+ export const README = "# @bridge_gpt/mcp-server\n\nThe Bridge MCP is an end-to-end accelerator for shipping code within SFCC, powered by [Bridge API](https://bridgegpt-api.com). Works with Claude Code, Github Copilot, Cursor, Windsurf, and OpenAI Codex.\n\n## Install\n\nFrom your **project root**, run:\n\n```bash\nnpx -y @bridge_gpt/mcp-server install\n```\n\nThat is the whole setup command. It works whether or not you already have a Bridge\naccount — it will ask.\n\n> We recommend **the command above** instead of the `npm i @bridge_gpt/mcp-server`\n> one in npm's sidebar, because it **will make set up much easier**.\n\n**What it will do**\n\n- **Bootstraps the Bridge MCP for you** — one command and your editor's agent can\n use Bridge's tools and slash commands on this project.\n- Registers a `bridge` MCP server in your editor's MCP config, leaving any\n other servers you have configured untouched.\n- Creates and updates the files it needs inside your project root: slash commands\n and agent definitions for your editor (`.claude/commands/`, `.cursor/commands/`,\n and the equivalents your editor uses), your editor's MCP config, and `.bridge/`\n for your project manifest and pipeline definitions.\n- Stores your Bridge credential outside the project, so the MCP server and the\n tooling that spawns its own shells can find it without you configuring anything.\n Re-running `install` still asks for the credential unless you supply it through\n `--api-key` or `BAPI_API_KEY` — the installer writes that store, it does not read\n it back.\n- Writes outside your project root only when you pick a host whose configuration is\n global: OpenAI Codex (`~/.codex/config.toml`) and GitHub Copilot CLI\n (`~/.copilot/mcp-config.json`).\n\n**Prerequisites**\n\n- **Node.js 18 or newer** (`node --version`), which is what provides `npx`.\n- **A project directory** — run the command from the folder your editor opens: your\n repository root, the one containing `.git`. No `package.json` is required — SFCC\n cartridge repos, Python, Go, Rust, and other non-Node projects work the same way.\n- **An MCP-capable editor or CLI**: Claude Code, GitHub Copilot in VS Code, GitHub\n Copilot CLI, Cursor, Windsurf, or OpenAI Codex.\n- **No Bridge account needed.** The installer can create one for you from just an\n email address.\n\n## Contents\n\n- [Install](#install)\n- [Installation details](#installation-details)\n - [Installing, step by step](#installing-step-by-step)\n - [What to expect](#what-to-expect)\n - [Troubleshooting](#troubleshooting)\n- [Usage Documentation](#usage-documentation)\n - [Regularly useful](#regularly-useful)\n - [Occasionally useful](#occasionally-useful)\n - [Now and then](#now-and-then)\n - [Workflow commands](#workflow-commands)\n - [Operational commands](#operational-commands)\n - [Extra Capabilities](#extra-capabilities)\n- [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools)\n- [CLI Subcommands](#cli-subcommands)\n- [Custom Pipelines](#custom-pipelines)\n- [Environment Variables](#environment-variables)\n- [Worktree credentials and the `mcp-invoke` shim](#worktree-credentials-and-the-mcp-invoke-shim)\n- [Reference](#reference)\n\nFor advanced epic/multi-agent orchestration, see [CONDUCTOR.md](./docs/CONDUCTOR.md).\n\n## Installation details\n\n### Installing, step by step\n\n**1. Open a terminal in your project root.** This matters: the installer writes\nyour slash commands and MCP config relative to the directory you run it from. If\nyou run it in your home directory, your editor will not find any of it.\n\n**2. Run the command.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install\n```\n\n**3. Answer the sign-in question.** On a first run it asks whether you already have\na token:\n\n```\n1. Yes, I have received a token\n2. No, I need one\n```\n\n- Choose **2** if you have nothing yet. It asks for your email address and a name\n for your new Bridge project, then creates both for you.\n- Choose **1** if someone gave you a token — either a Bridge API key or an invite\n code. Paste it at the hidden prompt; you do not have to say which kind it is,\n because the installer recognizes it. Nothing is echoed as you type.\n\nThere is no default answer, so pressing Enter alone selects nothing. If you would\nrather not be asked, pass the answer up front instead — see\n[Choosing how you sign in](#choosing-how-you-sign-in).\n\n**4. Pick which editors to configure.** The installer detects the MCP hosts on your\nmachine and asks which ones to set up. Pick every editor you actually use for this\nproject; you can re-run the command later to add another.\n\n**5. Reload your MCP host.** Editors read their MCP configuration at startup, so a\nfreshly written config is not live until you reload. Restart the editor, or use its\n\"reload MCP servers\" action. In Claude Code you will also be asked to trust the\nproject's `.mcp.json` the first time.\n\n**6. Finish in the agent session the installer opens — when it opens one.** The\nlast thing the installer does is offer to open a fresh agent session running\n`/install-bridge`, which reads your codebase, fills in the remaining project\nsettings, and prints a short report of what Bridge can help with. Let it finish.\n\nThree things all have to hold for that session to open: your selection has to\ninclude a host the installer can launch, the run has to be on an interactive\nterminal, and you have to accept the consent prompt (*\"Bridge can configure and set\nup this project for you automatically. Open a `<tool>` session to do that now?\n(Y/n)\"*). Claude Code is the only selection that launches on its own. A\nCursor-only, Copilot, Copilot CLI, Codex, or Windsurf selection, a non-interactive\nrun, or a declined prompt all print the command to continue by hand instead. Pass\n`--agent claude` or `--agent cursor-agent` to override the decision outright.\n\n**7. Follow the next step the session shows you, if it shows one.** The installer\nasks the server what should happen next and shows that command only when there is\none to show — most often `/learn-repository`, which it recommends when the project\nstill needs its architecture, testing, review, and correctness standards documented\nand your key can run it. The installer deliberately does not run it for you. Those\nstandards are what make every later plan, critique, and review match how your\nproject actually works, and they only need to be gathered once per project — the\nresult is shared with everyone on the team. If the session shows no next step,\nthere is nothing for you to run.\n\nWant to see what would happen without changing anything? Add `--dry-run`.\n\n<details>\n<summary id=\"what-to-expect\"><strong>What to expect</strong></summary>\n\n**Files that appear in your project**\n\n| Path | What it is | Commit it? |\n|---|---|---|\n| `.claude/commands/`, `.cursor/commands/` | The slash commands your editor runs | Yes |\n| `.claude/agents/` and editor equivalents | Agent definitions used by those commands | Yes |\n| `.bridge/config` | Your project manifest — the repository name and which MCP targets to provision. Deliberately secret-free | Yes |\n| `.bridge/pipelines/`, `.bridge/instructions/` | Editable pipeline definitions | Yes |\n| `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json` | MCP registrations for your editor. These can carry your key, so the installer git-ignores them | No |\n\nThe installer tells you which of these are safe to commit and never recommends\ncommitting anything that can hold a credential.\n\n**Prompts you will see.** More than the sign-in question, in three groups:\n\n- *Always on a first bare interactive run:* the sign-in question, a hidden prompt\n for a token (or a visible one for an email), a project name for a brand-new\n project, a picker for which editors to configure, and an offer to connect GitHub\n (`Connect GitHub? [y/N]:`).\n- *Conditional on your situation:* a confirmation when the directory has no `.git`\n (default **No**, and declining aborts); a *\"Which tool should open? [1-N]\"*\n chooser when your selection contains more than one launchable tool; and the\n consent prompt before the final agent session.\n- *Overwrite confirmations, each default **No** and each skippable with `--force`:*\n a saved key for this project already exists; a host config already contains a\n `BAPI_API_KEY`; a **git-tracked** config would receive your real key; a saved but\n expired self-serve signup would be discarded.\n\n**A fresh agent session opens at the end — if your selection can launch one.** See\nstep 6 above for the three conditions. Use `--agent cursor-agent` if you want\nCursor's agent instead of Claude Code.\n\n**Selecting Windsurf prints instructions instead of writing config.** Windsurf's\nglobal `mcp_config.json` is never modified automatically; the installer reports the\nentry for you to paste yourself. Codex and Copilot CLI *are* written automatically,\neven though their files are global too.\n\n**Your key is stored for the tools that read the store.** The MCP server and the\nshell-spawned tooling (`start-tickets` and its model routing) resolve it from\n`~/.config/bridge/credentials.json` on their own. The **installer** does not: a\nrepeat `install` prompts for the credential again unless you pass `--api-key` or\nset `BAPI_API_KEY` in the environment.\n\n**A next step, when the project needs one.** The session closes with whatever\ncommand the server says comes next, and stays quiet when there is nothing to\nrecommend. `/learn-repository` is the usual one: it is recommended when the project\nstill needs its conventions documented and your key can run it. It is never\nautomatic — until someone runs it, Bridge's agents work from your code alone rather\nthan from your project's documented conventions.\n\n**Indexing happens on its own.** There is no \"index my repository?\" question. Once\nyour project has the settings it needs, indexing starts server-side. You never have\nto ask for it.\n\n</details>\n\n<details>\n<summary id=\"troubleshooting\"><strong>Troubleshooting</strong></summary>\n\n**\"My editor doesn't see any Bridge tools.\"** Two usual causes. First, the config\nwas written somewhere your editor is not looking — re-run the installer from the\ndirectory your editor actually opens, and check that a `bridge` entry exists in\nthat project's MCP config. Second, the editor has not been reloaded since the file\nwas written; restart it. In Claude Code, also confirm you accepted the trust prompt\nfor the project's `.mcp.json`.\n\n**\"I ran it in the wrong folder.\"** Nothing is broken. Depending on which editors\nwere detected, a run can leave `.bridge/`, `.bridge/install-state.json`, `.claude/`,\n`.cursor/commands/`, `.cursor/mcp.json`, `.vscode/mcp.json`, `.github/agents/`,\n`.mcp.json`, and appended `.gitignore` lines. Remove only what that run created and\nre-run the command from the right directory — if you already had a `.vscode/`,\n`.cursor/`, or `.gitignore` there, keep the parts you had before.\n\n**\"It seems to hang with no output.\"** If you ran the bare command\n(`npx -y @bridge_gpt/mcp-server`) with no subcommand, you started the MCP *server*,\nnot the installer. It is waiting for an editor to connect over stdio, which is\nexactly what it should do when your editor launches it — but from a terminal it\nlooks like a hang. It prints a line saying so. Press Ctrl-C and run\n`npx -y @bridge_gpt/mcp-server install` instead. The explicit spelling\n`npx -y @bridge_gpt/mcp-server serve` starts the server on purpose.\n\n**\"It can't reach Bridge\" or \"my key was rejected.\"** The installer checks\nconnectivity before it saves your **credential** anywhere, so a failure here has not\nwritten your key into a config or stored it for later. It has already\nscaffolded the project files by then — slash commands, agents, pipelines,\n`.bridge/config`, and secret-free per-host MCP placeholders — so expect those to\nexist; re-running is safe and refreshes them. A\nrejected key means the credential is not valid for that project — check the project\nname you gave, and generate a fresh key on the Bridge web UI's **Security** page if\nneeded. A network failure usually means a proxy or VPN is in the way.\n\n**\"Which repository name should I use?\"** The one registered with Bridge. If you\nhave an existing key, the installer usually resolves it for you; when it cannot, it\nasks, and `--repo <name>` answers it up front.\n\n**Still stuck? Ask the installer to diagnose itself.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server doctor\n```\n\n`doctor` is strictly read-only. It reports what it found — configs, registrations,\ncredential availability, prerequisites — and changes nothing.\n\n</details>\n\n<details>\n<summary id=\"choosing-how-you-sign-in\"><strong>Choosing how you sign in</strong></summary>\n\nThree routes lead to the same place. The interactive question above picks one for\nyou; these flags pick it up front and skip the question entirely.\n\n**No account yet — sign up with an email.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install --email you@example.com\n```\n\nCreates a brand-new Bridge project for that address and your first admin key in one\ncommand. No account, no key, and no invite needed beforehand. The address labels\nyour new workspace and may receive a setup message; delivery is best-effort, so\nnothing waits on it. The email is visible as you type (it is not a secret) and is\nnever written to a log. This is the same route as answering **2** at the prompt.\n\n**You were sent an invite code.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install --invite\n```\n\nRedeems the invite, creates your project, and mints your first admin key. Run it\n*without* a value, as shown: the installer then asks for the code at a hidden\nprompt, so the code never lands in your shell history. `--invite <code>` and the\n`BAPI_INVITE` environment variable exist for scripting, but both expose the code to\nyour shell history and to the process list.\n\n**Your team already has a project and gave you an API key.**\n\n```bash\nnpx -y @bridge_gpt/mcp-server install --api-key <key>\n```\n\nOr omit the flag and paste the key at the hidden prompt. Generate a key on the\nBridge web UI's **Security** page (**Create New Key**, role **Admin**) and copy it\nimmediately — it is shown once. `BAPI_API_KEY` works too.\n\nIf you paste an invite code where a key was expected, or a key where an invite was\nexpected, the installer recognizes the mismatch and tells you before anything is\ncreated or spent.\n\n</details>\n\n<details>\n<summary><strong>Installer flags</strong></summary>\n\n| Flag | What it does |\n|---|---|\n| `--email <addr>` | Sign up for a new Bridge project with just an email address |\n| `--invite [code]` | Redeem an invite code. Omit the value for the hidden prompt (recommended) |\n| `--api-key <key>` | Use an existing Bridge API key |\n| `--repo <name>` | Name the registered repository instead of resolving or asking for it |\n| `--tools <list>` | Configure specific MCP hosts without the picker. Accepted IDs are exactly `claude-code`, `cursor`, `copilot-vscode`, `copilot-cli`, `codex`, and `windsurf` (e.g. `claude-code,cursor`); any other value is a parse error |\n| `--agent claude\\|cursor-agent` | Which agent to open for the final configuration step. **No default** — without this flag the agent is derived from the hosts you selected, and an explicit value always wins, including for a host you did not select |\n| `--dry-run` | Preview every step without writing, contacting Bridge, resolving or prompting for a credential, or opening anything. Genuinely inert: it returns before the project-root prompt, before the repository is resolved, and before any tool-selection prompt, so a value it cannot know locally (an unresolved repository name, an unselected tool) is shown as **not yet known** rather than guessed |\n| `--force` | Overwrite an existing stored key without asking |\n| `-h`, `--help` | Full usage |\n\n`--email`, `--invite`, and `--api-key` are mutually exclusive — each names a\ndifferent way to arrive, and the installer will not guess between them.\n\n</details>\n\n<details>\n<summary><strong>Setting up an MCP host by hand</strong></summary>\n\nThe installer configures your editors for you. Do this only if you would rather\nwrite the config yourself, or if you use a host it cannot write automatically.\n\nScaffold the project files and write a secret-free MCP registration. Run it from\nthe same project root `install` uses — your repository root, the one containing\n`.git`. No `package.json` is required:\n\n```bash\nnpx -y @bridge_gpt/mcp-server --init\n```\n\n`--init` always creates `.mcp.json`, and adds `.vscode/mcp.json` or\n`.cursor/mcp.json` when it detects those editors. Each generated entry carries\n`BAPI_BASE_URL`, `BAPI_REPO_NAME`, `BAPI_DOCS_DIR`, and `BAPI_PROJECT_ROOT`, and\n**never** `BAPI_API_KEY` — the server resolves the credential itself at runtime.\n\nSo the manual work left after `--init` is narrower than writing an entry from\nscratch: correct `BAPI_REPO_NAME` if it was written as the `YOUR_REPO_NAME`\nplaceholder, and supply your credential through a supported source (`BAPI_API_KEY`\nin the entry's `env` block, `BAPI_API_KEY` in the server's environment, or the\n`~/.config/bridge/credentials.json` store).\n\nWrite the entry yourself instead — for a host `--init` does not touch, or because\nyou would rather — using the shapes below. Add `\"serve\"` as the last launcher\nargument, as shown: it is the explicit way to say \"start the MCP server.\" Pin the\npackage to an exact version and pass `--prefer-offline`, which is what the\ngenerated entries do and what keeps npx from resolving a different build on some\nlater boot.\n\n<!-- BEGIN GENERATED: mcp-host-examples (managed by mcp_server/scripts/sync-readme-host-examples.js — DO NOT EDIT BY HAND) -->\n<details>\n<summary><strong>Claude Code (.mcp.json)</strong></summary>\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.52\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>VS Code / Copilot (.vscode/mcp.json)</strong></summary>\n\n```json\n{\n \"servers\": {\n \"bridge\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.52\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>Cursor (.cursor/mcp.json)</strong></summary>\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.52\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>GitHub Copilot CLI (~/.copilot/mcp-config.json)</strong></summary>\n\nCopilot CLI reads a single global file. The installer writes this one for you when\nyou select `copilot-cli`; the shape below is what it produces.\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"type\": \"local\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.52\", \"serve\"],\n \"tools\": [\"*\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>Windsurf (~/.codeium/windsurf/mcp_config.json)</strong></summary>\n\nWindsurf only supports global MCP configuration.\n\n```json\n{\n \"mcpServers\": {\n \"bridge\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.52\", \"serve\"],\n \"env\": {\n \"BAPI_BASE_URL\": \"https://bridgegpt-api.com\",\n \"BAPI_REPO_NAME\": \"your-repo\",\n \"BAPI_API_KEY\": \"your-api-key\",\n \"BAPI_DOCS_DIR\": \"docs/tmp\"\n }\n }\n }\n}\n```\n</details>\n\n<details>\n<summary><strong>OpenAI Codex (~/.codex/config.toml)</strong></summary>\n\n```toml\n[mcp_servers.bridge]\ncommand = \"npx\"\nargs = [\"-y\", \"--prefer-offline\", \"@bridge_gpt/mcp-server@0.2.52\", \"serve\"]\n\n[mcp_servers.bridge.env]\nBAPI_BASE_URL = \"https://bridgegpt-api.com\"\nBAPI_REPO_NAME = \"your-repo\"\nBAPI_API_KEY = \"your-api-key\"\nBAPI_DOCS_DIR = \"docs/tmp\"\n```\n\n> Codex users: set `BAPI_PROJECT_ROOT` manually in your config (see [Environment Variables](#environment-variables)).\n</details>\n<!-- END GENERATED: mcp-host-examples -->\n\nAfter saving, reload your editor and ask your assistant to call the `ping` tool to\nconfirm the connection.\n\nAn entry with no trailing `serve` still starts the server — bare invocation means\n\"server\" permanently, and nothing rewrites an existing config to add the token.\n\n</details>\n\n<details>\n<summary><strong>Upgrading Bridge</strong></summary>\n\n```bash\nnpx -y @bridge_gpt/mcp-server@latest --upgrade\n```\n\n`upgrade` fetches the latest published version, refreshes your scaffolded slash\ncommands, agents, and pipelines, updates the version pin in your MCP config, and\nopens a session so you can reconnect. It is also available as the\n`/upgrade-bridge` slash command.\n\nUse the `@latest` form. It applies to the short-lived *upgrader* process: without\nit, npx may reuse a cached older copy of the package and \"upgrade\" you with the\nbuild you are trying to replace. The exact `MAJOR.MINOR.PATCH` pin the upgrader\nwrites into your MCP config is deliberately different — host configs stay pinned\nto an exact release so a project's server is reproducible.\n\n`upgrade` reports **per config file**, because a project can have several\n(`.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`) and they can disagree:\n\n```\nLauncher pins:\n .mcp.json: 0.2.16 -> 0.2.36\n .cursor/mcp.json: already 0.2.36\n```\n\nWhen every applicable launcher pin was already at the target, it prints\n`Already up-to-date.` — that status comes from comparing your configs, not from\nthe version of the CLI process. A non-zero exit means the upgrade did **not**\nconverge, and nothing is reported as complete in that case. The causes:\n\n- the npm registry lookup failed **and** this process was not started from\n `@latest`, so the target version could not be confirmed — the likeliest one\n offline, and why the canonical command uses `@latest`;\n- a launcher pin is already **newer** than the target, which an automated repin\n must never downgrade;\n- an unreadable or unparseable config, a launcher carrying a version range or a\n dist-tag rather than an exact release, or two Bridge registrations in one file;\n- a competing local install it could not remove, or a pin that failed post-write\n verification;\n- the upgrade finished but left an **unconfigured** MCP entry — one that would\n authenticate as nobody.\n\nThe server checks for updates on startup. The check is cached for a day and never\nblocks startup. When a newer version is known, it surfaces in two places you do\nnot have to go looking for: a one-line warning on the server's **stderr**, and a\nshort advisory attached to the ordinary `tools/list` response so the agent in the\nsession can see that some tools may be missing or renamed in the older build.\nNeither requires calling `ping` or `doctor`.\n\nRe-running `install` on an already-configured project is safe: it refreshes the\nscaffolded files without overwriting your stored credential unless you pass\n`--force`.\n\n</details>\n\n## Usage Documentation\n\nThis is the Bridge API tooling worth knowing about as a software engineer — the things you'd ask an agent to do — grouped by how often you would use them. Each entry covers **what it does**, **when it's useful**, **how to use it**, and its **flags**. The behind-the-scenes plumbing is summarized at the end under [Extra Capabilities](#extra-capabilities), and a full enumeration lives in [Reference](#reference).\n\nWorking in a Salesforce B2C Commerce codebase? Bridge also ships SFCC platform tools — read-only introspection under the `sfcc` profile, and nine destructive writes under the separate `sfcc-write` opt-in. See [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools).\n\nFor invocation, prefer the slash command — it's deterministic. A free-text example is shown only where natural-language phrasing reliably maps to the right automation; high-consequence or easily-misread automations show only the slash command on purpose.\n\n<!-- The three tier sections below are GENERATED from TWO catalogs by\n scripts/sync_mcp_server_readme.py: api/library/config/mcp_tool_catalog.json,\n the authoritative MCP tool catalog, and api/library/config/workflow_catalog_lib.py,\n the immutable catalog of slash-command workflows (which have no MCP registration\n and therefore cannot live in the JSON artifact). Edit the curated tool metadata in\n scripts/sync_mcp_tool_catalog.py and the workflow definitions in\n workflow_catalog_lib.py — never the JSON artifact and never the text between the\n markers. Generation order is: sync_mcp_tool_catalog.py, then\n sync_mcp_server_readme.py, then `cd mcp_server && npm run build` (which bundles this\n file into readme.generated.ts, served as the MCP resource bridge://readme).\n Everything outside the marker pair — including the sections below it — is hand-written. -->\n\n<!-- BEGIN GENERATED: mcp-tool-documentation (managed by scripts/sync_mcp_server_readme.py — DO NOT EDIT BY HAND) -->\n### Regularly useful\n\nThe tools worth knowing for most tickets.\n\n**1. Review Ticket**\n- **What it does:** Runs a full quality review of a ticket: clarifying questions and a critique plus an alternate-model second opinion, then evaluates the findings and produces a decision page for accepting or rejecting them.\n- **When it's useful:** (Refinement) Right after a ticket is drafted, before anyone starts building — to surface gaps and tighten it.\n- **How to use it:** `/review-ticket BAPI-123` (command only — \"review\" as free text is easily mistaken for a freehand agent review). For several tickets at once, `/review-tickets BAPI-123 BAPI-456` opens one terminal tab per ticket and reviews them in parallel with no worktrees; every `/review-ticket` flag applies, and `--review KEY=auto,rounds=N` sets per-ticket overrides.\n- **Flags:** `--auto` auto-accept findings and skip the approval gates · `--rounds=1` a cheaper single-pass review that still evaluates findings and captures decisions · `--rounds=2` force the full second-opinion review · omit `--rounds` to let the difficulty-adaptive review policy decide.\n\n**2. Start Tickets**\n- **What it does:** Creates one git worktree per ticket and spawns an agent session in each to implement them in parallel.\n- **When it's useful:** (Implementation | Automation) When you're ready to start building one or more refined tickets concurrently.\n- **How to use it:** `/start-tickets BAPI-248 BAPI-250` (see [CLI Subcommands](#cli-subcommands) for the full flag table and cross-platform behavior).\n- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default · `--workflow implement|review-and-implement` selects which slash command each spawned worktree runs · `--rounds=1|2` forwarded to the review phase, valid only with `--workflow review-and-implement` · `--tier cheap|basic|premium` coarse model-routing override.\n\n**3. Review and Start**\n- **What it does:** Spawns one worktree per ticket; each session reviews the ticket inline and, after a per-ticket proceed/halt gate, hands off to a **fresh implementation session** that reuses the same worktree — review and implementation run in two separate agent contexts, not one shared session.\n- **When it's useful:** (Refinement | Implementation | Automation) The **recommended front door** for \"review these tickets, then implement the ones that pass,\" starting from existing ticket keys (unlike `/full-automation`, which only accepts an idea).\n- **How to use it:** `/review-and-start BAPI-248 BAPI-250` (single or multiple keys flow through the identical code path).\n- **Flags:** `--auto` auto-approves both the review and the implementation phase of every spawned session · `--rounds=1|2` forwarded to the review phase · `--agent`, `--base-branch`, `--max-parallel`, `--dry-run` mirror `/start-tickets`.\n\n**4. Explore Ticket**\n- **What it does:** Maps the code paths, dependencies, and project conventions a task would touch, settles its acceptance criteria with you on a decision page, then compares the viable implementation approaches and their trade-offs and writes up a proposed design. Along the way it surfaces the ambiguities that still need deciding and can pull in optional web or deep research where the answer is not in the code.\n- **When it's useful:** (Architecture | Refinement) Before writing a ticket or a plan, when you're unsure how a change would fit the existing code and want the open questions and the realistic options laid out first.\n- **How to use it:** `/explore-ticket <task>` — *\"Explore the codebase for how we'd add a Mistral LLM provider and recommend 2–3 implementation options.\"*\n- **Flags:** None.\n\n**5. Council**\n- **What it does:** Fans your problem out to two different models and returns their approaches, in technical, design, discovery, or general mode.\n- **When it's useful:** (Architecture | Refinement) Early, when you want a spread of approaches — technical for how to build it, design for how it should look, discovery for what still needs figuring out before a real ticket exists, general for a quick brief-driven pass before the repository is indexed.\n- **How to use it:** `/council <question>`\n- **Flags:** `--mode` selects one of four modes, passed to the underlying `request_council` tool as e.g. `mode: \"discovery\"`: `technical` (the default — implementation/architecture approaches), `design` (UI/UX and visual direction), `discovery` (stakeholder discovery questions, grouped into `Technical Discovery Questions` and `Business / Stakeholder Discovery Questions` and tagged `[HUMAN]`/`[CODE]`/`[TICKET]`), and `general` (brief-driven ideation from your task description alone). `technical` and `discovery` are codebase-grounded and need an indexed repository; `general` needs no code index at all, so it works immediately after install. The legacy boolean `design=true` still works and maps to `mode: \"design\"`.\n\n**6. Deep Research**\n- **What it does:** Runs multi-source, fact-checked web research on a technical topic and returns a cited report.\n- **When it's useful:** (Architecture | Refinement) When a decision hinges on outside knowledge — libraries, best practices, standards — that you do not already have.\n- **How to use it:** `/bridge-research <question>`\n- **Flags:** None.\n\n### Occasionally useful\n\nGood to know, but not needed every day.\n\n**1. Upload Ticket**\n- **What it does:** Creates a real Jira issue from a drafted ticket, including child tickets under an epic; your agent should confirm with you before creating it.\n- **When it's useful:** (Refinement) The final step after drafting — to get the ticket into your tracker so it can be tracked and worked.\n- **How to use it:** Ask your agent to create the ticket; it should confirm with you before creating the live issue.\n- **Flags:** Name the issue type (Bug / Story / Task / Epic) and, for a child ticket under an epic, the parent key.\n\n**2. Plan Ticket**\n- **What it does:** Generates a step-by-step implementation plan for a ticket that references real files in your codebase.\n- **When it's useful:** (Refinement | Implementation) Once a ticket is solid and you want a concrete build plan before — or instead of — auto-implementing it.\n- **How to use it:** `/plan-ticket BAPI-123`\n- **Flags:** `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check the plan with a second provider.\n\n**3. Create Doc**\n- **What it does:** Generates a design document for a ticket — a technical design document, a functional spec, or a product requirements document.\n- **When it's useful:** (Architecture | Refinement) When a ticket needs a fuller design write-up before planning or implementation, in the shape that fits your audience.\n- **How to use it:** `/create-doc BAPI-123 --doc-type tdd` (or `fsd` / `prd`)\n- **Flags:** `--doc-type tdd|fsd|prd` which document to generate (required) · `--provider <name>` choose the model provider · `--second-opinion <provider>` cross-check with a second provider.\n\n**4. Second Opinion**\n- **What it does:** Gets an immediate critique of any text from a different model family, without saving an artifact.\n- **When it's useful:** (Architecture | Refinement | Implementation) Any time you want a quick sanity check on a plan, draft, or decision from a fresh perspective.\n- **How to use it:** Ask your agent — \"Get a second opinion from Gemini on whether the BAPI-123 plan's migration step is safe to run against production.\"\n- **Flags:** Pick the provider (anthropic / openai / gemini) and the tier (cheap / basic / premium).\n\n**5. Generate Image**\n- **What it does:** Generates an image from a text prompt using a provider image model, spending provider credits on every call.\n- **When it's useful:** (Architecture | Refinement) When you want a quick visual — a UI mockup, diagram, or illustration — to anchor a design discussion or attach to a ticket.\n- **How to use it:** Ask your agent — \"Generate an image of a dashboard showing SOC2 evidence freshness as a traffic-light grid.\"\n- **Flags:** `provider` openai (`gpt-image-2`) / gemini (Imagen, which adds an invisible SynthID watermark) · `quality` low (default, cheapest) / medium / high · `size` 1024x1024 / 1024x1536 / 1536x1024. The image is saved to `BAPI_DOCS_DIR/images/` and also returned inline.\n\n### Now and then\n\nUseful once in a while.\n\n**1. Reimplement Ticket**\n- **What it does:** Gathers the context and attachments added since the last pass so a targeted follow-up change can be made.\n- **When it's useful:** (Implementation) After review feedback or new screenshots, when you need a targeted second pass rather than a fresh build.\n- **How to use it:** `/reimplement-ticket BAPI-123`\n- **Flags:** None.\n\n**2. Update Ticket**\n- **What it does:** Rewrites a ticket's description, fully replacing what is there today.\n- **When it's useful:** (Refinement) After review, to fold the resolved questions and fixes back into the ticket itself.\n- **How to use it:** Ask your agent — \"Replace BAPI-123's description with: <new text>.\" It fully overwrites the live description, which is hard to reverse.\n- **Flags:** None.\n\n**3. Get Ticket**\n- **What it does:** Retrieves the full details of a ticket, including its summary, status, and description, and optionally the model tier its difficulty routes to.\n- **When it's useful:** (Refinement | Implementation) Any time you want the agent to read a ticket before acting on it.\n- **How to use it:** Ask your agent — \"Pull up BAPI-123 and show me its description, status, and acceptance criteria.\"\n- **Flags:** `include_model_tier` adds the model tier the ticket's difficulty routes to; leave it off for an ordinary read.\n\n**4. Search Tickets**\n- **What it does:** Searches across the tickets in your project.\n- **When it's useful:** (Refinement) When you need to find tickets by project, status, or wording rather than by key.\n- **How to use it:** Ask your agent — \"Search our project for open tickets mentioning rate limiting.\"\n- **Flags:** Narrow the search by project, status, issue type, or free text.\n\n**5. Write Comment**\n- **What it does:** Posts a comment on a ticket.\n- **When it's useful:** (Refinement | Implementation) To leave context, status, or a decision trail on the ticket.\n- **How to use it:** Ask your agent — \"Post a comment on BAPI-123: blocked on the expired Atlassian token — will retry after it is rotated.\"\n- **Flags:** A long comment can be attached as a file instead of inlined.\n\n**6. Read Comments**\n- **What it does:** Reads the comment thread on a ticket.\n- **When it's useful:** (Refinement | Implementation) When the discussion on a ticket matters and you want the agent to read it before acting.\n- **How to use it:** Ask your agent — \"Read the comments on BAPI-123 and summarize what was decided.\"\n- **Flags:** None.\n\n**7. Ticket Attachments**\n- **What it does:** Downloads files from a ticket to your disk, or attaches a local file to a ticket.\n- **When it's useful:** (Refinement | Implementation) When a ticket has design files or logs you need locally, or you want to attach output back to it.\n- **How to use it:** Ask your agent — \"Download the design mockups attached to BAPI-123 into my docs folder,\" or \"Attach build-log.txt to BAPI-123.\"\n- **Flags:** Choose the direction (download from the ticket, or upload to it) and, for a download, where the files should land.\n\n**8. Estimate Epic**\n- **What it does:** Estimates an epic, or an explicit group of tickets you name.\n- **When it's useful:** (Architecture | Refinement) When you need a sizing pass across an epic, or across a set of tickets you name explicitly.\n- **How to use it:** `/estimate-epic BAPI-123`\n- **Flags:** Pass an epic key, or an explicit list of ticket keys to estimate as one group.\n<!-- END GENERATED: mcp-tool-documentation -->\n\n### Workflow commands\n\nSlash commands that drive several tools at once. Start Tickets, Review and Start, and Explore Ticket are documented above under [Regularly useful](#regularly-useful) — the rest live here.\n\n**1. Implement Ticket**\n- **What it does:** Full build for one ticket: generate a plan, write the code, commit, open a PR, and monitor CI.\n- **When it's useful:** (Implementation) When a ticket is ready and you want it taken from plan to open PR in one go.\n- **How to use it:** `/implement-ticket BAPI-123` (command only — \"implement X\" as free text almost always triggers a freehand build instead of the Bridge plan→code→PR→CI pipeline).\n- **Flags:** `--auto` skip the approval gates (e.g. auto-commit/push).\n\n**2. Jira Ticket Writer**\n- **What it does:** An agent that drafts a well-structured Jira ticket from a plain description, applying your project's standards.\n- **When it's useful:** (Refinement) When you have an idea in your head and want a properly-formatted ticket draft without writing it by hand.\n- **How to use it:** Ask your agent, *\"Use the jira ticket writer to turn our conversation into a ticket.\"* The other ticket commands draft through it automatically.\n- **Flags:** None — name a specific standards file in your request to have it applied when drafting.\n\n**3. Idea to Ticket**\n- **What it does:** Turns a one-line idea into a Jira Task/Spike (or an Epic plus child tickets), with research, duplicate detection, and a critique pass built in.\n- **When it's useful:** (Refinement | Automation) When you have a rough idea and want a fully-formed, uploaded ticket without the manual draft-and-refine loop.\n- **How to use it:** `/idea-to-ticket <idea>`\n- **Flags:** None.\n\n**4. Plan Epic**\n- **What it does:** Decomposes a large epic into sub-tasks with a structured exploration doc for each.\n- **When it's useful:** (Architecture | Refinement) When a feature is too big for one ticket and you need it broken down and scoped.\n- **How to use it:** `/plan-epic <epic>` — *\"Decompose the epic 'migrate PayPal token storage off Custom Objects' into sub-tasks with an exploration doc for each.\"*\n- **Flags:** None.\n\n#### Ticket-authoring posture\n\n`/explore-ticket`, `/idea-to-ticket`, and `/plan-epic` all decide ticket shape\nthe same way, as does the `jira-ticket-writer` agent they draft through. A fresh install inherits this with no configuration\nstep and no server call; the full rationale and the closed exception list ship as\n`docs/bridge-ticket-authoring.md`.\n\n- **Drafted by the writer.** Every ticket body — epic parent, epic child, and\n ordinary sibling alike — goes through the `jira-ticket-writer` agent. Nothing\n composes a ticket description inline.\n- **Sized toward L, overflowing upward.** `L` (target) → `XL` (when the work\n does not fit in `L`) → `M` (third choice) → `S` (only when unavoidable). A\n slice that outgrows `L` becomes one `XL` ticket rather than two `L` ones —\n splitting a coherent slice to fit a band buys another worktree, another PR, and\n another rebase for nothing. This binds a standalone ticket and an epic child\n alike. Past roughly 40 files or ~3000 LOC it splits anyway, into the largest\n coherent pieces available.\n- **Grouped at three.** Three or more tickets is an epic: an epic parent plus an\n ordered child manifest, shown in full at an approval gate before anything is\n created. One or two are ordinary siblings — no epic parent, no manifest. The\n threshold is exactly three.\n- **Decomposed once, rendered many.** One pass freezes the split; body drafting\n then fans out one writer invocation per entry against that frozen manifest. A\n rendering invocation never re-splits, merges, reorders, or rescopes.\n- **Handed off once.** An epic handoff names exactly one entry point,\n [`drive-epic`](#drive-epic) — never a choice between conductors.\n\n**5. Run Tests**\n- **What it does:** Runs the unit and E2E suites and autonomously triages/fixes failures (via the test-correction agent).\n- **When it's useful:** (Implementation) After making changes, to confirm everything passes and auto-fix straightforward breakages.\n- **How to use it:** `/run-tests`\n- **Flags:** `--unit-only` skip the E2E suite · `--skip-e2e` same, phrased the other way.\n\n**6. Learn Repository**\n- **What it does:** Researches and documents the repo's architecture, testing, review, and correctness standards, then saves them to Bridge for future agents.\n- **When it's useful:** (Setup/Learning) When onboarding a new repo, or after big changes, so Bridge's agents follow your conventions.\n- **How to use it:** `/learn-repository`\n- **Flags:** None.\n\n**7. Teach Bridge**\n- **What it does:** Takes a plain-English instruction, figures out which standards field it belongs to, and merges it in (admin only).\n- **When it's useful:** (Setup/Learning) When you notice the agents missing a convention and want to correct it in one sentence.\n- **How to use it:** `/teach-bridge <teaching>` — *\"Teach Bridge: always use data-testid selectors in E2E tests.\"*\n- **Flags:** None.\n\n### Operational commands\n\nRepository and ticket housekeeping commands, beyond the tiers above. Implementation, commit/push, PR creation, and CI monitoring are steps of the `implement-ticket` recipe rather than standalone commands:\n\n| Command | What it does |\n|---|---|\n| `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |\n| `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |\n\n> Commands are designed for Claude Code. Other editors may support slash commands differently — check your editor's documentation for how to invoke prompt files.\n\n### Extra Capabilities\n\nBehind-the-scenes capabilities an agent gains from the MCP tools — mostly invoked automatically by the commands above, rarely requested by name:\n\n- **Ship a PR end-to-end:** commit & push, open a pull request, transition the Jira status, and discover/poll CI checks — the commit-and-push, PR-creation, and CI-monitoring steps of the `implement-ticket` recipe, driven by its bundled instruction assets.\n- **Design documents** for a ticket (TDD / FSD / PRD, generated and retrieved through `create_doc` / `get_doc`).\n- **Index the codebase** so Bridge's agents can reason about it: queue/parse the repo and check parse status (`parse_repository`), regenerate the directory map.\n- **Read & tune project config/standards:** list/read/update config fields, fetch project standards, and the per-topic `learn-*` commands that populate them.\n- **Ticket lifecycle bookkeeping:** track tickets and backfill workflow-state timestamps (`scan-tickets`), search across tickets, read comments, list attachments.\n- **Pipeline machinery:** fetch a resolved pipeline recipe (the step list behind the orchestration commands) and execute its steps agent-side.\n- **Decision page** generation for capturing human review decisions as structured data.\n- **Connectivity check:** ping Bridge to confirm the API key and repository resolve.\n- **Retrieve any generated artifact** (`get_*` for plans, critiques, questions, councils, research, design docs) without regenerating it.\n- **Tiered-section execution telemetry** recording (internal measurement).\n\n## Salesforce B2C Commerce (SFCC) Tools\n\nSalesforce's official MCP server, `@salesforce/b2c-dx-mcp`, covers developer-experience tasks — cartridge deploy, step debugging, MRT bundle push, and SCAPI Custom API scaffolding. **As of `@salesforce/b2c-dx-mcp` 1.1.2 (published 2026-05-20)** it has no way to *read* the platform's object model, custom objects, or site configuration — exactly the context an AI coding agent needs to write correct Commerce code and to inspect a sandbox before touching it. That comparison is dated on purpose: its basis is this repository's hand-maintained [vendor manual](../docs/mcp/b2c-commerce-developer.md), pinned to the same version, so a new Salesforce toolset ages the claim visibly instead of rotting silently. Bridge's SFCC tools install side-by-side with `b2c-dx-mcp` (they don't duplicate its surface) and close that gap with **OCAPI Data API introspection** of system objects, custom object definitions, and site preferences — plus, behind a separate opt-in, a set of sandbox-bounded writes.\n\n**Every SFCC tool is restricted to a developer sandbox, and the restriction is checked at invocation time against the hostname your credentials actually resolve to** — not against anything the caller passes in. If `dw.json` or `SFCC_HOSTNAME` names a host Bridge does not recognize as a developer sandbox, every SFCC tool refuses with a `403` before contacting it. See [Sandbox enforcement](#sandbox-enforcement).\n\n**Credentials stay local** — in `dw.json` or `SFCC_*` env vars — and are never sent to Bridge. The `sfcc` profile registers read-only tools; the nine destructive write tools require the separate `sfcc-write` opt-in (see [Read and write profiles](#read-and-write-profiles)).\n\nFor a step-by-step OCAPI client setup guide (including the Business Manager permissions grant), see [docs/install/sfcc-integration.md](./docs/install/sfcc-integration.md).\n\n<details>\n<summary><strong>Setup</strong></summary>\n\nEvery SFCC tool must be enabled with a profile (step 3) — including the two diagnostics, `sfcc_setup_status` and `check_permissions`, which belong to the `sfcc` profile. A session on the default `core` profile registers no SFCC tool at all. Changing `BRIDGE_MCP_PROFILE` requires an MCP client restart.\n\n**Prerequisites:** a running SFCC **sandbox** and its hostname, plus an Account Manager API client (`client-id` + `client-secret`).\n\n**1. Set the repo `version` config field** to your SFCC project type — one of `sfra | pwakit | sitegenesis | storefrontnext | hybrid`. The call-time gate reads this; a non-SFCC value blocks the OCAPI tools — the eight reads, the nine writes, and `check_permissions`. It does **not** block `sfcc_setup_status`, and it does not block `sfcc_log_query`: log query runs on its own gate, which reads neither the `version` field nor `dw.json` and instead probes the backend log capability (log access is WebDAV Basic auth, a different boundary from OCAPI's OAuth). Set the field via your normal config path, the `config_field` MCP tool (operation `update`, field `version`), or the `/teach-bridge` skill.\n\n**2. Provide SFCC credentials.** Create a `dw.json` in your project root:\n\n```json\n{\n \"hostname\": \"zzzz-001.sandbox.us01.dx.commercecloud.salesforce.com\",\n \"client-id\": \"<account-manager-client-id>\",\n \"client-secret\": \"<account-manager-client-secret>\"\n}\n```\n\nAccepted key spellings: `hostname`/`host`, `client-id`/`clientId`/`client_id`, `client-secret`/`clientSecret`/`client_secret`. `dw.json` is auto-added to git exclude and must never be committed.\n\nCredentials resolve in **three tiers, highest first** — the environment wins over `dw.json`, not the other way round:\n\n1. An explicit dotted `instance` argument on the call, **plus** `SFCC_CLIENT_ID` and `SFCC_CLIENT_SECRET` in the environment. Secrets are never read from `dw.json` on this tier, so an explicit instance without those two env values is an error.\n2. `SFCC_HOSTNAME` **and** `SFCC_CLIENT_ID` **and** `SFCC_CLIENT_SECRET`, all three set.\n3. `dw.json`.\n\nBecause tier 2 outranks tier 3, a stale `SFCC_HOSTNAME` left in the environment silently wins over the `dw.json` you are looking at. Check both when a tool reports an unexpected host.\n\n**Use a single-config `dw.json`, or set all three `SFCC_*` variables.** A multi-entry `configs[]` array is **rejected outright** — it is not a working setup that merely requires an explicit `instance` on every call. Two things make that workaround unavailable: an explicit `instance` takes tier 1, which needs the client id and secret in the environment anyway, and most tools cannot accept a hostname at all — the value must contain a dot, and the site-preference tools constrain `instance` to `staging | development | sandbox | production`, none of which is a hostname.\n\n**3. Enable the tools you want.** Add the groups to `BRIDGE_MCP_PROFILE` in the MCP server `env` block (it is comma-separated), then **restart the MCP client**:\n\n```json\n\"env\": { \"BRIDGE_MCP_PROFILE\": \"sfcc\" }\n```\n\n`sfcc` gives the eight read tools plus `sfcc_log_query`. For the nine destructive write tools as well, use `\"sfcc,sfcc-write\"`; `full` expands to every group and is therefore write-capable. Without any of these, only the diagnostic tools are registered.\n\n**4. Verify and grant OCAPI access.** With `sfcc` selected in step 3 (both diagnostics live in that profile), ask your agent to run `sfcc_setup_status` (expect all checks ✓), then `check_permissions`. If it reports HTTP 401/403, it prints the exact OCAPI Settings JSON to paste in Business Manager (**Administration → Site Development → Open Commerce API Settings → Data API** tab); save it there and re-run. Restart the MCP client after any credential or env change — a running session does not pick them up.\n\nRead what it prints before pasting it. The output is **two labelled blocks**, and they are not equivalent:\n\n- **READ/SEARCH TOOL GRANTS** — what the `sfcc` read tools need: `get` on `/system_object_definitions`, and `get` + `post` on `/system_object_definitions/**`, `/site_preferences/**`, and `/custom_object_definitions/**`. The `post` is OCAPI's convention for its `*_search` endpoints, not a mutation — but it is a grant you are pasting, so it is labelled for what it is rather than as \"read-only\".\n- **MUTATION GRANTS** — required by the nine `sfcc-write` tools and by nothing else: `put`/`patch` on `/system_object_definitions/**` and `/custom_object_definitions/**`, and `patch` on `/site_preferences/**`. Paste this block only if you intend to enable `sfcc-write`.\n\nNeither block grants `delete`, and neither pastes the global `resource_id: \"/**\"` that would cover every Data API resource. Each entry names one resource family — `/system_object_definitions`, `/custom_object_definitions/**`, `/site_preferences/**` — so the wildcard is scoped to the family, not to the API. Within a family it is still broad, and `write_attributes` is `(**)`, so a throwaway sandbox is the right place for these.\n\n</details>\n\n### Sandbox enforcement\n\nEvery SFCC tool — all twenty, reads and writes alike, including the diagnostics —\npasses through one check before its own logic runs: **the hostname your\ncredentials actually resolve to must be a recognized developer sandbox.**\n\n- The check reads `credentials.hostname`, the value that goes into the OCAPI\n URL. It does not read the `instance` tool argument. Omitting `instance`, or\n passing `instance: \"sandbox\"`, has no effect on the decision — neither one\n selects or proves anything about the target. A dotted `instance` still\n *selects* a host through the documented credential precedence, but the host it\n selects is then validated like any other, so `check_permissions` cannot be\n aimed at a named production instance.\n- It **fails closed.** An unrecognized, malformed, or unparseable hostname is\n refused with HTTP `403`, `error.code: \"TARGET_NOT_SANDBOX\"`, and\n `error.details.failure_class: \"target-not-sandbox\"`, before any request leaves\n your machine.\n\nThe accepted hostname forms are:\n\n| Form | Example |\n|---|---|\n| `<realm>-<nnn>.sandbox.<region>.dx.commercecloud.salesforce.com` | `zzzz-001.sandbox.us01.dx.commercecloud.salesforce.com` |\n| `<realm>-<nnn>.sandbox.dx.commercecloud.salesforce.com` | `zzzz-001.sandbox.dx.commercecloud.salesforce.com` |\n| `<realm>-<nnn>.dx.commercecloud.salesforce.com` | `zyis-001.dx.commercecloud.salesforce.com` |\n\nAnything else is refused. In particular, hosts whose leading label names an\nenvironment (`production-…`, `staging-…`, `development-…`) are rejected even\nwhen they otherwise fit a form above, and the legacy `*.demandware.net` domain\nis not accepted at all — sandbox, staging, and production instances share that\ndomain with no suffix that separates them.\n\n### Read and write profiles\n\n`sfcc` and `sfcc-write` are **independent** groups. Neither implies the other.\n\n| `BRIDGE_MCP_PROFILE` | SFCC tools registered |\n|---|---|\n| unset / `core` | none |\n| `estimation` | none — this group adds only `estimate_epic`, which is not an SFCC tool |\n| `sfcc` | `sfcc_setup_status`, `check_permissions`, the 8 OCAPI read tools, and `sfcc_log_query` |\n| `sfcc-write` | the 9 destructive write tools **only** — it does not imply `sfcc`, so neither diagnostic and no read tool is registered |\n| `sfcc,sfcc-write` | all 20 |\n| `full` | all 20 — `full` includes `sfcc-write` and is therefore write-capable, and it also includes `estimation` |\n\n**`check_permissions` needs `sfcc`, even when you are diagnosing writes.** It is\nthe tool that prints the mutation-grant OCAPI Settings JSON on a 401/403, but it\nis registered by the `sfcc` group, not `sfcc-write`. An `sfcc-write`-only session\ntherefore cannot run it. Use `BRIDGE_MCP_PROFILE=sfcc,sfcc-write` whenever you\nneed to diagnose write grants.\n\n**Diagnostics moved into `sfcc`.** `sfcc_setup_status` and `check_permissions`\nused to register unconditionally, on every session including non-SFCC ones. They\nno longer do. The accepted consequence is that a session without `sfcc` can no\nlonger ask the server why SFCC tools are missing — run `npx -y @bridge_gpt/mcp-server doctor`\nfor that instead, which reports the resolved profile without needing any tool.\n\n**Migration.** Enabling `sfcc` used to register the nine write tools as well. It\nno longer does. If you were relying on SFCC writes through\n`BRIDGE_MCP_PROFILE=sfcc`, change it to `BRIDGE_MCP_PROFILE=sfcc,sfcc-write`.\nUsers of `BRIDGE_MCP_PROFILE=full` keep write access and need no change.\n\n### Tools\n\nTwenty tools in total, none of them registered unconditionally: the `sfcc` profile's **read-only** surface (the two diagnostics, eight OCAPI reads, and `sfcc_log_query`), plus the nine destructive writes that only the separate `sfcc-write` profile registers — see [Read and write profiles](#read-and-write-profiles). Every one of them is bounded to a developer sandbox by the same invocation-time check. All twenty are enumerated below.\n\nAn oversized response is saved in full to `BAPI_DOCS_DIR/sfcc/` and replaced by a parseable JSON descriptor — `truncated: true`, the `saved_path` it was written to, and the `page` metadata (`returned`, `total` when OCAPI supplied one, `has_more`) — so the collection metadata survives even though the data itself is on disk. If that save fails, the complete payload is returned inline instead, still as parseable JSON.\n\nAttribute-definition reads and writes can return an attribute's `default_value` at `projection: \"full\"`, and Bridge withholds it — every key is preserved except that one, whose value becomes `[REDACTED_BY_BRIDGE]` — from the inline response, the saved file, and a successful write echo alike. Attribute defaults are intentionally unavailable through this MCP surface; Business Manager is the supported path to read one.\n\n**Diagnostics** (registered by the `sfcc` profile)\n- `sfcc_setup_status` — report on every prerequisite: Bridge API key, repo name, `version` config, `dw.json` presence/uniqueness, AM (OCAPI) token acquisition, and the independent **SFCC Log Query (WebDAV)** capability that gates `sfcc_log_query`.\n- `check_permissions` — probe OCAPI access via `GET /system_object_definitions`; on 401/403, print the exact OCAPI Settings JSON to paste in Business Manager (read/search grants for the `sfcc` tools, mutation grants for the `sfcc-write` tools). An explicit `instance` hostname is still subject to the sandbox check below.\n\n**System object model — reads** (needs the `sfcc` profile)\n- `system_object_list` — list system object types (Product, Order, Customer, …).\n- `system_object_get` — fetch one system object type's definition.\n- `system_object_attribute_search` — search a type's attribute definitions; prefer this over a full dump when hunting a specific `c_` custom attribute.\n\n**System object model — writes** (needs the `sfcc-write` profile; every one is a destructive write, sandbox only)\n- `system_object_attribute_definition_create` — create an attribute definition via `PUT /system_object_definitions/{type}/attribute_definitions/{id}`.\n- `system_object_attribute_definition_update` — update one via `PATCH` on the same path.\n- `system_object_attribute_group_create` — create an attribute group via `PUT /system_object_definitions/{type}/attribute_groups/{id}`.\n- `system_object_attribute_group_update` — update one via `PATCH` on the same path.\n- `system_object_attribute_assign_to_group` — assign an existing attribute definition into a group via `PUT …/attribute_groups/{group}/attribute_definitions/{def}`.\n- `custom_preference_definition_create` — define a custom site or organization preference via `PUT /system_object_definitions/{SitePreferences|OrganizationPreferences}/attribute_definitions/{id}`.\n\n**Custom object definitions** (reads need `sfcc`; the two writes need `sfcc-write`)\n- `custom_object_definition_attributes_get` — fetch attribute definitions for a known custom object type (`default_value` withheld). OCAPI cannot enumerate custom object type *IDs* directly, so `object_type` must be known — but it is discoverable: call `system_object_list` at `projection: \"full\"` for each custom type's `display_name` and `attribute_definition_count`, derive a candidate id (e.g. strip spaces from `\"Product Quality Result\"` → `ProductQualityResult`), and confirm it by checking that this tool's returned attribute count matches that row's `attribute_definition_count`.\n- `custom_object_definition_attribute_search` — search attribute definitions within a known custom object type (`default_value` withheld). Read-only — creating a custom object *type* isn't possible via OCAPI; that's a future v2 metadata-import capability. Same discovery path as above applies to `object_type`.\n- `custom_object_definition_attribute_create` — **write** (sandbox only): create an attribute definition on a known custom object type via `PUT /custom_object_definitions/{type}/attribute_definitions/{id}`. TYPE creation is never attempted (the type must pre-exist). Echoes paste-ready OCAPI grant JSON on 403.\n- `custom_object_definition_attribute_update` — **write** (sandbox only): update an attribute definition via an ETag-conditional `PATCH …/attribute_definitions/{id}`; surfaces 409/412 conflicts and echoes grant JSON on 403.\n\n**Site preferences** (reads need `sfcc`; the write needs `sfcc-write`; sandbox only)\n- `site_preference_group_list` — list the preference groups on a site. This is the discovery tool the other two reads depend on: both take a group, and this is how you find one.\n- `site_preference_get` — list the preference **identifiers** in a group.\n- `site_preference_search` — search/filter preference identifiers within a group.\n- `site_preference_values_set` — **write** (sandbox only): set custom preference values via `PATCH /site_preferences/preference_groups/{group}/sandbox` with a flat map of `c_`-prefixed ids to string/number/boolean/string[] values. A bad group returns 404 `CustomPreferenceGroupNotFoundException`; echoes grant JSON on 403.\n\n> **Site preference values are write-only through this surface.** `site_preference_get` and `site_preference_search` return **ids only, never values** — an unset preference and one set to the empty string are indistinguishable. So you can *set* a value with `site_preference_values_set` and have no way to read it back through an MCP tool. Business Manager is the supported path to read a preference value.\n\n**On-demand log query** (needs the `sfcc` profile)\n- `sfcc_log_query` — query redacted, filtered SFCC logs on demand. `environment` and `time_range` (`start`/`end`) are **required** — production, \"all environments\", and an open-ended period are never inferred. The tool calls a Bridge backend endpoint that runs the pull → redaction → filter pipeline server-side and returns scoped, redacted findings; **WebDAV credentials, retrieval, redaction, and filtering all stay server-side and single-sourced.** It holds no credentials of its own.\n - **Guardrails.** Selection is bounded by log-file `prefixes` (max 5), the time range, a scanned-entry cap (`max_entries`, ≤ 2000), a finding cap, and a per-snippet length cap. High-volume prefix classes (`info`, `jobs`, `debug`, `customdebug`) impose a **stricter 6-hour** max range (vs. 24h for the error class) because `info-*` runs ~1 MB/day versus `error-*` at ~13 KB median — a wide window over a high-volume prefix is **rejected**, never silently narrowed.\n - **Response order.** Resolved scope (`environment`, `time_range`, `applied_prefixes`) and cap `status` first, redacted `findings` second, retrieval/truncation `metadata` last.\n - **Statuses & errors.** `ready`, `no_matching_findings`, `results_truncated`; plus `VALIDATION_ERROR` (bad/oversized scope, caught before any network call), `NOT_CONFIGURED` (503 — the log capability isn't set up; run `sfcc_setup_status`, whose step 6 reports it), and `BAD_GATEWAY`/`SERVICE_UNAVAILABLE` on a retrieval/backend failure.\n - **Auth is separate from OCAPI.** Log retrieval uses **HTTP Basic auth** — a Business Manager username + a **40-character WebDAV access key** — *not* the OCAPI Account Manager OAuth token the other SFCC tools use. A valid AM bearer token 401s on `/Logs`. `sfcc_setup_status` step 5 (AM/OCAPI token) and step 6 (WebDAV log access) are independent: one can be green while the other is not.\n - **Local / air-gapped fallback.** The primary path above is the only path this tool takes. For air-gapped development, the documented fallback is Salesforce's own **`@salesforce/b2c-cli`**, shelled out to directly: `b2c logs get --since <window> --search <q> --json`. There is no MCP alternative to reach for — `@salesforce/b2c-dx-mcp` ships **no `logs_*` tool** as of 1.1.2, and every log workflow in the vendor toolkit goes through the CLI anyway (see the [vendor manual](../docs/mcp/b2c-commerce-developer.md)). It is **not** the primary path because its credentials live client-side and its output has **not** passed Bridge's redaction/filter. If you use it, its output must be treated as raw: route it back through the same server-side Python `LogSource` composition and `RedactionPort`/T3 filter workflow — never paste or relay unredacted CLI output to an LLM.\n\n## CLI Subcommands\n\nBeyond `--init` / `--upgrade`, the package ships operational subcommands of the **single `bridge-api-mcp-server` bin** (not separate binaries) — so they travel with the package to every consumer. See [Usage Documentation → Start Tickets](#regularly-useful) for *when* to use `start-tickets`; this section is the full CLI reference.\n\n### `start-tickets`\n\nSpawns one Worktrunk worktree + selected-agent session per Jira ticket and backs the `/start-tickets` slash command. The agent defaults to **Claude Code** (`claude`) and is configurable via `--agent`. For existing ticket keys, `/review-and-start` (see [Usage Documentation → Review and Start](#regularly-useful)) is the recommended enriched front door over this CLI's `--workflow review-and-implement` seam; using the CLI directly (below) remains the advanced/lower-level path.\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]\n```\n\n| Flag | Default | Meaning |\n|---|---|---|\n| `--agent claude\\|cursor-agent` | `claude` | Agent command to launch in each worktree |\n| `--workflow implement\\|review-and-implement` | `implement` | Slash command each spawned worktree runs. `implement` is byte-identical to today's `/implement-ticket <KEY> [--auto]`. `review-and-implement` spawns `/review-and-implement <KEY> [--auto] [--rounds=<n>]`, which runs `/review-ticket` then, after a per-ticket halt gate, `/implement-ticket` in the same session. `--auto` applies to the selected workflow as a whole. |\n| `--rounds 1\\|2` | unset | Review round count forwarded to the review phase. Review-only — valid only with `--workflow review-and-implement`. |\n| `--tier cheap\\|basic\\|premium` | unset (difficulty routing) | Coarse model-routing override. Bypasses **only** the per-ticket difficulty/tier lookup (`GET /jira/tickets/{KEY}/model-tier`) and applies this one tier to every ticket; the tier is still mapped to a model through the centralized agent registry and any configured `difficulty_model_tier_overrides`, then validated. It is **not** a raw `--model` alias and never carries an API key or credential. A malformed value fails open to premium routing. Used by the `/review-and-implement` handoff to reuse the review-time tier. |\n| `--terminal terminal\\|iterm` | auto-detect via `$TERM_PROGRAM` | Override the macOS terminal app (honored on macOS only) |\n| `--dry-run` | off | Print intended actions; create no worktrees, open no tabs (any OS) |\n| `--branch KEY=BRANCH` | `feature/<KEY>` | Use a custom branch for that ticket (repeatable) |\n| `--base-branch <BRANCH>` | `main` | Cut new worktrees from `<BRANCH>` and refresh `origin/<BRANCH>` instead of `main` |\n| `--no-refresh-main` | off (the configured base branch is refreshed) | Skip refresh of the configured base branch (default `main`). Historical flag name preserved for backward compatibility — despite the name, it now skips refresh of whatever `--base-branch` resolves to. |\n| `--max-parallel N` | `3` | Max worktrees created concurrently |\n| `--conductor` | off | Opt into the Conductor system (per-worker `BAPI_CONDUCTOR_*` env + Claude hook injection, lifecycle ledger events, and a supervisor peer tab). **Default off** — a plain run spawns `cd <worktree> && <agent> '/implement-ticket <KEY>'`. |\n| `-h`, `--help` | — | Show usage |\n\nEach `KEY` must match `[A-Z]+-[0-9]+` (e.g., `BAPI-248`). The CLI creates/switches each worktree up front (throttled by `--max-parallel`), then opens one tab/session per successful worktree running the selected agent's `'/implement-ticket <KEY>'` — `claude '/implement-ticket <KEY>'` by default, or `cursor-agent '/implement-ticket <KEY>'` with `--agent cursor-agent`. The `/implement-ticket <KEY>` prompt is unchanged for both agents. To launch Cursor Agent instead of Claude Code:\n\n```\nnpx -y @bridge_gpt/mcp-server start-tickets --agent cursor-agent BAPI-248\n```\n\n**Difficulty-based model routing.** Before launching each agent, the CLI selects an implementation **model tier** from the ticket's `difficulty` (1-2 → cheap, 3-6 → basic, 7-10 → premium) and injects it as a `--model` flag at the spawn boundary. The Python backend returns only the coarse tier (`GET /jira/tickets/{KEY}/model-tier`, computing + caching difficulty on demand); this CLI alone maps a tier to the agent-specific alias (`claude`: `haiku`/`sonnet`/`opus`; `cursor-agent`: version-suffixed strings validated against `cursor-agent --list-models`). It is gated per repo by `difficulty_model_routing_enabled` (default **ON**) with an optional `difficulty_model_tier_overrides` JSON map (tier → alias). Routing is **fail-open**: missing credentials, an evaluation failure/timeout, a backend `fallback`, an invalid/unavailable alias, an unadvertised Cursor model, or an agent without `--model` support all default to the **premium** (Opus) model — and when even the premium alias cannot be resolved/validated for the agent, `--model` is omitted (the agent uses its default) — each surfaced as a per-ticket warning rather than failing the spawn. `--dry-run` does **not** create worktrees or open tabs, but it **does** resolve routing read-only to preview the `--model` each tab would use.\n\n**Coarse `--tier` override.** Passing `--tier cheap|basic|premium` bypasses **only** the per-ticket difficulty lookup above and applies that one tier to every ticket; the tier is still resolved to an alias through the same agent registry + `difficulty_model_tier_overrides` and validated the same way (including the live `cursor-agent --list-models` check). It is never treated as a raw `--model` alias, and no API key or credential belongs in the spawned command (the CLI resolves credentials itself via `resolveBapiCredentials`). A malformed/unrecognized `--tier` value is **fail-open**: the CLI logs one concise warning and routes every ticket on the premium (Opus) fallback rather than aborting. `/review-and-implement` uses this flag to hand its review-time tier snapshot to the fresh implementation session.\n\n**Conductor observability (opt-in via `--conductor`, BAPI-394).** Conductor is **off by default**. With `--conductor`, a run mints a conductor `run_id`, emits events into the local ledger (`~/.config/bridge/events.db`), opens a supervisor peer tab, and (for Claude Code) injects a secret-free lifecycle hook into each worktree; it also sets `BRIDGE_MCP_PROFILE=conductor` so workers get the conductor MCP tools. Observability is best-effort — a conductor failure never blocks a spawn, and `--dry-run` performs no conductor side effects. (Epic-tick dispatch always enables conductor internally.) See **[CONDUCTOR.md](./docs/CONDUCTOR.md)** for the full observability, git-hook, done-gate, and auto-merge reference.\n\n**Cross-platform spawning.** The CLI routes spawning per platform; `--dry-run` previews the platform-correct command form on any OS. An unsupported `process.platform` (not `darwin`/`win32`/`linux`) fails fast with a clear \"unsupported platform\" message.\n\n- **macOS** — opens a Terminal.app or iTerm tab via `osascript`.\n- **Windows** — creates worktrees with **`git-wt`** (Worktrunk's winget alias) and opens a tab via **Windows Terminal (`wt.exe new-tab`)**, falling back to **`Start-Process powershell.exe`** when Windows Terminal is absent. Requires **Git for Windows / Git Bash** (Worktrunk runs its `pre-start` / `post-start` hooks via Git Bash). The Worktrunk binary (`git-wt`) and the tab launcher (`wt.exe`) are resolved independently and never conflated.\n- **Linux** — creates one detached **tmux** session per ticket (pane kept open after the agent exits); attach with `tmux attach -t <session>`. A missing `tmux` produces a clear, actionable error.\n\nPer-OS prerequisites: macOS `wt`, `git`, `osascript`; Windows `git-wt`, Git for Windows / Git Bash, Windows Terminal or PowerShell; Linux `wt`, `git`, `tmux`. Set `BAPI_WORKTRUNK_BIN` to override the Worktrunk executable name/path for nonstandard installs (`doctor` honors it too). The read-only `doctor` subcommand (below) additionally surfaces a missing `uv` — Worktrunk's `pre-start` hook runs `uv`, but live preflight does not check it — and the selected agent's command; run `doctor --agent cursor-agent` to also check `cursor-agent` (it prints `cursor-agent login` as an informational auth reminder).\n\n### `doctor`\n\nThe package also ships a strictly **read-only** `doctor` subcommand — titled **`bridge doctor — read-only diagnostics`** — that diagnoses your whole Bridge install without changing anything:\n\n```\nnpx -y @bridge_gpt/mcp-server doctor [--agent <name>]\n```\n\nThe report always leads with the advisory **`Install status`** section (repo identity, credential resolution, server connectivity, bootstrap-field completeness, integration credentials, indexing state) **before** the `start-tickets` prerequisite diagnostics; the launcher-cache and MCP tool-surface sections follow. `Install status` is read-only GETs only and never affects the exit code.\n\nThe report also includes a **Claude login** advisory: whether the host's own\n`~/.claude.json` carries a login marker. This is informational only — it never\nblocks the doctor run and cannot guarantee the next worker spawn will\nauthenticate. See\n[Claude login for conductor workers](#claude-login-for-conductor-workers).\n\nIt is **read-only**: it never installs anything, modifies your system, adds an npm `postinstall`, spawns a terminal, or starts the MCP server, and there is no `--fix`. For each prerequisite it prints found/missing and, when missing, the exact per-OS install command **as a manual instruction you run yourself**. The checked set is the `start-tickets` preflight prerequisites **plus `uv`** **plus the selected agent's command** (`claude` by default, or `cursor-agent` with `--agent cursor-agent`). The Worktrunk binary is probed via the resolved name (honoring `BAPI_WORKTRUNK_BIN`), not a hard-coded one. **Exit code:** `0` when all required prerequisites are present, non-zero when any is missing or the platform is unsupported. A failing `start-tickets` preflight now hints you to run `doctor` for an actionable diagnostics report.\n\n**`claude-review` workflow lineage (advisory, read-only).** `doctor` also reports\nwhether the current branch's `.github/workflows/claude-review.yml` matches the\nrepository default branch's copy. This matters because\n`anthropics/claude-code-action` refuses to run when the two differ — so a\nlong-lived base branch that has fallen behind silently blinds automated review for\n**every pull request based on it**, even though those pull requests change no\nworkflow file at all. The probe runs `git symbolic-ref`, `git rev-parse`, and two\n`git show` reads against the local object database: it **fetches nothing, spawns\nnothing, and creates no worktree**, and an unresolvable ref simply reports the\ncomparison as unavailable. It is **advisory only** — it is registered as a\ndoctor-only diagnostic and **never changes `doctor`'s required-prerequisite exit\ncode**, whether it finds drift or cannot complete the comparison. Spawning a\nworker against a stale base prints the same advisory and **still spawns the\nworker**. Remedy and full diagnosis:\n`docs/claude/runbooks/claude-review-base-branch-drift.md`.\n\n### `drive-epic`\n\nThe one conductor entry point every Bridge surface names. Give it an epic key and\nit reads conductor readiness for your repository and routes to the single path\nyour project can actually run:\n\n```\nnpx -y @bridge_gpt/mcp-server drive-epic <EPIC>\n```\n\nYou are never asked to choose. Bridge currently has two conductors and a standing\nrule that they must never operate on the same epic — two transition authorities on\none epic wedge it permanently — so the choice is made structurally rather than by\njudgement. Readiness green routes to the v2 bootstrap below (pass `--plan-file`\nand `drive-epic` runs it for you); readiness not green prints the interactive\npilot instruction instead. If readiness is **unknown** — unreachable,\nunauthorized, or malformed — it escalates and prints no conductor invocation at\nall, because an unknown owner is not the same as a not-ready one. No branch,\nincluding every error path, ever offers you two paths.\n\n`drive-epic` owns no branch behavior of its own — it forwards `--feature-branch\n<name>` and `--into-base` verbatim to `setup-epic`, which decides the strategy (see\nbelow). With neither flag, a multi-ticket epic runs on `epic/<KEY>`.\n\nTwo conductors is a transitional state. When one is eliminated, `drive-epic` is\nthe only thing that changes.\n\n### `setup-epic`\n\nBootstraps an Epic Conductor v2 run in one command — creates the epic run, stores the plan DAG, and approves it:\n\n```\nnpx -y @bridge_gpt/mcp-server setup-epic --epic-key <KEY> --plan-file <path-to-epic-plan.dag.json>\n```\n\nThe plan sidecar is produced by the `decompose-epic` pipeline step. `setup-epic` validates it locally (unique ticket keys, resolvable dependency references, acyclicity) before sending anything, so a malformed plan fails legibly instead of as a bare HTTP 400. It is **idempotent**: re-running it on an epic that already has a live run reuses that run rather than minting a second one. `--dry-run` validates and previews the calls without mutating anything; `--json` emits a single machine-readable result object.\n\n**The command above uses an epic branch.** For a plan with **two or more** nodes and\nno branch flag, `setup-epic` derives `epic/<KEY>`: every child-ticket PR targets that\nbranch, and one draft integration PR targets the repository base branch. This holds on\nevery path, CI and `--json` included. A single-node plan keeps the repository base\nbranch, matching plain `start-tickets`.\n\n> **Behavior change (BAPI-1009).** The same no-flag command previously ran children\n> directly into the repository base branch. **Pass `--into-base` to restore that**, if\n> your automation depends on it. Pass `--feature-branch <name>` to override the derived\n> name, or to force a branch for a single-node plan. Passing both is a parse error,\n> refused before any file read, credential resolution, or network call.\n\nOnce the plan is approved, the **server-side reconciler** picks the run up within ~30s. To execute claimed jobs on your machine, run `executor`:\n\n```\nnpx -y @bridge_gpt/mcp-server executor --repo <name>\n```\n\n### `conduct-epic`\n\nThe deterministic half of the `/conduct-epic` loop: it owns the epic branch, a\nversioned local checkpoint, a per-epic lock, and the read-only probes the loop\ndecides on. It never creates or mutates an `epic_run` — that is the server-side\nv2 reconciler's job, and `init` refuses to start when one is already active.\n\n```\nnpx -y @bridge_gpt/mcp-server conduct-epic <verb> [flags]\n```\n\n**Verbs**\n\n| Verb | Flags |\n| --- | --- |\n| `init <EPIC>` | `--tickets K1,K2,…` (required), `--base-branch <b>`, `--checkpoint-path <p>`, `--dry-run`, `--json` |\n| `status <EPIC>` | `--json` (required), `--checkpoint-path <p>` |\n| `checkpoint set <EPIC>` | `--ticket <KEY>` (required), `--field <name> <value>` (repeatable), `--journal \"<line>\"`, `--checkpoint-path <p>` |\n| `finish <EPIC>` | `--checkpoint-path <p>`, `--json` |\n| `spawn <EPIC>` | `--ticket <KEY>` and `--prompt-file <path>` (required), `--agent claude\\|cursor-agent`, `--checkpoint-path <p>`, `--json` |\n\n**Local state.** Everything lives *outside* the repository, under\n`~/.config/bridge/conduct/<repo>/` (honoring `XDG_CONFIG_HOME`), so it resolves\nidentically from the main checkout and from any worktree and can never be\ncommitted by an agent running `git add`:\n\n| Path | Purpose |\n| --- | --- |\n| `<EPIC>.json` | the version-1 checkpoint (file `0600`, directory `0700`) |\n| `<EPIC>.json.prev` | the previous valid checkpoint, retained on every write |\n| `<EPIC>.lock` | the per-epic lock |\n| `<EPIC>/prompts/<KEY>-<kind>-<n>.md` | prompt files the caller writes for `spawn` |\n\n`status` prints the resolved `checkpoint_path`. To unpark a run a human edits the\ncheckpoint (`needs_human` → `null`, plus the ticket's `status`/counters);\n`last_seen_head`, `ci_last_poll`, and `lock` are observational and are never\nhand-edited.\n\n**`init` runs ONE preflight** that reports *every* failure in a single pass and\nwrites nothing unless all of them pass: `gh auth status`; Worktrunk resolves\n(honoring `BAPI_WORKTRUNK_BIN`); Bridge credentials resolve; `auto_merge_enabled`\nis on — or is turned on by PUTting the *complete* effective config back with just\nthat flag flipped, which prints a line beginning `announced:`; at least one\nrequired CI check exists (an empty required set would make the done gate pass\nvacuously); no active server-side `epic_run` for the key; the lock is free or its\nowner is provably dead; the base branch exists on `origin` after `git fetch`; and\nthe indexed-branch override is either absent or this epic's own — a re-`init`\nafter a crash is accepted and its `original_base_branch` becomes the default base,\nwhile a *foreign* override is refused by name. `resolve-ci-checks` is called\nexactly once either way, because that call is what warms the `poll-ci-checks`\ncache the first `status` depends on. Only then does `init` push\n`epic/<EPIC>` to `origin` at the fetched base tip (no local checkout), repoint the\nindex, write the checkpoint, and take the lock. `--dry-run` prints the validated\nplan and mutates nothing. A second `init` refuses with `already initialized`.\n\n**Failure posture is split on purpose.** In `status`, each probe fails *open*: a\n`gh`, CI, review, or parse failure leaves that sub-object `null`, adds an entry to\n`probe_errors`, and the command still exits `0` with a complete object — the loop\nmust be able to read its own checkpoint during a GitHub outage. Everything else\nfails *closed*: a corrupt or wrong-version checkpoint makes every verb but `init`\nexit non-zero **without rewriting it**, and `checkpoint set`, `spawn`, and\n`finish` refuse a lock held by another live process. `status` never takes the lock.\n\n**Exit codes.** `0` on success — including a missing checkpoint\n(`checkpoint_exists: false`) and an idempotent second `finish`. Non-zero on any\nother failure, with a one-line reason on stderr. With `--json`, stdout is exactly\none JSON object carrying `ok`.\n\n**Credentials** resolve only from `BAPI_API_KEY` or the user-scoped\n`bapi:<repo>` credential target, travel only in the `X-API-Key` header, and never\nappear in a command argument, in stdout/stderr, or in a journal line.\n\n### Conductor (epic & multi-agent orchestration)\n\nConductor is an **opt-in, off-by-default** layer for epic supervision, inter-agent messaging, done-gate evaluation, local git-hook event producers, and conditional auto-merge. Its full reference — the v2 architecture (server-side reconciler + local executor), `setup-epic`, `conductor install-git-hooks`, the supervisor `done_gate_config` / `auto_merge_enabled` settings, and the observability stream — lives in **[CONDUCTOR.md](./docs/CONDUCTOR.md)**.\n\n> The v1 `conductor epic-tick` command is **frozen** — it throws on every invocation. There is nothing to schedule locally, and `conductor doctor` flags any epic-tick schedule left over from an earlier release so you can cancel it.\n\n## Custom Pipelines\n\nYou can create your own pipelines by adding JSON files to `.bridge/pipelines/`. Running `--init` scaffolds this directory with a `README.md` and an example pipeline to get you started.\n\nThe easiest way to write a custom pipeline is to describe what you want to automate to your AI coding agent and have it draft the JSON for you. The schema is straightforward, and agents like Claude Code understand it well — just describe the steps you want, and the agent will produce a working pipeline file.\n\n**What you can build:**\n\n- Any sequence of Bridge MCP tool calls and free-form agent tasks\n- Parameterized workflows using variables (e.g., `{ticket_key}`)\n- Approval gates that pause for user confirmation before sensitive steps\n- Per-step error handling — halt immediately or warn and continue\n\n**Ideas for custom pipelines:**\n\n- A standup pipeline that fetches your open tickets and summarizes their status\n- A ticket triage pipeline that runs critiques on a batch of new tickets\n- A pre-merge checklist that runs tests, checks linting, and posts a summary comment\n\n**Step types:**\n\n| Type | What it does |\n|---|---|\n| `mcp_call` | Calls an MCP tool with the given params |\n| `agent_task` | Gives the AI a free-form instruction (inline or from a file in `.bridge/instructions/`) |\n\nVariables are declared in the `variables` array and referenced as `{variable_name}` in params and instructions. Each step supports `on_error: \"halt\"` (default) or `\"warn_and_continue\"`, and `requires_approval: true` to pause before execution.\n\n**System variables:**\n\nTwo variables are automatically available in every pipeline without declaring them:\n\n| Variable | What it does |\n|---|---|\n| `{provider}` | Routes AI generation to a specific LLM provider (`openai`, `anthropic`, or `gemini`). Pass it through to any `request_*` tool param to control which provider handles that step. Omit it (or leave it empty) to use the project default. |\n| `{second_opinion}` | Runs AI generation through a different provider than the default, acting as a cross-check. Set to `\"auto\"` to let Bridge pick the second provider automatically. When set, it takes precedence over `{provider}`. Use this when you want two independent AI perspectives on the same task — for example, running clarifying questions and a critique twice (once with each provider) produces better results than a single pass. |\n\nSee `.bridge/pipelines/README.md` for the full schema reference.\n\nIf a custom pipeline has the same key as a built-in pipeline, the custom version takes precedence (a warning is logged at startup).\n\n## Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `BAPI_BASE_URL` (server, installer) | No | `https://bridgegpt-api.com` | Bridge API base URL. The MCP server and the `install` CLI both fall back to the production default |\n| `BAPI_BASE_URL` (`executor` subcommand) | **Yes** | _(none)_ | The `executor` deliberately has **no** production fallback — it refuses to start rather than guess a target |\n| `BAPI_REPO_NAME` | Yes | _(none)_ | Jira project/repository identifier configured in Bridge API |\n| `BAPI_API_KEY` | No | _(none)_ | A Bridge credential **is** required; this environment variable is only the first place the server looks for it. When it is unset the server resolves the credential from the user-scoped store (`~/.config/bridge/credentials.json`, target `bapi:<repo>`), which is why generated MCP registrations are secret-free |\n| `BAPI_PROJECT_ROOT` | No | _(see fallback order)_ | Absolute path to project root. Anchors `BAPI_DOCS_DIR` and `BAPI_PIPELINES_DIR` resolution. Resolved once, in order: `BAPI_PROJECT_ROOT` → the connected client's MCP `roots/list` → `CLAUDE_PROJECT_DIR` → `process.cwd()`. Several paths *write* it into a generated registration (`--init`, host-config provisioning, the worktree `mcp-invoke` shim) — that is provenance, not a runtime default |\n| `SFCC_HOSTNAME` | No | _(none)_ | SFCC sandbox hostname. Part of the environment credential tier — `SFCC_HOSTNAME`, `SFCC_CLIENT_ID`, and `SFCC_CLIENT_SECRET` must **all three** be set for that tier to apply, and a complete tier takes precedence over `dw.json` |\n| `SFCC_CLIENT_ID` | No | _(none)_ | Account Manager API client id. See `SFCC_HOSTNAME` — all three are needed together. Also required on its own when a tool is called with an explicit dotted `instance` |\n| `SFCC_CLIENT_SECRET` | No | _(none)_ | Account Manager API client secret. See `SFCC_HOSTNAME` — all three are needed together. Never sent to Bridge; it goes only to the Account Manager token endpoint |\n| `CLAUDE_CODE_OAUTH_TOKEN` | No | _(none)_ | The supported headless authentication input for conductor workers. Export it into the **executor process's own** environment; Bridge forwards it unchanged into the worker and stores it nowhere — no credential-store entry, no disk, never sent to Bridge. See [Claude login for conductor workers](#claude-login-for-conductor-workers) |\n| `BAPI_INSTALL_DEBUG` | No | _(unset)_ | Set to any non-empty value to unlock raw diagnostics in `install` and the `apply_install_manifest` path — the underlying error message and stack behind an `unexpected error` summary. The installer's own failure text tells you to set it |\n| `BAPI_SIGNUP_EMAIL` | No | _(none)_ | Selects the self-serve signup route without `--email`. Precedence: `--email` first, then this variable, then the visible interactive prompt |\n| `BAPI_INVITE` | No | _(none)_ | Invite code for `install`, for scripting. Like `--invite <code>`, it exposes the code to your shell history and the process list — prefer bare `--invite` and the hidden prompt |\n| `BAPI_PLANE_PYTHON` | No | `python` | Executable used for the Python members of `plane up`. Point it at a venv interpreter when `python` on `PATH` is not the one you want |\n| `BAPI_PLANE_UVICORN` | No | `uvicorn` | Executable used for the server member of `plane up` |\n| `BAPI_DOCS_DIR` | No | `docs/tmp` | Local directory for saving plans, critiques, and research reports |\n| `BAPI_PIPELINES_DIR` | No | `.bridge/pipelines` | Directory for user-defined custom pipeline JSON files |\n| `BAPI_WORKTRUNK_BIN` | No | `wt` (`git-wt` on Windows) | Override the Worktrunk executable name/path used by `start-tickets` for nonstandard installs |\n| `BAPI_TMUX_SESSION` | No | `bridge-start-tickets` | Override the tmux session-name prefix used by `start-tickets` on Linux |\n| `BAPI_MCP_UPGRADE_ADVICE_ENABLED` | No | _(enabled)_ | MCP-local opt-out for proactively surfacing upgrade advice in pipeline recipe preambles. Set to `false`/`0`/`no`/`off`/`disabled` to suppress. Disabling it does **not** change the `/jira/ping` response or server-side upgrade computation — it only gates the recipe-preamble convention |\n| `BAPI_MCP_TOOL_SURFACE_GATING_ENABLED` | No | _(enabled)_ | MCP-local kill switch for **dynamic tool-surface capability gating** (see [Dynamic tool-surface gating](#dynamic-tool-surface-gating-capability-availability)). Default-on; set to `false`/`0`/`no`/`off`/`disabled` to skip the startup probe, the recurring poll, and the custom `tools/list` handler entirely, restoring the SDK's previous full profile-derived surface. Fail-open: any probe timeout, unreachable backend, non-2xx, malformed payload, incomplete evaluation, or unsupported schema advertises the full profile |\n| `BAPI_MCP_TOOL_SURFACE_POLL_ENABLED` | No | _(disabled)_ | Opt IN to the recurring tool-surface **poll**. Default-**off**: a session gates once via the startup probe and never re-probes. Set to `true`/`1`/`yes`/`on`/`enabled` to restore the jittered 12–18 s heartbeat that pushes `notifications/tools/list_changed` on mid-session capability changes. No effect when gating itself is disabled |\n| `BAPI_MCP_UPDATE_CHECK_ENABLED` | No | _(enabled)_ | MCP-local kill switch for the cached update check run at startup. Default-on; set to `false`/`0`/`no`/`off`/`disabled` to skip it entirely — no npm registry request, no update-cache read/write, no stderr advice, and no `tools/list` advisory decoration. Any other value is treated as enabled. Useful behind a firewall/registry-restricted network, and used by the payload-measurement harness so a stale local cache can never perturb a capture |\n| `CURSOR_API_KEY` | No | _(none)_ | API key used to authenticate `cursor-agent` when launching `start-tickets`/scheduled runs with `--agent cursor-agent`. Not needed for the default Claude Code agent |\n| `BRIDGE_MCP_PROFILE` | No | `core` | Startup-time tool registration profile — a **comma-separated** list of groups controlling which tool groups are registered when the server starts. `core` is always implicitly included. Valid groups: `core` (default — normal coding tools only), `conductor` (+ 8 conductor/event/supervisor tools), `pipeline-authoring` (+ 5 pipeline run/admin tools — `get_pipeline_recipe` is NOT gated; it stays in `core` because the recipe-driven slash commands depend on it), `estimation` (+ `estimate_epic`, which is **hidden by default** — `/estimate-epic` needs this group), `sfcc` (+ the 8 heavy SFCC read tools, `sfcc_log_query`, and the `sfcc_setup_status`/`check_permissions` diagnostics — read-only, see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools)), `sfcc-write` (+ the 9 destructive SFCC write tools — independent of `sfcc`, which does not enable them; see [Read and write profiles](#read-and-write-profiles)), and `full` (shortcut that expands to every group, **including `estimation` and `sfcc-write`**). Example: `sfcc,conductor`; use `sfcc,sfcc-write` for reads plus writes. Unknown, blank, or malformed tokens are dropped (falling back to `core`). Dynamic mid-session switching via `tools/list_changed` is unsupported — groups are resolved once at process startup, so **changing this variable requires restarting the MCP server**. That is separate from the learn/onboarding tools, which are gated per session and can appear mid-session without a restart (see [Learn/onboarding tool availability](#learnonboarding-tool-availability)). Conductor/epic sessions launched via `start-tickets --conductor` **merge** `conductor` into the parent process's already-resolved groups at the spawn boundary rather than replacing them — a project running on `sfcc` spawns workers on `core,sfcc,conductor`. A normal `start-tickets` run stays on `core`. |\n\nEnvironment values are **trimmed**, and only a non-empty result wins. A\nwhitespace-only `BAPI_API_KEY` therefore does not override anything: it falls\nthrough to credential-store resolution exactly as an unset variable would.\n\n## Learn/onboarding tool availability\n\nThree tools are **gated per session** rather than by profile:\n\n- `apply_install_manifest`\n- `invite_member`\n- `regenerate_directory_map`\n\nThey are registered in the `core` profile like any other core tool, but the\nserver starts them **disabled**, so a client that honors mid-session tool-list\nchanges does not see them in its first `tools/list`. They are revealed — all\nthree together, once per session — by either of two successful requests:\n\n1. a successful **`get_install_manifest`** call, or\n2. a **`get_pipeline_recipe`** request for the **`learn-repository`** pipeline.\n\nOn reveal the server emits `notifications/tools/list_changed`, so a client that\nhonors it picks the three tools up **without a restart**.\n\n`get_install_manifest` and `persist_routing_credential` stay ungated in `core` at\nall times, so the install and `start-tickets` flows always have their entry\npoints. Only the first of those two is a trigger — `persist_routing_credential`\nnever reveals anything; it is listed here because `/start-tickets` calls it and\nit therefore must never be gated.\n\n**Unrecognized clients get all three at startup.** The hide-then-reveal behavior\napplies only to clients verified to honor mid-session `tools/list_changed`. Any\nother client — including one that sends no name, a blank name, or a name the\nserver does not recognize — is enabled at session initialization instead and\ntherefore sees all three tools in its **first** `tools/list`. This is deliberate:\nthe failure mode of guessing wrong should be an extra tool, never a permanently\nmissing one.\n\n**A failed request changes nothing.** Only a genuinely successful manifest\nreveals the tools — the response must parse as JSON and carry a nonblank\n`snapshot_token`. An error response, a non-JSON body, or a manifest without a\ntoken leaves the three tools hidden, and the response you receive is unchanged by\nthe check either way. Likewise, only the exact `learn-repository` pipeline is a\ntrigger; other recipes and failed lookups reveal nothing.\n\n**Capability filtering still applies independently.** Revealing these tools does\nnot override the backend capability gate described below. The effective surface\nis the *intersection* of the two, so a tool can be enabled for the session and\nstill be absent from `tools/list` because this repository lacks the integration\nit needs. That is expected behavior, not a bug — check the integration first.\n\n## Dynamic tool-surface gating (capability availability)\n\nThe **effective advertised tool surface** is the intersection of three things:\n\n1. **Startup profile registration** — which tool groups `BRIDGE_MCP_PROFILE`\n registered at process start (see above).\n2. **Current SDK-enabled state** — a tool the server has disabled for another\n reason (e.g. `poll_ci_checks` when `ci_check_config` is unset) stays hidden.\n3. **Backend capability availability** — the set of tool IDs the backend would\n currently hard-block for this repo, reported by `GET /jira/mcp/tool-surface`.\n\nOn startup the server issues one bounded probe to that endpoint and installs a\ncustom `tools/list` handler that subtracts the backend-blocked IDs (intersected\nwith the locally advertised surface) from what it advertises. That single startup\nprobe is the default: the surface is gated once per session and the server does\nnot re-probe. Installed integrations change rarely and MCP clients re-list on\nreconnect, so a permanent per-session heartbeat — multiplied across every\nconcurrent worktree/agent session — was pure request noise against the backend.\nOpt in with `BAPI_MCP_TOOL_SURFACE_POLL_ENABLED=true` to restore the jittered\n12–18 s re-probe that emits `notifications/tools/list_changed` whenever the\neffective visible set actually changes, so a connected client converges to the\ncurrent surface mid-session without a reconnect.\n\n**Fail-open by design.** A probe timeout, unreachable backend, non-2xx response,\nmalformed payload, incomplete evaluation, or unsupported schema version all\nadvertise the **full** existing profile baseline — gating never removes a tool on\na doubtful signal.\n\n**Hidden ≠ disabled.** A capability-hidden tool remains **registered and\ncallable**, including through in-process pipelines. Hiding affects `tools/list`\nprojection only; it never calls `.disable()` or mutates the SDK `enabled` flag,\nbecause doing so would also block `tools/call` and the in-process dispatch path —\nthe backend remains the authoritative enforcement boundary, returning its own\nrefusal for a stale call rather than a local \"disabled\" error.\n\n**Kill switch.** Set `BAPI_MCP_TOOL_SURFACE_GATING_ENABLED` to an accepted false\ntoken (`false`/`0`/`no`/`off`/`disabled`) to skip the probe, the poll, and the\ncustom handler entirely, restoring the SDK's previous full profile-derived\nsurface.\n\n**Client convergence and the reconnect escape hatch.** Clients that honor\n`notifications/tools/list_changed` converge automatically. A client that does not\nhonor the notification must **reconnect or start a new MCP server session** to\nobserve the current surface; no project MCP configuration change is required.\n\n**Diagnosing the surface.** Run `doctor` (its advisory \"MCP tool surface\"\nsection reports the kill-switch state, reachability, decision reason, blocked\ncount, physical tool IDs, and catalog revision via a single read-only GET), or\nread the server's stderr gating decision lines (`tool-surface gating: reason=…\nhidden=… revision=… hidden_tools=[…]`).\n\n## Worktree credentials and the `mcp-invoke` shim\n\nWhen `start-tickets` creates a git worktree, it provisions a Bridge API MCP\nregistration into that worktree so Claude Code (`.mcp.json`) and Cursor\n(`.cursor/mcp.json`) can reach the server immediately. These registrations are\n**secret-free**: they contain no `env` block and no API key. Instead they point\nat an internal subcommand of the published single CLI bin, `mcp-invoke`:\n\n```bash\nnpx -y @bridge_gpt/mcp-server@<VERSION> mcp-invoke --target bapi --project-root <ABS_WORKTREE_PATH>\n```\n\n`mcp-invoke` is not a separate binary — it is a positional subcommand of\n`bridge-api-mcp-server`. It resolves the repo identity from the absolute\n`--project-root` (the committed `.bridge/config` manifest, falling back to the\ngit common dir), resolves credentials from the home-directory credential store,\nand then spawns the real MCP server with that environment.\n\n### Credential store\n\nCredentials live outside the repository, keyed by `bapi:<repo_name>`:\n\n```json\n{\n \"bapi:<repo_name>\": {\n \"BAPI_API_KEY\": \"...\"\n }\n}\n```\n\nResolution order:\n\n1. `BAPI_API_KEY` in the parent environment (overrides the file entirely).\n2. `$XDG_CONFIG_HOME/bridge/credentials.json`, else `~/.config/bridge/credentials.json`.\n3. `~/.bridge/credentials.json` (only when the primary path is absent).\n\nOn POSIX systems, lock the file down so only you can read it:\n\n```bash\nchmod 600 ~/.config/bridge/credentials.json\n```\n\n`mcp-invoke` warns (but continues) if the file is group/world-readable, and it\nnever creates or initializes the credential file for you.\n\n### Populating the credential store\n\nThe same store also backs the shell-spawned `start-tickets` CLI (its\ndifficulty→model routing runs in a Bash process that cannot see an `env` block in\n`.mcp.json` / `.cursor/mcp.json`), so the store must hold the key for routing to\nwork. Two supported paths write it for you:\n\n- **Install-time upsert.** `/install-bridge`'s final stage persists the validated\n routing credential into `~/.config/bridge/credentials.json` (target\n `bapi:<repo>`) via the `persist_routing_credential` MCP tool — the tool resolves\n the key inside the MCP process and writes the store, so no secret crosses the\n wire.\n- **One-shot migration.** If a key currently lives only in `.mcp.json` /\n `.cursor/mcp.json`, migrate it into the user-scoped store with the consent-gated\n command (a compatibility aid, not a live fallback):\n\n ```bash\n npx -y @bridge_gpt/mcp-server credentials migrate-agent-config [--write-credentials]\n ```\n\n Run it without `--write-credentials` to preview; add the flag to write the store.\n\n### Claude login for conductor workers\n\nConductor workers are **not** isolated into a private Claude configuration\ndirectory — they run with the executor host's own `HOME`, so a worker\nauthenticates the same way any interactive `claude` invocation on that host\ndoes. The prerequisite is simple: run\n\n```bash\nclaude login\n```\n\non the executor host, once, the normal way. Bridge never stores, resolves, mints,\nrotates, validates, or diagnoses this credential — it is entirely the operator's\nown Claude CLI state, exactly as if you were running `claude` at the terminal\nyourself.\n\n**Headless hosts.** If the executor host has no interactive login session\navailable (a service-launched executor, a CI-style runner), export\n`CLAUDE_CODE_OAUTH_TOKEN` into the **executor process's own environment**\nyourself before starting it:\n\n```bash\nexport CLAUDE_CODE_OAUTH_TOKEN=\"$(claude setup-token)\" # run once, wherever you can browser-login\n```\n\nBridge forwards that value **unchanged**, byte-for-byte, into the direct worker\nprocess environment — nothing else. It is never written to disk, never placed in\na generated launchd/systemd service unit, never placed in project configuration\n(`.mcp.json` / `.cursor/mcp.json`), and never sent to Bridge servers. There is no\ncredential store entry for it and no lifecycle tracking: expiry, rotation, and\nvalidity are entirely the operator's own responsibility, the same as any other\nvalue you choose to export into a process environment.\n\n**`ANTHROPIC_API_KEY` is never forwarded to a worker**, under any circumstance —\nthere is no fallback path for it.\n\n`mcp-server doctor` reports a single advisory **Claude login** line — whether\n`~/.claude.json` on the host it runs on carries a login marker. This is\ninformational only: it cannot confirm the next worker spawn will authenticate,\nand it never blocks the doctor run or changes its exit code.\n\n## Reference\n\nThe full surface, for when you need the complete enumeration. Day-to-day, use [Usage Documentation](#usage-documentation) instead — you don't call MCP tools directly; you ask your AI assistant to perform a task, or compose tools into a pipeline.\n\n### MCP tools\n\nThe authoritative tool catalog covers **72 tools** (enumerated below). What's actually registered in a session depends on `BRIDGE_MCP_PROFILE`: the default `core` profile loads a trimmed subset, and the conductor/pipeline-authoring/SFCC tools are added only under their respective profiles (see [Environment Variables](#environment-variables)). Async AI tools follow a request/get pattern: call the `request_*` tool to kick off generation, then the matching `get_*` tool to retrieve the result (or pass `wait_for_result: true` to poll automatically).\n\n- **Connectivity & identity** — `ping` (its JSON also carries `docs_dir`, `role`, and `customer_type`)\n- **Team & access** — `invite_member` (admin-only; mints a scoped access key for a teammate on an already-configured project — the plaintext key is shown exactly once)\n- **Jira tickets** — `get_tickets`, `get_ticket`, `create_ticket`, `update_ticket_description`, `add_comment`, `get_comments`\n- **Attachments** — `attachment` (operations: `upload`, `download`, `list`)\n- **AI generation (request/get)** — `request_plan_generation`/`get_plan`, `create_doc`/`get_doc` (design docs by `doc_type`: tdd/architecture/fsd/prd, where `architecture` is an alias of `tdd`), `request_ticket_review` (writes both `get_clarifying_questions` and `get_ticket_critique`), `request_reimplement_context`/`get_reimplement_context`, `request_council`/`get_council`, `request_deep_research`/`get_deep_research`\n- **Other AI** — `second_opinion`, `generate_image`, `generate_decision_page`, `visual_diff` (deterministic pixel diff of a rendered URL vs a design comp)\n- **Ticket lifecycle** — `track_ticket`, `update_ticket_state`, `get_ticket_state`, `get_ticket_state_tree` (live repo-wide lifecycle + dependency tree; read-only, no mutation parameter)\n- **Jira status** — `update_jira_status` (pass `status: \"auto\"` to resolve the configured post-PR status server-side)\n- **Repository & CI** — `parse_repository` (`action`: `start`, `status`), `regenerate_directory_map`, `create_pull_request`, `merge_pull_request`, `resolve_ci_checks`, `poll_ci_checks`\n- **Pipelines & automation** — `get_pipeline_recipe` (returns a fully resolved recipe the agent executes step-by-step)\n- **Config** — `get_project_standards`, `config_field` (operations: `get`, `update`, `list`)\n\n### Bundled pipelines\n\nPipelines are declarative, multi-step workflows your AI agent executes step-by-step — each a JSON recipe chaining MCP tool calls and free-form agent tasks, with variable substitution, per-step error handling, and optional approval gates. You can also write your own (see [Custom Pipelines](#custom-pipelines)).\n\n| Pipeline | Description | Invoke with |\n|---|---|---|\n| `implement-ticket` | Generate a plan, execute the implementation, commit, open a PR, and monitor CI | `/implement-ticket PROJ-123` |\n| `review-ticket` | Full ticket quality review: clarifying questions + critique plus an automatic second-opinion pass, then evaluation and decision capture. The backend owns round orchestration; pass `--rounds=1` for a single-pass review or `--rounds=2` to force the full second-opinion review, or omit `--rounds` to let the backend decide adaptively. | `/review-ticket PROJ-123` |\n| `idea-to-ticket` | Turn an idea into a Jira Task/Spike (or Epic + children) with research, dedup, and critique | `/idea-to-ticket \"<idea>\"` |\n| `plan-epic` | Decompose an epic into sub-tasks with a structured exploration doc for each | `/plan-epic \"<epic>\"` |\n| `full-automation` | Client-side wrapper: runs the `idea-to-ticket` recipe, then hands the created keys to `/review-and-start --auto` | `/full-automation \"<idea>\"` |\n| `learn-repository` | Analyze codebase architecture, testing, review, and documentation standards, then upload to Bridge | `/learn-repository` |\n\n### Pipeline response envelope\n\n`get_pipeline_recipe` returns a fully resolved recipe: an ordered `steps` array\nplus `total_steps`, `auto_approve`, and `execution_mode`. Each step is either an\n`mcp_call` (a tool name with resolved params) or an `agent_task` (instruction\ntext). `execution_mode` is `inline` — the calling agent executes every step\nitself and keeps its own durable record through `record_phase_result` /\n`record_checkpoint`. There is no server-side orchestrator and no run to resume.\n";
@@ -0,0 +1,60 @@
1
+ /**
2
+ * review-synthesis-config — the MCP side of the Phase-1 review-synthesis switch
3
+ * (BAPI-977).
4
+ *
5
+ * Reads the SAME environment variable as the Python backend
6
+ * (`src/python/llms/review_synthesis_config.py`), so Phase 1 is one switch to
7
+ * set and one switch to unset. Default-OFF: unset, blank, or any unrecognized
8
+ * value leaves the legacy instruction pair selected, which is the documented
9
+ * revert path.
10
+ *
11
+ * Selection is centralized here and in `selectPhase1Instructions` below rather
12
+ * than spread through recipe execution: `resolveRecipe` receives an already
13
+ * selected instruction map, so no conditional reaches step resolution and no
14
+ * state exists in which one of the two affected instructions is Phase 1 while
15
+ * the other is legacy.
16
+ */
17
+ import { parseDefaultOffEnvFlag } from "./env-flags.js";
18
+ /** The single Phase-1 switch, shared with the Python runtime. */
19
+ export const REVIEW_SYNTHESIS_PHASE1_ENV_VAR = "REVIEW_SYNTHESIS_PHASE1_ENABLED";
20
+ /**
21
+ * The bundled instruction keys this feature swaps, as an ordered pair of
22
+ * `[legacyKey, phase1Key]`. Both entries move together — that pairing is what
23
+ * makes atomic selection structural rather than a matter of care at the call
24
+ * site.
25
+ */
26
+ export const PHASE1_INSTRUCTION_PAIRS = [
27
+ ["evaluate-and-recommend.md", "phase1/evaluate-and-recommend.md"],
28
+ ["capture-review-decisions.md", "phase1/capture-review-decisions.md"],
29
+ ];
30
+ /**
31
+ * Whether Phase-1 review synthesis is enabled for this process.
32
+ *
33
+ * `env` defaults to `process.env` so production callers pass nothing; tests
34
+ * inject an explicit object rather than mutating the real environment.
35
+ */
36
+ export function reviewSynthesisPhase1Enabled(env = process.env) {
37
+ return parseDefaultOffEnvFlag(env[REVIEW_SYNTHESIS_PHASE1_ENV_VAR]);
38
+ }
39
+ /**
40
+ * Resolve the instruction map a recipe should be built from.
41
+ *
42
+ * Disabled (the default) returns the supplied map **by reference**, so every
43
+ * lookup yields the original generated constant rather than a normalized copy.
44
+ * Enabled returns a shallow copy in which both legacy keys point at their
45
+ * Phase-1 content; a Phase-1 variant missing from the bundle leaves that pair
46
+ * on its legacy entry, so a partial build degrades to legacy behavior instead
47
+ * of throwing inside recipe resolution.
48
+ */
49
+ export function selectPhase1Instructions(instructions, env = process.env) {
50
+ if (!reviewSynthesisPhase1Enabled(env))
51
+ return instructions;
52
+ const selected = { ...instructions };
53
+ for (const [legacyKey, phase1Key] of PHASE1_INSTRUCTION_PAIRS) {
54
+ const phase1Content = instructions[phase1Key];
55
+ if (typeof phase1Content === "string") {
56
+ selected[legacyKey] = phase1Content;
57
+ }
58
+ }
59
+ return selected;
60
+ }