Skip to main content
Glama

copilot-mcp

A simple Python MCP server (stdio) exposing Copilot-style tools to VS Code.

Group

Tools

Docs

get_tool_docs (also resources docs://tools, docs://tools/{group})

Filesystem

workspace_info, read_file, create_file, edit_file, edit_file_multi, replace_lines, list_dir, delete_file

Search

grep, glob

Shell

run_command

Git

git_status, git_diff, git_log, git_add, git_commit, git_init, git_branch_create, git_switch, git_tag_create, git_push, git_blame, git_show, git_branch_list, git_stash_list, git_remote_info, git_diff_stat

Python

run_tests, lint, format_code

Web

web_fetch (confirmation required, saves sanitised markdown)

Data / docs

convert_data, query_data, script_to_markdown, markdown_to_script, pandoc_convert

API testing

http_request (with assertions), api_load_test

Todo scratchpad

todo_list, todo_add, todo_update, todo_remove

Policy / audit

policy_info, audit_tail (optional policy file and audit log, see docs/tools/policy.md)

Diagnostics

system_info, list_processes, list_ports, network_interfaces, dns_lookup, check_tcp_port, list_services, service_status, read_event_logs, cim_query, environment_variables

Workspace-focused. File, search, git, Python and conversion tools only touch paths inside the open workspace (set by COPILOT_MCP_WORKSPACE, normally ${workspaceFolder}); relative paths resolve against it, and .. or symlink escapes are rejected. run_command is not confined: its working directory must be in the workspace, but the command itself runs with your user's permissions. See docs/security.md.

Behaviour notes

  • Edits: edit_file, edit_file_multi and replace_lines accept dry_run=true to return a unified diff. Multi-edits are atomic.

  • Git: only creation and push are exposed. There is no force push, branch/tag/ref deletion, reset or rebase; refs are validated so +ref, :ref and option-like names are rejected.

  • web_fetch: asks for confirmation through MCP elicitation on every call and refuses if the client cannot ask. It blocks non-http(s) URLs, embedded credentials, private/loopback/link-local hosts (re-checked on every redirect), executable/archive/document extensions (including in Content-Disposition and double extensions) and non-text content types. HTML is stripped of scripts, forms, frames, event handlers, javascript:/data: links, comments and invisible/bidi characters before conversion. It returns only the path of the saved .md file (default ./fetched/). Fetched text is still untrusted input for the model.

  • convert_data: json, yaml, toml, csv, xml and psd1. Reading .psd1 uses Import-PowerShellDataFile through PowerShell (pwsh if present, else Windows PowerShell 5.1; no code execution); writing is done natively. XML is parsed with defusedxml. pandoc_convert needs pandoc installed and runs with --sandbox.

  • script_to_markdown / markdown_to_script: heuristic literate conversion (column-0 comments are prose, other lines are code).

  • API testing: http_request may target localhost and private addresses, unlike web_fetch. Sensitive response headers are redacted.

  • Diagnostics: read-only and cross-platform (systemd/journalctl on Linux, PowerShell on Windows, launchctl/log on macOS). cim_query works on Windows only. Listing other users' socket owners may need elevated rights.

Related MCP server: vscode-helper

Documentation

Build a deployable zip with python scripts/build.py (see docs/development.md).

Full documentation lives in docs/: getting started, architecture, security model, development guide, and a detailed page per tool group under docs/tools/.

Setup

python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest

VS Code

.vscode/mcp.json registers the server. Open the folder in VS Code, run MCP: List Servers, start (or restart after code changes) copilot-mcp, then use Agent mode in Copilot Chat.

Adding tools

Add a function with type hints and a docstring to a module in src/copilot_mcp/tools/, list it in that module's TOOLS, and register new modules in server.py.

License

MIT (c) 2026 Josh Hollandsworth. Provided as is, without warranty; see the license for details.

Available Tools

56 tools
api_load_testA

Tiny load probe: send requests requests (max 200) with concurrency workers (max 20) and report status counts, errors and latency percentiles. Use only against systems you own.

Inputs: url (required), method, timeout, requests, +1 more. Returns: result

audit_tailB

Return the most recent audit log entries (JSON lines), if auditing is enabled.

Inputs: lines. Returns: result

check_tcp_portB

Test whether a TCP connection to host:port succeeds, with connect time.

Inputs: host (required), port (required), timeout. Returns: result

cim_queryA

Query CIM/WMI classes via Get-CimInstance (Windows only), e.g. Win32_OperatingSystem, Win32_LogicalDisk, Win32_NetworkAdapterConfiguration, Win32_Service. filter is a WQL WHERE clause such as "DriveType=3". Read-only.

Inputs: class_name (required), limit, filter, namespace, +1 more. Returns: result

convert_dataA

Convert between json, yaml, toml, csv, xml and psd1 (PowerShell data files). Provide either path or text. from_format is inferred from the file extension when omitted. With output_path the result is written there; otherwise it is returned. Reading psd1 needs pwsh (restricted language mode, no code execution); XML uses @attr / #text conventions.

Inputs: to_format (required), path, text, from_format, +1 more. Returns: result

create_fileC

Create a file (and parent dirs) with the given content.

Inputs: path (required), content (required), overwrite. Returns: result

delete_fileC

Delete a file or directory (recursively).

Inputs: path (required). Returns: result

dns_lookupB

Resolve a hostname to its addresses using the system resolver.

Inputs: host (required). Returns: result

edit_fileA

Replace old_string with new_string in a file. Fails unless it matches exactly once (or replace_all is true). dry_run=true returns a unified diff without writing.

Inputs: path (required), new_string (required), old_string (required), dry_run, +1 more. Returns: result

edit_file_multiA

Apply several edits atomically. Each edit is {"old_string": ..., "new_string": ..., optional "replace_all": "true"}. Edits apply in order; if any fails nothing is written.

Inputs: path (required), edits (required), dry_run. Returns: result

environment_variablesB

Environment variables of the server process. Values of secret-looking names are redacted.

Inputs: name_contains. Returns: result

format_codeB

Format Python code with ruff; check_only=True only reports.

Inputs: cwd, path, check_only. Returns: result

get_tool_docsA

Return usage docs and examples for a tool group (filesystem, search, shell, git, python, web-fetch, data-conversion, api-testing, diagnostics) or 'index'. Call this after a tool error.

Inputs: topic. Returns: result

git_addC

Stage files.

Inputs: paths (required), repo. Returns: result

git_blameA

Show who last changed each line of a file (read-only). Optionally limit to a line range.

Inputs: path (required), repo, end_line, start_line. Returns: result

git_branch_createB

Create a new branch (and optionally switch to it). Never overwrites an existing branch.

Inputs: name (required), repo, switch, start_point. Returns: result

git_branch_listA

List branches with their last commit and upstream (read-only). all_branches includes remotes.

Inputs: repo, all_branches. Returns: result

git_commitC

Commit staged changes.

Inputs: message (required), repo. Returns: result

git_diffB

Show git diff (optionally staged and/or for one path).

Inputs: path, repo, staged. Returns: result

git_diff_statC

Summarise changes per file (read-only), compact for large diffs. base compares with a revision.

Inputs: base, repo, staged. Returns: result

git_initA

Initialise a new git repository (non-destructive; existing repos are left intact).

Inputs: repo, initial_branch. Returns: result

git_logC

Show recent commits.

Inputs: repo, max_count. Returns: result

git_pushB

Push a branch (default: current) to a remote. Force pushes and ref deletion are not supported. tags=true pushes tags instead of a branch.

Inputs: repo, tags, branch, remote, +1 more. Returns: result

git_remote_infoA

List configured remotes and their URLs (read-only). Credentials in URLs are masked.

Inputs: repo. Returns: result

git_showB

Show a commit's message and diff (read-only). stat_only=true gives only the file summary.

Inputs: rev, path, repo, stat_only. Returns: result

git_stash_listC

List stashes (read-only).

Inputs: repo. Returns: result

git_statusC

Show short git status.

Inputs: repo. Returns: result

git_switchB

Switch to an existing branch. Git refuses if it would overwrite local changes.

Inputs: name (required), repo. Returns: result

git_tag_createB

Create a tag (annotated if message given). Never moves or replaces an existing tag.

Inputs: name (required), repo, target, message. Returns: result

globA

Find files by glob pattern (e.g. '**/*.py'), relative to path. Skips git-ignored files unless respect_gitignore=false. Use offset to page.

Inputs: pattern (required), path, offset, max_results, +1 more. Returns: result

grepA

Regex search file contents. Returns path:line: text matches. Skips git-ignored files (and .git, node_modules, .venv) unless respect_gitignore=false. Use offset to page.

Inputs: pattern (required), path, offset, file_glob, +3 more. Returns: result

http_requestA

Send an HTTP request (localhost and private addresses allowed) and report status, timing, redacted headers and a truncated body. Optional assertions: expect_status, expect_json ({"data.items[0].id": 5} path->value), expect_contains (substring of body), max_latency_ms. Result begins with PASS/FAIL when assertions are given.

Inputs: url (required), body, method, params, +9 more. Returns: result

lintC

Lint Python code with ruff (optionally auto-fix).

Inputs: cwd, fix, path. Returns: result

list_dirB

List directory entries; directories end with '/'.

Inputs: path. Returns: result

list_portsB

List TCP/UDP sockets with owning process. May need elevated rights to see other users' PIDs.

Inputs: protocol, listening_only. Returns: result

list_processesB

List processes (pid, name, user, cpu%, memory) sorted by 'memory' or 'cpu' or 'pid'.

Inputs: limit, sort_by, name_contains. Returns: result

list_servicesA

List services (systemd on Linux, Get-Service on Windows, launchctl on macOS). state: e.g. running / stopped (Windows) or running / failed / active (systemd).

Inputs: state, name_contains. Returns: result

markdown_to_scriptB

Extract code from a markdown file. Only fenced blocks tagged with language are kept unless extract_all_blocks is true. Prose becomes comments when include_prose is true.

Inputs: path (required), language (required), output_path, include_prose, +1 more. Returns: result

network_interfacesA

NIC configuration: addresses, netmask, MAC, MTU, speed and up/down state, plus I/O counters.

Inputs: no named parameters. Returns: result

pandoc_convertB

Convert documents with pandoc (docx, html, md, rst, pdf output needs a LaTeX engine...). Runs in --sandbox mode so the document cannot read other files or run filters.

Inputs: input_path (required), output_path (required), to_format, from_format. Returns: result

policy_infoA

Show the active policy file path and contents, and where the audit log is written.

Inputs: no named parameters. Returns: result

query_dataA

Extract values from a json/yaml/toml/csv/xml/psd1 file with a path expression such as 'servers[0].name', 'users[].email' or '.port'. Returns JSON (a list of matches).

Inputs: path (required), query (required), format. Returns: result

read_event_logsB

Recent system events. Linux: journalctl (source = unit name, level = minimum severity). Windows: Get-WinEvent (source = log name, default 'System'; level = exact level). macOS: log show. level: critical|error|warning|info|verbose.

Inputs: level, source, contains, max_entries, +1 more. Returns: result

read_fileA

Read a text file. Lines are 1-based and inclusive; end_line=-1 reads to EOF.

Inputs: path (required), end_line, start_line. Returns: result

replace_linesB

Replace lines start_line..end_line (1-based, inclusive) with new_text. Use end_line=start_line-1 to insert before start_line.

Inputs: path (required), end_line (required), new_text (required), start_line (required), +1 more. Returns: result

run_commandA

Run a shell command and return exit code, stdout and stderr. The working directory must be inside the workspace (default: the workspace root). The command itself is NOT confined: a shell can still reach any path the server's user can.

Inputs: command (required), cwd, timeout. Returns: result

run_testsC

Run pytest on a path with optional extra args (e.g. '-k name -x').

Inputs: cwd, args, path. Returns: result

script_to_markdownB

Convert a script into literate markdown: column-0 comment blocks (and PowerShell <# #> blocks) become prose, everything else becomes fenced code. Heuristic and meant for documentation; markdown_to_script reverses it.

Inputs: path (required), language, output_path. Returns: result

service_statusC

Detailed status of one service.

Inputs: name (required). Returns: result

system_infoB

OS, kernel, uptime, CPU, load, memory, swap and disk usage for this machine.

Inputs: no named parameters. Returns: result

todo_addA

Add a todo (status pending) to the workspace scratchpad and return the updated list.

Inputs: text (required). Returns: result

todo_listA

List the workspace todo scratchpad. Optional status filter: pending, in_progress, done.

Inputs: status. Returns: result

todo_removeA

Remove one todo from the scratchpad by id; returns the updated list.

Inputs: id (required). Returns: result

todo_updateA

Change a todo's status (pending, in_progress, done) and/or text; returns the updated list.

Inputs: id (required), text, status. Returns: result

web_fetchA

Download a web page (after explicit user confirmation), sanitise it and save it as a local markdown file. Returns the file path only; read the file to consume it. Executable/archive/ document extensions and non-text content types are rejected; scripts, forms, iframes, event handlers and unsafe links are stripped. Private/loopback addresses are blocked.

Inputs: url (required), output_dir. Returns: result

workspace_infoA

Show the workspace root this server is confined to and whether confinement is active.

Inputs: no named parameters. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 56 tools

    Disambiguation4/5

    Most tools target clearly different resources or actions, and the domain grouping helps agents choose correctly. There is some overlap among file-editing tools (edit_file, edit_file_multi, replace_lines) and between run_command and specialized tools like run_tests/lint/format_code, but descriptions make the intended boundaries reasonably clear.

    Naming Consistency3/5

    All names use snake_case and several domain prefixes (git_, todo_) provide useful grouping. However, the naming pattern mixes verb_noun (read_file, list_dir), noun_verb/prefix_noun_verb (git_branch_create, todo_add), and bare nouns (grep, glob, lint), so it is readable but not fully predictable.

    Tool Count2/5

    56 tools is far above the typical well-scoped range and is likely to burden tool selection and context use. The tools are grouped sensibly by domain, but the overall surface is too large for a single MCP server.

    Completeness4/5

    The server covers a very broad coding-assistant surface: filesystem operations, git workflows, Python lint/test/format, web/API testing, diagnostics, data conversion, todos, and policy/audit helpers. Some lifecycle operations are missing or indirect, such as git pull/fetch/merge/clone and file move/copy, but run_command provides a fallback for many gaps.

    Maintenance

    ActivityNo data
    ResponsivenessSyncing

    Related MCP Connectors

    Related MCP Servers