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.
CODEX_HOME as the Codex clients whose persisted threads should be visibleA local App Server listener, for example:
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.
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/listthread/loaded/listthread/readThere 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.
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.
For a visual end-to-end explanation, see the Git-based npm installation guide. 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:
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:
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:
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:
npm uninstall --global codex-chief-of-staff
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.
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:
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.
| 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.
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.
With a shared App Server already listening and at least one visible thread:
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.
After global installation, register the PATH command in the personal Codex configuration:
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:
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:
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.