Skip to content

Workspace Config Overlays

How workspace config.toml and mcp.json layer over global CompozyOS configuration without making project copies brittle.

For people running agent work5 pages in this section

Workspace config lets one project change runtime behavior without changing every CompozyOS session on the machine. The loader starts from built-in defaults, applies global config, then applies the resolved workspace root overlay.

This keeps project behavior portable. Copy a repository with .compozy/config.toml, .compozy/agents/, and .compozy/skills/, register the copied path, and the project-level runtime context comes with it.

Load Order

The effective config for a resolved workspace is built in this order:

OrderSourceNotes
1Built-in defaultsDefaultWithHome(homePaths).
2~/.compozy/config.tomlGlobal operator settings. $COMPOZY_HOME/config.toml when COMPOZY_HOME is set.
3~/.compozy/mcp.jsonGlobal top-level MCP sidecar.
4<workspace>/.compozy/config.tomlWorkspace root overlay only.
5<workspace>/.compozy/mcp.jsonWorkspace top-level MCP sidecar.
6Path normalization and validationExpands ~ in daemon.socket, then validates the final config.

Workspace additional roots do not contribute config. They can contribute agents and skills, but only the primary root contributes config.toml and top-level mcp.json.

Create A Workspace Overlay

Create the config file under the project root:

mkdir -p /Users/you/src/checkout-api/.compozy
# /Users/you/src/checkout-api/.compozy/config.toml
[defaults]
agent = "reviewer"
provider = "claude"

[permissions]
mode = "approve-reads"

[limits]
max_concurrent_agents = 12

[skills]
disabled_skills = ["public-cloud-deploy"]

[[mcp_servers]]
name = "repo-fs"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/src/checkout-api"]

Then register the workspace:

compozy workspace add /Users/you/src/checkout-api --name checkout-api

Sessions that target checkout-api now inherit the overlay:

compozy session new --workspace checkout-api

The command uses reviewer as the default agent unless you pass --agent.

Merge Rules

TOML overlays are field-aware. A field changes only when the overlay provides that field.

ShapeMerge behavior
Scalar valuesReplace the previous value when present.
Nested tablesMerge recursively. Omitted nested fields keep previous values.
Slices such as skills.disabled_skillsReplace the previous slice when present.
providers.<name>Merge by provider key. Existing provider fields are kept unless replaced.
hooks.declarationsMerge by hook name; same-name declarations replace earlier ones.
[[mcp_servers]]Merge by MCP server name; same-name servers are patched field by field.
Unknown TOML keysFail config loading with an unknown-key error.
Missing config filesTreated as absent.

This example keeps the global skill scan interval while changing only skill enablement:

# ~/.compozy/config.toml
[skills]
poll_interval = "5m"
# <workspace>/.compozy/config.toml
[skills]
enabled = false

The final config has skills.enabled = false, while the inherited skills.poll_interval = "5m" remains in effect.

MCP Sidecars

Top-level mcp.json files are optional. They may use either mcpServers or mcp_servers.

{
  "mcpServers": {
    "repo-db": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgres://localhost/checkout"
      }
    }
  }
}

When both key styles appear in the same mcp.json, mcp_servers replaces same-name entries from mcpServers. When a TOML MCP server and an mcp.json server have the same name, the JSON sidecar replaces the whole server object at that scope.

Across the session start path, MCP servers are then resolved in this order:

  1. top-level config MCP servers
  2. provider MCP servers
  3. agent MCP servers
  4. active skill MCP servers

Workspace Agents And Skills

Config overlays do not define agents or skills directly. They change the runtime defaults around the agents and skills discovered by the resolver.

