websocket
Specialized skill for WebSocket protocol implementation and testing. Generate RFC 6455 compliant implementations, validate handshake and framing, test with Autobahn Test Suite, implement compression, and debug connection issues.
Best use case
websocket is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Specialized skill for WebSocket protocol implementation and testing. Generate RFC 6455 compliant implementations, validate handshake and framing, test with Autobahn Test Suite, implement compression, and debug connection issues.
Teams using websocket should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/websocket/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How websocket Compares
| Feature / Agent | websocket | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Specialized skill for WebSocket protocol implementation and testing. Generate RFC 6455 compliant implementations, validate handshake and framing, test with Autobahn Test Suite, implement compression, and debug connection issues.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
SKILL.md Source
# websocket
You are **websocket** - a specialized skill for WebSocket protocol implementation and testing, providing deep expertise in RFC 6455 compliance, real-time messaging, and performance optimization.
## Overview
This skill enables AI-powered WebSocket operations including:
- Generating RFC 6455 compliant implementations
- Validating WebSocket handshake and framing
- Testing with Autobahn Test Suite
- Implementing permessage-deflate compression
- Debugging WebSocket connection issues
- Generating subprotocol handlers
- Analyzing WebSocket traffic
## Prerequisites
- WebSocket-capable runtime (Node.js, Python, Go, etc.)
- Optional: `wscat` or `websocat` for CLI testing
- Optional: Autobahn Test Suite for compliance testing
## Capabilities
### 1. WebSocket Handshake
Implement RFC 6455 compliant handshake:
```javascript
const crypto = require('crypto');
const http = require('http');
const WS_MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
function computeAcceptKey(secWebSocketKey) {
return crypto
.createHash('sha1')
.update(secWebSocketKey + WS_MAGIC_STRING)
.digest('base64');
}
function handleUpgrade(req, socket) {
// Validate upgrade request
if (req.headers['upgrade']?.toLowerCase() !== 'websocket') {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
return false;
}
const key = req.headers['sec-websocket-key'];
if (!key) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
return false;
}
// Validate key format (16 bytes base64 encoded)
const keyBytes = Buffer.from(key, 'base64');
if (keyBytes.length !== 16) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
return false;
}
const acceptKey = computeAcceptKey(key);
// Optional: Handle subprotocol negotiation
const requestedProtocols = req.headers['sec-websocket-protocol']?.split(',').map(p => p.trim()) || [];
const selectedProtocol = negotiateProtocol(requestedProtocols);
// Build response headers
let response = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${acceptKey}`
];
if (selectedProtocol) {
response.push(`Sec-WebSocket-Protocol: ${selectedProtocol}`);
}
// Optional: Handle extensions
const extensions = negotiateExtensions(req.headers['sec-websocket-extensions']);
if (extensions) {
response.push(`Sec-WebSocket-Extensions: ${extensions}`);
}
socket.write(response.join('\r\n') + '\r\n\r\n');
return true;
}
function negotiateProtocol(requested) {
const supported = ['graphql-ws', 'wamp.2.json', 'mqtt'];
return requested.find(p => supported.includes(p)) || null;
}
function negotiateExtensions(extensionHeader) {
// Example: permessage-deflate negotiation
if (extensionHeader?.includes('permessage-deflate')) {
return 'permessage-deflate; server_no_context_takeover; client_no_context_takeover';
}
return null;
}
```
### 2. WebSocket Frame Parsing
Parse and create WebSocket frames:
```javascript
const OPCODES = {
CONTINUATION: 0x0,
TEXT: 0x1,
BINARY: 0x2,
CLOSE: 0x8,
PING: 0x9,
PONG: 0xA
};
class WebSocketFrame {
constructor() {
this.fin = true;
this.rsv1 = false;
this.rsv2 = false;
this.rsv3 = false;
this.opcode = OPCODES.TEXT;
this.masked = false;
this.maskingKey = null;
this.payload = Buffer.alloc(0);
}
static parse(buffer) {
if (buffer.length < 2) return { frame: null, consumed: 0 };
const frame = new WebSocketFrame();
let offset = 0;
// First byte: FIN, RSV1-3, Opcode
const byte0 = buffer[offset++];
frame.fin = (byte0 & 0x80) !== 0;
frame.rsv1 = (byte0 & 0x40) !== 0;
frame.rsv2 = (byte0 & 0x20) !== 0;
frame.rsv3 = (byte0 & 0x10) !== 0;
frame.opcode = byte0 & 0x0F;
// Second byte: MASK, Payload length
const byte1 = buffer[offset++];
frame.masked = (byte1 & 0x80) !== 0;
let payloadLength = byte1 & 0x7F;
// Extended payload length
if (payloadLength === 126) {
if (buffer.length < offset + 2) return { frame: null, consumed: 0 };
payloadLength = buffer.readUInt16BE(offset);
offset += 2;
} else if (payloadLength === 127) {
if (buffer.length < offset + 8) return { frame: null, consumed: 0 };
// JavaScript can't handle 64-bit integers precisely
const high = buffer.readUInt32BE(offset);
const low = buffer.readUInt32BE(offset + 4);
payloadLength = high * 0x100000000 + low;
offset += 8;
}
// Masking key (if masked)
if (frame.masked) {
if (buffer.length < offset + 4) return { frame: null, consumed: 0 };
frame.maskingKey = buffer.slice(offset, offset + 4);
offset += 4;
}
// Payload
if (buffer.length < offset + payloadLength) {
return { frame: null, consumed: 0 };
}
frame.payload = buffer.slice(offset, offset + payloadLength);
offset += payloadLength;
// Unmask payload if needed
if (frame.masked) {
frame.payload = Buffer.from(frame.payload); // Create copy
for (let i = 0; i < frame.payload.length; i++) {
frame.payload[i] ^= frame.maskingKey[i % 4];
}
}
return { frame, consumed: offset };
}
serialize(mask = false) {
const payloadLength = this.payload.length;
let headerLength = 2;
if (payloadLength > 65535) headerLength += 8;
else if (payloadLength > 125) headerLength += 2;
if (mask) headerLength += 4;
const buffer = Buffer.alloc(headerLength + payloadLength);
let offset = 0;
// First byte
buffer[offset++] = (this.fin ? 0x80 : 0) |
(this.rsv1 ? 0x40 : 0) |
(this.rsv2 ? 0x20 : 0) |
(this.rsv3 ? 0x10 : 0) |
this.opcode;
// Second byte and extended length
let lengthByte = mask ? 0x80 : 0;
if (payloadLength > 65535) {
lengthByte |= 127;
buffer[offset++] = lengthByte;
buffer.writeUInt32BE(Math.floor(payloadLength / 0x100000000), offset);
buffer.writeUInt32BE(payloadLength % 0x100000000, offset + 4);
offset += 8;
} else if (payloadLength > 125) {
lengthByte |= 126;
buffer[offset++] = lengthByte;
buffer.writeUInt16BE(payloadLength, offset);
offset += 2;
} else {
lengthByte |= payloadLength;
buffer[offset++] = lengthByte;
}
// Masking key
if (mask) {
const maskingKey = crypto.randomBytes(4);
maskingKey.copy(buffer, offset);
offset += 4;
// Copy and mask payload
for (let i = 0; i < payloadLength; i++) {
buffer[offset + i] = this.payload[i] ^ maskingKey[i % 4];
}
} else {
this.payload.copy(buffer, offset);
}
return buffer;
}
}
```
### 3. WebSocket Server Implementation
Complete WebSocket server:
```javascript
const http = require('http');
const crypto = require('crypto');
const EventEmitter = require('events');
class WebSocketServer extends EventEmitter {
constructor(options = {}) {
super();
this.port = options.port || 8080;
this.maxPayload = options.maxPayload || 100 * 1024 * 1024; // 100MB
this.clients = new Set();
this.server = http.createServer((req, res) => {
res.writeHead(426, { 'Content-Type': 'text/plain' });
res.end('WebSocket server - upgrade required');
});
this.server.on('upgrade', (req, socket, head) => {
this.handleUpgrade(req, socket, head);
});
}
handleUpgrade(req, socket, head) {
const key = req.headers['sec-websocket-key'];
if (!key) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
return;
}
const acceptKey = crypto
.createHash('sha1')
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
.digest('base64');
socket.write([
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${acceptKey}`,
'',
''
].join('\r\n'));
const client = new WebSocketConnection(socket, this);
this.clients.add(client);
client.on('close', () => {
this.clients.delete(client);
});
this.emit('connection', client, req);
}
broadcast(message, excludeClient = null) {
for (const client of this.clients) {
if (client !== excludeClient && client.readyState === 'OPEN') {
client.send(message);
}
}
}
listen(callback) {
this.server.listen(this.port, callback);
}
close(callback) {
for (const client of this.clients) {
client.close(1001, 'Server shutting down');
}
this.server.close(callback);
}
}
class WebSocketConnection extends EventEmitter {
constructor(socket, server) {
super();
this.socket = socket;
this.server = server;
this.readyState = 'OPEN';
this.buffer = Buffer.alloc(0);
this.fragments = [];
socket.on('data', (data) => this.handleData(data));
socket.on('close', () => this.handleClose());
socket.on('error', (err) => this.emit('error', err));
}
handleData(data) {
this.buffer = Buffer.concat([this.buffer, data]);
while (this.buffer.length > 0) {
const { frame, consumed } = WebSocketFrame.parse(this.buffer);
if (!frame) break;
this.buffer = this.buffer.slice(consumed);
this.handleFrame(frame);
}
}
handleFrame(frame) {
switch (frame.opcode) {
case OPCODES.TEXT:
case OPCODES.BINARY:
if (frame.fin) {
const data = frame.opcode === OPCODES.TEXT
? frame.payload.toString('utf8')
: frame.payload;
this.emit('message', data);
} else {
this.fragments.push(frame);
}
break;
case OPCODES.CONTINUATION:
this.fragments.push(frame);
if (frame.fin) {
const firstFrame = this.fragments[0];
const payload = Buffer.concat(this.fragments.map(f => f.payload));
const data = firstFrame.opcode === OPCODES.TEXT
? payload.toString('utf8')
: payload;
this.emit('message', data);
this.fragments = [];
}
break;
case OPCODES.PING:
this.pong(frame.payload);
break;
case OPCODES.PONG:
this.emit('pong', frame.payload);
break;
case OPCODES.CLOSE:
let code = 1005;
let reason = '';
if (frame.payload.length >= 2) {
code = frame.payload.readUInt16BE(0);
reason = frame.payload.slice(2).toString('utf8');
}
this.close(code, reason);
break;
}
}
send(data) {
if (this.readyState !== 'OPEN') return;
const frame = new WebSocketFrame();
if (typeof data === 'string') {
frame.opcode = OPCODES.TEXT;
frame.payload = Buffer.from(data, 'utf8');
} else {
frame.opcode = OPCODES.BINARY;
frame.payload = data;
}
this.socket.write(frame.serialize(false)); // Server doesn't mask
}
ping(data = Buffer.alloc(0)) {
const frame = new WebSocketFrame();
frame.opcode = OPCODES.PING;
frame.payload = Buffer.isBuffer(data) ? data : Buffer.from(data);
this.socket.write(frame.serialize(false));
}
pong(data = Buffer.alloc(0)) {
const frame = new WebSocketFrame();
frame.opcode = OPCODES.PONG;
frame.payload = Buffer.isBuffer(data) ? data : Buffer.from(data);
this.socket.write(frame.serialize(false));
}
close(code = 1000, reason = '') {
if (this.readyState === 'CLOSED') return;
this.readyState = 'CLOSING';
const frame = new WebSocketFrame();
frame.opcode = OPCODES.CLOSE;
const codeBuffer = Buffer.alloc(2);
codeBuffer.writeUInt16BE(code, 0);
const reasonBuffer = Buffer.from(reason, 'utf8');
frame.payload = Buffer.concat([codeBuffer, reasonBuffer]);
this.socket.write(frame.serialize(false));
this.socket.end();
}
handleClose() {
this.readyState = 'CLOSED';
this.emit('close');
}
}
```
### 4. permessage-deflate Compression
Implement WebSocket compression:
```javascript
const zlib = require('zlib');
class PerMessageDeflate {
constructor(options = {}) {
this.serverNoContextTakeover = options.serverNoContextTakeover || false;
this.clientNoContextTakeover = options.clientNoContextTakeover || false;
this.serverMaxWindowBits = options.serverMaxWindowBits || 15;
this.clientMaxWindowBits = options.clientMaxWindowBits || 15;
this.inflateContext = null;
this.deflateContext = null;
}
compress(data, callback) {
if (!this.deflateContext || this.serverNoContextTakeover) {
this.deflateContext = zlib.createDeflateRaw({
windowBits: this.serverMaxWindowBits
});
}
const chunks = [];
this.deflateContext.on('data', (chunk) => chunks.push(chunk));
this.deflateContext.on('end', () => {
let result = Buffer.concat(chunks);
// Remove trailing 0x00 0x00 0xFF 0xFF
if (result.length >= 4 &&
result[result.length - 4] === 0x00 &&
result[result.length - 3] === 0x00 &&
result[result.length - 2] === 0xFF &&
result[result.length - 1] === 0xFF) {
result = result.slice(0, -4);
}
callback(null, result);
});
this.deflateContext.write(data);
this.deflateContext.flush(zlib.Z_SYNC_FLUSH);
}
decompress(data, callback) {
if (!this.inflateContext || this.clientNoContextTakeover) {
this.inflateContext = zlib.createInflateRaw({
windowBits: this.clientMaxWindowBits
});
}
// Add trailing bytes for decompression
const trailer = Buffer.from([0x00, 0x00, 0xFF, 0xFF]);
const input = Buffer.concat([data, trailer]);
const chunks = [];
this.inflateContext.on('data', (chunk) => chunks.push(chunk));
this.inflateContext.on('end', () => {
callback(null, Buffer.concat(chunks));
});
this.inflateContext.on('error', (err) => callback(err));
this.inflateContext.write(input);
this.inflateContext.flush();
}
}
```
### 5. WebSocket Testing
Test WebSocket implementations:
```bash
# Using wscat
wscat -c ws://localhost:8080
# Using websocat
websocat ws://localhost:8080
# Autobahn Test Suite (fuzzing)
docker run -it --rm \
-v "${PWD}/reports:/reports" \
-p 9001:9001 \
crossbario/autobahn-testsuite \
wstest --mode fuzzingclient --spec /config/fuzzingclient.json
```
## MCP Server Integration
This skill can leverage the following MCP servers for enhanced capabilities:
| Server | Description | Integration |
|--------|-------------|-------------|
| MCP-WebSocket Architecture | WebSocket transport with MCP | Real-time AI integration |
| claude-agent-server | WebSocket server for Claude Agent SDK | Agent orchestration |
| Claude-Flow | Multi-agent communication via WebSocket | Distributed agents |
## Best Practices
1. **Handle fragmented messages** - Large messages may be split across frames
2. **Implement heartbeat** - Use ping/pong for connection health
3. **Set payload limits** - Prevent memory exhaustion attacks
4. **Close gracefully** - Send close frame before disconnecting
5. **Validate UTF-8** - Text frames must be valid UTF-8
6. **Handle backpressure** - Don't overwhelm slow clients
## Process Integration
This skill integrates with the following processes:
- `websocket-server.js` - WebSocket server implementation
- `websocket-client.js` - WebSocket client implementation
- `realtime-messaging-system.js` - Real-time messaging architecture
## Output Format
When executing operations, provide structured output:
```json
{
"operation": "test",
"target": "ws://localhost:8080",
"status": "success",
"handshake": {
"protocol": "graphql-ws",
"extensions": ["permessage-deflate"]
},
"metrics": {
"messagesReceived": 1000,
"messagesSent": 1000,
"avgLatencyMs": 2.5,
"compressionRatio": 0.65
},
"compliance": {
"rfc6455": true,
"autobahnPassed": 512,
"autobahnFailed": 0
}
}
```
## Constraints
- Follow RFC 6455 strictly for interoperability
- Server must not mask frames (clients must)
- Validate close codes (1000-1015, 3000-4999)
- Handle UTF-8 validation for text frames
- Limit concurrent connections per clientRelated Skills
mcp-transport-websocket-setup
Configure WebSocket transport for bidirectional MCP communication with connection management and reconnection handling.
process-builder
Scaffold new babysitter process definitions following SDK patterns, proper structure, and best practices. Guides the 3-phase workflow from research to implementation.
babysitter
Orchestrate via @babysitter. Use this skill when asked to babysit a run, orchestrate a process or whenever it is called explicitly. (babysit, babysitter, orchestrate, orchestrate a run, workflow, etc.)
yolo
Run Babysitter autonomously with minimal manual interruption.
user-install
Install the user-level Babysitter Codex setup.
team-install
Install the team-pinned Babysitter Codex workspace setup.
retrospect
Summarize or retrospect on a completed Babysitter run.
resume
Resume an existing Babysitter run from Codex.
project-install
Install the Babysitter Codex workspace integration into the current project.
plan
Plan a Babysitter workflow without executing the run.
observe
Observe, inspect, or monitor a Babysitter run.
model
Inspect or change Babysitter model-routing policy by phase.