# pms-infra-agent-socket
----
## β οΈ Caution
**Do not grant unrestricted control to AI.**
Unsupervised use or misuse may lead to unintended consequences.
All AI systems must remain strictly under human oversight and control.
Use responsibly, with full awareness and at your own risk.
----
## π Overview
**`pms-infra-agent-socket`** is a Haskell infrastructure library that provides AI agents with direct, low-level control over socket-based communication endpoints.
Unlike higher-level abstractions, this library exposes raw byte-stream access over both TCP/IP and Unix Domain Sockets, allowing agents to handle protocol-specific exchanges such as Telnet IAC negotiation, binary protocol framing, and real-time I/O monitoring with fine-grained control.
The library is a core component of the [`pty-mcp-server`](https://github.com/phoityne/pty-mcp-server) ecosystem and implements the `agent-socket-*` family of MCP tools.
---
## π§ Provided MCP Tools
### `agent-socket-open`
Opens a socket connection for subsequent read/write operations.
Supports both **TCP** (`host` + `port`) and **Unix Domain Socket** (`file`) connections via a single tool.
- `host` β TCP host name or IP address
- `port` β TCP service name or port number
- `file` β Unix domain socket path (absolute path required on Windows)
Only one socket connection can be active at a time.
### `agent-socket-close`
Closes the active socket connection and releases all associated resources.
### `agent-socket-read`
Reads up to the specified number of bytes from the active socket connection and returns the data as a **UTF-8 string**.
Returns an empty string if no data is available before timeout.
- `length` β Maximum number of bytes to read
> β οΈ If the received data contains non-UTF-8 bytes (e.g., binary protocols, IAC bytes), use `agent-socket-read-byte` instead.
### `agent-socket-read-byte`
Reads up to the specified number of bytes from the active socket connection and returns the data as an **uppercase hex string** (e.g., `FF0A1B41`).
Use this for binary protocols or when precise byte-level inspection is required.
- `length` β Maximum number of bytes to read
### `agent-socket-write`
Writes the specified **UTF-8 string** to the active socket connection.
- `data` β Text data to write
> β οΈ Escape sequences such as `\r\n` in the string are sent as literal characters. To send exact byte sequences (e.g., CR+LF = `0D0A`), use `agent-socket-write-byte`.
### `agent-socket-write-byte`
Decodes the specified **hex string** and writes the resulting bytes to the active socket connection.
Use this for binary protocols or when precise byte-level control is required.
- `data` β Uppercase hex string to decode and write (e.g., `DEADBEEF0D0A`)
---
## π‘ Usage Notes
### AI-to-AI communication
Any two AI agents that each have a `pty-mcp-server` instance can communicate with each other directly over TCP β no shared infrastructure, message broker, or custom integration required.
One agent uses the `agent-server-*` tools to act as a TCP server; the other uses the `agent-socket-*` tools (this library) to act as a TCP client. The agents exchange messages using a lightweight text protocol, and each agent interprets and responds to the other's messages autonomously.
```
βββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
β AI Agent A β β AI Agent B β
β (e.g. Claude on Machine A) β β (e.g. GPT on Machine B) β
β β β β
β pty-mcp-server β TCP β pty-mcp-server β
β agent-server-listen :19999 ββββββββββΊβ agent-socket-open β
β agent-server-write β β agent-socket-read β
β agent-server-events β β agent-socket-write β
βββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
Server role Client role
```
#### What this enables
- **Collaborative task solving** β Agent A breaks down a problem and delegates subtasks to Agent B, collecting results over the socket.
- **Cross-model review** β One model generates code or text; another model on a different machine reviews it and sends back comments.
- **Heterogeneous agent pipelines** β Chain agents of different models or specialisations (Claude, GPT, Gemini, β¦) into a processing pipeline across machines.
- **Autonomous negotiation** β Agents can exchange proposals, counter-proposals, and decisions without human involvement in each round-trip.
- **Distributed tool use** β Agent B may have access to tools (databases, sensors, local files) that Agent A does not. Agent A requests operations from Agent B over the socket.
#### Communication protocol
The `pty-mcp-server` ecosystem ships a lightweight handshake and messaging protocol for AI-to-AI sessions. Prompt skills (`skill_agent_server.md` / `skill_agent_client.md`) are provided so each agent knows exactly how to play its role.
**Handshake sequence:**
```
Server β Client : HELLO? name?\r\n
Server β Client : NAME: <name>\r\n
Server β Client : RULES: MSG:<content>\r\n | REPLY:<content>\r\n | BYE\r\n | HEX:<hex>\r\n
Server β Client : ACK\r\n
```
**Conversation:**
```
Server β Client : MSG: <content>\r\n
Server β Client : REPLY: <content>\r\n
```
**Graceful shutdown (server-initiated):**
```
Server β Client : BYE\r\n
Server β Client : ACK\r\n β server waits for this before closing
Server : agent-server-close
```
#### Full session example
The following shows a complete exchange where Agent A (server role) delegates a task to Agent B (client role).
**Agent A β server side**
```
agent-server-listen host=172.16.0.43 port=19999
β "listening."
--- Agent B connects ---
agent-server-events
β [{ "tag": "ClientConnected" }]
agent-server-write-byte "48454C4C4F3F206E616D653F0D0A" ("HELLO? name?\r\n")
agent-server-events (poll until BytesReceived)
β bytes: "4E414D453A20416765742D420D0A" ("NAME: Agent-B\r\n")
agent-server-write-byte "<hex of RULES: MSG:... | REPLY:... | BYE\r\n>"
agent-server-events (poll for ACK)
β bytes: "41434B0D0A" ("ACK\r\n")
--- handshake complete ---
agent-server-write-byte "<hex of MSG: Please summarise this text: ...\r\n>"
agent-server-events (poll for REPLY)
β bytes: "<hex of REPLY: Here is the summary: ...\r\n>"
agent-server-write-byte "<hex of BYE\r\n>"
agent-server-events (poll for ACK)
β bytes: "41434B0D0A" ("ACK\r\n")
agent-server-close
```
**Agent B β client side**
```
agent-socket-open host=172.16.0.43 port=19999
β socket connected to 172.16.0.43:19999
agent-socket-read length=256
β "HELLO? name?\r\n"
agent-socket-write-byte "<hex of NAME: Agent-B\r\n>"
agent-socket-read length=256
β "RULES: MSG:<content>\r\n | REPLY:<content>\r\n | BYE\r\n | HEX:<hex>\r\n"
agent-socket-write-byte "<hex of ACK\r\n>"
--- handshake complete ---
agent-socket-read length=1024
β "MSG: Please summarise this text: ...\r\n"
--- Agent B processes the request autonomously ---
agent-socket-write-byte "<hex of REPLY: Here is the summary: ...\r\n>"
agent-socket-read length=256
β "BYE\r\n"
agent-socket-write-byte "<hex of ACK\r\n>"
agent-socket-close
```
> π‘ Always use `agent-socket-write-byte` (not `agent-socket-write`) to ensure `\r\n` is sent as correct CRLF bytes.
> Generate hex strings with: `python3 -c "print('your message\r\n'.encode().hex())"`
---
### IAC / Telnet negotiation
Telnet IAC processing is the **responsibility of the agent**, not the library.
Use `agent-socket-read-byte` and `agent-socket-write-byte` to handle IAC sequences at the byte level.
Example negotiation flow:
```
β FFFD18FFFD20FFFD23FFFD27 (Server: IAC DO TERMINAL-TYPE/SPEED/X-DISPLAY/NEW-ENVIRON)
β FFFC18FFFC20FFFC23FFFC27 (Agent: IAC WONT x4)
β FFFB03FFFD01FFFD1FFFFB05 (Server: IAC WILL SGA / DO ECHO / DO NAWS / WILL STATUS)
β FFFD03FFFB01FFFC1FFFFD05 (Agent: IAC DO SGA / WILL ECHO / WONT NAWS / DO STATUS)
β "localhost login: " (Login prompt β read as UTF-8 string)
β "phoityne\r\n" (Username β send as bytes: 70686F6974796E650D0A)
β "Password: " (Password prompt)
```
Full login session example (confirmed with a real Linux Telnet server):
```
agent-socket-open host=172.16.0.171 port=23
β socket connected to 172.16.0.171:23
β FFFD18FFFD20FFFD23FFFD27
(IAC DO TERMINAL-TYPE / DO TERMINAL-SPEED / DO X-DISPLAY / DO NEW-ENVIRON)
β FFFC18FFFC20FFFC23FFFC27
(IAC WONT x4)
β FFFB03FFFD01FFFD1FFFFB05FFFD21
(IAC WILL SGA / DO ECHO / DO NAWS / WILL STATUS / DO REMOTE-FLOW-CONTROL)
β FFFD03FFFB01FFFC1FFFFD05FFFC21
(IAC DO SGA / WILL ECHO / WONT NAWS / DO STATUS / WONT REMOTE-FLOW-CONTROL)
β FFFE01FFFB01 + "Kernel 6.12.0-...\r\nlocalhost login: "
β 61692D6167656E740D0A ("ai-agent\r\n" β username as bytes)
β "ai-agent\r\nPassword: "
β 61692D6167656E740D0A ("ai-agent\r\n" β password as bytes)
β "Last login: ...\r\n[ai-agent@localhost ~]$ "
β 686F73746E616D650D0A ("hostname\r\n")
β "hostname\r\nlocalhost.localdomain\r\n[ai-agent@localhost ~]$ "
β 657869740D0A ("exit\r\n")
β "exit\r\nlogout\r\n"
agent-socket-close
β socket is closed.
```
### Unix Domain Socket on Windows
Unix Domain Socket (`file` parameter) is supported on **Windows 10 build 1803 and later** at the OS level.
However, the socket server side must be implemented in a runtime that supports `AF_UNIX` on Windows.
- **Python 3.13.7 (Windows)**: `socket.AF_UNIX` is unavailable unless **Developer Mode** is enabled.
- **PowerShell / .NET**: `System.Net.Sockets.UnixDomainSocketEndPoint` works without Developer Mode.
- **pty-mcp-server (Haskell)**: Confirmed working on Windows 11 via `Network.Socket` with `AF_UNIX`.
---
## ποΈ Architecture
### Module Structure
```
PMS.Infra.Agent.Socket
βββ DM.Type -- Data type definitions (SocketData, AppData, tool parameter types)
βββ DM.Constant -- Constants
βββ DS.Core -- Core domain service logic
βββ DS.Utility -- Utility functions (TCP connect, Unix Domain Socket connect)
βββ App.Control -- Application control: tool dispatch and socket lifecycle management
```
### Key Design Points
- **Single active connection**: Only one socket can be open at a time per server instance. State is managed via `STM.TMVar`.
- **Dual connection mode**: `agent-socket-open` supports both TCP (`host`/`port`) and Unix Domain Socket (`file`) via a single unified interface.
- **Non-blocking reads**: Read operations return immediately with available data, allowing the agent to poll at its own pace.
- **Byte-level I/F**: In addition to UTF-8 string I/F, hex string I/F is provided for binary protocol handling. IAC processing and protocol framing are delegated to the agent.
---
## π¦ Dependencies
- [`pms-domain-model`](https://github.com/phoityne/pms-domain-model)
---
## π Credits & License
- **Execution & Process Lead:** Claude Sonnet 4.6, Gemini 3 Flash, GPT-5.5
- **Direction & Policy:** phoityne
- **License:** Apache-2.0 β see [LICENSE](./LICENSE)
---