Skip to main content

TCP & WebSocket

TCP synchronous request

Connect, send, read response, close — in a single call. Works in all modes.

import { tcpRequest } from "@luminarys/sdk-as";

// Plain TCP
const result = tcpRequest(
"redis:6379",
Uint8Array.wrap(String.UTF8.encode("PING\r\n")),
);
const text = String.UTF8.decode(result.buffer);

// TLS (verified)
const result2 = tcpRequest(
"api.example.com:443",
Uint8Array.wrap(String.UTF8.encode("GET / HTTP/1.0\r\nHost: api.example.com\r\n\r\n")),
true, // tls
);

// TLS (self-signed cert, dev only)
const result3 = tcpRequest(
"localhost:9443",
Uint8Array.wrap(String.UTF8.encode("GET /ping HTTP/1.1\r\nHost: localhost\r\n\r\n")),
true, // tls
true, // insecure
);

TcpRequestOptions:

FieldTypeDefaultDescription
addrstringrequiredHost:port to connect to
databytesrequiredPayload to send
tlsboolfalseUse TLS encryption
insecureboolfalseSkip TLS certificate verification (dev only)
timeout_msint0 (30s)Total timeout for connect+send+read
max_bytesint0 (1 MB)Max response size

TcpRequestResult:

FieldTypeDescription
databytesResponse payload

TCP persistent connection

Agent mode only

Callback-based TCP requires the built-in agent. In MCP mode, use tcpRequest instead.

Push-model: incoming data is delivered to a callback method. tcpClose can be safely called from inside the callback.

import { tcpConnect, tcpWrite, tcpClose } from "@luminarys/sdk-as";

// Plain TCP
const connId = tcpConnect("redis:6379", "on_data");

// TLS (verified)
const connId2 = tcpConnect("api.example.com:443", "on_data", true);

// TLS (self-signed, dev only)
const connId3 = tcpConnect("localhost:9443", "on_data", true, true);

// Send data
tcpWrite(connId, Uint8Array.wrap(String.UTF8.encode("PING\r\n")));

// Close (safe to call from callback)
tcpClose(connId);

TcpConnectOptions:

FieldTypeDefaultDescription
addrstringrequiredHost:port to connect to
callbackstring""Skill method name for incoming data (empty = drain)
tlsboolfalseUse TLS encryption
insecureboolfalseSkip TLS certificate verification (dev only)
server_namestring""Override TLS SNI hostname (empty = use host from addr)
timeout_msint0 (30s)Dial timeout

Callback signature

The callback method receives a ConnEvent with connection ID, data, and error info. Use @skill:callback to hide from MCP.

import { tcpClose, ERROR_KIND_EOF } from "@luminarys/sdk-as";

// @skill:callback
// @skill:method on_data "TCP read callback."
// @skill:param conn_id required "Connection ID"
// @skill:param data optional "Received bytes" type:bytes
// @skill:param error_kind optional "Error kind"
// @skill:param error_msg optional "Error message"
export function onData(_ctx: Context, conn_id: string, data: Uint8Array,
error_kind: string, error_msg: string): string {
if (data.length > 0) {
// process data...
const text = String.UTF8.decode(data.buffer);
}
if (error_kind == ERROR_KIND_EOF) {
tcpClose(conn_id); // safe from callback
}
return "ok";
}

ConnEvent fields (delivered as method parameters):

FieldTypeDescription
conn_idstringConnection ID
databytesReceived data (empty on error events)
error_kindstringError classification (empty on data events)
error_msgstringError details (empty on data events)

Error kinds (delivered to callback when connection fails):

ConstantValueDescription
ERROR_KIND_NONE / ErrorKindNone / ErrorKind::None""Data event — no error
ERROR_KIND_EOF / ErrorKindEOF / ErrorKind::Eof"eof"Remote peer closed gracefully
ERROR_KIND_RESET / ErrorKindReset / ErrorKind::Reset"reset"Connection reset by peer
ERROR_KIND_TIMEOUT / ErrorKindTimeout / ErrorKind::Timeout"timeout"Read deadline exceeded
ERROR_KIND_TLS / ErrorKindTLS / ErrorKind::Tls"tls"TLS handshake or record error
ERROR_KIND_IO / ErrorKindIO / ErrorKind::Io"io"Generic I/O error

WebSocket

Agent mode only

WebSocket requires the built-in agent. Not available in MCP mode.

Connect, send, close

import { wsConnect, wsSend, wsClose, HttpHeader, WS_MESSAGE_TEXT } from "@luminarys/sdk-as";

const h = new HttpHeader(); h.name = "Authorization"; h.value = "Bearer token123";
const connId = wsConnect("wss://api.example.com/ws", [h], 0, "on_ws_msg");

// Send text message
wsSend(connId, Uint8Array.wrap(String.UTF8.encode('{"type":"subscribe"}')), WS_MESSAGE_TEXT);

// Close with normal code
wsClose(connId, 1000, "normal closure");

wsConnect parameters:

FieldTypeDefaultDescription
urlstringrequiredWebSocket URL (ws:// or wss://)
headersHeader[][]Request headers (e.g. Authorization)
timeout_msint0 (30s)Handshake timeout
callbackstring""Skill method name for incoming messages
insecureboolfalseSkip TLS certificate verification for wss:// (dev only)

Message type constants

ConstantValueDescription
WS_MESSAGE_TEXT / WsMessageText / WS_MESSAGE_TEXT"text"UTF-8 text message
WS_MESSAGE_BINARY / WsMessageBinary / WS_MESSAGE_BINARY"binary"Binary message
WS_MESSAGE_CLOSE / WsMessageClose / WS_MESSAGE_CLOSE"close"Close frame received

Callback signature

The callback receives a WsEvent with the message data. Use @skill:callback to hide from MCP.

import { wsClose, WS_MESSAGE_TEXT, WS_MESSAGE_CLOSE, ERROR_KIND_EOF } from "@luminarys/sdk-as";

// @skill:callback
// @skill:method on_ws_msg "WebSocket callback."
// @skill:param conn_id required "Connection ID"
// @skill:param data optional "Message payload" type:bytes
// @skill:param message_type optional "text, binary, or close"
// @skill:param close_code optional "Close status code"
// @skill:param close_text optional "Close reason"
// @skill:param error_kind optional "Error kind"
// @skill:param error_msg optional "Error details"
export function onWsMsg(_ctx: Context, conn_id: string, data: Uint8Array,
message_type: string, close_code: i64, close_text: string,
error_kind: string, error_msg: string): string {
if (message_type == WS_MESSAGE_TEXT) {
const text = String.UTF8.decode(data.buffer);
// process text message...
}
if (message_type == WS_MESSAGE_CLOSE || error_kind == ERROR_KIND_EOF) {
// connection closing
}
return "ok";
}

WsEvent fields (delivered as method parameters):

FieldTypeDescription
conn_idstringConnection ID
databytesMessage payload (empty on close/error)
message_typestring"text", "binary", or "close"
close_codeintWebSocket close status code (only when message_type = "close")
close_textstringClose reason text (only when message_type = "close")
error_kindstringError classification (same constants as TCP)
error_msgstringError details

Permissions

tcp:
enabled: true
allowlist:
- "redis:6379"
- "*.internal:*"

http:
enabled: true
allow_websocket: true
allowlist:
- "wss://api.example.com/**"
  • TCP: tcp.enabled: true, optionally tcp.allowlist with host:port patterns
  • WebSocket: http.enabled: true + http.allow_websocket: true with WebSocket URL in http.allowlist