Skip to main content
Glama
jogalanza

Copilot Workspace MCP

by jogalanza

Copilot Workspace MCP v1.3

MCP server that lets Microsoft 365 Copilot and Copilot Studio (incl. Teams) list, read and write files in named code workspaces. Each chat keeps its own workspace, and the choice survives server restarts (SQLite state via the built-in node:sqlite).

Quick start

npm install
Copy-Item .env.example .env   # then set WORKSPACES, ALLOWED_ROOTS
npm run new-key               # generates MCP_API_KEY in .env
npm run dev                   # http://localhost:8790/mcp
npm test                      # unit + end-to-end tests

Requires Node.js >= 22.13. Expose port 8790 with a dev tunnel and register https://<tunnel-host>/mcp in the client (the /mcp suffix is required).

Related MCP server: VibeBridge

Standalone executable (download, run, done)

A single-file app that sets everything up for you: no clone, no npm install, no build, no manual tunnel.

Download and run

Get the archive for your system from the latest release, unpack it and run the executable inside. Each archive also contains a short README.txt; SHA256SUMS.txt is on the release page.

System

Archive

Run

Windows (x64)

copilot-workspace-mcp-v<version>-windows-x64.zip

double-click copilot-workspace-mcp.exe (SmartScreen: More info > Run anyway, the app is not code-signed)

macOS (Apple silicon / Intel)

...-macos-arm64.tar.gz / ...-macos-x64.tar.gz

xattr -d com.apple.quarantine ./copilot-workspace-mcp && ./copilot-workspace-mcp (if macOS still refuses: codesign --sign - ./copilot-workspace-mcp)

Linux (x64 / arm64)

...-linux-x64.tar.gz / ...-linux-arm64.tar.gz

./copilot-workspace-mcp

On first run it:

  1. asks for the workspace folder to expose (default: current folder) and generates an API key, saved in %APPDATA%\copilot-workspace-mcp\config.env (Windows) or ~/.config/copilot-workspace-mcp/config.env

  2. downloads the devtunnel CLI into that folder if it is not on PATH, and signs you in if needed

  3. creates a persistent tunnel (same public URL on every run), starts the server and hosts the tunnel

  4. prints the public https://.../mcp URL and the API key (key shown on first run or with --show-key)

