Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when building real-time communication systems with WebSockets or Socket.IO. Invoke for bidirectional messaging, horizontal scaling with Redis, presence tracking, room management.
.claude/skills/jeffallan-websocket-engineer/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -1% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 111% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 62% | 0% |
npx wscat -c ws://localhost:3000); confirm auth rejection on missing/invalid tokens, room join/leave events, and message deliveryLoad detailed guidance based on context:
| Topic | Reference | Load When | |-------|-----------|-----------| | Protocol | references/protocol.md | WebSocket handshake, frames, ping/pong, close codes | | Scaling | references/scaling.md | Horizontal scaling, Redis pub/sub, sticky sessions | | Patterns | references/patterns.md | Rooms, namespaces, broadcasting, acknowledgments | | Security | references/security.md | Authentication, authorization, rate limiting, CORS | | Alternatives | references/alternatives.md | SSE, long polling, when to choose WebSockets |
jsimport { createServer } from "http"; import { Server } from "socket.io"; import { createAdapter } from "@socket.io/redis-adapter"; import { createClient } from "redis"; import jwt from "jsonwebtoken"; const httpServer = createServer(); const io = new Server(httpServer, { cors: { origin: process.env.ALLOWED_ORIGIN, credentials: true }, pingTimeout: 20000, pingInterval: 25000, }); // Authentication middleware — runs before connection is established io.use((socket, next) => { const token = socket.handshake.auth.token; if (!token) return next(new Error("Authentication required")); try { socket.data.user = jwt.verify(token, process.env.JWT_SECRET); next(); } catch { next(new Error("Invalid token")); } }); // Redis adapter for horizontal scaling const pubClient = createClient({ url: process.env.REDIS_URL }); const subClient = pubClient.duplicate(); await Promise.all([pubClient.connect(), subClient.connect()]); io.adapter(createAdapter(pubClient, subClient)); io.on("connection", (socket) => { const { userId } = socket.data.user; console.log(`connected: ${userId} (${socket.id})`); // Presence: mark user online pubClient.hSet("presence", userId, socket.id); socket.on("join-room", (roomId) => { socket.join(roomId); socket.to(roomId).emit("user-joined", { userId }); }); socket.on("message", ({ roomId, text }) => { io.to(roomId).emit("message", { userId, text, ts: Date.now() }); }); socket.on("disconnect", () => { pubClient.hDel("presence", userId); console.log(`disconnected: ${userId}`); }); }); httpServer.listen(3000);
jsimport { io } from "socket.io-client"; const socket = io("wss://api.example.com", { auth: { token: getAuthToken() }, reconnection: true, reconnectionAttempts: 10, reconnectionDelay: 1000, // initial delay (ms) reconnectionDelayMax: 30000, // cap at 30 s randomizationFactor: 0.5, // jitter to avoid thundering herd }); // Queue messages while disconnected let messageQueue = []; socket.on("connect", () => { console.log("connected:", socket.id); // Flush queued messages messageQueue.forEach((msg) => socket.emit("message", msg)); messageQueue = []; }); socket.on("disconnect", (reason) => { console.warn("disconnected:", reason); if (reason === "io server disconnect") socket.connect(); // manual reconnect }); socket.on("connect_error", (err) => { console.error("connection error:", err.message); }); function sendMessage(roomId, text) { const msg = { roomId, text }; if (socket.connected) { socket.emit("message", msg); } else { messageQueue.push(msg); // buffer until reconnected } }
When implementing WebSocket features, provide:
Socket.IO, ws, uWebSockets.js, Redis adapter, sticky sessions, nginx WebSocket proxy, JWT over WebSocket, rooms/namespaces, acknowledgments, binary data, compression, heartbeat, backpressure, horizontal pod autoscaling
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-15 | pass→pass | 15,654 | 12,253 | -22% | 1 | 1 | 0% | 2,663 | 3,573 | +34% | 0 | 0 | — |
case-21 | pass→pass | 11,139 | 10,633 | -5% | 1 | 1 | 0% | 2,095 | 3,471 | +66% | 0 | 0 | — |
case-22 | pass→pass | 16,615 | 14,524 | -13% | 1 | 1 | 0% | 2,964 | 3,912 | +32% | 0 | 0 | — |
case-01 | fail→pass | 25,883 | 18,093 | -30% | 1 | 1 | 0% | 5,685 | 5,631 | -1% | 0 | 0 | — |
case-02 | pass→pass | 14,641 | 15,068 | +3% | 1 | 1 | 0% | 2,959 | 4,458 | +51% | 0 | 0 | — |
case-03 | fail→pass | 10,714 | 15,400 | +44% | 1 | 1 | 0% | 2,102 | 4,442 | +111% | 0 | 0 | — |
case-04 | pass→pass | 13,845 | 11,984 | -13% | 1 | 1 | 0% | 2,429 | 3,865 | +59% | 0 | 0 | — |
case-05 | pass→pass | 12,487 | 9,197 | -26% | 1 | 1 | 0% | 2,537 | 3,277 | +29% | 0 | 0 | — |
case-06 | pass→fail | 17,183 | 15,191 | -12% | 1 | 1 | 0% | 3,296 | 4,435 | +35% | 0 | 0 | — |
case-07 | pass→pass | 16,945 | 15,523 | -8% | 1 | 1 | 0% | 3,224 | 4,631 | +44% | 0 | 0 | — |
case-08 | pass→fail | 10,147 | 7,842 | -23% | 1 | 1 | 0% | 2,073 | 3,111 | +50% | 0 | 0 | — |
case-09 | pass→pass | 17,460 | 13,684 | -22% | 1 | 1 | 0% | 3,024 | 4,289 | +42% | 0 | 0 | — |
case-10 | pass→pass | 13,347 | 11,440 | -14% | 1 | 1 | 0% | 2,548 | 3,677 | +44% | 0 | 0 | — |
case-11 | fail→pass | 16,481 | 17,998 | +9% | 1 | 1 | 0% | 3,166 | 5,156 | +63% | 0 | 0 | — |
case-12 | pass→pass | 11,726 | 11,616 | -1% | 1 | 1 | 0% | 2,328 | 3,957 | +70% | 0 | 0 | — |
case-13 | fail→pass | 14,757 | 12,557 | -15% | 1 | 1 | 0% | 2,522 | 3,807 | +51% | 0 | 0 | — |
case-14 | fail→pass | 9,034 | 4,977 | -45% | 1 | 1 | 0% | 1,463 | 2,375 | +62% | 0 | 0 | — |
case-16 | fail→pass | 14,077 | 11,648 | -17% | 1 | 1 | 0% | 2,662 | 3,714 | +40% | 0 | 0 | — |
case-17 | pass→pass | 13,409 | 14,716 | +10% | 1 | 1 | 0% | 2,565 | 4,512 | +76% | 0 | 0 | — |
case-18 | pass→pass | 11,950 | 10,423 | -13% | 1 | 1 | 0% | 2,509 | 3,490 | +39% | 0 | 0 | — |
case-19 | fail→fail | 14,978 | 14,607 | -2% | 1 | 1 | 0% | 3,101 | 4,473 | +44% | 0 | 0 | — |
case-20 | pass→pass | 14,955 | 14,241 | -5% | 1 | 1 | 0% | 2,931 | 4,370 | +49% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.