From aec5663ff4a531f5b41b9fc4243b1b1b69a07005 Mon Sep 17 00:00:00 2001 From: Dan Moseley <6385855+danmoseley@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:43:15 -0600 Subject: [PATCH 1/5] Improve Copilot CLI permissions config docs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../use-copilot-cli/allowing-tools.md | 11 +- .../cli-config-dir-reference.md | 180 +++++++++++++++++- 2 files changed, 188 insertions(+), 3 deletions(-) diff --git a/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md b/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md index 8a60ae7ad9df..3ff53df0106b 100644 --- a/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md +++ b/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md @@ -22,6 +22,10 @@ While read-only operations like searching, reading files, and running read-only You can allow or deny permissions for tools either when you start the CLI or during your interactive session. If you haven't granted permission prior to starting a session, {% data variables.copilot.copilot_cli_short %} will prompt you for permission each time it needs to perform a potentially destructive action. You can choose to allow the tool this one time, or for the remainder of the session. +If you choose the option to allow similar requests for the current location, the approval is saved in `permissions-config.json` in your {% data variables.copilot.copilot_cli_short %} configuration directory. The saved approval is then applied the next time you start the CLI in the same repository or directory. Command-line options such as `--allow-tool` and `--deny-tool` apply only to the current session and aren't written to `permissions-config.json`; deny rules still take precedence over saved approvals. + +For details about where saved approvals are stored, how locations are matched, and the full `permissions-config.json` schema, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#permissions-configjson). + ## Layers of tool controls There are two layers of control you can use when specifying tool permissions in command-line options. You can: @@ -59,7 +63,7 @@ The value for each of these options is a comma-separated list of tool kinds, whi If you specify a tool with `--allow-tool`, the AI model can choose to use that tool without prompting you for permission. If you specify a tool with `--deny-tool`, the AI model cannot use that tool at all, even if it would be the best choice for completing a task. -Deny rules always take precedence over allow rules, even when `--allow-all` is set. +Deny rules always take precedence over allow rules, even when `--allow-all` is set or a matching approval has been saved in `permissions-config.json`. ### Examples @@ -94,9 +98,12 @@ The following command-line options give {% data variables.copilot.copilot_cli_sh The `/reset-allowed-tools` slash command revokes all permissions you granted during the current interactive session. This applies equally to permissions you gave by responding to prompts, and to the use of the `/allow-all` or `/yolo` slash commands. -Using `/reset-allowed-tools` resets the permissions to the default, or to the state defined by any command-line options you used when you started {% data variables.copilot.copilot_cli_short %}. For example, if you start a {% data variables.copilot.copilot_cli_short %} interactive session with the option `--allow-tool='shell(git:*)'`, and then you allow and deny further permissions during the session by responding to prompts, when you then use the `/reset-allowed-tools` command, the CLI's permissions return to the original `--allow-tool='shell(git:*)'` state, with no other permissions allowed or denied. As you continue to work in the session, you will be prompted again if {% data variables.product.prodname_copilot_short %} needs additional permissions. +Using `/reset-allowed-tools` resets the permissions to the default, or to the state defined by any command-line options you used when you started {% data variables.copilot.copilot_cli_short %}. It also clears saved tool approvals for the current location from `permissions-config.json`. For example, if you start a {% data variables.copilot.copilot_cli_short %} interactive session with the option `--allow-tool='shell(git:*)'`, and then you allow and deny further permissions during the session by responding to prompts, when you then use the `/reset-allowed-tools` command, the CLI's permissions return to the original `--allow-tool='shell(git:*)'` state, with no other permissions allowed or denied. As you continue to work in the session, you will be prompted again if {% data variables.product.prodname_copilot_short %} needs additional permissions. + +To remove saved approvals for a different location, edit or delete the relevant location entry in `permissions-config.json`. For more information, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#permissions-configjson). ## Further reading * [AUTOTITLE](/copilot/how-tos/copilot-cli/cli-best-practices#configure-allowed-tools) * [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-command-reference) +* [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#permissions-configjson) diff --git a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md index 343c5627ad09..7c179a8c3fa9 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md @@ -122,11 +122,189 @@ Stores internal application state that is managed automatically by the CLI, incl ### `permissions-config.json` -Stores your saved tool and directory permission decisions, organized by project location. When you approve a tool or grant access to a directory, the CLI records the decision here so you aren't prompted again in the same project. +Stores your saved tool and directory permission decisions, organized by project location. When you approve a tool or grant access to a directory for the current location, the CLI records the decision here so you aren't prompted again in the same repository or directory. > [!NOTE] > If you want to reset permissions for a project, you can delete the relevant entry from this file. However, editing the file while a session is running may cause unexpected behavior. +#### File location + +{% data variables.copilot.copilot_cli_short %} resolves the file from the configuration directory. + +| Priority | Source | File used | +|----------|--------|-----------| +| 1 | `--config-dir=DIRECTORY` | `DIRECTORY/permissions-config.json` | +| 2 | `COPILOT_HOME` | `$COPILOT_HOME/permissions-config.json` | +| 3 | Default | `~/.copilot/permissions-config.json` | + +The `--config-dir` option is a legacy option. Prefer `COPILOT_HOME` when you need to change the configuration directory. + +On Windows, the default file is typically: + +```text +C:\Users\YOUR-USER\.copilot\permissions-config.json +``` + +Older builds used an extensionless file named `permissions-config`. If `permissions-config.json` doesn't exist but the extensionless file does, the CLI still honors the legacy file. Use `permissions-config.json` for new edits. + +Previous XDG-based configuration locations are migrated to `~/.copilot` at startup when `COPILOT_HOME` isn't set. + +#### Location keys + +The top-level `locations` object is keyed by an absolute path. + +* For a Git repository, use the Git root used for permission scoping. +* Linked worktrees resolve to the main repository root, so they share permissions with the main worktree. +* Submodules use their own working directory. +* For a directory that isn't in a Git repository, use the normalized current working directory. + +The key must match the location where {% data variables.copilot.copilot_cli_short %} is running. If the key doesn't match, the saved approvals won't apply. + +The CLI loads the matching location's approvals when a session starts and when the active working directory changes. + +#### Schema + +The file must contain a JSON object. + +| Field | Type | Required | Default | Allowed values | Description | +|-------|------|----------|---------|----------------|-------------| +| `locations` | Object | No | `{}` | Absolute path keys | Map of location keys to saved approvals. | +| `locations.` | Object | No | `{}` | Any absolute location key | Saved approvals for one repository or directory. | +| `locations..tool_approvals` | Array | No | `[]` | Approval objects | Tools approved for this location. | +| `locations..allowed_directories` | Array of strings | No | `[]` | Absolute directory paths | Extra directories that the path gate can access for this location. Each directory must exist when the CLI applies the configuration. | +| `locations..tool_approvals[].kind` | String | Yes | None | `commands`, `read`, `write`, `mcp`, `mcp-sampling`, `memory`, `custom-tool`, `extension-management`, `extension-permission-access` | Selects the approval type. | +| `commandIdentifiers` | Array of strings | Yes, for `commands` | None | Command identifiers | Shell command identifiers to approve. | +| `serverName` | String | Yes, for `mcp` and `mcp-sampling` | None | MCP server name | MCP server to approve. | +| `toolName` | String or `null` | Yes, for `mcp` | None | MCP tool name, or `null` | MCP tool to approve. Use `null` to approve every tool on the server. | +| `toolName` | String | Yes, for `custom-tool` | None | Custom tool name | Custom tool to approve by exact name. | +| `operation` | String | No, for `extension-management` | Omitted | Extension operation name | Extension-management operation to approve. Omit this field to approve all extension-management operations. | +| `extensionName` | String | Yes, for `extension-permission-access` | None | Extension name | Extension whose access to permission-gated capabilities is approved. | + +`permissions-config.json` doesn't support deny rules, "ask" rules, default modes, URL rules, tool filtering, or repository-local shared policy. For those behaviors, use command-line options such as `--deny-tool`, `--available-tools`, `--excluded-tools`, `--allow-url`, and `--deny-url`. Saved URL rules are stored in `config.json`, not in `permissions-config.json`. + +Unknown fields aren't part of the schema. The CLI may ignore them, and later writes may remove them. + +#### Approval kinds + +Each item in `tool_approvals` must be one of the following objects. + +| `kind` | Required fields | Optional fields | Meaning | +|--------|-----------------|-----------------|---------| +| `commands` | `commandIdentifiers` | None | Approves matching shell command identifiers. | +| `read` | None | None | Approves read tool requests. Interactive CLI sessions already approve reads automatically, so this is usually unnecessary. | +| `write` | None | None | Approves file creation and modification tool requests. A path prompt can still apply for paths outside the allowed directories. | +| `mcp` | `serverName`, `toolName` | None | Approves one MCP tool, or every tool on the server when `toolName` is `null`. | +| `mcp-sampling` | `serverName` | None | Approves MCP sampling requests for one server. | +| `memory` | None | None | Approves memory write and vote requests. | +| `custom-tool` | `toolName` | None | Approves a custom tool by exact name. | +| `extension-management` | None | `operation` | Approves extension management. If `operation` is omitted, all extension-management operations match. | +| `extension-permission-access` | `extensionName` | None | Approves an extension's access to permission-gated capabilities. | + +For MCP approvals, `serverName` must match the configured MCP server name exactly. Use the raw server name from your MCP configuration, not a sanitized tool-name prefix. + +#### Shell command matching + +`commandIdentifiers` match the command identifiers extracted from a shell request. Matching is exact except for the `:*` suffix. + +| Pattern | Matches | Doesn't match | +|---------|---------|---------------| +| `git status` | `git status` | `git status --short` | +| `git:*` | `git`, `git status`, `git push` | `gitea` | +| `gh pr:*` | `gh pr`, `gh pr view`, `gh pr create` | `gh repo view` | + +The `:*` suffix isn't a general glob pattern. It matches the exact stem, or the stem followed by a space and more text. + +#### Directory matching + +`allowed_directories` entries allow {% data variables.copilot.copilot_cli_short %} to access paths inside those directories without a separate path prompt. They don't approve the tool operation itself. For example, editing a file in an allowed directory can still require a `write` approval. + +Each `allowed_directories` entry must be an absolute, non-empty, accessible directory. The CLI resolves symlinks before comparing paths, blocks UNC network paths unless they are extended-length local paths, compares paths case-insensitively on Windows, and compares paths case-sensitively on other platforms. If an entry can't be applied, the CLI logs a warning and skips that entry. + +#### Examples + +Allow all Git subcommands and access an extra local directory: + +```json copy +{ + "locations": { + "C:\\src\\my-repo": { + "tool_approvals": [ + { + "kind": "commands", + "commandIdentifiers": ["git:*"] + } + ], + "allowed_directories": ["C:\\src\\shared-docs"] + } + } +} +``` + +Allow selected commands while still asking before file writes: + +```json copy +{ + "locations": { + "/Users/YOUR-USER/src/my-repo": { + "tool_approvals": [ + { + "kind": "commands", + "commandIdentifiers": [ + "git status", + "git diff", + "git log", + "npm test", + "npm run build" + ] + } + ] + } + } +} +``` + +Approve file writes and one MCP server for a repository: + +```json copy +{ + "locations": { + "/home/YOUR-USER/src/my-repo": { + "tool_approvals": [ + { + "kind": "write" + }, + { + "kind": "mcp", + "serverName": "github-mcp-server", + "toolName": null + } + ] + } + } +} +``` + +Approve one MCP tool and memory writes: + +```json copy +{ + "locations": { + "C:\\src\\my-repo": { + "tool_approvals": [ + { + "kind": "mcp", + "serverName": "github-mcp-server", + "toolName": "search_code" + }, + { + "kind": "memory" + } + ] + } + } +} +``` + ### `session-state/` Contains session history data, organized by session ID in subdirectories. Each session directory stores an event log (`events.jsonl`) and workspace artifacts (plans, checkpoints, tracked files). This data enables session resume (`--resume` or `--continue`). From 128719b92f5d97e28eead0d226d3a050cc594b59 Mon Sep 17 00:00:00 2001 From: Dan Moseley <6385855+danmoseley@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:43:17 -0600 Subject: [PATCH 2/5] Clarify persisted Copilot CLI approvals Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../how-tos/copilot-cli/use-copilot-cli/allowing-tools.md | 6 +++++- .../copilot-cli-reference/cli-config-dir-reference.md | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md b/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md index 3ff53df0106b..26000df36f58 100644 --- a/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md +++ b/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md @@ -22,7 +22,11 @@ While read-only operations like searching, reading files, and running read-only You can allow or deny permissions for tools either when you start the CLI or during your interactive session. If you haven't granted permission prior to starting a session, {% data variables.copilot.copilot_cli_short %} will prompt you for permission each time it needs to perform a potentially destructive action. You can choose to allow the tool this one time, or for the remainder of the session. -If you choose the option to allow similar requests for the current location, the approval is saved in `permissions-config.json` in your {% data variables.copilot.copilot_cli_short %} configuration directory. The saved approval is then applied the next time you start the CLI in the same repository or directory. Command-line options such as `--allow-tool` and `--deny-tool` apply only to the current session and aren't written to `permissions-config.json`; deny rules still take precedence over saved approvals. +## Persisted permissions + +If you answer "yes, always" or otherwise choose the option to allow similar requests for the current location, the approval is saved in `~/.copilot/permissions-config.json`. The saved approval is then applied the next time you start the CLI in the same repository or directory. + +Command-line options such as `--allow-tool` and `--deny-tool` apply only to the current session and aren't written to `permissions-config.json`. Deny rules still take precedence over saved approvals. For details about where saved approvals are stored, how locations are matched, and the full `permissions-config.json` schema, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-config-dir-reference#permissions-configjson). diff --git a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md index 7c179a8c3fa9..6584fca8a8c7 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md @@ -284,7 +284,7 @@ Approve file writes and one MCP server for a repository: } ``` -Approve one MCP tool and memory writes: +Approve one MCP tool, memory writes, and extension permission access: ```json copy { @@ -298,6 +298,10 @@ Approve one MCP tool and memory writes: }, { "kind": "memory" + }, + { + "kind": "extension-permission-access", + "extensionName": "my-extension" } ] } From 00d0dd2b94d5f799d544d7b1b499c5e8e1c18d2e Mon Sep 17 00:00:00 2001 From: Dan Moseley <6385855+danmoseley@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:46:25 -0600 Subject: [PATCH 3/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../how-tos/copilot-cli/use-copilot-cli/allowing-tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md b/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md index 26000df36f58..1899ed77857a 100644 --- a/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md +++ b/content/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools.md @@ -24,7 +24,7 @@ You can allow or deny permissions for tools either when you start the CLI or dur ## Persisted permissions -If you answer "yes, always" or otherwise choose the option to allow similar requests for the current location, the approval is saved in `~/.copilot/permissions-config.json`. The saved approval is then applied the next time you start the CLI in the same repository or directory. +If you answer "yes, always" or otherwise choose the option to allow similar requests for the current location, the approval is saved to `permissions-config.json` in your configuration directory (by default, `~/.copilot/permissions-config.json`). Command-line options such as `--allow-tool` and `--deny-tool` apply only to the current session and aren't written to `permissions-config.json`. Deny rules still take precedence over saved approvals. From c05b0e9ed0be59da9a5b2d2971c2ffeda8db3065 Mon Sep 17 00:00:00 2001 From: Dan Moseley <6385855+danmoseley@users.noreply.github.com> Date: Thu, 25 Jun 2026 19:24:49 -0600 Subject: [PATCH 4/5] Note automatic removal of orphaned location entries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../reference/copilot-cli-reference/cli-config-dir-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md index 6584fca8a8c7..21edde012781 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md @@ -125,7 +125,7 @@ Stores internal application state that is managed automatically by the CLI, incl Stores your saved tool and directory permission decisions, organized by project location. When you approve a tool or grant access to a directory for the current location, the CLI records the decision here so you aren't prompted again in the same repository or directory. > [!NOTE] -> If you want to reset permissions for a project, you can delete the relevant entry from this file. However, editing the file while a session is running may cause unexpected behavior. +> If you want to reset permissions for a project, you can delete the relevant entry from this file. However, editing the file while a session is running may cause unexpected behavior. The CLI automatically removes entries whose location path no longer exists on disk. #### File location From 005129aad4e707a61e08a9179df83105db7e4df7 Mon Sep 17 00:00:00 2001 From: hubwriter Date: Fri, 26 Jun 2026 11:54:18 +0100 Subject: [PATCH 5/5] Apply suggestion from @hubwriter --- .../reference/copilot-cli-reference/cli-config-dir-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md index 21edde012781..c77393fa5364 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md @@ -180,7 +180,7 @@ The file must contain a JSON object. | `operation` | String | No, for `extension-management` | Omitted | Extension operation name | Extension-management operation to approve. Omit this field to approve all extension-management operations. | | `extensionName` | String | Yes, for `extension-permission-access` | None | Extension name | Extension whose access to permission-gated capabilities is approved. | -`permissions-config.json` doesn't support deny rules, "ask" rules, default modes, URL rules, tool filtering, or repository-local shared policy. For those behaviors, use command-line options such as `--deny-tool`, `--available-tools`, `--excluded-tools`, `--allow-url`, and `--deny-url`. Saved URL rules are stored in `config.json`, not in `permissions-config.json`. +`permissions-config.json` doesn't support deny rules, "ask" rules, default modes, URL rules, tool filtering, or repository-local shared policy. For those behaviors, use command-line options such as `--deny-tool`, `--available-tools`, `--excluded-tools`, `--allow-url`, and `--deny-url`. Saved URL rules are stored in `settings.json`, not in `permissions-config.json`. Unknown fields aren't part of the schema. The CLI may ignore them, and later writes may remove them.