Skip to main content
Spring Boot autoconfiguration for the ACP Java SDK. Provides auto-configured clients, agents, and transports with property-driven configuration.
As of 0.80.0, Spring Boot support is part of the ACP Java SDK itself: acp-spring-boot-starter and acp-spring-boot-autoconfigure moved from org.springaicommunity (version 0.13.0 and earlier) into com.agentclientprotocol, versioned together with the rest of the SDK. The package changed too (com.agentclientprotocol.sdk.spring.boot.autoconfigure, replacing com.agentclientprotocol.autoconfigure); the spring.acp.* property names did not. There is no relocation POM, so update the coordinates and imports directly. See the 0.80.0 migration guide for the full change.

Quick Start

Add the starter dependency:

Client

Configure the transport in application.properties and inject the client:

Agent

Annotate a Spring bean with @AcpAgent and add handler methods:
For stdio agents, redirect logging to stderr and keep the JVM alive:
A stdio agent now exits on its own when its client closes the agent’s input: the autoconfiguration closes the application context once the transport ends, which ends spring.main.keep-alive with it. Set spring.acp.agent.shutdown-on-transport-end=false to keep the old behavior, where the agent stays up after its transport ends.

Configuration Properties

Client

Setting a capability property to true without registering its handler in an AcpClientCustomizer fails at startup: the SDK’s client builder now requires a handler for every capability it advertises, and the autoconfiguration’s error names the property to turn off (or the customizer to add), for example “fs.readTextFile needs readTextFileHandler… The capabilities come from spring.acp.client.capabilities.read-text-file=true”. The application’s own sessionUpdateHandler, registered through a customizer, replaces the autoconfiguration’s DEBUG-logging one rather than running alongside it.

Agent

port and max-concurrent-streams-per-connection moved under transport.http.listener.*: they mean nothing in a servlet web application (which serves the endpoint on its own server and server.port), only for the SDK’s own standalone listener, so they live under their own prefix rather than beside the properties both modes share.

Transport Selection

The client transport is selected automatically based on which properties are set:
  • Set spring.acp.client.transport.stdio.command → stdio transport
  • Set spring.acp.client.transport.websocket.uri → WebSocket transport
  • Set spring.acp.client.transport.http.uri → Streamable HTTP transport
  • Set spring.acp.client.transport.type → explicit selection (takes precedence; http without the URI property fails at startup with a message naming it)
Setting more than one of stdio.command, websocket.uri, and http.uri with no explicit type now fails at startup instead of silently picking one: “Several ACP client transports are configured [STDIO, HTTP]; choose one with spring.acp.client.transport.type.” Set type, or leave only one property set.
The agent defaults to stdio transport. Set spring.acp.agent.enabled=false to disable, or spring.acp.agent.transport.type=http to serve it remotely; see below.

Remote Agents over Streamable HTTP

Set spring.acp.agent.transport.type=http, and add the module your kind of application needs. Every remote connection gets its own agent runtime, created from the SDK’s AcpAgentFactory, all served by your one @AcpAgent bean, so that bean must be thread-safe.
How the endpoint is served, and which module it needs, depends on what’s already on the classpath:
  • Servlet web application (spring-boot-starter-web present): add com.agentclientprotocol:acp-http-servlet (no Jetty dependency). The agent mounts on the application’s own server, at spring.acp.agent.transport.http.path (default /acp), serving Streamable HTTP, SSE, and WebSocket, all on server.port, through the application’s own filter chain: Spring Security rules on /acp apply to the WebSocket handshake too, and the authenticated principal reaches the endpoint. The listener.* properties are ignored in this mode.
  • Reactive web application (spring-boot-starter-webflux present): add com.agentclientprotocol:acp-http-webflux. The agent mounts the same way, as a RouterFunction bean (acpRouterFunction) on server.port, Reactor Netty by default: Streamable HTTP, SSE and WebSocket on one path, through the application’s own WebFilters, so a SecurityWebFilterChain applies to /acp the same way. The listener.* properties are ignored here too.
  • Non-web application (no spring-boot-starter-web or -webflux): add com.agentclientprotocol:acp-streamable-http-jetty. The SDK’s own Jetty listener opens spring.acp.agent.transport.http.listener.port and serves Streamable HTTP, a WebSocket upgrade on the same path, and cleartext HTTP/2 (h2c).
