Skip to content

Commit 2cb8f7e

Browse files
author
Mark Pollack
committed
docs: fix README examples for sync/async agent patterns
- Add sync agent example as primary "Hello World" (simpler, no Mono) - Fix async agent to use agent.start().then(agent.awaitTermination()).block() instead of agent.start().block() which doesn't block correctly - Update streaming updates section with both sync and async patterns - Fix agent-to-client requests to show AtomicReference pattern for accessing agent instance in prompt handler - Fix client file handlers to show typed request parameters - Remove erroneous .block() call on sync client in capability section
1 parent d71d051 commit 2cb8f7e

File tree

1 file changed

+102
-38
lines changed

1 file changed

+102
-38
lines changed

README.md

Lines changed: 102 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -68,35 +68,68 @@ var response = client.prompt(new PromptRequest(
6868
client.close();
6969
```
7070

71-
### 2. Hello World Agent
71+
### 2. Hello World Agent (Sync)
7272

73-
Create a minimal ACP agent:
73+
Create a minimal ACP agent using the sync API (recommended for simplicity):
7474

7575
```java
7676
import com.agentclientprotocol.sdk.agent.*;
7777
import com.agentclientprotocol.sdk.agent.transport.*;
7878
import com.agentclientprotocol.sdk.spec.AcpSchema.*;
79-
import reactor.core.publisher.Mono;
8079
import java.util.List;
80+
import java.util.UUID;
8181

8282
// Create stdio transport
8383
var transport = new StdioAcpAgentTransport();
8484

85-
// Build agent with handlers
85+
// Build sync agent - handlers use plain return values (no Mono!)
86+
AcpSyncAgent agent = AcpAgent.sync(transport)
87+
.initializeHandler(req ->
88+
new InitializeResponse(1, new AgentCapabilities(), List.of()))
89+
.newSessionHandler(req ->
90+
new NewSessionResponse(UUID.randomUUID().toString(), null, null))
91+
.promptHandler((req, updater) -> {
92+
// Send updates using blocking void method
93+
updater.sendUpdate(req.sessionId(),
94+
new AgentMessageChunk("agent_message_chunk",
95+
new TextContent("Hello from the agent!")));
96+
// Return response directly (no Mono!)
97+
return new PromptResponse(StopReason.END_TURN);
98+
})
99+
.build();
100+
101+
// Run agent (blocks until client disconnects)
102+
agent.run();
103+
```
104+
105+
### 2b. Hello World Agent (Async)
106+
107+
For reactive applications, use the async API:
108+
109+
```java
110+
import com.agentclientprotocol.sdk.agent.*;
111+
import com.agentclientprotocol.sdk.agent.transport.*;
112+
import com.agentclientprotocol.sdk.spec.AcpSchema.*;
113+
import reactor.core.publisher.Mono;
114+
import java.util.List;
115+
import java.util.UUID;
116+
117+
var transport = new StdioAcpAgentTransport();
118+
86119
AcpAsyncAgent agent = AcpAgent.async(transport)
87120
.initializeHandler(req -> Mono.just(
88-
new InitializeResponse(1, new AgentCapabilities(), List.of())
89-
))
121+
new InitializeResponse(1, new AgentCapabilities(), List.of())))
90122
.newSessionHandler(req -> Mono.just(
91-
new NewSessionResponse(java.util.UUID.randomUUID().toString(), null, null)
92-
))
93-
.promptHandler((req, updater) -> Mono.just(
94-
new PromptResponse(StopReason.END_TURN)
95-
))
123+
new NewSessionResponse(UUID.randomUUID().toString(), null, null)))
124+
.promptHandler((req, updater) ->
125+
updater.sendUpdate(req.sessionId(),
126+
new AgentMessageChunk("agent_message_chunk",
127+
new TextContent("Hello from the agent!")))
128+
.then(Mono.just(new PromptResponse(StopReason.END_TURN))))
96129
.build();
97130

