vtcode-acp-client: The Rust crate that makes one ACP connection feel global on purpose
vtcode-acp-client is a tiny thread-safe seam for sharing a single Agent Client Protocol connection across a Rust agent stack, without passing state through every layer.
- vtcode-acp-client treats global state as a deliberate architectural seam, not a convenience shortcut.
- Its real job is to make one ACP connection safely reachable across many Rust modules with OnceLock and Arc.
- The crate is tiny because it refuses to become a framework and instead solves one hard integration problem cleanly.
- Its value comes from sitting inside a broader ACP ecosystem that standardizes how editors and agents talk.
Global singletons usually deserve suspicion. In vtcode-acp-client, the singleton is the point. The crate exists to hold one live Agent Client Protocol connection and make it available anywhere in the VT Code stack without dragging connection state through every function call.
A seam, not a framework
The repository is almost aggressively small. The logic lives in src/lib.rs, and the core pattern is familiar to Rust developers: OnceLock for one-time initialization and Arc for shared ownership. That combination gives the crate a narrow promise. Register the connection once. Read it anywhere. Do both without racing.
ACP client implementation for inter-agent communication and orchestration
What the code is really doing
use std::sync::{Arc, OnceLock};
use agent_client_protocol::AgentSideConnection;
static ACP_CLIENT: OnceLock<Arc<AgentSideConnection>> = OnceLock::new();
pub fn register_acp_client(client: Arc<AgentSideConnection>) -> Result<(), Arc<AgentSideConnection>> {
ACP_CLIENT.set(client)
}
pub fn acp_client() -> Option<Arc<AgentSideConnection>> {
ACP_CLIENT.get().cloned()
}
That API says a lot with very little. register_acp_client tries to install the connection once, and if something is already there, it hands the caller back the same Arc instead of discarding it. acp_client returns an Option, so consumers have to handle the uninitialized case explicitly. No hidden nulls. No mutable global object. Just one shared handle with a clear lifecycle.
Why this pattern works in Rust
The trade-off is intentional. A normal global variable invites accidental mutation, but a OnceLock<Arc<...>> is closer to a sealed registry. It lets VT Code keep one ACP session alive while multiple subsystems, editors, or agent-facing modules observe the same state. That matters in a protocol stack, where duplication is not just wasteful. It can create competing connections, split messages, and confusing lifecycle bugs.
| Approach | What you gain | What you risk | Best fit |
|---|---|---|---|
| Pass the connection through every layer | No global state | Boilerplate, plumbing, and brittle signatures | Small call stacks |
| Create a new client per module | Simple local ownership | Duplicate sessions and harder coordination | Independent tools with no shared state |
| OnceLock + Arc singleton | One shared ACP connection with safe reads | A deliberate global seam | A protocol client used across many Rust modules |
The bigger ACP story
The Agent Client Protocol (ACP) standardizes communication between code editors/IDEs and coding agents. Similar to how the Language Server Protocol (LSP) standardized language server integration.
That standardization context is what makes this crate interesting. VT Code is not building a one-off integration path. It is participating in a protocol layer that can plug into editors and other agent-aware tools. vtcode-acp-client is the unglamorous part that makes the integration durable: one connection, one registration point, many readers.
The result is not a large SDK or a sprawling abstraction. It is a small, opinionated utility that keeps the ACP connection where the rest of the system can reach it. That restraint is the story. In a codebase built around agent communication, the most useful component may be the one that does almost nothing except keep state from becoming chaos.