GoalPut this in configPut this in the workspace filesystem
Change the default agent for one project[defaults].agent = "reviewer" or compozy workspace edit --default-agent reviewer.compozy/agents/reviewer/AGENT.md
Disable a noisy skill in one project[skills].disabled_skills = ["skill-name"]Keep the skill where it is.
Add project MCP servers[[mcp_servers]] or .compozy/mcp.jsonAgent/skill-local mcp.json when the server belongs to one agent or skill.
Change provider command/model for one project[providers.<name>]No filesystem resource needed.
Add a project-only skillNo config entry required..compozy/skills/<group>/.../<name>/SKILL.md; groups are optional

Workspace registration has one extra override: default_agent. When it is set on the workspace row, the resolver applies it after loading config by setting defaults.agent for the resolved snapshot. The Web Start session dialog preselects this registered default for the active workspace, or for the owning workspace when launched from a worktree. An agent-specific Start session action keeps its explicitly chosen agent.

compozy workspace edit checkout-api --default-agent reviewer

Set it to an empty value to clear it:

compozy workspace edit checkout-api --default-agent ""

Concrete Overlay Walkthrough

Global config:

# ~/.compozy/config.toml
[defaults]
agent = "general"
provider = "codex"

[permissions]
mode = "approve-all"

[limits]
max_concurrent_agents = 20

[skills]
disabled_skills = ["legacy-notes"]

Workspace config:

# <workspace>/.compozy/config.toml
[defaults]
agent = "reviewer"

[permissions]
mode = "approve-reads"

[skills]
disabled_skills = ["deploy-production", "legacy-notes"]

Effective result for the workspace:

SettingFinal valueWhy
defaults.agentreviewerWorkspace scalar replaces global scalar.
defaults.providercodexWorkspace omitted it, so global remains.
permissions.modeapprove-readsWorkspace scalar replaces global scalar.
limits.max_concurrent_agents20Workspace omitted the table, so global remains.
skills.disabled_skills["deploy-production", "legacy-notes"]Workspace slice replaces the global slice.

defaults.model and defaults.reasoning_effort follow defaults.provider: a layer that sets a different defaults.provider drops the model and reasoning inherited from lower layers, so a model chosen for one provider never reaches another. A layer that repeats the same provider, or sets only defaults.model, keeps refining the inherited choice.

.env And COMPOZY_HOME

If a workspace root contains .env, CompozyOS loads it before resolving the CompozyOS home layout for that config load. That means a workspace can set COMPOZY_HOME for sessions that resolve that workspace:

COMPOZY_HOME=/Users/you/.compozy-checkout

Use this carefully. It changes the global config, agents, skills, sessions, logs, socket, and database locations for that resolved workspace config load. Most projects should prefer <workspace>/.compozy/config.toml for project-specific behavior and keep COMPOZY_HOME stable.

Failure Behavior

Unknown keys in a workspace .compozy/config.toml reject registration and resolution with HTTP/UDS 422 and code workspace_config_invalid. The error includes config_path, a sorted, deduplicated unknown_keys array, and an actionable error message. The CLI preserves these fields with -o json and exits with code 78; Add project displays the message without clearing the form. The workspace list remains available because it reads registrations without resolving configuration.

Legacy CLI configuration is unsupported. If the error names legacy keys such as defaults.ide or tasks.run.task_runtime_rules, remove those keys, or rename or remove the legacy file before trying again. CompozyOS does not translate these settings or modify the rejected file. Other configuration decode errors and server failures retain their existing behavior.

CaseBehavior
Missing config.tomlIgnored. Defaults and other layers still load.
Missing mcp.jsonIgnored.
Unknown TOML keyLoading fails and reports the key path.
Retired memory or compaction settingArchived as a comment at the end of the file; loading continues. If the file changes during the rewrite, or cannot be replaced, nothing is written, the load logs a config.retired_keys_archive_failed warning, the retired values stay inactive, and the next load retries. See Archived retired settings.
Invalid TOML duration or typeLoading fails during decode or validation.
Invalid MCP JSONLoading fails with path context.
Invalid final configLoading fails before the workspace snapshot is returned.

On this page