# Codex Chief of Staff A read-only MCP server that monitors threads visible through one shared Codex App Server. It distinguishes active work, loaded idle threads, persisted history, stale cache, and unavailable state without steering or mutating any thread. ## Prerequisites - Node.js 24 and npm - A Codex App Server using the same `CODEX_HOME` as the Codex clients whose persisted threads should be visible - A local App Server listener, for example: ```sh codex app-server --listen ws://127.0.0.1:4500 ``` The App Server WebSocket transport is experimental. Plain `ws://` is accepted only for loopback; use authenticated `wss://` for a remote endpoint. ## Architecture and safety boundary The stdio MCP child connects to the configured App Server, completes `initialize`/`initialized`, reads every page from `thread/list` and `thread/loaded/list`, and reconciles that snapshot after reconnect. Between reconciliations it consumes `thread/status/changed` notifications. State and the bounded change ring exist only in process memory. Only these upstream request methods are compiled and runtime-allowlisted: - `thread/list` - `thread/loaded/list` - `thread/read` There is no generic upstream-call tool. The server does not resume, fork, steer, interrupt, execute for, archive, delete, subscribe to, or mutate metadata on a thread. It stores no snapshot, message, or event journal on disk and sends no proactive notifications. ## Develop from the source checkout ```sh npm ci npm run typecheck npm run compile npm test npm run verify:runtime npm start ``` `npm start` launches the stdio MCP server from `dist/src/index.js`. Diagnostics go to stderr; stdout is reserved for MCP. ## Install from an immutable Git revision For a visual end-to-end explanation, see the [Git-based npm installation guide](docs/git-based-npm-install.html). It documents why the repository commits the prebuilt runtime, how npm turns an immutable Git revision into a real global package, what the isolated verifier proves, and the complete release, upgrade, rollback, removal, and troubleshooting workflows. The guide is repository documentation for operators and maintainers; it is intentionally excluded from the installed npm payload. The canonical repository is `https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git`. Git access and any required trust or authentication configuration are host prerequisites; keep credentials out of package URLs and command output. Preflight the exact release tag before installation: ```sh git ls-remote --exit-code \ https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git \ refs/tags/v0.1.1 ``` Install only an immutable release tag or full 40-character commit. Do not use `#main` for a reproducible installation. The repository already contains the compiled runtime, so lifecycle scripts are disabled and no TypeScript toolchain is needed on the consumer machine: ```sh npm run verify:git-package -- \ 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1' npm install --global \ --ignore-scripts \ --cache /private/tmp/codex-chief-of-staff-npm-cache \ 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1' command -v codex-chief-of-staff npm list --global --depth=0 codex-chief-of-staff ``` For a commit pin, replace `v0.1.1` in both commands with the full commit reported by `git ls-remote`; abbreviated commits and branches are rejected by the verifier. The verifier uses its own temporary global prefix to avoid changing the installed package. The end-user install intentionally omits `--prefix` so the executable is placed on the normal global PATH. Installing `v0.1.1` with the command above upgrades the existing `0.1.0` local-tarball installation in place. The package name, `codex-chief-of-staff` executable, and MCP registration do not change. Restart Codex CLI after installation, then use `/mcp` or `codex mcp list --json` to confirm the existing registration discovers the replacement executable. To roll back, reinstall the verified `0.1.0` tarball or a previously verified immutable Git revision, then restart Codex CLI again: ```sh npm install --global \ --ignore-scripts \ --cache /private/tmp/codex-chief-of-staff-npm-cache \ /Users/dnabracz/Work/packages/codex-chief-of-staff-0.1.0.tgz npm install --global \ --ignore-scripts \ --cache /private/tmp/codex-chief-of-staff-npm-cache \ 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1' ``` Remove the globally installed package without changing Codex configuration with: ```sh npm uninstall --global codex-chief-of-staff ``` ## Build and verify a local npm package The package remains `private` and `UNLICENSED`, so it cannot be published accidentally. Maintainers use the explicit `pack:verified` workflow, which compiles, tests, checks committed-runtime freshness, and invokes `npm pack --ignore-scripts`. A raw `npm pack` does not run those release gates. ```sh mkdir -p /Users/dnabracz/Work/packages npm run pack:verified -- \ --pack-destination /Users/dnabracz/Work/packages \ --cache /private/tmp/codex-chief-of-staff-npm-cache npm run verify:package -- \ /Users/dnabracz/Work/packages/codex-chief-of-staff-0.1.1.tgz ``` The verifier installs only the selected tarball into a temporary prefix, launches its installed `codex-chief-of-staff` executable, checks the MCP initialization instructions and exact four-tool inventory, and removes the temporary installation. The explicit `/private/tmp` cache avoids the known ownership problem in the current user npm cache; it does not modify or repair the user cache. Install the verified artifact using the normal global npm prefix, which must already expose its binary directory on the PATH used to launch Codex: ```sh npm install --global \ --cache /private/tmp/codex-chief-of-staff-npm-cache \ /Users/dnabracz/Work/packages/codex-chief-of-staff-0.1.1.tgz command -v codex-chief-of-staff ``` Create a new version and filename whenever package contents change. Upgrade by globally installing the higher-version tarball with the same command. On a machine with a healthy npm cache, the explicit `--cache` option can be omitted. ## Configuration | Variable | Meaning | Default | | --- | --- | --- | | `CODEX_APP_SERVER_URL` | `ws://` loopback or remote `wss://` endpoint | `ws://127.0.0.1:4500` | | `CODEX_APP_SERVER_TOKEN_ENV` | Name of another environment variable holding the bearer token | unset | Token values are resolved indirectly, used only for the WebSocket authorization header, and never included in configuration errors or normal output. ## MCP tools - `overview({ limit? })` returns recent top-level `cli`, `vscode`, and `appServer` threads. It excludes `exec`, subagent, and guardian threads and never includes message bodies. - `list_threads({ statuses?, sources?, cwdPrefix?, recentSeconds?, includeSubagents?, limit?, cursor? })` filters and pages the normalized inventory. Working-directory prefix matching includes the exact root and its descendants. - `inspect_thread({ threadId, maxItems?, maxCharacters? })` explicitly reads one stored thread with `thread/read(includeTurns: true)` and returns bounded recent user/assistant context without loading or subscribing to the thread. - `wait_for_change({ afterRevision, timeoutMs?, maxChanges? })` long-polls a process-local revision and returns normally on change, cancellation, or timeout. Every result carries connection state, freshness, revision, last synchronization time, and coverage caveats. `active` means the shared App Server reports active processing; `idle` means loaded without an active turn; `notLoaded` is persisted history and is not proof of a running process. On disconnect, cached data remains available only as explicitly stale data. Live status does not cover a Codex Desktop runtime or independently launched App Server unless that runtime uses this configured shared server. The MCP initialization instructions designate this server as the primary source for current Codex thread state. A general Chief-of-Staff or running-thread request should call `overview` first, without browsing documentation, inspecting operating-system processes, or reading persisted session files as substitutes for live status. Use `list_threads` for explicit filters, `inspect_thread` for one selected thread, and `wait_for_change` for revision-based monitoring. ## Opt-in live smoke test With a shared App Server already listening and at least one visible thread: ```sh CODEX_CHIEF_OF_STAFF_SMOKE=1 npm run smoke ``` Optionally set `CODEX_CHIEF_OF_STAFF_SMOKE_THREAD_ID` to inspect a particular synchronized thread. The smoke workflow lists the complete inventories, classifies at least one known thread, explicitly inspects one thread, prints only bounded metadata, and can emit only the three allowlisted read methods. ## Codex MCP registration, upgrade, and rollback After global installation, register the PATH command in the personal Codex configuration: ```sh codex mcp add codex-chief-of-staff \ --env CODEX_APP_SERVER_URL=ws://127.0.0.1:4500 \ -- codex-chief-of-staff codex mcp list --json ``` For an authenticated remote endpoint, also pass `--env CODEX_APP_SERVER_TOKEN_ENV=NAME` and ensure `NAME` exists in the launch environment. Do not put the token value in the command. Rollback is independent of the App Server and thread histories: ```sh codex mcp remove codex-chief-of-staff npm uninstall --global codex-chief-of-staff ``` Before changing personal configuration, the same entry can be validated non-persistently: ```sh codex mcp list --json \ -c 'mcp_servers.codex-chief-of-staff.command="codex-chief-of-staff"' \ -c 'mcp_servers.codex-chief-of-staff.env.CODEX_APP_SERVER_URL="ws://127.0.0.1:4500"' ``` The resulting entry must show `"name": "codex-chief-of-staff"`, `"enabled": true`, and the stdio transport above. No personal Codex configuration belongs in this repository. When a private npm registry becomes available, retain the executable name and MCP behavior, choose a scoped package name, remove the local-only publication guard, and publish a newly versioned artifact. The registration can then use a pinned registry package without changing the server protocol.