Skip to content

Settings and configuration persistence

Settings and configuration persistence

This document explains how the extracted Copilot CLI bundle loads, merges, migrates, and writes settings/configuration. In the analyzed app.js, configuration is not one file or one object. It is a family of stores and runtime overlays covering user settings, location permissions, MCP/LSP config, plugin state, sandbox policy, URL permissions, trusted folders, feature flags, auth metadata, terminal setup prompts, and session runtime options.

Because app.js is bundled/minified, symbol names are unstable. Line references below are searchable anchors in the extracted bundle and will shift across releases.

Source anchors

AreaAnchor strings / minified symbolsApprox. app.js lineWhat it shows
Config root resolutionVs(...), configDir, COPILOT_HOME, .copilot234Runtime config root comes from explicit settings, env var, or home directory.
Store factoryload, write, writeKey, path, directoryFiles236Reusable JSON/config store helper with caching and key writes.
Store kindsDEFAULT, SETTINGS, MCP, LSP, LOCATION_PERMISSIONS236Multiple logical config stores exist.
User settings schemaallowedUrls, deniedUrls, disabledMcpServers, enabledMcpServers, sandbox, trustedFolders236, 239Settings schema covers permissions, URLs, MCP, sandbox, plugins, UI, and more.
Plugin persistenceinstalledPlugins, enabledPlugins, writeKey528Plugin manager writes installed/enabled plugin state.
URL permissionsallowedUrls, deniedUrls, addAllowedUrl, addDeniedUrl555URL allow/deny entries are persisted in settings.
MCP persistencedisabledMcpServers, enabledMcpServers, mcp-config.json4945, 4949, 7717MCP server enablement and config are stored separately.
Sandbox persistencesandbox, resolvedRuntimeSettings(), /sandbox enable1254, 1333Sandbox enablement is read/written via runtime settings.
Trusted folderstrustedFolders, isFolderTrusted, addTrustedFolder6021Trusted folders are normalized and persisted.
Terminal setupaskedSetupTerminals4512“Already asked” prompts are persisted to avoid repeated setup prompts.
Config migrationconfig.json, settings.json, Settings migration7441-7443Legacy config can be migrated/merged into settings.
Relocation filessession-state, session-store.db, installed-plugins, mcp-config, permissions-config7445State/config files are relocated into .copilot roots.
CLI options--config-dir, --additional-mcp-config, --allow-url, --allow-all-urls7774, 8221CLI options override or augment persisted config.

Configuration roots

The root path helper resolves the Copilot config/state home in this order:

  1. explicit runtime configDir if present;
  2. COPILOT_HOME environment variable;
  3. default ~/.copilot directory.

The same helper is reused across settings, MCP config, LSP config, permissions config, state, plugin cache, session state, and migration paths. The bundle also supports tilde expansion and relative-path normalization for user-supplied paths.

Store families

The config store factory defines logical store kinds equivalent to:

Store kindPurpose
config / defaultMain user/runtime configuration.
settingsUser settings-style keys, often migrated from legacy config.
mcpMCP server configuration.
lspLanguage server configuration.
permissionsLocation-scoped permission persistence.

Each store has helpers for:

HelperRole
load(scope, settings)Read and parse a JSON/config file.
write(object, scope, settings)Merge/write an object.
writeKey(key, value, scope, settings)Set/delete one key in a store.
path(scope)Compute the on-disk path.
directoryFiles(...)Enumerate files in a config directory.

The store helper caches loaded values by configDir and scope when caching is enabled. It also supports both .json and extensionless legacy file names.

writeKey semantics

writeKey is used heavily because many commands update one setting without replacing the entire file.

Its behavior is roughly:

  1. Resolve the store path for a scope and runtime settings.
  2. If the target value is undefined and the file does not exist, delete the cache entry and return.
  3. Load existing JSON or start with {}.
  4. Apply normalization/validation transforms if configured.
  5. Set object[key] = value, or delete the key if value is undefined.
  6. Validate the resulting object.
  7. Update cache when validation succeeds; clear cache if validation fails.
  8. Write the file with restricted mode.

This allows commands like /sandbox enable, MCP enable/disable, URL permission additions, and plugin installs to mutate settings incrementally.

User settings schema

The user settings schema is broad. Evidence anchors show support for:

Setting areaExample keys
UI/displaytheme, colorMode, renderMarkdown, footer, statusLine, screenReader.
Model/runtimemodel, effortLevel, continueOnAutoMode, stream, streamerMode.
URLsallowedUrls, deniedUrls.
PluginsinstalledPlugins, enabledPlugins, extraKnownMarketplaces.
MCPdisabledMcpServers, enabledMcpServers.
Sandboxsandbox.enabled, filesystem policy, raw policy, raw config.
Skills/agentsskillDirectories, disabledSkills, custom-agent settings.
HooksdisableAllHooks, hooks.
Trust/auth metadatatrustedFolders, lastLoggedInUser, loggedInUsers, copilotTokens.
UX suppressionaskedSetupTerminals, suppressInitFolders, first-launch flags.

The schema is passthrough() in several places, meaning future keys can survive parsing even when the current bundle does not explicitly understand them.

