Emacs Integration
The mcp__emacs__emacs_eval tool evaluates Emacs Lisp in the running Emacs instance.
Use Emacs (emacs_eval) for:
- Semantic operations (LSP-aware):
elisp
1(eglot-rename "new_name") ; LSP knows scope, not just text
2(xref-find-references "func") ; Understands imports, aliases
3(flymake-diagnostics) ; Type errors, not just syntax
4(project-find-file) ; Project-aware file finding
- Elisp codebase exploration (STRONGLY PREFERRED over grep/glob):
elisp
1;; Discover symbols matching a pattern
2(apropos-internal "agent-shell.*hook" 'boundp) ; Find variables
3(apropos-internal "^gptel-make" 'fboundp) ; Find functions
4
5;; Get function/variable documentation instantly
6(describe-function 'agent-shell-mcp-server-start)
7(describe-variable 'agent-shell-mode-hook)
8
9;; Check current runtime state
10(agent-shell-mcp-server-running-p) ; Live state check
11(symbol-value 'some-variable) ; Current value
12
13;; Find files
14(directory-files-recursively "~/.emacs.d" "\\.el$")
Use CLI tools (Bash/Grep) only for:
bash
1grep -r "TODO" --include="*.py" . # Bulk text search in non-Elisp
2sed -i 's/old/new/g' *.py # Global text replace
3find . -name "*.test.js" ; File finding by pattern
Rule of thumb: For Emacs/Elisp codebases, ALWAYS use emacs_eval first. It's more token-efficient (returns only what you need), provides live runtime state, and understands Elisp semantics. Only fall back to grep/glob for non-Elisp code or bulk text operations.
Why Elisp is Powerful for Agents
- No access control - Everything is public, agent can modify anything
- Advice system - Wrap any function without modifying source:
(advice-add 'fn :before #'my-fn)
- Instant eval - No compilation step, immediate feedback
- Self-documenting -
(describe-function 'fn) at runtime
- Hooks everywhere - Event-driven observation and intervention
Documentation Discovery
Query Emacs directly for any Elisp documentation:
elisp
1(describe-function 'function-name)
2(describe-variable 'variable-name)
3(apropos "search-term")
Current Context
The Claude Code session runs in an agent-shell-mode buffer. Get context:
elisp
1major-mode ; => agent-shell-mode
2(buffer-name) ; => "Claude Code Agent @ project-name"
3(agent-shell--current-shell) ; Get the shell object
Self-Prompting with Timers
Set up a timer to automatically queue a prompt after idle time:
elisp
1(defvar my-agent-idle-timer nil)
2
3(defun my-agent-idle-prompt ()
4 "Queue a nudge to the agent after idle timeout."
5 (when-let ((shell-buf (get-buffer "Claude Code Agent @ .emacs.d")))
6 (with-current-buffer shell-buf
7 ;; Use enqueue - agent-shell-insert errors if agent is busy
8 (agent-shell--enqueue-request
9 :prompt "Continue working on the current task."))))
10
11;; Start: prompt after 15 minutes (900 seconds) of no activity
12(setq my-agent-idle-timer
13 (run-with-idle-timer 900 t #'my-agent-idle-prompt))
14
15;; Stop the timer
16(when my-agent-idle-timer
17 (cancel-timer my-agent-idle-timer)
18 (setq my-agent-idle-timer nil))
Edge case: agent-shell-insert with :submit t returns "Busy" or "Not yet supported" errors while the agent is processing. Use agent-shell--enqueue-request instead - it queues the request to run after the current task completes.
Queueing Requests
Queue prompts that execute when the agent is no longer busy:
elisp
1;; Queue a request (runs when current task completes)
2(agent-shell--enqueue-request :prompt "Next task to do")
3
4;; Check pending queue
5(with-current-buffer (agent-shell--current-shell)
6 (alist-get :pending-requests agent-shell--state))
Response Size Limits
Large return values may exceed the MCP response limit. Strategies:
elisp
1;; Truncate long strings
2(substring (buffer-string) 0 (min 10000 (length (buffer-string))))
3
4;; Return counts instead of full lists
5(length (buffer-list))
6
7;; Filter before returning
8(seq-take (buffer-list) 10)
9
10;; Write to file, return path
11(with-temp-file "/tmp/output.txt"
12 (insert large-content))
13"/tmp/output.txt" ; Return just the path
Buffer Context Gotcha
Evaluations run in the agent-shell buffer by default. To operate on other buffers:
elisp
1;; WRONG: operates on agent-shell buffer
2(buffer-string)
3
4;; RIGHT: explicitly target a buffer
5(with-current-buffer "target-buffer"
6 (buffer-string))
7
8;; Or use a temp buffer for scratch work
9(with-temp-buffer
10 (insert-file-contents "/path/to/file")
11 (buffer-string))
Periodic Background Tasks
Use run-at-time for scheduled operations:
elisp
1;; Run once after 5 minutes
2(run-at-time 300 nil #'my-function)
3
4;; Run every 60 seconds
5(run-at-time t 60 #'my-repeating-function)
6
7;; Cancel a timer
8(cancel-timer timer-object)
Coordinating Multiple Agents
If multiple agent-shell sessions exist:
elisp
1;; List all agent buffers
2(agent-shell-buffers)
3
4;; Get project-specific buffers
5(agent-shell-project-buffers)
6
7;; Queue request to a specific shell (safe even if busy)
8(with-current-buffer "Claude Code Agent @ other-project"
9 (agent-shell--enqueue-request :prompt "message"))
Inspecting Agent State
Access internal state for debugging or coordination:
elisp
1(with-current-buffer (agent-shell--current-shell)
2 ;; Available state keys
3 (mapcar #'car agent-shell--state)
4 ;; => (:agent-config :buffer :client :request-count :pending-requests ...)
5
6 ;; Check request count
7 (alist-get :request-count agent-shell--state)
8
9 ;; Check if authenticated
10 (alist-get :authenticated agent-shell--state))
Error Handling
Errors return with isError: true. To handle gracefully in Elisp:
elisp
1(condition-case err
2 (potentially-failing-operation)
3 (error (format "Failed: %s" (error-message-string err))))
Event-Driven Agent Behavior (Hooks)
Emacs's hook system enables event-driven agent behavior:
elisp
1;; OBSERVE user actions
2(add-hook 'after-save-hook #'agent-on-file-saved)
3(add-hook 'compilation-finish-functions #'agent-on-compile-done)
4(add-hook 'flymake-diagnostic-functions #'agent-on-diagnostics)
5(add-hook 'buffer-list-update-hook #'agent-on-focus-change)
6
7;; INTERVENE at key moments
8(add-hook 'before-save-hook #'agent-maybe-format)
9(add-hook 'find-file-hook #'agent-setup-buffer)
10
11;; ADVICE any function non-invasively
12(advice-add 'compile :before #'agent-log-compile-command)
13(advice-add 'save-buffer :after #'agent-notify-save)
This enables proactive assistance without polling - the agent becomes an event-driven observer that reacts to user actions in real-time.
Key Insight: Dynamic Modification
Unlike static languages, Elisp lets agents modify behavior at runtime:
elisp
1;; Add logging to ANY function, instantly, no rebuild
2(advice-add 'some-function :before
3 (lambda (&rest args) (message "Called with: %S" args)))
4
5;; Remove it just as easily
6(advice-remove 'some-function #'my-advice-fn)
This is the core advantage over TypeScript/VS Code extensions - no compilation, no packaging, no restart. The agent can experiment and iterate in real-time.
Repository Structure
This is a literate Emacs configuration using Org-Babel. Assumes Emacs 30+ as baseline.
Configuration Hierarchy
All modules are loaded at startup via org-babel-load-file:
init.org (2,288 lines) - MAIN ENTRY POINT
├── emacs24.helm.org - Helm completion
├── emacs24.smartparens.org - Structural editing
├── emacs24.hacky.org - Misc utilities
├── emacs24.python.org - Python dev
├── emacs24.eshell.org - Shell with Git prompt
├── emacs24.ess.org - R/statistics
├── emacs24.elisp.org - Elisp dev
├── emacs24.js2.org - JavaScript
├── emacs24.sql.org - SQL (5 formatters, sqlfluff preferred)
├── emacs24.scheme.org - Scheme
├── emacs29.llm.org - LLM/AI integration
└── private.org - API keys (external, not in repo)
Note: "emacs24" naming is historical, not version-specific.
Key Sections in init.org
| Lines | Purpose |
|---|
| 1-150 | Core setup: GC, keybindings, platform detection, straight.el |
| 150-450 | Org mode configuration |
| 374-450 | External module loading |
| 450-2100 | Language modes, UI, navigation |
| 2100+ | Themes, finalization, auto-tangle |
Runtime Data (do not commit)
bookmarks - Emacs bookmark locations
keyfreq - Key frequency statistics
prompts/ - LLM prompt templates
snippets/ - Yasnippet templates
elpa/, straight/repos/ - Package caches
eln-cache/ - Native compilation cache
Package Management
Uses straight.el with use-package for declarative configuration. ~116 packages configured.
Known Issues (documented for cleanup)
- emacs24.eshell.org: Git status runs shell commands on every keystroke (performance)
- emacs24.hacky.org:
date2unix defined twice; legacy keyboard macros
- emacs24.scheme.org: xscheme is ancient/unmaintained
- emacs24.js2.org: js2-mode superseded by js-ts-mode