98-
// Start and run
99-
agent.start().block();
131+
// Start and await termination
132+
agent.start().then(agent.awaitTermination()).block();
100133
```
101134

102135
---
@@ -105,8 +138,23 @@ agent.start().block();
105138

106139
### 3. Streaming Updates
107140

108-
Send real-time updates to the client during prompt processing:
141+
Send real-time updates to the client during prompt processing.
142+
143+
**Agent (Sync) - recommended:**
144+
```java
145+
.promptHandler((req, updater) -> {
146+
// Blocking void calls - simple and straightforward
147+
updater.sendUpdate(req.sessionId(),
148+
new AgentThoughtChunk("agent_thought_chunk",
149+
new TextContent("Thinking...")));
150+
updater.sendUpdate(req.sessionId(),
151+
new AgentMessageChunk("agent_message_chunk",
152+
new TextContent("Here's my response.")));
153+
return new PromptResponse(StopReason.END_TURN);
154+
})
155+
```
109156

157+
**Agent (Async):**
110158
```java
111159
.promptHandler((request, updater) -> {
112160
return updater.sendUpdate(request.sessionId(),
@@ -119,43 +167,59 @@ Send real-time updates to the client during prompt processing:
119167
})
120168
```
121169

122-
Clients receive updates via the session update consumer:
123-
170+
**Client - receiving updates:**
124171
```java
125-
AcpAsyncClient client = AcpClient.async(transport)
172+
AcpSyncClient client = AcpClient.sync(transport)
126173
.sessionUpdateConsumer(notification -> {
127-
System.out.println("Update: " + notification.update());
128-
return Mono.empty();
174+
var update = notification.update();
175+
if (update instanceof AgentMessageChunk msg) {
176+
System.out.print(((TextContent) msg.content()).text());
177+
}
129178
})
130179
.build();
131180
```
132181

133182
### 4. Agent-to-Client Requests
134183

135-
Agents can request file operations from the client:
184+
Agents can request file operations from the client.
136185

186+
**Agent (Sync) - reading files:**
137187
```java
138-
.promptHandler((request, updater) -> {
139-
// Read a file from the client's filesystem
140-
return agent.readTextFile(new ReadTextFileRequest(
141-
request.sessionId(), "/src/Main.java", null, null))
142-
.flatMap(file -> {
143-
String content = file.content();
144-
// Process content...
145-
return Mono.just(new PromptResponse(StopReason.END_TURN));
146-
});
147-
})
148-
```
188+
// Store agent reference for use in prompt handler
189+
AtomicReference<AcpSyncAgent> agentRef = new AtomicReference<>();
149190

150-
Clients must register handlers for agent requests:
191+
AcpSyncAgent agent = AcpAgent.sync(transport)
192+
.promptHandler((req, updater) -> {
193+
AcpSyncAgent agentInstance = agentRef.get();
151194

195+
// Read a file from the client's filesystem
196+
var fileResponse = agentInstance.readTextFile(
197+
new ReadTextFileRequest(req.sessionId(), "pom.xml", null, 10));
198+
String content = fileResponse.content();
199+
200+
// Write a file
201+
agentInstance.writeTextFile(
202+
new WriteTextFileRequest(req.sessionId(), "output.txt", "Hello!"));
203+
204+
return new PromptResponse(StopReason.END_TURN);
205+
})
206+
.build();
207+
208+
agentRef.set(agent); // Store reference before running
209+
agent.run();
210+
```
211+
212+
**Client - registering file handlers:**
152213
```java
153-
AcpAsyncClient client = AcpClient.async(transport)
154-
.readTextFileHandler(params -> {
155-
// Read file and return content
156-
ReadTextFileRequest req = /* unmarshal params */;
214+
AcpSyncClient client = AcpClient.sync(transport)
215+
.readTextFileHandler((ReadTextFileRequest req) -> {
216+
// Handlers receive typed requests directly
157217
String content = Files.readString(Path.of(req.path()));
158-
return Mono.just(new ReadTextFileResponse(content));
218+
return new ReadTextFileResponse(content);
219+
})
220+
.writeTextFileHandler((WriteTextFileRequest req) -> {
221+
Files.writeString(Path.of(req.path()), req.content());
222+
return new WriteTextFileResponse();
159223
})
160224
.build();
161225
```
@@ -166,7 +230,7 @@ Check what features the peer supports before using them:
166230

167231
```java
168232
// Client: check agent capabilities after initialize
169-
client.initialize(new InitializeRequest(1, clientCaps)).block();
233+
client.initialize(new InitializeRequest(1, clientCaps));
170234

171235
NegotiatedCapabilities agentCaps = client.getAgentCapabilities();
172236
if (agentCaps.supportsLoadSession()) {

0 commit comments

Comments
 (0)