Skip to content

Commit 2c2a818

Browse files
committed
feat(acp-nats-ws): add WebSocket-to-NATS bridge binary crate
Translates ACP messages between WebSocket connections and NATS, letting browser-based UIs and remote clients talk to distributed agent backends over a standard WebSocket endpoint. - axum server with per-connection ACP bridge via LocalSet + spawn_local - Duplex-pipe adapter bridging WS text frames to newline-delimited JSON-RPC streams (aligned with MCP SEP-1288) - Graceful shutdown (SIGINT/SIGTERM) with broadcast drain and LocalSet post-drain for sub-task cleanup - OpenTelemetry integration (logs, metrics, traces) and file logging - CLI + env-var configuration with clap, matching acp-nats-stdio
1 parent 9ab513a commit 2c2a818

11 files changed

Lines changed: 1306 additions & 0 deletions

File tree

rsworkspace/Cargo.lock

Lines changed: 157 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "acp-nats-ws"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
acp-nats = { path = "../acp-nats" }
8+
agent-client-protocol = "0.9.4"
9+
async-compat = "0.2.5"
10+
async-nats = "0.45.0"
11+
axum = { version = "0.8.8", features = ["ws"] }
12+
clap = { version = "4.5", features = ["derive", "env"] }
13+
futures-util = { version = "0.3", features = ["sink"] }
14+
opentelemetry = "0.31.0"
15+
opentelemetry-appender-tracing = "0.31.0"
16+
opentelemetry-otlp = { version = "0.31.0", features = ["http-json", "logs", "metrics", "reqwest-rustls"] }
17+
opentelemetry_sdk = { version = "0.31.0", features = ["rt-tokio", "logs", "metrics"] }
18+
tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros", "signal", "net", "sync", "io-util"] }
19+
tower-http = { version = "0.6.8", features = ["trace"] }
20+
tracing = "0.1.44"
21+
tracing-opentelemetry = "0.32.1"
22+
tracing-subscriber = { version = "0.3.22", features = ["env-filter", "fmt", "json"] }
23+
trogon-std = { path = "../trogon-std" }
24+
25+
[dev-dependencies]
26+
trogon-std = { path = "../trogon-std", features = ["test-support"] }
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# ACP NATS WebSocket
2+
3+
Translates [Agent Client Protocol](https://agentclientprotocol.com) (ACP) messages between WebSocket connections and [NATS](https://nats.io), letting browser-based UIs and remote clients talk to distributed agent backends over a standard WebSocket endpoint.
4+
5+
For managed NATS infrastructure in production, we recommend <a href="https://synadia.com"><img src="../acp-nats-stdio/assets/synadia-logo.png" alt="Synadia" width="20" style="vertical-align: middle;"> Synadia</a>.
6+
7+
```mermaid
8+
graph LR
9+
A1[Client1] <-->|ws| B[acp-nats-ws]
10+
A2[Client2] <-->|ws| B
11+
AN[ClientN] <-->|ws| B
12+
B <-->|NATS| C[Backend]
13+
```
14+
15+
## Features
16+
17+
- Multiple concurrent WebSocket connections, each with its own ACP session
18+
- Bidirectional ACP bridge with request forwarding
19+
- OpenTelemetry integration (logs, metrics, traces)
20+
- Graceful shutdown (SIGINT/SIGTERM) with per-connection drain
21+
- Custom prefix support for multi-tenancy
22+
23+
## Quick Start
24+
25+
```bash
26+
docker run -p 4222:4222 nats:latest
27+
28+
cargo build --release -p acp-nats-ws
29+
30+
./target/release/acp-nats-ws
31+
```
32+
33+
Connect with any WebSocket client:
34+
35+
```bash
36+
websocat ws://127.0.0.1:8080/ws
37+
```
38+
39+
## Configuration
40+
41+
### WebSocket Server
42+
43+
| Variable | CLI Flag | Description | Default |
44+
|----------|----------|-------------|---------|
45+
| `ACP_WS_HOST` | `--host` | Listen address | `127.0.0.1` |
46+
| `ACP_WS_PORT` | `--port` | Listen port | `8080` |
47+
48+
### ACP
49+
50+
| Variable | Description | Default |
51+
|----------|-------------|---------|
52+
| `ACP_PREFIX` | Subject prefix for multi-tenancy | `acp` |
53+
| `ACP_OPERATION_TIMEOUT_SECS` | Timeout for NATS request/reply operations | built-in default |
54+
| `ACP_PROMPT_TIMEOUT_SECS` | Timeout for prompt round-trips | built-in default |
55+
| `ACP_NATS_CONNECT_TIMEOUT_SECS` | NATS connection timeout | `10` |
56+
57+
CLI flag `--acp-prefix` overrides `ACP_PREFIX`.
58+
59+
### NATS
60+
61+
| Variable | Description | Default |
62+
|----------|-------------|---------|
63+
| `NATS_URL` | Server URL(s), comma-separated for failover | `localhost:4222` |
64+
65+
### NATS Authentication
66+
67+
Resolved in priority order — the first match wins:
68+
69+
| Priority | Variable(s) | Method |
70+
|----------|-------------|--------|
71+
| 1 | `NATS_CREDS` | Credentials file path |
72+
| 2 | `NATS_NKEY` | NKey seed |
73+
| 3 | `NATS_USER` + `NATS_PASSWORD` | Username/password |
74+
| 4 | `NATS_TOKEN` | Token |
75+
76+
If none are set, the connection is unauthenticated.
77+
78+
### Observability
79+
80+
| Variable | Description |
81+
|----------|-------------|
82+
| `RUST_LOG` | Tracing filter directive (default: `info`) |
83+
| `ACP_LOG_DIR` | Directory for file-based logging |
84+
85+
### OpenTelemetry
86+
87+
Traces, metrics, and logs are exported over HTTP. The following [OTLP environment variables](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) are supported:
88+
89+
| Variable | Description |
90+
|----------|-------------|
91+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Collector base URL (e.g. `http://localhost:4318`) |
92+
| `OTEL_EXPORTER_OTLP_HEADERS` | Custom headers, comma-separated `key=value` pairs (e.g. auth tokens) |
93+
| `OTEL_EXPORTER_OTLP_TIMEOUT` | Export timeout in milliseconds (default: `10000`) |
94+
| `OTEL_RESOURCE_ATTRIBUTES` | Additional resource attributes, comma-separated `key=value` pairs |
95+
96+
`OTEL_SERVICE_NAME` is hardcoded to `acp-nats-ws` and cannot be overridden.

0 commit comments

Comments
 (0)