SPEAKER_00
I'm Bennett, I work at Zedd and we built an AI code editor, all written in Rust. And last year was, as you probably all know, the rise of the AI coding agent and terminal user interfaces with every major model provider building Cloud Code, Codex, Gemini, CLI and so on. And so at Zedd we asked ourselves, how can we let users bring their agent of choice to our tool and enjoy a nice interface that is unified across all of them? And so that's why we decided we need some type of protocol called agent client protocol, which is similar to MCP or LSP. It's a JSON RPC based protocol. And the idea is that agents and clients can talk to each other through a unified interface.
SPEAKER_00
And it's online, it's open source, you can contribute if you want. At this point we have a wide variety of agents already supporting this either by an adapter that translates the agent's native language to the ACP one. And then we have, for example, open code and cursor having ACP mode built into their CLI agents. And we also have a bunch of clients at this point up to 40 that implement this, including open claw for example. Open claw itself is a client and a server, a client and an agent. And JetBrains and Obsidian and other people are supporting this. Great. So I'm going to do a live coding session. Let's see how well that goes.
SPEAKER_00
So, right. We have some pre-existing code. So this is Zed. Here I have some TypeScript code. Also bear with me. I'm a Rust developer. I have basically zero clue about TypeScript. So if you see anything that you don't do in TypeScript, tell me afterwards. But here's a very minimal coding agent that just doesn't support ACP, but it's the bare minimum you need to build a coding agent. So all it has is really two tools. One to read a file and one to edit an existing file. This is pretty basic. It provides the model has to provide a path and it has to provide an old text that is then just replaced with new text and that's everything.
SPEAKER_00
And then we have in this case, I'm using Anthropic. There's a way to prompt the agent with the user can prompt the agent, which is the function that we were going to call here. And then we enter the agent loop and that is the way all agents work is the model APIs are stateless. So you just attach some conversation up to this point. You call the endpoint. In this case, it's the Anthropic API. And then we get a message from the model and either it can be an end turn. That means the model decided to output some text and do nothing or it can call a tool. And in this case, it would be a read or a write tool call.
SPEAKER_00
And then in the case of a tool call, we run the, for example, the edit file tool call locally, collect the result and then send it up to the model again. That's where there's this loop. And then we call the API again with the conversation up to that point. And that's everything. And then we have this handle tool call function here, which handles read and write tool calls, which does what you expect. We get the paths. We read it from the file system and we return some result. Right. So now the question is how do we make this thing ACP compatible? And hopefully we can do it in 10 minutes. So let's see.
SPEAKER_00
So I have some boilerplate here. In this case, I'm using the TypeScript SDK, as I said. And the way this works, you implement the agent interface provided by this library. And then you have to, at minimum, implement these three, four functions. So the first one that we're looking at is this initialize here. All we really have to do here is respond with the protocol version we support, which in this case is just the latest. There's also some capabilities like client and the agent can itself advertise capabilities of stuff it supports. But we're building a mere minimal coding agent, so we don't support anything outside that's necessary.
SPEAKER_00
And then for us, authentication is irrelevant since it's just using my API key from an infar. And then there's this concept of sessions in ACP. So basically every time you start with Red and Z or in a different editor or client, you call a new session. And in a session, you can prompt. And then from there on, you can get your output. So all we really do here is we generate a random ID. Then we instantiate this coding agent with the current working directory, which we get from the client. And we store it in our internal map and then just return the ID to the client so that the client knows the ID. And that is then used inside prompt.
SPEAKER_00
Because what prompt does in this case, it prompts the prompt request, if we go to the definition here, all this is doing is it provides a prompt, which is an array of content blocks that can be text, images, whatever, the session ID for reference. And so all we have to do here is we look it up in our internal state. We get the relevant agent for that current session. And then I have a helper function here, which in this case ignores everything that's not text because we don't support images and stuff. And then we call this prompt function I showed earlier, which then runs the tool calling loop.
SPEAKER_00
And then as a nice to have feature, we also support cancellation. That's also pretty simple. So these are the four minimal things we have to implement. And I have this hooked up in Z just by there's no special code. All I have is I tell Z there's some ACP compatible agent, and you can run it with node and then my path to that agent.js file. So when I do that in here as well. So when I run, I'm going to build. I'm going to build, right? loop. And so then just as a nice to have feature, we also support cancellation. That's also pretty simple. So these are the four minimal things we have to implement.
SPEAKER_00
And I have this hooked up in Zed just by—there's no special code. All I have is I tell Zed there's some ACP compatible agent, and you can run it with node and then my path to that agent.js file. So when I, let's do that in here as well. So when I run, I'm going to build. I'm going to build, right? And then I'm going to restart, yeah, launch a new ACP demo agent. And you can see if I ask it for something, you can see weight indicator. And now it's done. So nothing. Which is what we expect, right? Because what we can see here, if I go over, oops, if I go over here, we have an ACP debug view in Zed, so to make it easier for us to develop.
SPEAKER_00
And you can see Zed sends a session new request. We respond with a session ID. And as soon as I type in something, we get prompt. And now we got a stop reason, right? So these red arrows come from the adapter that we are building from the agent. But it's not outputting tokens, right? Behind the scenes, it's obviously Anthropic is giving us tokens. But how do we make them show up in Zed? So that's the next thing that we want to focus on. So the first thing that we need to do, our coding agent itself needs to have a way to send something over the connection. So what we're going to do here is the coding agent is going to take the ACP connection and then also the session ID.
SPEAKER_00
And then inside here, we have to provide this connection and the ID. And then if we go, okay, I'm just going to close this, right? If we go back here, now inside of a prompt, this is the Anthropic SDK. So in this case, you can just use this stream on. That's the Anthropic SDK. You can react to a text event. So every time we get a chunk, we send what ACP calls a session update. As I said, a session is a single thread or a conversation. And then you can send updates, notifications to the client that are not a usual request response, right? It can happen at any time and the client reacts to it. And so here, we are associating the session update with a session ID.
SPEAKER_00
And then there's a type of session updates. There are multiple. We will see more in a second. But this agent message chunk is just, okay, hey, here's some new output from the model. And so if we build again, we go back to Zed. I'm going to have to restart the agent. I go here. And I say, hello. Hello, hello. Well, here's Opus talking, right? You can see we're streaming in these individual chunks, right? Right there we got from Anthropic. So far, so good. Let's hope the demo gods stay with us. Right. So that's one piece. The next thing, though, is I want to—it's a coding agent, right? It's supposed to edit code.
SPEAKER_00
So I can actually tell it already because we have tool support, right? I can tell it to read, for example, this helpers.ts file. And it's going to give me, if the Wi-Fi isn't too slow, a description of what this file actually contains. If you look here, that's basically the description of the file. But again, you don't see it in the UI, right? So now we need to emit more session updates, similar to what we did for text. So if we actually go towards the read file, what we need to do here is the way it works usually in ACP, you emit an initial tool call update, which I'm going to paste in here. So, yeah. Again, we're emitting a session update.
SPEAKER_00
In this case, it's a type of tool call. And then there are multiple properties we can specify, for example, the title, some metadata that Zed uses, for example, for yeah, using some icons and stuff like this. And then we indicate that the status is in progress. And you can also associate tool calls with actual locations. And so now we signaling over ACP that something is in progress, right? This tool call. But then we also want to, at the end, once the tool call has finished, at the bottom here, we want to send another update. And in this case, it's not tool call itself, but it's a tool call update.
SPEAKER_00
So you have to emit on the agent, you have to emit a tool call, a session update of type tool call. And then once the client knows about it, you can emit updates for that. And this is the way that we set the status. And we can also return the content. And then one additional thing that I'm going to do here is, instead of, you can see here, we're calling, I guess it's a bit hard to see, but here we're calling FSREADFile, right? So we're just using the native file system APIs. But ACP actually proxies the file system too. The client can provide a file system capability, and if it does, we can proxy those file system calls over ACP.
SPEAKER_00
And it makes sense, for example, for an editor, right? You want to—if I have unsaved changes in my buffer, they're not actually on the file system, but the agent should still see them. So that's why we call this read text file over ACP. And so, now if you go back here and hit npm run build, and then go back here, restart, and say read this file. Oops, not Rust. Then we should be seeing, yeah. So now we see two calls, right? OK. Something is going wrong because everything is duplicated. Yeah. But here, you can see the output of the actual file.
SPEAKER_00
You want to, if I have unsafe chains in my buffer, they're not actually on the file system, but the agent should still see them. So that's why we call this a retext file over ACP. And so, now if you go back here and hit npm run build, and then go back here, restart, and say read this file. Oops, not Rust. Then we should be seeing, yeah. So now we see two calls, right? OK. Something is going wrong because everything is duplicated. Yeah. But here, you can see the output of the actual file. And because I think I'm low on time, we're going to do the same for edit file. And I'm going to compile again. And if I, again, restart. And say, add a comment at the top of agent, source agent dot TypeScript, then you're going to see, hello world is good. Hopefully Opus is going to decide to add hello world at the top of the file. And we actually get a diff here because over ACP we send a diff of the file. OK.
SPEAKER_00
[SPEAKER_01] Is it obligating the first two tables? Yeah. I think there's something going wrong with the connection. Yeah. No time to debug right now. Sorry. Am I out of time or do I still have? [SPEAKER_02] Is that what? [SPEAKER_02] It's quite gone.
SPEAKER_00
OK. Maybe we can see. So now that, OK. Just another minute. Now that the coding agent supports reading and writing files, we can actually. Oh, great. OK. Then I don't need to rush. Now we can bootstrap the agent itself, right? The coding agent supports reading and writing files. I can just ask it. I prepared a prompt here to add a terminal tool to itself, right? Let's see how this works out with the duplication we're seeing. But basically, I'm telling it something about the ACP APIs. And there are some APIs in ACP, which also, that's another capability of the client, where the client can advertise that it supports creating terminal and managing terminals for the agent. The agent can also do it itself, of course. So we can do this. But it's a nice way for us to add some more interactivity. And so the agent here decided to add a new tool description. Now it, yeah. I'm vibe coding basically this terminal tool. And let's see if it actually builds. It does. Well, that's good. And then, again, I'm going to restart the agent. I'm going to run our demo agent. And I'm going to ask it to run sleep 5 ls. Let's see. There you go. You can see a terminal running. Sleeping five seconds. There is the output. And there's, yeah. And that's it, basically. Thank you. Yeah, so that's how you build an ACP compatible coding agent in 15 minutes or so. Yeah, if you want to check it out, just go to agentclientprotocol.com. In case anyone wants the demo code, but please don't use it in production. It's all agent generated. It's not uploaded yet, but it's an empty repository. I'm going to upload it in a second. Yeah, thank you for listening. Happy to answer questions. That's it. Yeah.
SPEAKER_00
[SPEAKER_01] How does it handle the diffs? Is it only on the client side? The diffs, yeah, we have, in ACP, there are multiple content types. And one content type is diff. And so the agent sends old text, new text, and then that does the diffing for you. Yeah. [SPEAKER_02] Okay, great. I'll show you. Yeah. Cool. Do I need to go? Or can I? One more. Okay, one more question. Yes? [SPEAKER_02] So the connection, is it all mobile hosts between that?
SPEAKER_00
Yeah, the connection works over standard I/O. There are some folks, I think, from the JetBrains people are working on, remote transport, which we're going to have soon, I think. So, yeah. But right now it works over standard I/O. Yeah. And it's going to give me, if the Wi-Fi isn't too slow, it's giving me a description of what this file actually contains. Like if you look here, that's basically the description of the file. But again, you don't see it in the UI, right? So now we need to emit more session updates, similar to like what we did for text. So if we actually go towards the read file, kind of what we need to do here is like the
SPEAKER_00
way it works usually in ACP, like you emit an initial tool call update, which I'm going to paste in here. So, yeah. Again, we're emitting a session update. In this case, it's a type of tool call. And then there are multiple properties we can specify, for example, like the title, kind of some kind of metadata that Zed uses, for example, for like, yeah, using some icons and stuff like this. And then we indicate that the status is in progress. And you can also like associate tool calls with like actual locations. And so now we signaling over ACP that something is in progress, right? This tool call.
SPEAKER_00
But then we also want to, at the end, like once the tool call has finished, like at the bottom here, we kind of want to send another update. And in this case, it's not tool call itself, but it's a tool call update. So, you have to emit on the agent, you have to emit like a tool call, like session update of type tool call. And then once the client knows about it, you can emit updates for that. And this is the way that we set the status. And we can also return the content. And then one additional thing that I'm going to do here is the, instead of like, you can see here, we're calling, I guess it's a bit hard to see, but like here we're calling FSREADFile, right?
SPEAKER_00
So, we're just using the native file system APIs. But ACP actually proxies the file system too. Like the client can provide a file system capability, and if it does, we can like proxy those file system two calls over ACP. And it kind of makes sense, for example, for an editor, right? You want to, if I have unsafe chains in my buffer, they're not actually on the file system, but the agent should still see them. So that's why we call this like a retext file over ACP. And so, now if you go back here and hit npm run build, and then go back here, restart, and say read this file. Oops, not Rust.
SPEAKER_00
Then we should be seeing, yeah. So now we see two calls, right? OK. Something is going wrong because everything is duplicated.
SPEAKER_00
Yeah. But like here, you can see the output of the actual file. And because I think I'm low on time, we're basically going to do the same for edit file.
SPEAKER_00
And I'm going to compile again. And if I, again, restart.
SPEAKER_00
And say, add a comment at the top of agent, source agent dot TypeScript, then you're going to see, hello world is good.
SPEAKER_00
Hopefully Opus is going to decide to add hello world at the top of the file. And we actually get a diff here because the over ACP we send like a diff of the file. OK.
SPEAKER_01
Is it obligating the first two tables? Yeah.
SPEAKER_00
I think there's something going wrong with the connection. I'm, yeah. Yeah. No time to debug right now. Sorry. Am I out of time or do I still have?
SPEAKER_02
Um. Is that what? It's quite gone.
SPEAKER_00
OK. Maybe like we can see. So now that, OK. Just another minute. Now that the coding agent supports reading and writing files, we can actually. Oh, great. OK. Then I don't need to rush. Now we can kind of bootstrap the agent itself, right? Like the coding agent supports reading and writing files. I can just ask it. I prepared a prompt here to add a terminal tool to itself, right? Let's see how this works out with like the duplication we're seeing. But basically, I'm telling it something about the ACP APIs. And there's some APIs in ACP, which also, that's another like capability of the client, where
SPEAKER_00
the client can advertise that it supports grading terminal and managing terminals for the agent. Like the agent can also do it itself, of course. So we can do this. But it's a nice way for us to add some more interactivity. And so the agent here decided to add a new tool description. Now it, yeah. I'm vibe coding basically this terminal tool. And let's see if it actually builds. It does. Well, that's good. And then, again, I'm going to restart the agent. I'm going to run our demo agent. And I'm going to ask it to run sleep5ls. Let's see. There you go. You can see a terminal running. Sleeping five seconds. There is the output. And there's, yeah. And that's it, basically.
SPEAKER_00
Thank you. Yeah, so that's how you kind of build an ACP compatible coding agent in 15 minutes or so. Yeah, if you want to check it out, just go to agentclientprotocol.com. In case anyone wants the demo code, but please don't use it in production. It's all agent generated. It's not uploaded yet, but it's an empty repository. I'm going to upload it in a second. Yeah, thank you for listening. Happy to answer questions. That's it. Yeah.
SPEAKER_01
How does it handle the diffs? Is it only on the client side?
SPEAKER_00
The diffs, yeah, we have, like, in ACP, there are multiple content types. And one content type is diff. And so the agent sends old text, new text, and then that does the diffing for you. Yeah.
SPEAKER_02
Okay, great. I'll show you.
SPEAKER_00
Yeah. Cool. Do I need to go? Or can I? One more. Okay, one more question. Yes?
SPEAKER_02
So the connection, is it all mobile hosts between that?
SPEAKER_00
Yeah, the connection works over standard I.O. There are some folks, I think, from the JetPrints people are working on, like, remote transport, which we're going to have soon, I think. So, yeah. But right now it works over standard I.O. Yeah.
namenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamename