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.

8 min read · vinhnx/vtcode-acp-client

A narrow brass conduit feeds one central switch into several separate rooms, each room representing a different module that needs the same live connection. The image explains the crate's main idea: a single protocol link can be shared safely instead of duplicated everywhere.
One connection, many consumers. The crate exists to make that shared seam boring and safe.
Key Takeaways

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

vinhnx, Author/Maintainer · crates.io package page
A hedcut-style portrait of the repository owner, based on their verified GitHub avatar. This gives the article a face for the maintainer behind the crate and grounds the project's minimal design in a real builder.

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.

The crate sits between many consumers and one protocol connection, turning shared ownership into a simple read path.

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.

ApproachWhat you gainWhat you riskBest fit
Pass the connection through every layerNo global stateBoilerplate, plumbing, and brittle signaturesSmall call stacks
Create a new client per moduleSimple local ownershipDuplicate sessions and harder coordinationIndependent tools with no shared state
OnceLock + Arc singletonOne shared ACP connection with safe readsA deliberate global seamA 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.

formulahendry, Developer of VS Code ACP Client · DEV Community post

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.