{"id":50289142,"url":"https://github.com/cj-price/emacs-devtools-mcp","last_synced_at":"2026-05-28T04:33:17.098Z","repository":{"id":359148172,"uuid":"1221640905","full_name":"cj-price/emacs-devtools-mcp","owner":"cj-price","description":"An MCP server that runs inside Emacs and lets coding agents introspect frames, buffers, faces, keymaps, hooks, and your init.el — the chrome-devtools-mcp loop, for Emacs.","archived":false,"fork":false,"pushed_at":"2026-05-20T15:20:13.000Z","size":101,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"trunk","last_synced_at":"2026-05-20T20:30:56.123Z","etag":null,"topics":["agent","claude-code","devtools","elisp","emacs","mcp","model-context-protocol"],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cj-price.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-26T13:46:30.000Z","updated_at":"2026-05-20T15:18:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cj-price/emacs-devtools-mcp","commit_stats":null,"previous_names":["cj-price/emacs-devtools-mcp"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/cj-price/emacs-devtools-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cj-price%2Femacs-devtools-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cj-price%2Femacs-devtools-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cj-price%2Femacs-devtools-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cj-price%2Femacs-devtools-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cj-price","download_url":"https://codeload.github.com/cj-price/emacs-devtools-mcp/tar.gz/refs/heads/trunk","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cj-price%2Femacs-devtools-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33594851,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","claude-code","devtools","elisp","emacs","mcp","model-context-protocol"],"created_at":"2026-05-28T04:33:16.235Z","updated_at":"2026-05-28T04:33:16.971Z","avatar_url":"https://github.com/cj-price.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# emacs-devtools-mcp\n\n[![CI](https://github.com/cj-price/emacs-devtools-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/cj-price/emacs-devtools-mcp/actions/workflows/ci.yml)\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server that runs\n*inside* Emacs and lets a coding agent introspect the running session: frames,\nwindows, buffers, faces, keymaps, hooks, advice, the message log, the\n*Warnings* buffer, and your `init.el`. The same loop\n[`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp)\ngives an agent for the browser, but for Emacs.\n\n## What you can ask an agent to do\n\n- *\"My modeline shows the wrong color for unsaved buffers — verify it.\"*\n  Agent calls `screenshot_frame`, `face-at`, `color_contrast`.\n- *\"My init.el started taking 4 seconds to load yesterday. Find the culprit.\"*\n  Agent calls `bisect_init` or `startup_profile`.\n- *\"Why is `C-c C-c` running the wrong command in this buffer?\"*\n  Agent calls `lookup_key`, `where_is`, `key_translation_trace`.\n- *\"Reproduce this bug, capture the backtrace, and tell me which advice\n  swallowed the error.\"* Agent calls `eval_elisp` and `capture_backtrace`.\n- *\"Run the package's ERT suite and summarize.\"* Agent calls `ert_run`.\n\n## Installing\n\nYou need: Emacs ≥30.1, `socat`, and `jq`. Everything except\n`screenshot_frame` works in a TTY or headless daemon; only the screenshot\ntool needs a graphical Emacs build (for `x-export-frames`). `xvfb-run`\nlets you run `make test-gui` and lets `spawn_emacs` start a screenshot-\ncapable subordinate daemon via `display_mode: \"xvfb-run\"`. The repo ships\na `shell.nix` that pins all of those.\n\n```sh\ngit clone https://github.com/cj-price/emacs-devtools-mcp.git\ncd emacs-devtools-mcp\nnix-shell --run 'make all'   # byte-compile + ERT + checkdoc\n```\n\nTo make the server part of your Emacs:\n\n```elisp\n(add-to-list 'load-path \"/path/to/emacs-devtools-mcp/lisp\")\n(require 'emacs-devtools-mcp)\n(require 'emacs-devtools-mcp-server)\n(emacs-devtools-mcp-server-start)   ;; binds the Unix socket\n;; (emacs-devtools-mcp-server-stop) to shut it down.\n```\n\n### Verify it works\n\nEnd-to-end smoke against a fresh subordinate Emacs (no host attach\nrequired) — should end with `summary: PASS=N FAIL=0`:\n\n```sh\nnix-shell --run 'make test-mcp'\n```\n\n### Wire it into your agent\n\nThe relay (`bin/emacs-devtools-mcp`) is a standard MCP stdio server, so\nany MCP-compatible client works (Claude Code, Codex, Cline, Continue,\nGoose, …). For Claude Code, copy `.mcp.json.example` and point it at the\nabsolute path of `bin/emacs-devtools-mcp`:\n\n```json\n{\n  \"mcpServers\": {\n    \"emacs-devtools\": {\n      \"command\": \"/abs/path/to/emacs-devtools-mcp/bin/emacs-devtools-mcp\",\n      \"env\": { \"EDMCP_NAME\": \"default\" }\n    }\n  }\n}\n```\n\nOther clients use the same `command` + `env` shape under their own\nconfiguration key.\n\n## Tool catalog\n\nAll tools accept an optional `target` (`{\"host\": true}` default or\n`{\"spawn\": \"\u003chandle\u003e\"}`). List-shaped tools take an optional `cursor` and\nreturn `next_cursor` when more results remain. JSON keys are `snake_case`.\n\n### Server / smoke\n\n| Tool | Description |\n|---|---|\n| `ping` | Echo back `pong` plus an optional message. Useful for the agent's first call. |\n\n### Spawn\n\n| Tool | Description |\n|---|---|\n| `spawn_emacs` | Start an `emacs -Q --bg-daemon` subordinate; optionally load an init file under the allowlist. `display_mode` is one of `\"host-inherit\"` (default; daemon inherits the host's `DISPLAY` and `WAYLAND_DISPLAY` — typically what an interactive Emacs has, headless under TTY hosts), `\"none\"` (scrubs both for a guaranteed-headless spawn even when the host has a display), or `\"xvfb-run\"` (wraps the launch in `xvfb-run -a` so `screenshot_frame` works against the spawn). Returns `{handle, server_name, pid, display_mode, idle_seconds, expires_at, attached}`. |\n| `attach_emacs` | Register a daemon you started yourself, by `server_name`. |\n| `list_handles` | Paginated active-handle listing with `idle_seconds` + `expires_at`. |\n| `kill_spawn` | Kill a daemon by handle and forget it. Named `kill_spawn` (not `kill_emacs`) so it cannot be misread as a request to terminate the host. |\n\n### Eval / debug\n\n| Tool | Description |\n|---|---|\n| `eval_elisp` | Evaluate FORM in `target` and return its printed value plus the *Messages* delta and any error. |\n| `edebug_instrument` / `edebug_uninstrument` | Mark a function for `edebug` stepping; restore. |\n| `capture_backtrace` | Evaluate FORM and return the backtrace at signal time, tail-truncated and redacted. |\n| `trace_function` / `untrace_function` / `trace_log` | `trace-function-foreground` round-trip, with paginated log readout. |\n\n### Buffer / state\n\n| Tool | Description |\n|---|---|\n| `list_buffers` | Optionally filtered by regex. |\n| `buffer_state` | Point/mark/mode/file metadata for a buffer. |\n| `buffer_substring` | Up to `max_bytes` of a buffer between `start` and `end`; sets `truncated: true` and `next_offset` when capped. |\n| `list_messages` | Last N lines of *Messages*, redacted. |\n| `list_warnings` | Paragraph-split contents of *Warnings*. |\n| `ert_run` | Run ERT for a selector; return summary. |\n| `describe_hooks` | All bound hooks, or contents of one. |\n\n### Keys\n\n| Tool | Description |\n|---|---|\n| `where_is` | All key sequences bound to a command in a keymap. |\n| `lookup_key` | Command bound to a key sequence. |\n| `describe_keymap` | Flattened binding listing under a keymap, optionally narrowed by prefix. |\n| `simulate_keys` | Run a `kbd` macro and return point/mark/buffer-diff/messages delta. |\n| `key_translation_trace` | Trace through the three keyboard translation maps. |\n\n### GUI\n\n| Tool | Description |\n|---|---|\n| `screenshot_frame` | Export FRAME as a base64 PNG MCP image content block via `x-export-frames` (graphical Emacs builds only; errors on builds where the probe fails). |\n| `get_frame_tree` | frames → windows → buffer metadata. |\n| `face_at` | Face at a 1-based line + 0-based column in a buffer. |\n| `describe_face` | Inheritance-resolved attributes plus docstring. |\n| `list_faces` | Alphabetical face listing, optionally filtered. |\n| `color_contrast` | WCAG contrast ratio between two colors. |\n\n### Init / startup\n\n| Tool | Description |\n|---|---|\n| `init_lint` | Byte-compile FILE in `emacs -Q --batch`; return diagnostics. Allowlist-checked; output redacted. |\n| `startup_profile` | CPU-profile FILE; return elapsed seconds plus the top 50 hotspot frames (redacted). |\n| `bisect_init` | Binary-search FILE for the form that triggers a predicate; returns `{culprit_form, line_start, line_end, probes, load_failed_probes}`. |\n\n## Configuration\n\nEvery user-facing knob is a `defcustom` under the parent\n`emacs-devtools-mcp` group or one of its four subgroups: `-server`,\n`-spawn`, `-tools`, `-security`. The ones you are most likely to touch:\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `emacs-devtools-mcp-server-name` | `\"default\"` | Socket name segment under `${XDG_RUNTIME_DIR}/edmcp/`. |\n| `emacs-devtools-mcp-init-allowlist` | `~/.config/emacs`, `~/.emacs.d`, project root | Paths agents may pass to `bisect_init` / `init_lint` / `startup_profile` / `spawn_emacs`. |\n| `emacs-devtools-mcp-max-response-bytes` | `262144` | Global hard cap on per-call payload (text). |\n| `emacs-devtools-mcp-max-image-response-bytes` | `8388608` | Cap for responses that include an MCP `image` block. |\n| `emacs-devtools-mcp-screenshot-max-pixels` | `2560×1600` | Refuse oversize frames. |\n| `emacs-devtools-mcp-spawn-idle-timeout` | `1800` | Reaper kills idle handles after this many seconds. |\n| `emacs-devtools-mcp-spawn-max-handles` | `4` | Cap on simultaneous subordinate daemons. |\n| `emacs-devtools-mcp-spawn-default-display-mode` | `host-inherit` | Default `display_mode` when `spawn_emacs` omits the field. |\n| `emacs-devtools-mcp-spawn-xvfb-run-program` | `xvfb-run` | Path to `xvfb-run`; used for `display_mode: \"xvfb-run\"` spawns. |\n| `emacs-devtools-mcp-slow-tool-timeout` | `25` | `with-timeout` cap for `:slow` tools. |\n| `emacs-devtools-mcp-bisect-max-probes` | `32` | Hard cap on `bisect_init` iterations. |\n| `emacs-devtools-mcp-init-batch-timeout` | `30` | Per-probe timeout in `emacs --batch`. |\n| `emacs-devtools-mcp-redact-extra-regexps` | `nil` | Additional patterns to scrub from any *Messages* / backtrace output. |\n\n`M-x customize-group RET emacs-devtools-mcp RET` walks the full set.\n\n## Security model\n\nThis is a developer tool for your own Emacs session. It is intentionally **not\nsandboxed** — `eval_elisp` runs Lisp with your full user privileges, and\n`bisect_init` / `startup_profile` execute arbitrary code from your real\n`init.el` in subordinate `emacs -Q --batch` subprocesses that inherit your\n`$HOME`. Treat it accordingly: only register it with agents you trust to act\non your behalf, and review what they're about to do before approving\ndestructive tool calls.\n\n## License\n\nGPL-3.0-or-later. Full text in `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcj-price%2Femacs-devtools-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcj-price%2Femacs-devtools-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcj-price%2Femacs-devtools-mcp/lists"}