Acp.el
An ACP (Agent Client Protocol) implementation in Emacs lisp as per https://agentclientprotocol.com
Install / Use
/learn @xenodium/Acp.elREADME
#+TITLE: acp.el #+AUTHOR: Álvaro Ramírez
- This project needs your funding
As you pay for those useful LLM tokens, consider [[https://github.com/sponsors/xenodium][sponsoring]] development and maintenance of this project.
=acp.el= enables us to build better integrations and tools into our beloved Emacs text editor. With your help, I can make this effort more [[https://github.com/sponsors/xenodium][sustainable]].
Thank you!
[[https://xenodium.com/][Alvaro]]
- acp.el
acp.el implements the Agent Client Protocol (ACP) for Emacs as per [[https://agentclientprotocol.com][agentclientprotocol.com]].
ACP is a standardized protocol for communicating with LLM agents like Gemini CLI, Claude Code, etc.
Note: This package is in the very early stages, isn't yet API stable, and is bound to change.
- Install
** Prerequisites
Install an LLM agent command-line tool like:
- Gemini CLI: =gemini-cli= - [[https://github.com/google-gemini/gemini-cli][github.com/google-gemini/gemini-cli]]
- Claude Code: =claude-code-acp= - [[https://www.anthropic.com/claude-code][anthropic.com/claude-code]]
** Package Installation Clone the repository and add to your Emacs load path:
#+begin_src emacs-lisp (add-to-list 'load-path "/path/to/acp.el/") (require 'acp) #+end_src
- Sample Usage
** Initialization
#+begin_src emacs-lisp :exports both (setq client (acp-make-client :command "gemini" :command-params '("--experimental-acp") :environment-variables (when api-key (list (format "GEMINI_API_KEY=%s" "your-api-key")))))
(acp-send-request :client client :request (acp-make-initialize-request :protocol-version 1) :on-success (lambda (response) (message "Initialize success: %s" response)) :on-failure (lambda (error) (message "Initialize failed: %s" error))) #+end_src
Initialize success:
#+RESULTS:
: ((protocolVersion . 1)
: (authMethods . [((id . oauth-personal)
: (name . Log in with Google)
: (description . :null))
: ((id . gemini-api-key)
: (name . Use Gemini API key)
: (description . Requires setting the GEMINI_API_KEY environment variable))
: ((id . vertex-ai)
: (name . Vertex AI)
: (description . :null))])
: (agentCapabilities (loadSession . :false)
: (promptCapabilities (image . t)
: (audio . t)
: (embeddedContext . t))))
Typically, you'd need to send a sequence of requests before you can send the agent prompts to interact with:
- =acp-make-initialize-request=
- =acp-make-authenticate-request= (not needed for Claude Code)
- =acp-make-session-new-request=
You may now send the agent prompts with:
- =acp-make-session-prompt-request=
- Subscriptions
#+begin_src emacs-lisp :lexical no :exports both (acp-subscribe-to-requests client (lambda (request) (message "Received request: %s" request)))
(acp-subscribe-to-notifications client (lambda (notification) (message "Received notification: %s" notification))) #+end_src
- Cleanup
#+begin_src emacs-lisp :lexical no (acp-shutdown client) #+end_src
- Logging
Logging can be enabled via:
#+begin_src emacs-lisp :lexical no (setq acp-logging-enabled t) #+end_src
Look out for =acp log= and =acp traffic= buffers.
The =acp traffic= is particularly useful to inspecting incomng or outgoing traffic.
[[file:traffic.gif]]
- APIs
#+BEGIN_SRC emacs-lisp :results table :colnames '("Function" "Type" "Description") :exports results (let ((rows)) (mapatoms (lambda (symbol) (let ((name (symbol-name symbol))) (when (and (string-match "^acp-" name) (not (string-match "^acp--" name)) (not (string-match "^acp-traffic" name)) (not (string-match "^acp-fakes" name)) (fboundp symbol)) (push `(,name ,(or (car (split-string (or (documentation symbol t) "No documentation") "\n")) "No documentation")) rows))))) (sort rows (lambda (a b) (string< (car a) (car b))))) #+END_SRC
#+RESULTS: | acp-logs-buffer | Get CLIENT logs buffer. | | acp-make-authenticate-request | Instantiate an "authenticate" request. | | acp-make-client | Create an ACP client. | | acp-make-error | Create a JSON-RPC error object. | | acp-make-fs-read-text-file-response | Instantiate a "fs/read_text_file" response. | | acp-make-fs-write-text-file-response | Instantiate a "fs/write_text_file" response. | | acp-make-initialize-request | Instantiate an "initialize" request. | | acp-make-session-cancel-notification | Instantiate a "session/cancel" request. | | acp-make-session-new-request | Instantiate a "session/new" request. | | acp-make-session-prompt-request | Instantiate a "session/prompt" request. | | acp-make-session-request-permission-response | Instantiate a "session/request_permission" response. | | acp-reset-logs | Reset CLIENT log buffers. | | acp-send-notification | Send NOTIFICATION from CLIENT. | | acp-send-request | Send REQUEST from CLIENT. | | acp-send-response | Send a request RESPONSE from CLIENT. | | acp-shutdown | Shutdown ACP CLIENT and release resources. | | acp-subscribe-to-errors | Subscribe to agent errors using CLIENT. | | acp-subscribe-to-notifications | Subscribe to incoming CLIENT notifications. | | acp-subscribe-to-requests | Subscribe to incoming CLIENT requests. |
- FAQ
** Why not use [[https://github.com/emacs-mirror/emacs/blob/master/lisp/jsonrpc.el][jsonrpc.el]]?
That was my initial intention, though it doesn't seem possible with [[https://github.com/emacs-mirror/emacs/blob/1d6ec2a0406c8a53fcf793b05453dbcc7e809d76/lisp/jsonrpc.el#L586][Content-Length automatically appended]] to requests sent. If you do know of a way, I'd love to know.
Related Skills
node-connect
335.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
335.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.5kCommit, push, and open a PR
