Skip to main content

java-server

java-server exposes an Agent over the AG-UI protocol. It reads a RunAgentInput, runs the agent, and streams the resulting events back as Server-Sent Events. It is the server-side counterpart of HttpAgent.
The module has two layers:
  • A transport-neutral core (com.agui.community.server) that handles parsing, running, framing and backpressure, with no dependency on an HTTP framework.
  • A JDK adapter (com.agui.community.server.jdk) for the built-in com.sun.net.httpserver.HttpServer, with no third-party dependencies.
For Spring Boot, see Spring. It builds on the same types.

JDK HTTP server

The handler blocks its request thread until the run’s stream is complete. HttpServer uses a single thread when no executor is set, so set an executor with enough threads for your concurrent runs. On Java 21+ you can use Executors.newVirtualThreadPerTaskExecutor().

Responses

If the agent signals onError, the handler writes a final RUN_ERROR event and closes the stream. It does not drop the connection.

Multiple agents

Pass an AgentRegistry to serve several agents from one context. The path segment after the context selects the agent:
The bare base path (/agent) works only when the registry holds exactly one agent. The single-agent constructor new JdkAgentHttpHandler(agent, serializer) is shorthand for a registry with one entry under the ID default, so that agent answers on both /agent and /agent/default. AgentRegistry is an interface with find(id) and ids(). Implement it yourself to resolve agents dynamically, for example per tenant or from a database.

Building blocks

To run on another stack, such as plain servlets, Netty, Vert.x or a WebSocket server, use the core types directly.

Custom transport

parse and run are separate steps. This lets you reject bad input with a 400 before you commit to a streaming 200:
The future returned by run always completes normally, whether the stream ended normally, on an error (after the RUN_ERROR frame), or because the client disconnected.

Backpressure and disconnects

EventRelaySubscriber requests one event at a time and asks for the next only after the previous frame has been written. A slow client therefore slows the agent down through the subscription. If EventSink.write throws an IOException, for example because the client went away, the subscriber cancels the subscription. Your agent should stop producing events when it is cancelled.

Other wire formats

The EventEncoder alone decides the framing. For a WebSocket transport that sends one JSON document per message, pass an encoder that skips the SSE framing:
The input is still parsed with the Serializer.