A local web UI (default http://localhost:8791, loopback only) opens automatically in its own app-style window using the installed Edge/Chrome, or the default browser if there is none (--no-ui / --no-open). It shows colour-coded live logs with level/source/text filters, pause, clear and download, tunnel status with a restart button, Settings for every variable in config.env (saving validates and restarts the server in place), and a Stop button. Ctrl+C, closing the console or Stop shuts down the tunnel first, then the server, cleanly. The browser tools (browser_*) are on by default; set ENABLE_BROWSER=false to disable them.

The tunnel allows anonymous clients by default (Copilot Studio / M365 Copilot cannot sign in to a dev tunnel; the API key still protects /mcp). Set TUNNEL_ALLOW_ANONYMOUS=false to restrict it to your own Microsoft account.

The console shows only errors and important lines (add --verbose for all dev tunnel output). Other flags: --reconfigure, --new-key, --show-key, --no-tunnel, --no-ui, --no-open, --help. Any variable from .env.example can be set in config.env, in a .env in the folder you start it from, or in the environment (later wins).

Build it yourself

Needs Node.js >= 24 and npm install once. The executable embeds the Node runtime of the machine (or target) it is built for.

Goal

Command

Output in release/

This machine's OS/arch

npm run build:standalone

copilot-workspace-mcp-<platform>-<arch>[.exe]

Linux x64 + arm64

npm run build:standalone:linux

copilot-workspace-mcp-linux-x64, -linux-arm64

macOS Intel + Apple silicon

npm run build:standalone:mac

copilot-workspace-mcp-darwin-x64, -darwin-arm64

Every target

npm run build:standalone:all

all of the above plus win32-x64, win32-arm64

Any mix

node scripts/build-standalone.mjs win32-x64 linux-arm64

Download-ready archives

npm run package (all targets) or npm run package:current

release/packages/*.zip, *.tar.gz, SHA256SUMS.txt

Set OUT_DIR=some/folder to write somewhere other than release/.

Building on each OS. Run npm install then npm run build:standalone on that machine. On macOS this also ad-hoc signs the binary (codesign, built into macOS), so it can run immediately on Apple silicon.

Cross-building from one machine. Targets for another OS/arch download that platform's official Node runtime (same version as yours, cached in .cache/node/) and inject the app into it, so you can produce Linux and macOS binaries from Windows. Cross-built Linux and Windows binaries run as they are. Cross-built macOS binaries cannot be code-signed off a Mac, so on the Mac run once codesign --sign - ./copilot-workspace-mcp (the release archives avoid this because CI builds them on macOS runners).

Releases. Pushing a tag such as v1.3.0 runs .github/workflows/release.yml: it builds on Windows, Linux (x64 and arm64) and macOS (Intel and Apple silicon) runners, packages the archives with scripts/package-release.mjs, and attaches them and SHA256SUMS.txt to a GitHub release. The same workflow can be started by hand (Actions > Release executables) to get the archives as a build artifact without a release.

How a chat's workspace is chosen

For every tool call the server resolves the workspace in this order:

  1. workspace tool argument (alias) - used by Microsoft 365 Copilot declarative agents

  2. the chat's saved binding, keyed by conversation id - Copilot Studio / Teams (conversation-id header, or conversationId argument set to System.Conversation.Id)

  3. the default workspace (DEFAULT_WORKSPACE, or switch_workspace with makeDefault)

Every file tool reply starts with [workspace: <alias>].

Client

Setup

Copilot Studio / Teams

Import docs/copilot-studio-connector.yaml, add input conversation-id = System.Conversation.Id, API key header api-key.

M365 Copilot declarative agent

Add docs/m365-agent-instructions.md to the agent instructions; configure API key auth.

MCP tools

Tool

Purpose

list_workspaces

Registered workspaces, default and this chat's current one

switch_workspace

Bind this chat to a workspace (makeDefault optional)

current_workspace

Which workspace this chat uses and why

add_workspace / remove_workspace

Register/unregister a folder (only under ALLOWED_ROOTS)

list_files

Glob within the workspace (skips node_modules, .git)

ws_read_file

Read a text file (size-capped)

ws_write_file

Atomic create/overwrite; blocked in .git, node_modules

Optional: headless browser and command runner

Both are off by default (see .env.example).

Tool

Enabled by

Purpose

browser_open, browser_screenshot, browser_text, browser_click, browser_type, browser_logs, browser_eval, browser_close

ENABLE_BROWSER=true

Drive a headless Edge/Chrome (one tab per chat). Screenshots are returned as images and saved under <workspace>/.screenshots/.

run_command, process_output, process_stop, list_processes

RUN_ALLOWED_COMMANDS non-empty

Run allowlisted commands in a workspace root, optionally in the background (dev servers), with output capture.

Typical flow: run_command npm run dev (background, readyText) -> browser_open the printed URL -> browser_screenshot -> browser_logs -> process_stop.

Safety: the browser only loads hosts in BROWSER_ALLOWED_HOSTS (default localhost; every sub-request and redirect is checked, file: is refused). run_command only starts commands that begin with an allowlist entry, rejects quotes, pipes, &, ;, $, % and redirects, runs without MCP_API_KEY in its environment, and each chat can only see and stop its own processes. Do not allowlist interpreters such as node or powershell. These tools give anyone holding the API key a way to run your project's scripts, so keep the key private and the allowlist small. Uses playwright-core against an installed browser; no browser download.

HTTP endpoints

All except /health require the API key (api-key header, Authorization: Bearer, or ?api_key=).

Method

Path

Purpose

GET

/health

Liveness (no paths revealed)

POST

/mcp

MCP Streamable HTTP (stateless)

GET / POST

/workspaces

List / register {"alias","path","makeDefault"?}

DELETE

/workspaces/:alias

Unregister

GET / POST

/workspace

Get / set default {"workspace":"<alias>"}

GET

/repository-summary?workspace=<alias>

Source file count

GET

/audit?limit=50

Recent tool calls

API key (MCP_API_KEY)

Every request except GET /health must carry the key; without it the server returns 401. The server refuses to start when MCP_API_KEY is empty (unless MCP_ALLOW_ANONYMOUS=true).

  1. Generate (writes/rotates the key in .env, prints it once): npm run new-key - or only print one: npm run new-key -- --print

  2. Restart the server (npm run dev); .env is read at startup.

  3. Send it in one of these ways (checked in this order):

    Where

    Example

    api-key header (recommended)

    api-key: <key>

    Bearer token

    Authorization: Bearer <key>

    Query string (avoid: ends up in logs)

    https://<host>/mcp?api_key=<key>

  4. Configure the clients

    • Copilot Studio: MCP wizard > Authentication API key > Type Header > name api-key (or use docs/copilot-studio-connector.yaml, which already declares it). Paste the key when creating the connection.

    • M365 Copilot declarative agent: API key auth registered in the Teams developer portal / Agents Toolkit; the key is stored by Microsoft, not in the app package.

    • curl / scripts: curl -H "api-key: $env:MCP_API_KEY" http://localhost:8790/workspaces

Rotate: npm run new-key, restart, then update the connection in each client. Never commit .env (it is git-ignored) and never paste the key into chat prompts.

Dependencies

Versions are pinned exactly in package.json (and .npmrc sets save-exact=true). Update deliberately: npm install <pkg>@<version>, then npm run typecheck && npm test. Check with npm run audit.

Known advisory: braces <= 3.0.3 (via fast-glob -> micromatch), CVE-2026-93687 / GHSA-vfj7-8cjw-p6xm, has no patched release. list_files mitigates it by capping glob patterns at 512 characters and 8 nesting levels; remove this note when a fixed braces ships.

Safety

  • Paths are resolved inside the workspace (also through symlinks/junctions); .. and absolute paths are rejected.

  • Writes into .git, node_modules or the state DB are refused; writes are atomic and size-limited.

  • Every tool call is recorded in the audit log (no file contents). Chat bindings expire after BINDING_TTL_DAYS (30) of inactivity; audit rows after 90 days.

Configuration

See .env.example. State DB default: %LOCALAPPDATA%\copilot-mcp\state.db.

License

MIT, see LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides Copilot Studio with six tools for secure workspace file operations and one-time execution of pre-approved Python jobs in isolated, network-less Docker containers, with strict access controls and audit logging.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables Gemini Spark to securely read, edit, and search a selected local workspace, execute shell commands, and integrate development tools via MCP with user approval safeguards.
    MIT