Shared client configuration
praetorctl clients projects an explicit server registry into client settings.
The IDE-driven agent setup goal extends this shared service to complete wrapper installation, native activation and ongoing enforcement checks for every selected coding agent. The current setup entry point covers MCP configuration; those later stages remain explicit work.
Use praetorctl clients capabilities or the read-only MCP tool
standards_client_capabilities for the current versioned adapter inventory.
It reports projection mode, documented destination and lifecycle definition
coverage. runtime_verified: false and activation: unverified mean that the
inventory has not inspected or activated any native client session.
The same report names, per client, the upstream schema Praetor checks that client's
formats against (schema.state, schema.version, schema.sources). Claude Code, Codex,
Gemini CLI and the opencode-v1 adapter are pinned; the others report none with the
reason. Client schemas lists each pin, how a bump flows and the formats
that have no upstream schema.
VS Code is not in that inventory (internal/clientid/clientid.go). The Praetor
VS Code extension offers the workspace standards-mcp server to VS Code chat
through the MCP server definition provider API, so praetorctl clients writes
no VS Code entry and the server is listed once. Settings, trust and path rules:
editors/vscode/README.md, section "MCP server".
For gateway endpoints, credential-file references, logical provider selectors and repository memory binding, start with connection profiles. The same registry can be supplied from a workstation directory, container mount, plugin host or private GitOps checkout. No account, endpoint, organization or home directory is assumed by the registry or its adapters.
This implements configuration preparation, explicit file application, and an operator-selected AGY permission projection. It does not yet implement a shared runtime tool pool, automatic trust, dispatch budgets or an independently updating data service. Follow the management data design for those tracked requirements.
Configuration roots per operating system
One helper in internal/clientsetup resolves every client location that differs
between operating systems. It is pure: the command layer hands it the operating
system, the home directory, APPDATA, LOCALAPPDATA and XDG_CONFIG_HOME, and the
helper reads neither the process environment nor the filesystem. The Linux, macOS
and Windows tables are therefore tested on every CI leg, whatever the host.
| Location | Linux | macOS | Windows |
|---|---|---|---|
| Antigravity global customization root | $HOME/.gemini/config |
same | %USERPROFILE%\.gemini\config |
| Antigravity workspace root | <repo>/.agents |
same | same |
| Claude Code global root | $HOME/.claude |
same | %USERPROFILE%\.claude |
| Cline global root | $HOME/.cline |
same | %USERPROFILE%\.cline |
| Codex global root | $HOME/.codex |
same | %USERPROFILE%\.codex |
| Continue global root | $HOME/.continue |
same | %USERPROFILE%\.continue |
| Gemini CLI global root | $HOME/.gemini |
same | %USERPROFILE%\.gemini |
| Kilo global root | <xdg>/kilo |
same | same |
| OpenCode global root | <xdg>/opencode |
same | same |
| Antigravity CLI settings | $HOME/.gemini/antigravity-cli/settings.json |
same | %USERPROFILE%\.gemini\antigravity-cli\settings.json |
| Antigravity IDE user settings | <config>/Antigravity/User/settings.json |
$HOME/Library/Application Support/Antigravity/User/settings.json |
%APPDATA%\Antigravity\User\settings.json |
| Claude desktop configuration | <config>/Claude/claude_desktop_config.json |
$HOME/Library/Application Support/Claude/claude_desktop_config.json |
%APPDATA%\Claude\claude_desktop_config.json |
| PowerShell console history | does not apply | does not apply | %APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt |
| Default binary directory | $HOME/.local/bin |
$HOME/.local/bin |
%LOCALAPPDATA%\Programs\praetor |
<config> is $XDG_CONFIG_HOME, or $HOME/.config when that variable is unset or
relative; the XDG base directory specification declares a relative value invalid. An
unset APPDATA or LOCALAPPDATA derives from the home directory. <xdg> follows the
same rule on every operating system, macOS and Windows included, because Kilo and
OpenCode resolve it through the xdg-basedir package, which ignores the platform.
A global root resolves in this order: an explicit override, then the client's
relocation variables, then the default. An override or variable that is relative,
contains . or .. segments, or names a missing directory is an error, never a
fallback. Each row in globalRoots (internal/clientsetup/roots.go) cites the
documentation or source it was read from; TestResolveEveryKnownClientPerOS pins the
defaults above.
| Client | Relocation variable | Root it selects | Evidence |
|---|---|---|---|
| Antigravity | ANTIGRAVITY_CONFIG_DIR, then GEMINI_CONFIG_DIR |
the value, then <value>/config |
unverified |
| Claude Code | CLAUDE_CONFIG_DIR |
the value | verified |
| Cline | CLINE_DIR |
the value | verified |
| Codex | CODEX_HOME |
the value | verified |
| Continue | CONTINUE_GLOBAL_DIR |
the value | verified |
| Gemini CLI | GEMINI_CLI_HOME |
<value>/.gemini |
verified |
Neither Antigravity variable occurs in the agy 1.2.5 binary. A root chosen through
them is marked unverified and may be relied on only after a native readback agrees.
praetorctl harvest and praetorctl workstation status read only the verified
variables (clientsetup.VerifiedGetenv). OPENCODE_CONFIG_DIR and KILO_CONFIG_DIR
are not relocations: both add a directory after the global root, which stays loaded.
Antigravity keeps a brain directory per product under $HOME/.gemini:
antigravity/brain, antigravity-ide/brain and antigravity-cli/brain.
harvest bundle and harvest memory used to read one directory each, and not the
same one. Both now read every directory that exists, in that order, and report when
more than one exists. When two directories hold the same conversation, the first
wins and the bundle report records the skip. harvest memory --brain=<dir> still
reads exactly that directory.
harvest bundle --home=<dir> describes a foreign home, so the APPDATA,
LOCALAPPDATA and XDG_CONFIG_HOME of the running process are not applied to it.
Prepare a configuration
Create a private registry with version 1 and literal executable arguments:
{
"version": 1,
"servers": [
{
"name": "shared-tools",
"command": "/absolute/path/to/your-mcp-bridge",
"args": ["--profile", "workstation"]
}
]
}
Use the actual installed bridge command and its supported arguments. Credential loading remains the bridge's responsibility; registry entries do not carry environment values or perform shell expansion. Keep private registry files and generated settings out of public commits.
praetorctl clients prepare --registry /private/registry.json --client gemini \
--existing /private/client/settings.json --out /private/new-review
The parent of --out must already exist and the output directory must be new.
Omit --existing when preparing a new configuration. plan.json records source
and registry hashes; the separate artifact contains the candidate settings.
Existing unrelated settings and matching servers' access options are preserved.
A same-name server with a different executable, arguments or transport is an
explicit conflict. Strict JSON and YAML are supported; JSONC, ambiguous keys and
YAML aliases are rejected.
Supported adapter identifiers are codex, claude, claude-local, gemini, agy,
opencode-v1, continue, cline and kilo. Exact schemas and source versions
are in the adapter reference.
Apply to an explicit destination
praetorctl clients apply --registry /private/registry.json --client gemini \
--target /private/client/settings.json --out /private/new-backup
The plan, backup and replacement share one observed snapshot. Existing bytes are
retained as config.before before any configuration replacement. Files, backup
directory entries and the backup's parent directory are synced first. The writer
rejects symlinks, overlapping target/backup paths and changes to the observed
target; it reads back the published bytes before reporting success. Existing
restrictive file permissions are retained. An error after publication does not
prove the target stayed unchanged: inspect the target and retained artifacts
before retrying. Cooperating writers serialize, but external editors can still
race the final comparison and rename.
Codex requires its native command workflow and cannot use clients apply. Its
prepared plan contains argument arrays for inspection and native configuration.
Cline and Kilo require an explicitly selected profile destination;
the command does not guess a client profile path.
Claude Code local scope (claude-local)
Claude Code stores a private server for one project in the projects map of
~/.claude.json. Pass the absolute project path explicitly; --project is
required only for this adapter. The registry uses absolute host executable paths.
The merge preserves other projects and every other setting in the file.
praetorctl clients prepare --registry /private/registry.json --client claude-local \
--project /absolute/path/to/project --existing "$HOME/.claude.json" \
--out /private/claude-local-review
praetorctl clients apply --registry /private/registry.json --client claude-local \
--project /absolute/path/to/project --target "$HOME/.claude.json" \
--out /private/claude-local-backup
Apply compares the observed ~/.claude.json snapshot immediately before
replacement and refuses a concurrent change. Claude Code can rewrite this file
while sessions run, so retry from a fresh snapshot after reviewing the diff.
Antigravity (agy)
Antigravity reads one strict-JSON mcp_config.json with an mcpServers map. The
agy adapter merges into it like any other JSON adapter: existing servers, their
env blocks and unknown keys are kept, and the replaced file is retained as
config.before. Name the destination explicitly:
# Host-wide configuration (default root; Windows: %USERPROFILE%\.gemini\config)
praetorctl clients apply --registry /private/registry.json --client agy \
--target "$HOME/.gemini/config/mcp_config.json" --out /private/agy-backup
# One workspace
praetorctl clients apply --registry /private/registry.json --client agy \
--target .agents/mcp_config.json --out /private/agy-workspace-backup
An existing entry of the same name that is remote (serverUrl, url, httpUrl)
or that names another executable or other arguments is a conflict, and nothing is
written. The workspace file holds absolute host paths, so it is written per host
and never committed: /.agents/mcp_config.json is ignored here, and praetorctl
adopt adds the same rule to an adopting repository. On Windows the registry
still demands an absolute literal executable, so an npx server is declared as
C:\Windows\System32\cmd.exe with the arguments /c, npx and the package.
After applying, check discovery with agy mcp list; the merge itself proves
nothing about the running client.
Govern AGY headless permissions
Headless AGY cannot prompt for approval. Its scoped grants live under
permissions.allow in $HOME/.gemini/antigravity-cli/settings.json, separately
from MCP server discovery. Declare only the tools a review needs in a fleet or
workstation operator document:
clients:
selected:
agy:
permissions:
manage: true
allow:
- read_file(/absolute/path/to/repository)
- mcp(hindsight/hindsight_list_knowledge_pages)
- mcp(hindsight/hindsight_search_knowledge_pages)
- mcp(hindsight/hindsight_read_knowledge_page)
- command(git status)
- command(git diff)
- command(git log)
- command(git show)
- command(git rev-parse)
- command(rg)
Replace the example repository path with the exact checkout to inspect. The MCP
names must match the server and tool names registered on that host. This
profile deliberately omits file-write grants and mutating Git commands. Do not
assume that the active workspace is implicitly readable in headless mode: AGY
1.2.7 returned denied_actions: [{action: read_file}] for a native file read
inside the active workspace when no matching read_file(...) grant was loaded.
Declare every read scope that the headless task needs. An omitted write grant
likewise leaves a native write unable to prompt and therefore denied.
Review the exact destination and append-only delta, then apply and verify it:
praetorctl clients permissions plan --client agy \
--fleet-config /configuration/fleet.yaml --out /private/agy-permission-plan
praetorctl clients permissions apply --client agy \
--fleet-config /configuration/fleet.yaml --out /private/agy-permission-backup
praetorctl clients permissions verify --client agy \
--fleet-config /configuration/fleet.yaml
The default destination is the per-OS AGY CLI settings path in the table above.
--home resolves that path under an explicit home; --target selects one exact
file. Fleet and workstation settings follow the flag, environment, install-manifest,
then built-in selection order documented in effective policy.
plan.json contains the target, source hash, before/after counts and exact grants
to append. Candidate settings remain a separate private artifact because unrelated
keys may contain private data. Apply preserves unknown settings and existing
allow/ask/deny rules, retains the exact previous bytes, rejects symlinks and
concurrent changes, replaces atomically, then reads back the result. The target
and --out directory must be disjoint: equality or containment in either direction
whether spelled directly or through a symlinked ancestor fails before artifact
writes. An absent target with an empty declared grant list
writes metadata only and omits the candidate settings artifact. With
permissions.manage: false, every action reports unmanaged without reading or
creating the target or artifact directory.
AGY evaluates conflicts as Deny > Ask > Allow. Plan, apply and verify reject a
declared allow grant intersecting an existing deny/ask rule by exact or
action-wide match, literal command prefix, recursive file scope, URL
domain/subdomain (an existing rule is compared by host even when it is spelled
with a scheme, userinfo, port, IPv6 brackets or path), or MCP server wildcard without removing that operator-owned
rule. A deny read_file scope also conflicts with an intersecting write_file
allow. The adapter does not interpret regex equivalence: a same-action regex on
either side fails closed with a cannot prove non-overlap diagnostic. An existing
URL rule whose target does not reduce to a canonical host (empty host, wildcard
label, backslash, non-canonical IP spelling) fails closed the same way. Native
workspace-relative and absolute file rules also fail closed when compared with
each other because their intersection depends on the runtime workspace root. Native
permission readback remains required for AGY's runtime scopes and any future
matcher syntax.
verify exits successfully only when every declared grant is present and no
supported higher-precedence conflict is detected. Its report
sets configuration_verified: true but always keeps runtime_verified: false:
settings readback does not prove that a native AGY session exercised a tool. Run a
native agy --print=/permissions --output-format=json readback to inspect the
merged project, shared and global permission scopes that AGY actually loaded, then
run a
bounded agy --print --mode plan --sandbox review without
--dangerously-skip-permissions to establish that separate runtime evidence.
For a read-only review, launch AGY from a disposable workspace outside the
repository under review and grant that repository only with read_file(...).
Prove both directions: an explicitly granted read completes, while a write to a
fresh, explicitly named path with no matching grant is reported in
denied_actions and leaves no file behind. Keep that negative target outside the
reviewed repository so an unexpected success cannot alter source. --mode plan
controls agent behavior rather than authorization, and --sandbox confines
terminal commands rather than the native file-writing tool; neither flag is a
substitute for the explicit least-privilege grants above.
These are adapter-specific workflows, not a universal lifecycle integration. Configuration is only the first acceptance stage. Verify native discovery, connection, user trust and a named tool call in each client. A configured server does not establish that every tool is authorized, that all schemas are loaded lazily or that a running client has reloaded its settings.