Content
#+TITLE: mcp.el - Model Context Protocol for Emacs Lisp
[[https://github.com/laurynas-biveinis/mcp.el/actions/workflows/elisp-test.yml][https://github.com/laurynas-biveinis/mcp.el/actions/workflows/elisp-test.yml/badge.svg]]
[[https://github.com/laurynas-biveinis/mcp.el/actions/workflows/linter.yml][https://github.com/laurynas-biveinis/mcp.el/actions/workflows/linter.yml/badge.svg]]
* Overview
=mcp.el= is an Emacs Lisp implementation of the [[https://modelcontextprotocol.io/][Model Context Protocol]] (MCP), an open standard for communication between AI applications, servers, and language models.
This library enables Emacs users and developers to create MCP servers that can provide context, tools, resources, and prompts to Large Language Models (LLMs).
* Features
- MCP server creation and management in Emacs
- API for tools to enable LLMs to execute Elisp functions
- Uses MCP stdio transport layer through an emacsclient-wrapping script
* Installation
=M-x package-vc-install https://github.com/laurynas-biveinis/mcp.el/=
* Usage
Start Emacs daemon, then do =M-x mcp-start= to start handling MCP requests and =M-x
mcp-stop= to stop.
For an MCP server example that uses this package, see [[https://github.com/laurynas-biveinis/elisp-mcp-dev][elisp-mcp-dev]].
To register with an MCP client, use the provided =emacs-mcp-stdio.sh= script as the
server with the arguments to initialize and to clean up specific MCP functionality,
for example, to register [[https://github.com/laurynas-biveinis/elisp-mcp-dev][elisp-mcp-dev]] with Claude Code, do
#+BEGIN_EXAMPLE
claude mcp add -s user -t stdio elisp-dev -- $HOME/.emacs.d/elpa/mcp.el/emacs-mcp-stdio.sh --init-function=elisp-dev-mcp-enable --stop-function=elisp-dev-mcp-disable
#+END_EXAMPLE
The script internally communicates with Emacs daemon through =emacsclient= and
handles serialization and deserialization so that arbitrary data survives transport.
** =emacs-mcp-stdio.sh= Options and Environment Variables
All options and environment variables are optional, although ~--init-function~ and
~--stop-function~ are very likely to be needed in order for the script to be useful.
*Script Options:*
| Option | Description |
|---------------------+--------------------------------------------------------|
| --init-function=NAME | Emacs function to call for MCP initialization |
| --stop-function=NAME | Emacs function to call when the script exits |
| --socket=PATH | Emacs server socket to connect to |
*Environment Variables:*
| Variable | Description |
|---------------------+--------------------------------------------------------|
| EMACS_MCP_DEBUG_LOG | Path to a log file for debug logging |
When =EMACS_MCP_DEBUG_LOG= is set to a valid file path, the script will log all requests and responses with timestamps for debugging purposes. If the path is invalid or not writable, the script will exit with an error.
** Tracing
The package provides an Elisp variable =mcp-log-io= that, when set to non-nil, enables logging of all JSON-RPC messages to the =*mcp-log*= buffer. This is useful for tracing MCP requests and responses:
#+begin_src elisp
;; Enable MCP communication tracing
(setq mcp-log-io t)
;; Start the MCP server
(mcp-start)
#+end_src
The =*mcp-log*= buffer will show incoming requests with ="-> (request)"= prefix and outgoing responses with ="<- (response)"= prefix, along with the full JSON message content. This makes it easy to inspect the communication flow between clients and the MCP server.
* API for dependent packages
=mcp.el= provides a simple API for setting up and using the Model Context Protocol in Emacs.
** Core Functions
#+begin_src elisp
(mcp-register-tool #'tool-handler-function
:id "tool-name"
:description "Tool description"
:title "User-friendly Tool Name")
#+end_src
** Tool Handlers
Tool handler functions should:
- Return an Elisp string for successful execution
- Call ~mcp-tool-throw~ for error returns
*** Tool Parameter Documentation
When registering tools with MCP, you can provide parameter descriptions that will be included in the JSON Schema.
Add an "MCP Parameters:" section to your handler function's docstring. This should be at the end of the docstring with each parameter in the format `parameter-name - description`:
#+begin_src elisp
(defun my-weather-handler (location)
"Get weather information for a LOCATION.
MCP Parameters:
location - city, address, or coordinates"
;; Function implementation...
)
(mcp-register-tool #'my-weather-handler
:id "get-weather"
:description "Get weather information")
#+end_src
Note: Do not include the "MCP Parameters:" section if your tool handler doesn't take any arguments.
*** Optional Tool Properties
When registering tools, you can provide optional properties:
| Property | Description | Example |
|--------------+--------------------------------------------------+--------------------------|
| :title | User-friendly display name for the tool | "Get Weather Forecast" |
| :read-only | If true, indicates tool doesn't modify environment | t or nil |
Example with optional properties:
#+begin_src elisp
(mcp-register-tool #'my-weather-handler
:id "get-weather"
:description "Get weather information"
:title "Weather Forecast"
:read-only t)
#+end_src
*** Utility Functions
MCP provides utility functions for creating standard JSON-RPC requests:
#+begin_src elisp
(defun mcp-create-tools-list-request (&optional id)
"Create a tools/list JSON-RPC request with optional ID.
If ID is not provided, it defaults to 1.")
(defun mcp-create-tools-call-request (tool-name &optional id args)
"Create a tools/call JSON-RPC request for TOOL-NAME with optional ID and ARGS.
TOOL-NAME is the registered identifier of the tool to call.
ID is the JSON-RPC request ID, defaults to 1 if not provided.
ARGS is an association list of arguments to pass to the tool.")
#+end_src
Example of tool call request with arguments:
#+begin_src elisp
;; Call a tool with arguments
(mcp-create-tools-call-request
"get-weather"
42
'(("location" . "New York")))
#+end_src
This is primarily useful for writing tests in the packages using mcp.el.
* Internals
** Transport
The main entry point for MCP communication is =mcp-process-jsonrpc=:
#+begin_src elisp
(defun mcp-process-jsonrpc (json-string)
"Process a JSON-RPC message JSON-STRING and return the response.
This is the main entry point for stdio transport in MCP.")
#+end_src
This is what =emacs-mcp-stdio.sh= calls through emacsclient.
* License
This project is licensed under the GNU General Public License v3.0 (GPLv3) - see the LICENSE file for details.
* Acknowledgments
- [[https://modelcontextprotocol.io/][Model Context Protocol]] specification
- [[https://github.com/modelcontextprotocol/python-sdk][Python MCP SDK]] implementation
You Might Also Like
Ollama
Ollama enables easy access to large language models on various platforms.

n8n
n8n is a secure workflow automation platform for technical teams with 400+...
OpenWebUI
Open WebUI is an extensible web interface for customizable applications.

Dify
Dify is a platform for AI workflows, enabling file uploads and self-hosting.

Zed
Zed is a high-performance multiplayer code editor from the creators of Atom.
MarkItDown MCP
markitdown-mcp is a lightweight MCP server for converting various URIs to Markdown.