One HTTP module per application type. A servlet web application adds acp-http-servlet; a reactive (WebFlux) web application adds acp-http-webflux; an application without a web server adds acp-streamable-http-jetty (the SDK’s own listener). The starter itself brings none of them.
A servlet web application on Tomcat whose classpath still puts an older Jakarta WebSocket API ahead of Tomcat’s own now fails at startup, naming the fix (acp-http-servlet, or managing jakarta.websocket-api to 2.2.0), instead of starting cleanly and failing every WebSocket send with NoSuchMethodError later, with clients just hanging. On Reactor Netty, an oversized WebSocket message is the endpoint’s own limit to enforce (closing with 1009); on Tomcat or Jetty as a WebFlux server, the container’s own, smaller message limit applies first (8 KB on Tomcat, 64 KB on Jetty), so a larger message closes there instead. Use Reactor Netty, or a servlet application, for large WebSocket messages. Only ACP’s own web-socket-idle-timeout applies in servlet mode, and in reactive mode on Reactor Netty (the default and recommended server for WebFlux): the framework’s own idle-timeout setting doesn’t reach the upgraded socket.
Known issue (0.80.0): Jetty as a WebFlux server closes idle ACP WebSockets early. Jetty’s own connection idle timeout (30 seconds by default) closes an ACP WebSocket after 30 seconds of no traffic, overriding ACP’s own web-socket-idle-timeout (30 minutes by default). Set server.jetty.connection-idle-timeout at least as long as spring.acp.agent.transport.http.web-socket-idle-timeout (for example 30m), or use Reactor Netty, the default and recommended server for WebFlux. A fix is planned for 0.80.1.
A client connects with spring.acp.client.transport.http.uri=http://host:port/acp for StreamableHttpAcpClientTransport, or spring.acp.client.transport.websocket.uri=ws://host:port/acp for WebSocketAcpClientTransport; both work against the servlet-mounted agent, the WebFlux-mounted agent, and the standalone listener alike. The client (its transport included) is closed exactly once, bounded by its request timeout plus 10 seconds, by the autoconfiguration’s own lifecycle, not left for Spring to infer a close() call; WebSocket and Streamable HTTP are equally safe to use from a Boot application, including closing twice in a row (closeGracefully() then a context shutdown), which no longer sends a second close frame or throws. Override the StreamableHttpAcpAgentTransport bean (standalone listener), the AcpHttpEndpoint bean (servlet or reactive mode), or the acpRouterFunction bean (reactive mode only) the same way as any other auto-configured bean, to customize beyond what the properties expose. AcpHttpEndpoint and the other host-contract types (AcpHttpExchange, AcpHttpReply, and the rest) are @UnstableAcpApi.

Enabling file access over HTTP (or any transport)

The client advertises no file system capability by default (spring.acp.client.capabilities.* default to false), since the autoconfiguration registers no handler for them. Register one with an AcpClientCustomizer and turn the capability on:
This is the same mechanism for terminal handlers and spring.acp.client.capabilities.terminal, and applies equally whether the client is talking stdio, WebSocket, or Streamable HTTP: the capability properties and the customizer are transport-independent. AcpClientCustomizer and the transport-type enum it and the other properties above use are not Spring-specific types: both live in com.agentclientprotocol.sdk.integration (AcpTransportType), the framework-neutral module Micronaut and Quarkus build on too; see Concepts: the framework-neutral layer.

Native integration, not a bolt-on

