I needed to build a real-time Minecraft server status checker for Minecraft ServerHub — a server list with 1,000+ servers. The naive approach (polling an API every few seconds) felt wasteful. So I went with Server-Sent Events (SSE) to stream results back to the client as soon as they arrive.
Here's exactly how it works, including the raw TCP protocol implementation.
The Minecraft Server List Ping Protocol
Minecraft Java servers use a custom binary TCP protocol for status pings. You have to:
- Open a raw TCP socket to port 25565
- Send a Handshake packet (packet ID
0x00) to set up the connection - Send a Status Request packet (packet ID
0x00again, but in the Status state) - Read the Status Response — a JSON payload with player count, version, MOTD, etc.
The binary format uses variable-length integers (VarInts) — each byte uses 7 bits for data and 1 bit to indicate if more bytes follow.
The SSE API Route (Next.js 15 App Router)
Instead of blocking until we have a result, the API route opens the TCP connection and streams events back as soon as data arrives. This means the UI can show "connecting…" → "checking…" → result in near real-time.
// app/api/ping/route.ts
export const runtime = "nodejs"; // required for raw TCP sockets
export async function GET(req: NextRequest) {
const host = new URL(req.url).searchParams.get("host") ?? "";
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
const send = (event: string, data: unknown) => {
controller.enqueue(
encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`)
);
};
try {
send("status", { stage: "connecting" });
const result = await pingJavaServer(host);
send("result", result);
} catch (err) {
send("error", { online: false, message: err instanceof Error ? err.message : "Unreachable" });
} finally {
controller.close();
}
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
});
}
Why SSE Instead of WebSockets?
- SSE is unidirectional (server → client only) — perfect for "fire a query, stream the result"
- No special server needed — works over standard HTTP, no WebSocket upgrade handshake
- Built-in reconnect — browsers auto-reconnect if the connection drops
- Simpler auth — cookies and headers work the same as regular HTTP requests
The Live Tool
You can try the finished tool at minecraft-serverhub.com/tools/server-status-checker — it checks any Java or Bedrock server in real time and shows the MOTD with formatting.
The full API documentation is also available if you want to build your own integration (no API key required, completely free).
If you have questions about the Minecraft ping protocol or the SSE implementation, drop them in the comments. Happy coding!
Top comments (0)