Content
#+TITLE: MCP.el - Model Context Protocol for Emacs
[[https://www.gnu.org/licenses/gpl-3.0][https://img.shields.io/badge/License-GPLv3-blue.svg]]
This is an Emacs client for interfacing with [[https://modelcontextprotocol.io/introduction][MCP]], supporting connections to MCP servers.
* Features
- Structured communication with MCP servers
- Support for filesystem and generic MCP servers
- Extensible tool and prompt system
- Asynchronous and synchronous operations
- Resource management capabilities
- Intuitive interface for managing server lifecycle (start/stop/restart)
- Integration with popular Emacs packages (e.g., gptel, llm)
* Installation
1. Install Emacs 30 or higher version
2. Add the following code to your configuration file ~/.emacs:
#+begin_src elisp
(add-to-list 'load-path "<path-to-mcp.el>")
(require 'mcp-hub)
#+end_src
* Usage
** Mcp hub
*** Configuring MCP Servers
#+begin_src elisp
(setq mcp-hub-servers
'(("filesystem" . (:command "npx" :args ("-y" "@modelcontextprotocol/server-filesystem" "/home/lizqwer/MyProject/")))
("fetch" . (:command "uvx" :args ("mcp-server-fetch")))
("qdrant" . (:url "http://localhost:8000/sse"))
("graphilit" . (
:command "npx"
:args ("-y" "graphlit-mcp-server")
:env (
:GRAPHLIT_ORGANIZATION_ID "your-organization-id"
:GRAPHLIT_ENVIRONMENT_ID "your-environment-id"
:GRAPHLIT_JWT_SECRET "your-jwt-secret")))))
#+end_src
You can use =mcp-hub-start-all-server= to start all MCP servers, such as launching all MCP servers after starting Emacs.
#+begin_src elisp
(add-hook 'after-init-hook
#'mcp-hub-start-all-server)
#+end_src
*** Managing MCP Servers
Use =mcp-hub= to launch the server management interface, which will automatically start all configured MCP servers.
#+html: <img src="https://github.com/user-attachments/assets/31877c0b-d71a-491d-9830-2afed8d6218a" alt="mcp-hub"/>
*** Keymap
| key | function | description |
|-----+----------------------------+-----------------------------------------|
| l | mcp-hub-view-log | View server logs |
| s | mcp-hub-start-server | Start server under cursor |
| k | mcp-hub-close-server | Stop server under cursor |
| r | mcp-hub-restart-server | Restart server under cursor |
| S | mcp-hub-start-all-server | Start all configured servers |
| R | mcp-hub-restart-all-server | Restart all configured servers |
| K | mcp-hub-close-all-server | Stop all running servers |
*** use with [[https://github.com/karthink/gptel][gptel]]
A function for registering all MCP tools.
#+begin_src elisp
(defun gptel-mcp-register-tool ()
(interactive)
(let ((tools (mcp-hub-get-all-tool :asyncp t :categoryp t)))
(mapcar #'(lambda (tool)
(apply #'gptel-make-tool
tool))
tools)))
#+end_src
Activate all MCP tools using gptel.
#+begin_src elisp
(defun gptel-mcp-use-tool ()
(interactive)
(let ((tools (mcp-hub-get-all-tool :asyncp t :categoryp t)))
(mapcar #'(lambda (tool)
(let ((path (list (plist-get tool :category)
(plist-get tool :name))))
(push (gptel-get-tool path)
gptel-tools)))
tools)))
#+end_src
Request gptel to cease using all mcp tools.
#+begin_src elisp
(defun gptel-mcp-close-use-tool ()
(interactive)
(let ((tools (mcp-hub-get-all-tool :asyncp t :categoryp t)))
(mapcar #'(lambda (tool)
(let ((path (list (plist-get tool :category)
(plist-get tool :name))))
(setq gptel-tools
(cl-remove-if #'(lambda (tool)
(equal path
(list (gptel-tool-category tool)
(gptel-tool-name tool))))
gptel-tools))))
tools)))
#+end_src
** Example [[https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem][filesystem]] server.
*** Establish the connection first.
#+begin_src elisp
(mcp-connect-server "filesystem" :command "npx" :args '("-y" "@modelcontextprotocol/server-filesystem" "~/Downloads/")
:initial-callback
#'(lambda (connection)
(message "%s connection" (jsonrpc-name connection)))
:tools-callback
#'(lambda (connection tools)
(message "%s tools: %s" (jsonrpc-name connection) tools))
:prompts-callback
#'(lambda (connection prompts)
(message "%s prompts: %s" (jsonrpc-name connection) prompts))
:resources-callback
#'(lambda (connection resources)
(message "%s resources: %s" (jsonrpc-name connection) resources)))
#+end_src
*** Define the use of tools.
The current text is being tested using the [[https://github.com/karthink/gptel/issues/514][gptel tool]] branch.Use =mcp-make-text-tool= to create standard tool call data ([[https://github.com/ahyatt/llm/discussions/124][Discussions]]).It is recommended to create tools within the tools-callback or wait for the mcp connect server to complete.
#+begin_src elisp
(mcp-make-text-tool "filesystem" "write_file")
#+end_src
This will generate a data structure where the function is an auto-generated synchronous or asynchronous lambda function for accessing the MCP server.
#+begin_src elisp
(list :function #'(lambda (&rest args)
;; Synchronous or asynchronous access to the MCP server's Lambda function.
)
:name "write_file"
:async nil
:description "Create a new file or completely overwrite an existing file with new content. Use with caution as it will overwrite existing files without warning. Handles text content with proper encoding. Only works within allowed directories."
:args ((:type "string" :name "path" :description "path")
(:type "string" :name "content" :description "content"))
:category "files")
#+end_src
*** Disconnect from the server.
#+begin_src elisp
(mcp-stop-server "filesystem")
#+end_src
*** Manual function call
**** Synchronize
#+begin_src elisp
(let ((connection (gethash "filesystem" mcp-server-connections)))
(mcp-call-tool "write_file" '(:path "filename or file path" :content "the file content")))
#+end_src
**** Asynchronous
#+begin_src elisp
(let ((connection (gethash "filesystem" mcp-server-connections)))
(mcp-async-call-tool connection
"write_file"
'(:path "filename or file path" :content "the file content")
#'(lambda (res)
;; handle res
(mcp--parse-tool-call-result res))
#'(lambda (code message)
;; handle error
(format "call %s tool error with %s: %s"
tool-name
code
message))))
#+end_src
*** Manual get prompt
Since the [[https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem][filesystem]] lacks prompts, the [[https://github.com/modelcontextprotocol/servers/tree/main/src/everything][everything]] server is used for demonstration.
**** Synchronize
#+begin_src elisp
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-get-prompt connection "complex_prompt" '(:temperature "1.0")))
#+end_src
**** Asynchronous
#+begin_src elisp
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-async-get-prompt connection
"complex_prompt"
'(:temperature "1.0")
#'(lambda (res)
(message "prompt: %s" res))
#'(lambda (code message)
(message "error call: %s, %s" code message))))
#+end_src
*** Manual get resources
Since the [[https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem][filesystem]] lacks resources, the [[https://github.com/modelcontextprotocol/servers/tree/main/src/everything][everything]] server is used for demonstration.
**** Synchronize
#+begin_src elisp
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-read-resource connection "test://static/resource/1"))
#+end_src
**** Asynchronous
#+begin_src elisp
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-async-read-resource connection "test://static/resource/1"
#'(lambda (resource)
(message "res: %s" resource))))
#+end_src
*** Get resource templates
Since the [[https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem][filesystem]] lacks resources, the [[https://github.com/modelcontextprotocol/servers/tree/main/src/everything][everything]] server is used for demonstration.
#+begin_src elisp
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-async-list-resource-templates connection
#'(lambda (connection templates)
(message "%s" templates))))
#+end_src
* Roadmap
- [X] HTTP SSE based MCP server connections
- [ ] mcp marketplace (browser and auto install mcp server)
- [ ] Simplified integration with other Emacs AI clients
- [ ] Expanded documentation
- [ ] Full MCP protocol client implementation
* License
This project is licensed under the GNU General Public License v3.0 - see the [[file:LICENSE][LICENSE]] file for details.