AI Assistants in Doom Emacs 31 on macOS with Clojure MCP server

[2025-06-17 Tue] on Yann Esposito's blog

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:

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.

Steps

Emacs

Install latest emacs-plus

brew install emacs-plus --with-native-comp --with-modern-black-dragon-icon
  1. 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 .

  1. 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))
    
  2. 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

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.