copilot-mcp-server-acp-bridge
v0.1.0
Published
Policy-scoped MCP server bridge for GitHub Copilot CLI ACP mode
Maintainers
Readme
copilot-mcp-server-acp-bridge
copilot-mcp-server-acp-bridge exposes GitHub Copilot CLI ACP mode as a
policy-scoped Model Context Protocol (MCP) server.
Externally, MCP clients call tools such as copilot_plan, copilot_review, and
copilot_edit. Internally, the bridge starts GitHub Copilot CLI as
copilot --acp --stdio, listens to Agent Client Protocol (ACP) session events,
handles ACP file/terminal/permission requests, and returns structured MCP tool
results.
Requirements
- Node.js
>=22 - GitHub Copilot CLI installed and authenticated
- A Copilot entitlement for the account you authenticate with
Install/authenticate Copilot CLI if needed:
npm install -g @github/copilot
copilot loginThe bridge auto-discovers common Copilot CLI install locations, including
Windows npm global shims and the VS Code Copilot extension launcher. Set
COPILOT_CLI_PATH only if auto-discovery fails.
Quick start with npx
After the package is published to npm, MCP clients can run it directly with
npx:
{
"mcpServers": {
"GitHub Copilot": {
"command": "npx",
"args": ["-y", "copilot-mcp-server-acp-bridge"]
}
}
}Or install it globally:
npm install -g copilot-mcp-server-acp-bridge
copilot-mcp-server-acp-bridgeQuick start from source
npm install
npm run build
node dist/index.jsFor local development:
npm run devMCP client configuration
For a published install, the normal local stdio config is:
{
"mcpServers": {
"GitHub Copilot": {
"command": "npx",
"args": ["-y", "copilot-mcp-server-acp-bridge"]
}
}
}For local source checkout development, use:
{
"mcpServers": {
"GitHub Copilot": {
"command": "node",
"args": ["C:\\Code\\copilot-mcp-server-acp-bridge\\dist\\index.js"]
}
}
}Transport defaults to stdio. HTTP mode is only used when explicitly configured
with COPILOT_ACP_HTTP_PORT or COPILOT_ACP_TRANSPORT=http.
Workspace selection
The default workspace is the directory where the MCP server process is launched.
Some clients launch MCP servers from an editor install directory instead of the
open repository. In that case, prefer passing workspaceRoot per tool call:
{
"workspaceRoot": "C:\\Code\\my-project",
"prompt": "Summarize this project",
"targetPaths": []
}You can also set a global fallback:
COPILOT_ACP_WORKSPACE=/absolute/path/to/repoPer-call workspaceRoot is the recommended model for multi-root clients or
multiple running MCP server instances.
MCP tools
| Tool | Purpose |
| --- | --- |
| copilot_health | Check bridge, Copilot CLI, ACP readiness, workspace, and session stats. |
| copilot_version | Report bridge, Node, SDK, and Copilot CLI versions. |
| copilot_plan | Read-only planning. targetPaths may be empty. |
| copilot_review | Read-only review. Can include/exclude git diff context. |
| copilot_edit | Scoped edit flow. Requires targetPaths. |
| copilot_edit_and_test | Scoped edit flow followed by an exact allowlisted validation command. |
| copilot_bulk_edit | Conservative bulk edit flow with chunked and single_session strategies. |
| copilot_session | List, cancel, or clear tracked ACP sessions. |
parallel_worktrees is recognized by copilot_bulk_edit but intentionally
rejected for now.
Common per-tool options
ACP-backed tools support these optional controls:
{
"workspaceRoot": "C:\\Code\\my-project",
"model": "gpt-5.3-codex",
"agent": "security-reviewer",
"reasoningEffort": "high",
"additionalDirs": ["C:\\Code\\shared-docs"]
}model: passed to Copilot as--model.agent: passed to Copilot as--agentfor configured custom agents.reasoningEffort: one oflow,medium,high, orxhigh; passed as--effort.additionalDirs: extra read-only context roots exposed to Copilot with--add-dir.
Global defaults are also available:
COPILOT_ACP_MODEL=gpt-5.3-codex
COPILOT_ACP_AGENT=security-reviewer
COPILOT_ACP_REASONING_EFFORT=high
COPILOT_ACP_ADDITIONAL_DIRS=/absolute/shared,/absolute/docsIf unset, Copilot CLI chooses its account/default model and behavior.
Safety and policy model
The bridge does not leave Copilot waiting on interactive approval prompts. ACP permission requests are answered deterministically from the effective policy.
The default policy is conservative:
- safe reads inside the primary workspace are allowed
- reads from
additionalDirsare allowed as context - writes are allowed only inside the primary workspace and only in edit-capable modes
- additional directories are read-only
- plan/review modes cannot write
- secret-like paths such as
.env, private keys,.ssh/**,.aws/**, and.npmrcare denied - shell command chains and dangerous commands such as
git push,npm install,npm publish,docker run, andterraform applyare denied - bridge-run validation commands must exactly match the requested
testCommandor the configured test allowlist
Policy defaults live in src/safety/policy.ts. The
effective policy is threaded through permission handling, filesystem handlers,
terminal handlers, target-path expansion, and validation command execution.
Trusted policy file
You can load a trusted user-owned policy file with:
COPILOT_ACP_POLICY_FILE=/absolute/path/to/policy.jsonDo not point this at an untrusted repository file. A trusted policy file can loosen or tighten permissions, so it should be owned by the user or deployment operator.
Example:
{
"approvalMode": "permissive-read",
"paths": {
"allowWrites": "primary-workspace",
"additionalDirsMode": "read-only",
"denySecretPaths": true,
"secretPatterns": [
".env",
".env.*",
"*.pem",
"*.key",
".ssh/**",
".aws/**",
".npmrc",
"*.secret"
]
},
"commands": {
"allowReadOnlyGit": true,
"testAllowlist": ["npm test", "npm run lint", "npm run typecheck"],
"allow": ["node scripts/safe.js"],
"deny": ["git push", "npm install", "npm publish", "docker run"],
"denyShellMetacharacters": true
},
"git": {
"requireCleanGit": true
}
}Policy merge order:
- built-in
DEFAULT_POLICY COPILOT_ACP_POLICY_FILE, if set- environment policy overrides such as
COPILOT_ACP_APPROVAL_MODE,COPILOT_ACP_ALLOW_WRITES,COPILOT_ACP_TEST_ALLOWLIST, andCOPILOT_ACP_REQUIRE_CLEAN_GIT
Policy via MCP client env vars
You do not have to create a policy file. The most useful policy fields can be
set directly in the MCP client env block:
{
"mcpServers": {
"GitHub Copilot": {
"command": "npx",
"args": ["-y", "copilot-mcp-server-acp-bridge"],
"env": {
"COPILOT_ACP_APPROVAL_MODE": "dangerous",
"COPILOT_ACP_ALLOW_WRITES": "anywhere",
"COPILOT_ACP_DENY_SECRET_PATHS": "true",
"COPILOT_ACP_TEST_ALLOWLIST": "npm test,npm run lint"
}
}
}
}Direct policy env vars:
| Env var | Values |
| --- | --- |
| COPILOT_ACP_APPROVAL_MODE | deny-by-default, strict, permissive-read, dangerous |
| COPILOT_ACP_ALLOW_WRITES | none, primary-workspace, workspace-and-additional, anywhere |
| COPILOT_ACP_ADDITIONAL_DIRS_MODE | read-only, none |
| COPILOT_ACP_ALLOW_READS | boolean |
| COPILOT_ACP_DENY_SECRET_PATHS | boolean |
| COPILOT_ACP_SECRET_PATTERNS | comma-separated secret glob patterns |
| COPILOT_ACP_COMMAND_ALLOWLIST | comma-separated exact commands to allow |
| COPILOT_ACP_COMMAND_DENYLIST | comma-separated denied command prefixes |
| COPILOT_ACP_ALLOW_READONLY_GIT | boolean |
| COPILOT_ACP_DENY_SHELL_METACHARS | boolean |
| COPILOT_ACP_TEST_ALLOWLIST | comma-separated exact validation commands |
| COPILOT_ACP_REQUIRE_CLEAN_GIT | boolean |
For example, to allow outside-workspace writes without creating a policy file:
{
"env": {
"COPILOT_ACP_APPROVAL_MODE": "dangerous",
"COPILOT_ACP_ALLOW_WRITES": "anywhere"
}
}Policy fields
| Field | Values / meaning |
| --- | --- |
| approvalMode | deny-by-default, strict, permissive-read, or dangerous. Current behavior is deterministic, not interactive. |
| paths.allowReads | Whether safe reads are allowed. |
| paths.allowWrites | none, primary-workspace, workspace-and-additional, or anywhere. |
| paths.additionalDirsMode | read-only or none. Additional dirs are never writable by default. |
| paths.denySecretPaths | Enables/disables secret-pattern blocking. |
| paths.secretPatterns | Glob-like patterns matched against relative paths. |
| commands.allowReadOnlyGit | Allows configured read-only git commands. |
| commands.readOnlyGitPrefixes | Read-only git command prefixes, e.g. git status, git diff. |
| commands.testAllowlist | Exact validation commands allowed in test-capable flows. |
| commands.allow | Additional exact commands to allow. Use sparingly. |
| commands.deny | Command prefixes to deny. Deny wins before read-only/test allows. |
| commands.denyShellMetacharacters | Blocks command chains such as &&, pipe operators, ;, redirects, and $(). |
| git.requireCleanGit | Default clean-git requirement for edit flows. |
Writing outside the workspace
By default, writes are limited to the primary workspaceRoot. If you explicitly
want Copilot to write outside the workspace when it asks for permission, set the
write scope with either a trusted user policy file or MCP client env vars.
Policy file form:
{
"paths": {
"allowWrites": "anywhere"
}
}This is intentionally not the default. Even with allowWrites: "anywhere", the
bridge still applies approval-mode checks, plan/review write bans, secret-path
blocking, .git write blocking, command deny lists, and shell-chain blocking.
MCP env form:
{
"env": {
"COPILOT_ACP_APPROVAL_MODE": "dangerous",
"COPILOT_ACP_ALLOW_WRITES": "anywhere"
}
}If you only want to allow writes in the primary workspace plus configured
additionalDirs, use:
{
"paths": {
"allowWrites": "workspace-and-additional"
}
}Use these settings only from a trusted user-owned policy file or an MCP client configuration that you control, not an untrusted repo-local policy file.
Approval modes
Approval modes are ordered from most restrictive to most permissive:
| Mode | Behavior |
| --- | --- |
| deny-by-default | Denies normal file reads/writes and denies commands unless they exactly match commands.allow. Useful for locked-down deployments. |
| strict | Default. Allows safe reads, edit-mode writes inside the primary workspace, read-only git commands, and exact allowlisted validation commands. Denies dangerous commands and secret paths. |
| permissive-read | “One below dangerous.” Allows safe reads and diagnostic commands such as node --version, npm -v, and python --help, but denies insert/update/delete/move/write operations and dangerous commands. Useful when Copilot may inspect broadly but should not mutate files. |
| dangerous | Allows most non-denied commands and normal edit-mode writes inside the primary workspace. Still enforces the configured deny list, shell-chain blocking, workspace boundaries, and secret-path checks. Use only for trusted local workflows. |
Example read/inspect-only policy:
{
"approvalMode": "permissive-read",
"paths": {
"allowReads": true,
"allowWrites": "none",
"additionalDirsMode": "read-only",
"denySecretPaths": true
},
"commands": {
"deny": ["git push", "npm install", "npm publish", "docker run"],
"denyShellMetacharacters": true
}
}Validation commands
copilot_edit_and_test and copilot_bulk_edit run validation through the
bridge, not by letting Copilot freely run shell commands. The command must be an
exact allowlisted command.
Example:
{
"instruction": "Update tests for the new helper",
"targetPaths": ["src/helper.ts", "tests/helper.test.ts"],
"testCommand": "npm test"
}Remote HTTP mode
Stdio is the default and recommended local transport. HTTP mode is optional:
COPILOT_ACP_HTTP_HOST=127.0.0.1
COPILOT_ACP_HTTP_PORT=8787
COPILOT_ACP_AUTH_TOKEN=replace-with-long-random-token
node dist/index.jsWhen binding to a non-loopback host, set COPILOT_ACP_AUTH_TOKEN.
Development and validation
npm install
npm run typecheck
npm test
npm run buildOn Windows, if PowerShell command resolution is slow or broken, invoking Node directly also works:
& "C:\Program Files\nodejs\node.exe" .\node_modules\typescript\bin\tsc --noEmit
& "C:\Program Files\nodejs\node.exe" .\node_modules\vitest\vitest.mjs run
& "C:\Program Files\nodejs\node.exe" .\node_modules\tsup\dist\cli-default.js --config tsup.config.tsPublishing status
The package is configured for public npm publishing under the name
copilot-mcp-server-acp-bridge. Before publishing a release, run the validation
and packaging checks below, then publish from an authenticated npm account:
npm run typecheck
npm test
npm run build
npm pack --dry-run
npm publish --access public