In servlet or reactive mode, the agent is mounted on the application’s own server: one server, one port (HTTP, SSE, and WebSocket together), one TLS setup, one lifecycle that starts and stops with the application, one place for metrics and logs, and Spring’s own threads. There’s nothing ACP-specific to configure alongside what the application already has. The application’s own HTTP metrics and tracing cover /acp automatically: a request to it is mounted on the application’s filter chain (servlet) or WebFilter chain (reactive), so Spring’s ServerHttpObservationFilter records the standard http.server.requests observation for it, method and status included, the same as any other endpoint, with no ACP-specific configuration. Proven by the SDK’s own tests, with a TestObservationRegistry against a real request, on both modes.

If you need it: securing /acp

Most ACP use (an IDE talking stdio, a handful of local sessions) needs no security configuration at all. If you do expose the agent remotely, it’s mounted behind Spring’s own request handling, so ordinary Spring Security applies to /acp the same as any other endpoint, the WebSocket handshake included, and the authenticated principal reaches the endpoint (AcpHttpExchange.principal()). Proven by the SDK’s own shared transport TCK running against Spring MVC, and a matching test for the reactive host: an unauthenticated request gets 401 on HTTP and on the WebSocket handshake, an authenticated one gets 200/101, same as any other secured endpoint.
In a reactive application the same goes into a SecurityWebFilterChain:
ACP clients aren’t browsers, so exempt /acp from CSRF as shown above; the endpoint refuses a browser request from a foreign origin on its own, regardless (see Transports).

Extension Beans

AcpInterceptor, ArgumentResolver, and ReturnValueHandler beans are added to the agent, in bean order, the same mechanism as the AcpClientCustomizer beans on the client side.

Where handlers run

spring.acp.agent.handler-executor names an Executor bean the agent’s handler methods run on (a plain TaskExecutor is adapted); none means the SDK’s own pool explicitly. Left unset, the context’s applicationTaskExecutor is used, but only when spring.threads.virtual.enabled=true (a virtual thread per handler): without virtual threads that executor is a pool of 8 threads by default, which would cap the prompts served at once, so the SDK’s own pool (see Concepts: Where handlers run) stays the default instead; name the bean explicitly to use it anyway.

Logging and sensitive data

See Transports: Logging and sensitive data for what the SDK logs at each level. Without a session-update handler of your own, session updates are logged at DEBUG, content included. A stdio agent’s standard error is logged at INFO, unfiltered, on com.agentclientprotocol.sdk.client.transport.agent-stderr; silence it with logging.level.com.agentclientprotocol.sdk.client.transport.agent-stderr=WARN.

Overriding Beans

All auto-configured beans back off when you provide your own. Define a custom AcpClientTransport, AcpSyncClient, AcpAsyncClient, AcpAgentTransport, AcpAgentFactory, StreamableHttpAcpAgentTransport, or acpServletRegistration bean and the autoconfiguration will use yours instead. A transport carries one client. The auto-configured AcpSyncClient wraps the AcpAsyncClient bean (new AcpSyncClient(asyncClient)), and a custom sync client should do the same. Building a second client from the transport fails at construction on ACP Java SDK 0.80.0.

Tutorial

Spring Boot Agent

Build an ACP agent as a Spring Boot application

Spring Boot Client

Use the autoconfigured ACP client in Spring Boot

Micronaut Integration

The equivalent integration for Micronaut applications

Quarkus Integration

The equivalent integration for Quarkus applications

Clients and Agents in Java

The annotation model this module builds on

Transports

Where ACP listens in every deployment shape, Spring Boot included

Requirements

  • Java 17+
  • Spring Boot 4.1
  • ACP Java SDK 0.80.0 (as of 0.80.0, acp-spring-boot-starter and acp-spring-boot-autoconfigure are modules of the SDK itself, built and versioned together with it, not a separate project pinning an SDK version)

Resources