As you pay for those useful LLM tokens, consider 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 sustainable.
Thank you!
acp.el implements the Agent Client Protocol (ACP) for Emacs as per 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 an LLM agent command-line tool like:
- Gemini CLI:
gemini-cli
- github.com/google-gemini/gemini-cli - Claude Code:
claude-code-acp
- anthropic.com/claude-code
Clone the repository and add to your Emacs load path:
(add-to-list 'load-path "/path/to/acp.el/")
(require 'acp)
(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)))
Initialize success:
((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
(acp-subscribe-to-requests client
(lambda (request)
(message "Received request: %s" request)))
(acp-subscribe-to-notifications client
(lambda (notification)
(message "Received notification: %s" notification)))
(acp-shutdown client)
Logging can be enabled via:
(setq acp-logging-enabled t)
Look out for *acp log*
and *acp traffic*
buffers.
The *acp traffic*
is particularly useful to inspecting incomng or outgoing traffic.
acp-logs-buffer | Get CLIENT logs buffer. |
acp-make-authenticate-request | Instantiate an “authenticate” request. |
acp-make-claude-client | Create Claude Code ACP client with API-KEY. |
acp-make-client | Create an ACP client. |
acp-make-gemini-client | Create a Gemini ACP client with API-KEY. |
acp-make-initialize-request | Instantiate an “initialize” request. |
acp-make-session-cancel-request | Instantiate a “session/cancel” request. |
acp-make-session-new-request | Instantiate a “session/new” request. |
acp-make-session-prompt-request | Instantiate an “session/prompt” request. |
acp-make-session-request-permission-response | Instantiate a “session/request_permission” response. |
acp-reset-logs | Reset CLIENT log buffers. |
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. |
Why not use jsonrpc.el?
That was my initial intention, though it doesn’t seem possible with Content-Length automatically appended to requests sent. If you do know of a way, I’d love to know.