This article is about how to setup Emacs (doom-emacs in particlar) to
work with most AI Assitants for Clojure projects using Clojure-MCP to
provide the AI assistant the ability to run commands in the Clojure
REPL.
Introduction
By the end of this article, you'll be able to give instructions to
the AI Assistant and it will use the REPL of your specific project to
execute them. For example, I asked the assistant to list all users in my
project's database (running locally, of course). With my setup, the
assistant could:
- analyze the project (source and document files)
- discover the service and functions that could perform the operation
to list all users
- ran the code in the repl, and got back all users
- used this answer to group the users (by org which makes sense for my
project)
- rendered the result as an org-mode file.
I ran into a few bumps along the way since this setup uses some
pretty cutting-edge tools and libraries. It took me a bit of time to
work through these issues, which is exactly why I'm putting this article
together - partly to help others who might face the same challenges, and
partly as a reference for my future self.
If you follow this guide you should endup with a fully working setup
with: Emacs + AI Assitant + Clojure-MCP development environment.
- Platform: macOS
- Editor: Emacs Plus 31 with Doom
Emacs
- Emacs packages:
gptel
for LLM interaction- mcp (Model
Context Protocol) support
- Development Stack:
clojure-mcp
server- Custom MCP startup scripts for project integration
Steps
Emacs
Install latest emacs-plus
brew install emacs-plus --with-native-comp --with-modern-black-dragon-icon
emacs-plus and native-comp shenanigans
Currently to fix emacs-plus from brew on macOS native-comp, add this
to the beginning of your ~/.emacs.d/early-init.el
.
;; FIX NATIVE COMP: https://github.com/d12frosted/homebrew-emacs-plus/issues/554#issuecomment-2909894593
(setenv "LIBRARY_PATH"
(mapconcat 'identity
'("/opt/homebrew/opt/gcc/lib/gcc/15"
"/opt/homebrew/opt/libgccjit/lib/gcc/15"
"/opt/homebrew/opt/gcc/lib/gcc/15/gcc/aarch64-apple-darwin24/15")
":"))
doom-emacs
As I started from an already existing doom-emacs
installation. I will not cover that part. Please refer to the doom-emacs
installation project
documentation if you want to install it .
Declare the new packages to use
In init.el
:
...
:tools
...
llm ;; <- uncomment this line
...
In ~/.doom.d/packages.el
:
(package! mcp
:pin "3a380185597f56290057f370ab65dde89a20a18f")
Then configure the packages:
In ~/.doom.d/config.el
:
;; LLM
(use-package! gptel
:init
;; declare the default llm configuration
;; for me this is co-pilot with claude-sonnet-4
(setq gptel-default-mode 'org-mode ;; I want the llm to reply to me with org-mode format.
gptel-backend (gptel-make-gh-copilot "Copilot")
gptel-model 'claude-sonnet-4)
(require 'gptel-integrations) ;; <- for mcp
;; declare another llm configuration, I also use GPT4All to consume locally only
(gptel-make-gpt4all "GPT4All"
:protocol "http"
:host "localhost:4891"
;; "~/Library/Application Support/nomic.ai/GPT4All"
:models '(Meta-Llama-3-8B-Instruct.Q4_0.gguf)))
(use-package! mcp
:after gptel
:config (require 'mcp-hub)
:init (setq mcp-hub-servers
;; this is the important block here
;; it tells that it can launch an MCP server for my-project by running the following
;; command line:
;; clojure-mcp my-project
'(("my-project" :command "clojure-mcp" :args ("my-project"))))
:hook (after-init . mcp-hub-start-all-server))
Rebuild / Install doom-emacs
After using another emacs version you need to rebuild your emacs
packages.
doom sync -u
doom rebuild
This will take a while.
Clojure-MCP
Update your global Clojure
aliases
The file ~/.clojure/deps.edn
:
{:aliases
{:mcp
{:deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"} ;; Required for stdio server
com.bhauman/clojure-mcp {:git/url "https://github.com/bhauman/clojure-mcp.git"
:git/tag "v0.1.4-alpha"
:git/sha "080b29b"}}
:exec-fn clojure-mcp.main/start-mcp-server
:exec-args {:port 7888}}}
}
Create
the command line to launch the Clojure MCP Server
#!/usr/bin/env bash
case $1 in
my-project) PORT=7888;;
*) echo "I don't know how to start this project ($1)" >&2
exit 1;
esac
clojure -X:mcp --port $PORT
Link Clojure MCP to
your Clojure project
It is important to perform the operation in the correct order. So
FIRST go to your project and start the repl. Personally
I work on a very big professional Clojure project that started even
before deps.edn
was thing in Clojure. So I
could start the repl with a lein command:
lein repl :start :port 7888
Got it! Here's your project setup in a friendlier tone:
Since my project is pretty large and has its own restart helpers, I
don't use the headless mode that the clojure-mcp docs suggest. Instead,
I fire up the project with :start mode so I can manually load all the
namespaces and get everything running with a the (reset!)
command.
Once my project is running (after reset!
) ONLY
THEN I start the Clojure mcp server.
I could test that by running my clojure-mcp my-project
command, and I get back a list of JSON objects lines:
> clojure-mcp my-project
{"jsonrpc":"2.0","method":"notifications/tools/list_changed"}
...
{"jsonrpc":"2.0","method":"notifications/resources/list_changed"}
...
{"jsonrpc":"2.0","method":"notifications/prompts/list_changed"}
So this is a good sign.
Using it
Now, everything should be ready to use.
After starting my repl, I launch emacs (if emacs was already started,
you probably need to restart your mcp server)
Once emacs is started, open gptel menu (SPC o l m
). Try
to add tools (if they are not there ; type M+
and start the
mcp if not started)
Once done, here you go.
SPC o l l
=> will create a new buffer to send orders
to the LLM. I started with:
Analyze the codebase structure, Document key files, dependencies and
available tools. Generate or update a comprehensive documentation in a
format optimized for LLM Assistants. Save this documentation in the file
PROJECT_SUMMARY.md
.
This should generate this PROJECT_SUMMARY.md
file and you should generally
always add this file to your LLM session context when working on your
project.
Now, to show how powerful this setup is, I could ask:
> Can you list all the users in the database?
And the AI Assistant (it was claude) was able to analyse the project,
discover the service to use, call the correct methods of this service to
list all users in my local environment DB. The result was then rendered
as a nice org-mode file where users were grouped by Org. This was even
better that what I could expect.
I could als ask how he could perform this operation, and the
assistant returned the commands he exectued in the repl.