Repository navigation
Configuration
~/.agents/
config/
config.json # unified config: all providers read this
mcp-config.json # shared MCP servers
AGENTS.md # shared rules
skills/ # shared skills (one dir per skill, each with SKILL.md)
data/
memory/ # long-term memory, datasets
plugins/ # plugins
state/
logs/ # logs
history/ # chat history, active sessions
cache/
models/ # model caches
venv/ # isolated environments
.ai/
config.json # shared team settings (commit this)
config.local.json # personal overrides (gitignored)
mcp-config.json # project MCP servers
mcp-config.local.json # personal MCP overrides (gitignored)
skills/ # project skills
AGENTS.md # project rules
ai-config init-project adds the two *.local.json files to .gitignore automatically.
~/.agents/config/config.json:
{
"shared": {
"model": "default-model",
"theme_mode": "dark",
"permissions": {
"allow": ["Read(**)", "Exec(git)"],
"deny": ["Exec(sudo)"],
"ask": ["Write(**/.env*)"]
}
},
"providers": {
"devin": {
"model": "provider-specific-model",
"permissions": {
"allow": ["Read(**)", "Exec(git)", "Exec(npm)"]
}
}
},
"skills": {
"enabled": ["skill-name"],
"paths": ["~/.agents/skills/", ".ai/skills/"]
}
}Top-level keys:
| Key | Purpose |
|---|---|
shared |
Settings applied to every provider unless overridden |
providers |
Per-provider overrides keyed by provider name |
context_servers |
MCP server definitions (lives in mcp-config.json, merged in at read time) |
skills |
enabled list plus paths to search for skills |
MCP servers live in ~/.agents/config/mcp-config.json under the context_servers key:
{
"context_servers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
}
}
}The legacy mcpServers key is still accepted on read; writes always use context_servers.
A server can be kept in the store without being exported by flagging it "disabled": true - sync skips disabled servers entirely, even when they are named in a provider's include list. Useful for dormant entries you may re-enable later.
Project MCP servers go in .ai/mcp-config.json, personal additions in .ai/mcp-config.local.json.
Sync is opt-in per provider: "providers.<name>.sync": true enables export, otherwise ai-config sync (no flags) writes nothing to that provider. Once enabled, providers.<name>.mcp scopes which servers it gets:
{
"providers": {
"cursor": { "mcp": { "include": ["github", "filesystem"] } },
"claude": { "mcp": { "exclude": ["internal-tools"] } }
}
}-
include: allowlist, only these servers are written to that provider -
exclude: these servers are dropped from the full set - Neither key: provider gets everything
--prune honors the same scope: it removes only servers outside the provider's scoped set, and only from that provider's MCP file.
From highest to lowest:
-
.ai/config.local.json(project, personal) -
.ai/config.json(project, shared) -
providers.<name>in~/.agents/config/config.json -
sharedin~/.agents/config/config.json
A provider integration should deep-merge in that order. See Provider Integration.
Trust boundary: project .ai/ directories are only loaded when both the .ai directory and its parent are owned by the current user. Configs owned by other users are skipped with a SecurityWarning — this blocks config injection from shared directories. On platforms where ownership cannot be verified (Windows), the config loads with a warning instead.
Permissions are three rule lists:
-
allow: patterns permitted without asking -
deny: patterns always blocked -
ask: patterns that require confirmation
Patterns look like Read(**), Exec(git), Write(**/.env*).
Never store API keys in config files. Use system keyrings or environment variables. The *.local.json files are for personal non-secret overrides; they are gitignored by init-project but that is not a substitute for proper secret storage.
DevArtsLab/tool-universal-ai-config | MIT License | pip install universal-ai-config