Connect Large Language Models directly to your Emacs environment! This MCP (Model Context Protocol) server exposes Emacs functionality through standardized tools, allowing LLMs like Claude to read and modify your buffers, execute elisp code, navigate files, and much more.
Installation: Place the files in your Emacs configuration directory and add to your config:
(add-to-list 'load-path "~/path-to/mcp-server")
(require 'mcp-server)
Alternatively, use package managers:
;; Using straight.el
(use-package mcp-server
:straight (:type git :host github :repo "rhblind/emacs-mcp-server"
:files ("*.el" "mcp-wrapper.py" "mcp-wrapper.sh"))
:config
(add-hook 'emacs-startup-hook #'mcp-server-start-unix))
;; Using use-package with manual path
(use-package mcp-server
:load-path "~/path-to/mcp-server"
:config
(add-hook 'emacs-startup-hook #'mcp-server-start-unix))
;; Using Doom Emacs package! macro
(package! mcp-server
:recipe (:type git :host github :repo "rhblind/emacs-mcp-server"
:files ("*.el" "mcp-wrapper.py" "mcp-wrapper.sh")))
Start the server: Run M-x mcp-server-start-unix
in Emacs. The server creates a Unix socket at ~/.config/emacs/.local/cache/emacs-mcp-server.sock
(or similar based on your Emacs configuration).
Connect Claude Desktop: Add this to your Claude Desktop configuration:
{
"mcpServers": {
"emacs": {
"command": "socat",
"args": ["-", "UNIX-CONNECT:~/.config/emacs/.local/cache/emacs-mcp-server.sock"],
"transport": "stdio"
}
}
}
That's it! Claude can now interact with your Emacs session.
Once connected, LLMs can perform powerful operations in your Emacs environment:
Code and Text Manipulation
- Read and write any buffer - get content from files, scratch buffers, terminals
- Execute elisp code - run any Emacs Lisp expression safely
- Navigate and edit - move cursor, insert text, select regions
- Manage files - open, save, create new files and buffers
Emacs Integration
- Run interactive commands - execute any
M-x
command programmatically - Access variables - read and modify Emacs configuration and state
- Window management - get layout information, manage splits
- Major mode operations - work with mode-specific functionality
Example Interactions:
- "Add a docstring to this function" → Claude reads your buffer, analyzes the function, and adds proper documentation
- "Refactor this code to use modern Python" → Claude reads the code, suggests improvements, and can apply changes directly
- "Create a new React component file" → Claude creates the file, adds boilerplate code, and opens it in a buffer
- "Fix the indentation in this buffer" → Claude reads the content and applies proper formatting
Current Tool:
eval-elisp
- Execute arbitrary elisp expressions safely and return the result
What you can do now: With the eval-elisp
tool, you can already accomplish a lot! LLMs can:
- Read any buffer:
(buffer-string)
or(with-current-buffer "filename.txt" (buffer-string))
- Write to buffers:
(insert "text")
or(with-current-buffer "filename.txt" (insert "text"))
- Navigate files:
(find-file "path/to/file")
or(switch-to-buffer "buffer-name")
- Get cursor position:
(point)
or(line-number-at-pos)
- Move cursor:
(goto-char 100)
or(goto-line 50)
- Get selections:
(if (region-active-p) (buffer-substring (region-beginning) (region-end)) "No selection")
- List buffers:
(mapcar #'buffer-name (buffer-list))
- Execute commands:
(call-interactively 'command-name)
or(command-name)
- Access variables:
variable-name
or(setq variable-name value)
Planned tools for future implementation include dedicated functions for buffer management (get-buffer-content
, set-buffer-content
, get-buffer-list
), navigation (get-point
, goto-point
, insert-at-point
), variable access (get-variable
, set-variable
), and window operations (get-window-configuration
).
Socket naming strategies: Choose how sockets are named based on your setup:
(setq mcp-server-socket-name nil) ; Default: emacs-mcp-server.sock
(setq mcp-server-socket-name 'user) ; User-based: emacs-mcp-server-{username}.sock
(setq mcp-server-socket-name 'session) ; Session-based: emacs-mcp-server-{username}-{pid}.sock
(setq mcp-server-socket-name "custom") ; Custom: emacs-mcp-server-custom.sock
Other configuration options:
;; Custom socket directory
(setq mcp-server-socket-directory "~/.config/emacs-mcp/")
;; Auto-start server when Emacs starts
(add-hook 'emacs-startup-hook #'mcp-server-start-unix)
;; Enable debug logging
(setq mcp-server-debug t)
The server implements comprehensive security measures including permission prompts for dangerous operations (file system access, process execution), caches decisions for the session, maintains an audit trail of all operations, validates all tool inputs using JSON Schema, protects against code injection, and limits operations to 30 seconds by default.
Operations requiring explicit permission include file system operations (delete-file
, write-region
), process execution (shell-command
, call-process
), and system functions (kill-emacs
, server-start
).
Server control: M-x mcp-server-start-unix
(start), M-x mcp-server-stop
(stop), M-x mcp-server-restart
(restart), M-x mcp-server-status
(show status)
Debugging and monitoring: M-x mcp-server-toggle-debug
(toggle debug logging), M-x mcp-server-list-clients
(show connected clients), M-x mcp-server-get-socket-path
(show socket path)
Security management: M-x mcp-server-security-show-audit-log
(view security log), M-x mcp-server-security-show-permissions
(view cached permissions)
Using shell wrapper (recommended):
{
"mcpServers": {
"emacs": {
"command": "/path/to/mcp-wrapper.sh",
"args": ["$HOME/.emacs.d/.local/cache/emacs-mcp-server.sock"],
"transport": "stdio"
}
}
}
Using Python wrapper:
{
"mcpServers": {
"emacs": {
"command": "python3",
"args": ["/path/to/mcp-wrapper.py", "$HOME/.emacs.d/.local/cache/emacs-mcp-server.sock"],
"transport": "stdio"
}
}
}
Direct socat (simple but less robust):
{
"mcpServers": {
"emacs": {
"command": "socat",
"args": ["-", "UNIX-CONNECT:$HOME/.emacs.d/.local/cache/emacs-mcp-server.sock"],
"transport": "stdio"
}
}
}
claude mcp add emacs ~/path-to/mcp-wrapper.py ~/.config/emacs/.local/cache/emacs-mcp-server.sock # Uses the python script
claude mcp add emacs ~/path-to/mcp-wrapper.sh ~/.config/emacs/.local/cache/emacs-mcp-server.sock # Uses the bash script
claude mcp add emacs-direct -- socat - UNIX-CONNECT:$HOME/.config/emacs/.local/cache/emacs-mcp-server.sock # Uses socat directly
# Real world, add to user scope so it's always available
claude mcp add emacs --scope user ~/.config/emacs/.local/straight/build-30.1/mcp-server/mcp-wrapper.py ~/.config/emacs/.local/cache/emacs-mcp-server.sock
Shell wrapper (mcp-wrapper.sh
) - Lightweight, uses socat, good for simple integrations:
./mcp-wrapper.sh ~/.emacs.d/.local/cache/emacs-mcp-server.sock
./mcp-wrapper.sh /custom/path/emacs-mcp-server-myinstance.sock
EMACS_MCP_DEBUG=1 ./mcp-wrapper.sh ~/.emacs.d/.local/cache/emacs-mcp-server.sock
Python wrapper (mcp-wrapper.py
) - More robust error handling, better cross-platform support:
./mcp-wrapper.py ~/.emacs.d/.local/cache/emacs-mcp-server.sock
./mcp-wrapper.py --list-sockets
EMACS_MCP_DEBUG=1 ./mcp-wrapper.py ~/.emacs.d/.local/cache/emacs-mcp-server.sock
Environment variables for both wrappers:
EMACS_MCP_TIMEOUT
: Connection timeout in seconds (default: 10)EMACS_MCP_DEBUG
: Enable debug logging
For your own MCP client implementation:
import socket
import json
# Connect to Emacs MCP Server
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("~/.emacs.d/.local/cache/emacs-mcp-server.sock")
# Send initialization
init_msg = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "draft",
"capabilities": {},
"clientInfo": {"name": "my-client", "version": "1.0.0"}
}
}
sock.send((json.dumps(init_msg) + "\n").encode())
response = sock.recv(4096).decode()
print(response)
Quick test: Run ./test/scripts/test-runner.sh
or test a specific socket with ./test/integration/test-unix-socket-fixed.sh ~/.emacs.d/.local/cache/emacs-mcp-server.sock
Adding custom tools:
(mcp-server-tools-register
"my-tool"
"My Custom Tool"
"Description of what this tool does"
'((type . "object")
(properties . ((param . ((type . "string")))))
(required . ["param"]))
(lambda (args)
(let ((param (alist-get 'param args)))
(format "Result: %s" param))))
Server won't start: Check if socket directory exists and is writable, verify no other server is using the same socket path, check Emacs Messages buffer for error details.
Socket not found:
- Check if Emacs MCP Server is running:
M-x mcp-server-status
- Verify socket path:
M-x mcp-server-get-socket-path
- List available sockets:
./mcp-wrapper.sh --list-sockets
Connection refused:
- Check socket permissions:
ls -la ~/.emacs.d/.local/cache/emacs-mcp-server*.sock
- Test socket connectivity:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | socat - UNIX-CONNECT:~/.emacs.d/.local/cache/emacs-mcp-server.sock
MCP client issues:
- Verify client configuration: Check JSON syntax and file paths
- Check client logs: Most MCP clients provide debug logging
- Test wrapper independently: Run wrapper script manually to verify connection
Permission errors: View audit log with M-x mcp-server-security-show-audit-log
, grant specific permissions with (mcp-server-security-grant-permission 'function-name)
, or disable prompting with (mcp-server-security-set-prompting nil)
.
Debug mode: Enable with (setq mcp-server-debug t)
or toggle with M-x mcp-server-toggle-debug
.
Multiple Emacs instances:
;; Instance 1
(setq mcp-server-socket-name "instance-1")
(mcp-server-start-unix)
;; Instance 2
(setq mcp-server-socket-name "instance-2")
(mcp-server-start-unix)
Dynamic socket naming:
(setq mcp-server-socket-name
(lambda ()
(format "emacs-%s-%d" (system-name) (emacs-pid))))
Performance tips:
- Use predictable naming to avoid socket discovery overhead
- Keep connections alive and reuse when possible
- Monitor clients with
M-x mcp-server-list-clients
- Stop unused servers to free resources
The server uses a modular design with a transport layer (Unix domain sockets, TCP planned), protocol layer (JSON-RPC 2.0 message handling), tool registry (Elisp functions exposed as MCP tools), and security layer (permission management and validation).
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ LLM Client │────│ Unix Socket │────│ Emacs MCP │
│ (Claude/Python) │ │ Transport │ │ Server │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
┌──────────────────┐
│ MCP Tools │
│ (Elisp Funcs) │
└──────────────────┘
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.