ProvenPin Install

ws WebSocket server with a Zod-validated message protocol: working versions (tested 2026-10-15)

A room chat server on the ws library with token auth on the upgrade URL, a Zod 4 discriminated union for every inbound frame and typed error replies, tested offline with real ws clients on an ephemeral localhost port.

passed4 passed · 0 failed2026-10-15 15:05 UTC
Status
passed
Last passed
2026-10-15 15:05 UTC
Tests
4 / 4
Language
TypeScript
Runtime
node v22.23.3
pinned versions5
ws@8.22.0zod@4.6.7tsx@4.23.15@types/node@26.6.5@types/ws@8.18.2
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38755782594

The code

Exactly the files that passed. Your agent can pull them with get_recipe("ws-zod-message-protocol") (install), or see them on GitHub.

package.json
{
  "name": "ws-zod-message-protocol",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "ws": "8.22.0",
    "zod": "4.6.7"
  },
  "devDependencies": {
    "tsx": "4.23.15",
    "@types/node": "26.6.5",
    "@types/ws": "8.18.2"
  }
}
src/server.ts
import { WebSocketServer, WebSocket } from "ws";
import type { IncomingMessage } from "node:http";
import { z } from "zod";

/** Client to server messages, as a discriminated union on "type". */
export const ClientMsg = z.discriminatedUnion("type", [
  z.object({ type: z.literal("join"), room: z.string().min(1).max(32) }),
  z.object({ type: z.literal("say"), room: z.string(), text: z.string().min(1).max(500) }),
  z.object({ type: z.literal("ping"), id: z.number().int() }),
]);
export type ServerMsg =
  | { type: "welcome"; user: string }
  | { type: "joined"; room: string; members: number }
  | { type: "message"; room: string; from: string; text: string }
  | { type: "pong"; id: number }
  | { type: "error"; code: "bad_json" | "invalid" | "not_in_room"; detail?: string };

const send = (ws: WebSocket, msg: ServerMsg) => ws.send(JSON.stringify(msg));

/**
 * A room chat server on ws. Auth is a token in the upgrade URL, checked in verifyClient-style
 * handling of the "connection" request; every inbound frame is validated with Zod.
 */
export function startServer(tokens: Record<string, string>, port = 0) {
  const wss = new WebSocketServer({ port });
  const rooms = new Map<string, Set<WebSocket>>();
  const userOf = new WeakMap<WebSocket, string>();

  wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
    const token = new URL(req.url ?? "/", "http://localhost").searchParams.get("token") ?? "";
    const user = tokens[token];
    if (!user) return ws.close(4401, "unauthorized");
    userOf.set(ws, user);
    send(ws, { type: "welcome", user });

    ws.on("message", (raw) => {
      let data: unknown;
      try { data = JSON.parse(raw.toString()); } catch { return send(ws, { type: "error", code: "bad_json" }); }
      const parsed = ClientMsg.safeParse(data);
      if (!parsed.success) return send(ws, { type: "error", code: "invalid", detail: parsed.error.issues[0].path.join(".") || parsed.error.issues[0].message });
      const msg = parsed.data;
      switch (msg.type) {
        case "ping":
          return send(ws, { type: "pong", id: msg.id });
        case "join": {
          const set = rooms.get(msg.room) ?? new Set();
          set.add(ws);
          rooms.set(msg.room, set);
          return send(ws, { type: "joined", room: msg.room, members: set.size });
        }
        case "say": {
          const set = rooms.get(msg.room);
          if (!set?.has(ws)) return send(ws, { type: "error", code: "not_in_room" });
          for (const peer of set) if (peer.readyState === WebSocket.OPEN) send(peer, { type: "message", room: msg.room, from: user, text: msg.text });
        }
      }
    });

    ws.on("close", () => { for (const set of rooms.values()) set.delete(ws); });
  });

  const ready = new Promise<number>((resolve) => wss.on("listening", () => resolve((wss.address() as { port: number }).port)));
  return { wss, ready, close: () => new Promise<void>((r) => { for (const c of wss.clients) c.terminate(); wss.close(() => r()); }) };
}
test/server.test.ts
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { WebSocket } from "ws";
import { startServer, type ServerMsg } from "../src/server.ts";

let srv: ReturnType<typeof startServer>;
let port: number;

before(async () => {
  srv = startServer({ "tok-ada": "ada", "tok-bob": "bob" });
  port = await srv.ready;
});
after(() => srv.close());

/** Connects and buffers incoming messages so tests can await the next one in order. */
async function connect(token: string) {
  const ws = new WebSocket(`ws://127.0.0.1:${port}/?token=${token}`);
  const queue: ServerMsg[] = [];
  const waiters: ((m: ServerMsg) => void)[] = [];
  ws.on("message", (raw) => {
    const m = JSON.parse(raw.toString()) as ServerMsg;
    const w = waiters.shift();
    w ? w(m) : queue.push(m);
  });
  const next = () => new Promise<ServerMsg>((resolve) => { const m = queue.shift(); m ? resolve(m) : waiters.push(resolve); });
  await new Promise((r, j) => { ws.once("open", r); ws.once("error", j); });
  return { ws, next, send: (v: unknown) => ws.send(typeof v === "string" ? v : JSON.stringify(v)) };
}

test("rejects connections without a valid token", async () => {
  const ws = new WebSocket(`ws://127.0.0.1:${port}/?token=nope`);
  const [code, reason] = await new Promise<[number, string]>((r) => ws.on("close", (c, why) => r([c, why.toString()])));
  assert.equal(code, 4401);
  assert.equal(reason, "unauthorized");
});

test("welcomes and answers ping", async () => {
  const c = await connect("tok-ada");
  assert.deepEqual(await c.next(), { type: "welcome", user: "ada" });
  c.send({ type: "ping", id: 7 });
  assert.deepEqual(await c.next(), { type: "pong", id: 7 });
  c.ws.close();
});

test("invalid frames get typed errors and the socket stays open", async () => {
  const c = await connect("tok-ada");
  await c.next();
  c.send("{not json");
  assert.deepEqual(await c.next(), { type: "error", code: "bad_json" });
  c.send({ type: "say", room: "general", text: "" });
  assert.deepEqual(await c.next(), { type: "error", code: "invalid", detail: "text" });
  c.send({ type: "shout" });
  assert.equal((await c.next() as { code: string }).code, "invalid");
  c.send({ type: "say", room: "general", text: "hi" });
  assert.deepEqual(await c.next(), { type: "error", code: "not_in_room" });
  assert.equal(c.ws.readyState, WebSocket.OPEN);
  c.ws.close();
});

test("broadcasts room messages to every member", async () => {
  const ada = await connect("tok-ada");
  const bob = await connect("tok-bob");
  await ada.next(); await bob.next();
  ada.send({ type: "join", room: "ops" });
  assert.deepEqual(await ada.next(), { type: "joined", room: "ops", members: 1 });
  bob.send({ type: "join", room: "ops" });
  assert.deepEqual(await bob.next(), { type: "joined", room: "ops", members: 2 });
  bob.send({ type: "say", room: "ops", text: "deploying" });
  const expected = { type: "message", room: "ops", from: "bob", text: "deploying" };
  assert.deepEqual(await ada.next(), expected);
  assert.deepEqual(await bob.next(), expected);
  ada.ws.close(); bob.ws.close();
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.