Runtime settings versus persisted settings

The session object stores runtimeSettings and exposes resolvedRuntimeSettings(). Slash commands use that to write to the correct config root. Examples:

Command/pathPersisted key
/sandbox enable / /sandbox disablesandbox.enabled.
/init suppresssuppressInitFolders.
reset approvalslocation-scoped permissions and allow-all state.
auto-mode continuationcontinueOnAutoMode.
terminal setup promptaskedSetupTerminals.

This distinction matters when the CLI is launched with a non-default configDir or with session-specific runtime settings.

Permission persistence

Permissions are split across several persistence paths:

Permission classPersistence behavior
Tool approvalsSession-scoped and location-scoped approval stores.
Path permissionsAllowed directories and all-paths mode are runtime/session state plus settings.
URL permissionsallowedUrls and deniedUrls in settings.
MCP server enablementdisabledMcpServers and enabledMcpServers.
Trusted folderstrustedFolders list in user settings.
Allow-all modeRuntime/session flag, reset by /reset-allowed-tools/approval reset paths.

The URL manager persists allow/deny entries through Is.writeKey("allowedUrls", ...) and Is.writeKey("deniedUrls", ...). Deny rules take precedence in the CLI help text and permission logic.

URL settings

URL settings are protocol-aware. The CLI help states:

  • domains without protocol default to HTTPS;
  • approving https://example.com does not approve http://example.com;
  • --allow-url and --deny-url accept URL/domain patterns;
  • --allow-all-urls enables unrestricted URL access;
  • --allow-all / --yolo are equivalent to all tools, all paths, and all URLs.

Persisted settings hold allowedUrls and deniedUrls, while runtime flags can set unrestricted mode for a session.

MCP settings

MCP config is loaded from multiple sources, including:

  • user ~/.copilot/mcp-config.json;
  • workspace .mcp.json;
  • installed plugins with MCP servers;
  • built-in/default GitHub MCP config;
  • additional runtime MCP config via --additional-mcp-config.

Server enable/disable state is stored separately with disabledMcpServers and enabledMcpServers. The /mcp disable path adds a server to disabledMcpServers and removes it from enabledMcpServers. The /mcp enable path does the inverse and may explicitly persist built-in enabled servers.

Plugin settings

Plugin install persists two related settings:

KeyPurpose
installedPluginsFull installed plugin records: name, marketplace, version, install timestamp, enabled flag, cache path, and source.
enabledPluginsEnablement map used during config merging.

The plugin cache itself lives under state (installed-plugins) while plugin/user config lives in config/settings stores. This avoids mixing package cache with editable user config.

Sandbox settings

The sandbox config schema includes:

  • sandbox.enabled;
  • filesystem readwritePaths, readonlyPaths, deniedPaths;
  • clearPolicyOnExit;
  • raw policy and raw config objects;
  • addCurrentWorkingDirectory.

The /sandbox slash command reads and writes sandbox.enabled through resolvedRuntimeSettings(). The shell spawn path later reads sandbox config/policy to construct platform sandbox behavior.

Trusted folders

Trusted-folder persistence is implemented by a small helper that:

  1. loads trustedFolders from user settings;
  2. canonicalizes/realpaths candidate paths;
  3. checks whether the candidate is inside an existing trusted folder;
  4. adds the normalized path only if it is not already covered;
  5. writes trustedFolders back with writeKey.

This prevents duplicates and makes trust checks robust against path normalization differences.

Migration and relocation

The bundle includes migration logic for legacy config.json / config files and settings-style keys.

The migration path:

  • reads config.json or config;
  • accepts either JSON object format or simple KEY=VALUE lines;
  • canonicalizes legacy key names;
  • compares values with existing settings.json;
  • warns if both files contain conflicting values;
  • writes migrated settings;
  • preserves unknown/remaining keys where appropriate.

A later relocation helper treats these as state files:

  • session-state;
  • session-store.db;
  • command-history-state;
  • command-history-state.json;
  • installed-plugins.

And these as config files:

  • config.json;
  • config;
  • mcp-config;
  • lsp-config;
  • permissions-config;
  • copilot-instructions.md;
  • mcp-oauth-config;
  • hooks.

Precedence model

The exact merge order is distributed, but the effective precedence is:

  1. built-in defaults;
  2. persisted user settings/config;
  3. workspace/repository config where applicable;
  4. plugin-provided config contributions;
  5. environment variables such as COPILOT_HOME and feature flag overrides;
  6. CLI flags such as --config-dir, --allow-url, --allow-all-urls, --plugin-dir, and --additional-mcp-config;
  7. session runtime changes made by slash commands or UI actions.

Some sources merge maps (enabledPlugins, marketplaces), some append arrays (additional MCP config, plugin dirs), and some override booleans directly (sandbox.enabled, continueOnAutoMode).

Relationship to other docs

  • integrations-permissions-config.md gives the broad integration view.
  • permission-system-design.md explains how persisted approvals are interpreted.
  • plugin-extension-architecture.md explains plugin install/cache state.
  • mcp-support-implementation.md explains MCP config merging.
  • sandboxing.md explains how persisted sandbox settings affect shell execution.

Created and maintained by Yingting Huang.