This is an Emacs client for interfacing with MCP, supporting connections to MCP servers.
- 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)
Need Emacs 30 or higher version
(use-package mcp
:ensure t
:after gptel
:custom (mcp-hub-servers
`(("filesystem" . (:command "npx"
:args ("-y" "@modelcontextprotocol/server-filesystem")
:roots ("/home/lizqwer/MyProject/")))
("fetch" . (:command "uvx" :args ("mcp-server-fetch")))
("qdrant" . (:url "http://localhost:8000/sse"))
("graphlit" . (
: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")))))
:config (require 'mcp-hub)
:hook (after-init . mcp-hub-start-all-server))(setq mcp-hub-servers
'(("filesystem" . (:command "npx"
:args ("-y" "@modelcontextprotocol/server-filesystem")
:roots ("/home/lizqwer/MyProject/")))
("fetch" . (:command "uvx" :args ("mcp-server-fetch")))
("qdrant" . (:url "http://localhost:8000/sse"))
("graphlit" . (
: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")))))You can use mcp-hub-start-all-server to start all MCP servers, such as launching all MCP servers after starting Emacs.
(add-hook 'after-init-hook
#'mcp-hub-start-all-server)Use mcp-hub to launch the server management interface, which will automatically start all configured MCP servers.
| 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 |
| + | mcp-hub-add-root | Add root directory to server |
| - | mcp-hub-remove-root | Remove root directory from server |
| = | mcp-hub-view-roots | View server’s root directories |
use with gptel
- For
gptelintegration, See the gptel mcp for details. - gptel-mcp.el is interface integration for gptel.el and mcp.el
Example filesystem server.
(mcp-connect-server "filesystem"
:command "npx"
:args '("-y" "@modelcontextprotocol/server-filesystem")
:roots '("~/Downloads/" "~/Documents/")
: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)))The filesystem server uses the MCP roots protocol to determine which directories it can access. Instead of passing directories as command-line arguments, you can specify them using the :roots parameter:
(mcp-connect-server "filesystem"
:command "npx"
:args '("-y" "@modelcontextprotocol/server-filesystem")
:roots '("/home/user/project1" "/home/user/project2"))You can also dynamically manage roots after the server is connected:
;; Add a new root directory
(mcp-add-root "filesystem" "/home/user/new-project")
;; Remove a root directory
(mcp-remove-root "filesystem" "/home/user/old-project")
;; Replace all roots
(mcp-set-roots "filesystem" '("/home/user/project1" "/home/user/project2"))
;; View current roots
(mcp-get-roots "filesystem")Roots can also be specified as plists with additional metadata:
(mcp-connect-server "filesystem"
:command "npx"
:args '("-y" "@modelcontextprotocol/server-filesystem")
:roots '((:uri "file:///home/user/project" :name "My Project")
"/home/user/downloads"))The current text is being tested using the gptel tool branch.Use mcp-make-text-tool to create standard tool call data (Discussions).It is recommended to create tools within the tools-callback or wait for the mcp connect server to complete.
(mcp-make-text-tool "filesystem" "write_file")This will generate a data structure where the function is an auto-generated synchronous or asynchronous lambda function for accessing the MCP server.
(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")(mcp-stop-server "filesystem")(let ((connection (gethash "filesystem" mcp-server-connections)))
(mcp-call-tool connection "write_file" '(:path "filename or file path" :content "the file content")))(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))))Since the filesystem lacks prompts, the everything server is used for demonstration.
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-get-prompt connection "complex_prompt" '(:temperature "1.0")))(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))))Since the filesystem lacks resources, the everything server is used for demonstration.
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-read-resource connection "test://static/resource/1"))(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-async-read-resource connection "test://static/resource/1"
#'(lambda (resource)
(message "res: %s" resource))))Since the filesystem lacks resources, the everything server is used for demonstration.
(let ((connection (gethash "everything" mcp-server-connections)))
(mcp-async-list-resource-templates connection
#'(lambda (connection templates)
(message "%s" templates))))- [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
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.