Skip to content

Multi Project Mode

hypnguyen1209 edited this page Aug 26, 2026 · 2 revisions

Multi-Project Mode

By default the server is pinned to one project: --work-dir is the project root, and every tool resolves against it. Multi-project mode turns --work-dir into an access root — a directory beneath which each conversation selects its own project — so one running server can serve many repositories without a process per repo.

Enable it with --multi-project or "multiProject": true.

codex-free --work-dir /path/to/projects --multi-project

One static codex.config.json is still read once at startup; selection changes only the effective work directory the project tools use, never the server configuration itself.


How a conversation binds

Each conversation binds a project exactly once, through set_project_root. When the exact path isn't known, list_projects provides a search step first.

  • Path rules. The path is relative to the access root or absolute, but its canonical target must be an existing directory inside that root. Traversal (..) and symlink escapes are rejected after canonicalization, so a link pointing outside can't smuggle a selection past the check.
  • GitHub URLs. path may instead be a GitHub URL — an HTTPS or SSH repository-root URL, an HTTPS branch URL ending /tree/<branch>, or an HTTPS pull-request URL ending /pull/<number>. Codex Free reuses an unambiguous matching local checkout, or runs a non-interactive git clone into the configured clone directory (projectCloneDir / --project-clone-dir, defaulting to the access root) before binding. Branch and PR URLs fetch the exact requested ref; if an existing source checkout sits on another commit, Codex Free leaves it untouched and binds a detached managed worktree at the requested commit — so worktree mode never requires the source checkout to already be at that target.
  • Binding scope. The binding belongs to the ChatGPT conversation, keyed from _meta["openai/session"] (hashed, never stored raw). Simultaneous chats can hold different projects, and a later turn recovers its own root after MCP reconnects or a server restart. A client with no ChatGPT conversation metadata falls back to a binding that lasts only the current MCP transport session.
  • No switching. A conversation cannot switch roots once bound — start another chat for another project. Re-selecting the same canonical path is idempotent.
  • Before selection. Project-scoped tools are unavailable and say why. list_projects and set_project_root are the two project-independent tools present for this workflow.

The project catalogue

list_projects searches a read-only catalogue assembled from two sources:

  1. Native Codex trust table — Codex records trust decisions in its user config:

    [projects."/absolute/path/to/project"]
    trust_level = "trusted"

    Codex Free reads those paths as candidates. It doesn't treat the table as exhaustive — entries may be stale or lack descriptions.

  2. Explicit projectCatalog.entries — operator-authored providers that can add aliases/descriptions or supply projects absent from the native table:

    {
      "multiProject": true,
      "projectCatalog": {
        "codexConfig": { "enabled": true, "trustedOnly": true },
        "entries": [
          {
            "path": "codex-free",
            "name": "Codex Free",
            "aliases": ["ChatGPT MCP bridge"],
            "description": "Rust MCP bridge exposing local programming tools to ChatGPT"
          }
        ]
      }
    }

Every candidate still passes Codex Free's own checks: it must exist, resolve to a directory, and canonicalize to the access root itself or a descendant. Missing entries, files, and symlink escapes are skipped; duplicate canonical targets are merged. Native Codex trust is only catalogue metadata plus the default trustedOnly filter — it never grants access outside --work-dir, and neither does an explicit entry.

list_projects returns a selector relative to the access root, passable unchanged as set_project_root.path. Its optional query matches names, aliases, descriptions, and selectors case-insensitively with exact/prefix/substring ranking. It never binds automatically — if several results are plausible, the agent's instructions require it to ask you rather than guess, because a wrong binding can't be changed in that conversation.

The native table is read live on every list_projects call, so newly trusted projects become discoverable without restarting. Project-local .codex/config.toml layers are not scanned — they matter only after selection. Catalogue construction never opens a candidate's README, source, .codex/, or AGENTS.md; contents stay unread until the conversation selects that project.

Per-conversation isolation

  • Worktrees. With the default worktree mode, the first conversation selecting a Git project uses the source checkout directly; a second conversation gets a detached managed worktree so concurrent chats never edit the same tree. See Worktree Isolation.
  • Saved state. With an explicit memory.dir, each selected project gets its own hashed child directory. Conversation bindings stay enabled even when memory.enabled is false. See Context and Memory.
  • Review checkpoints. Namespaced per conversation/project pair. See Review Checkpoints.

Managing bindings

To clear a stray binding, delete its file under ~/.codex-free/conversation-projects/. There is no tool to re-point an already-bound conversation. A managed worktree stays referenced while its binding exists; startup cleanup skips referenced or dirty worktrees and only removes older clean, unreferenced entries beyond keepCount.

Inspecting the catalogue without starting a server

codex-free projects list --work-dir /path/to/projects
codex-free projects list --work-dir /path/to/projects --query "codex free"
codex-free projects list --work-dir /path/to/projects --json
codex-free projects list --work-dir /path/to/projects --show-skipped

--show-skipped (local-only) also prints paths rejected as missing, untrusted, or outside the access root, plus merged duplicates. See CLI Reference.


Security note

Multi-project selection isolates Codex Free's structured tools and per-conversation state — it is not an OS sandbox. The entire access-root subtree is intentionally selectable by a new conversation, so treat the whole subtree as sensitive. See Security Model.


See also

Clone this wiki locally