server-sent-events — community server-sent-events, KLTN, vuthuonghai-steve, community, ai agent skill, ide skills, agent automation, AI agent skills, Claude Code, Cursor, Windsurf

v1.0.0
GitHub

About this Skill

Ideal for Real-Time Update Agents requiring unidirectional server-to-client communication for progress updates and live data feeds. Server-Sent Events (SSE) for real-time progress updates in Next.js/Vercel. TransformStream patterns, client consumption, progress tracking.

vuthuonghai-steve vuthuonghai-steve
[0]
[0]
Updated: 3/12/2026

Quality Score

Top 5%
48
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
> npx killer-skills add vuthuonghai-steve/KLTN/server-sent-events
Supports 19+ Platforms
Cursor
Windsurf
VS Code
Trae
Claude
OpenClaw
+12 more

Agent Capability Analysis

The server-sent-events skill by vuthuonghai-steve is an open-source community AI agent skill for Claude Code and other IDE workflows, helping agents execute tasks with better context, repeatability, and domain-specific guidance.

Ideal Agent Persona

Ideal for Real-Time Update Agents requiring unidirectional server-to-client communication for progress updates and live data feeds.

Core Value

Empowers agents to establish real-time server-sent event streams for progress updates, notifications, and live data feeds using SSE, enabling efficient unidirectional communication and leveraging HTTP protocol for simplicity.

Capabilities Granted for server-sent-events

Streaming live updates to clients
Sending progress notifications for long-running tasks
Broadcasting real-time data feeds

! Prerequisites & Limits

  • Unidirectional communication only
  • Limited connections
  • Not suitable for bidirectional communication or complex stateful interactions
Project
SKILL.md
11.2 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

Server-Sent Events (SSE)

Real-time server-to-client streaming for progress updates, notifications, and live data feeds.

When to Use SSE vs Alternatives

TechnologyUse WhenLimitations
SSEServer → Client only, progress updates, live feedsUnidirectional, limited connections
WebSocketsBidirectional chat, gaming, collaborationMore complex, stateful
PollingSimple updates, wide compatibilityInefficient, latency
HTTP StreamingLarge file downloadsNo structured events

Choose SSE for: Progress bars, status updates, notifications, log streaming, live dashboards.


Next.js App Router Implementation

Server-Side (Route Handler)

typescript
1// app/api/progress/route.ts 2import { NextRequest } from 'next/server' 3 4interface SSEMessage { 5 progress: number 6 message: string 7 status: 'progress' | 'complete' | 'error' 8} 9 10export async function POST(req: NextRequest) { 11 const body = await req.json() 12 13 // SSE Response Headers 14 const headers = { 15 'Content-Type': 'text/event-stream', 16 'Cache-Control': 'no-cache', 17 'Connection': 'keep-alive', 18 } 19 20 // Create transform stream for SSE 21 const stream = new TransformStream() 22 const writer = stream.writable.getWriter() 23 const encoder = new TextEncoder() 24 25 // Helper to send SSE messages 26 async function sendSSE(data: SSEMessage) { 27 await writer.write(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) 28 } 29 30 // Execute work with progress updates (async, don't await) 31 ;(async () => { 32 try { 33 await sendSSE({ progress: 0, message: 'Starting...', status: 'progress' }) 34 35 // Simulate work with progress updates 36 for (let i = 1; i <= 10; i++) { 37 await doSomeWork(i) 38 await sendSSE({ 39 progress: i * 10, 40 message: `Processing step ${i} of 10...`, 41 status: 'progress' 42 }) 43 } 44 45 await sendSSE({ progress: 100, message: 'Complete!', status: 'complete' }) 46 } catch (error) { 47 const message = error instanceof Error ? error.message : 'Unknown error' 48 await sendSSE({ progress: 0, message, status: 'error' }) 49 } finally { 50 await writer.close() 51 } 52 })() 53 54 return new Response(stream.readable, { headers }) 55}

Client-Side Consumption

typescript
1// React component 2'use client' 3 4import { useState } from 'react' 5 6interface ProgressState { 7 progress: number 8 message: string 9 status: 'idle' | 'progress' | 'complete' | 'error' 10} 11 12export function ProgressTracker() { 13 const [state, setState] = useState<ProgressState>({ 14 progress: 0, 15 message: '', 16 status: 'idle' 17 }) 18 19 async function startProcess() { 20 setState({ progress: 0, message: 'Connecting...', status: 'progress' }) 21 22 const response = await fetch('/api/progress', { 23 method: 'POST', 24 headers: { 'Content-Type': 'application/json' }, 25 body: JSON.stringify({ /* request data */ }) 26 }) 27 28 if (!response.body) { 29 setState({ progress: 0, message: 'No response body', status: 'error' }) 30 return 31 } 32 33 const reader = response.body.getReader() 34 const decoder = new TextDecoder() 35 let buffer = '' 36 37 while (true) { 38 const { done, value } = await reader.read() 39 if (done) break 40 41 buffer += decoder.decode(value, { stream: true }) 42 43 // Parse SSE messages from buffer 44 const lines = buffer.split('\n\n') 45 buffer = lines.pop() || '' // Keep incomplete message in buffer 46 47 for (const line of lines) { 48 if (line.startsWith('data: ')) { 49 const data = JSON.parse(line.slice(6)) 50 setState({ 51 progress: data.progress, 52 message: data.message, 53 status: data.status 54 }) 55 } 56 } 57 } 58 } 59 60 return ( 61 <div> 62 <button onClick={startProcess} disabled={state.status === 'progress'}> 63 Start Process 64 </button> 65 <progress value={state.progress} max={100} /> 66 <p>{state.message}</p> 67 </div> 68 ) 69}

SSE Message Format

Standard Format

data: {"key": "value"}\n\n

Rules:

  • Each message ends with \n\n (double newline)
  • Data line starts with data:
  • Multiple data lines allowed per message
  • Optional event:, id:, retry: fields

Named Events

typescript
1// Server 2async function sendNamedEvent(event: string, data: object) { 3 await writer.write(encoder.encode( 4 `event: ${event}\ndata: ${JSON.stringify(data)}\n\n` 5 )) 6} 7 8// Send different event types 9await sendNamedEvent('progress', { percent: 50 }) 10await sendNamedEvent('log', { message: 'Processing...' }) 11await sendNamedEvent('complete', { result: 'success' })
typescript
1// Client with EventSource (for GET endpoints) 2const source = new EventSource('/api/events') 3 4source.addEventListener('progress', (e) => { 5 const data = JSON.parse(e.data) 6 console.log('Progress:', data.percent) 7}) 8 9source.addEventListener('complete', (e) => { 10 const data = JSON.parse(e.data) 11 console.log('Done:', data.result) 12 source.close() 13})

Vercel Considerations

Critical: Background Task Termination

<EXTREMELY-IMPORTANT> On Vercel, SSE works differently than traditional servers. The function stays alive while the stream is open, but you must manage the lifecycle carefully. </EXTREMELY-IMPORTANT>
typescript
1// CORRECT: Stream keeps function alive until closed 2;(async () => { 3 try { 4 // Do all work here 5 for (const item of items) { 6 await processItem(item) 7 await sendSSE({ progress: calculateProgress(item) }) 8 } 9 await sendSSE({ status: 'complete' }) 10 } finally { 11 await writer.close() // MUST close to end function 12 } 13})() 14 15return new Response(stream.readable, { headers })

Timeout Limits

PlanMax Duration
Hobby10 seconds
Pro60 seconds
Enterprise900 seconds

For long operations, consider:

  • Breaking into smaller chunks
  • Using background jobs (Vercel Cron, external queue)
  • Providing estimated time warnings

Progress Patterns

Percentage-Based

typescript
1interface PercentageProgress { 2 progress: number // 0-100 3 message: string 4 status: 'progress' | 'complete' | 'error' 5} 6 7// Calculate progress for known item count 8const total = items.length 9for (let i = 0; i < items.length; i++) { 10 await processItem(items[i]) 11 await sendSSE({ 12 progress: Math.floor(((i + 1) / total) * 100), 13 message: `Processing ${i + 1} of ${total}...`, 14 status: 'progress' 15 }) 16}

Phase-Based

typescript
1interface PhaseProgress { 2 phase: string 3 phaseProgress: number 4 overallProgress: number 5 message: string 6} 7 8const phases = [ 9 { name: 'validate', weight: 10 }, 10 { name: 'process', weight: 70 }, 11 { name: 'finalize', weight: 20 }, 12] 13 14// Track progress across phases 15let overallProgress = 0 16for (const phase of phases) { 17 await sendSSE({ 18 phase: phase.name, 19 phaseProgress: 0, 20 overallProgress, 21 message: `Starting ${phase.name}...` 22 }) 23 24 // Do phase work... 25 26 overallProgress += phase.weight 27}

Indeterminate with Logs

typescript
1interface LogProgress { 2 type: 'log' | 'warning' | 'error' | 'complete' 3 message: string 4 timestamp: string 5} 6 7// For operations where progress can't be calculated 8await sendSSE({ 9 type: 'log', 10 message: 'Connecting to external service...', 11 timestamp: new Date().toISOString() 12})

Error Handling

Server-Side

typescript
1;(async () => { 2 try { 3 await riskyOperation() 4 await sendSSE({ status: 'complete', message: 'Done!' }) 5 } catch (error) { 6 // Always send error to client before closing 7 await sendSSE({ 8 status: 'error', 9 message: error instanceof Error ? error.message : 'Unknown error', 10 progress: 0 11 }) 12 } finally { 13 // ALWAYS close the stream 14 await writer.close() 15 } 16})()

Client-Side

typescript
1async function consumeSSE(url: string, body: object) { 2 try { 3 const response = await fetch(url, { 4 method: 'POST', 5 body: JSON.stringify(body) 6 }) 7 8 if (!response.ok) { 9 throw new Error(`HTTP ${response.status}`) 10 } 11 12 // Process stream... 13 } catch (error) { 14 // Handle network errors, aborts, etc. 15 if (error.name === 'AbortError') { 16 console.log('Request cancelled') 17 } else { 18 console.error('SSE failed:', error) 19 } 20 } 21}

Cancellation

typescript
1// Client with AbortController 2const controller = new AbortController() 3 4const response = await fetch('/api/progress', { 5 method: 'POST', 6 body: JSON.stringify(data), 7 signal: controller.signal 8}) 9 10// Cancel button handler 11function handleCancel() { 12 controller.abort() 13}

Real-World Example: Batch Processing

typescript
1// app/api/batch/route.ts 2export async function POST(req: NextRequest) { 3 const { items } = await req.json() 4 5 const stream = new TransformStream() 6 const writer = stream.writable.getWriter() 7 const encoder = new TextEncoder() 8 9 const sendSSE = async (data: object) => { 10 await writer.write(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)) 11 } 12 13 ;(async () => { 14 const results = { success: 0, failed: 0, errors: [] as string[] } 15 16 try { 17 await sendSSE({ 18 status: 'progress', 19 progress: 0, 20 message: `Processing ${items.length} items...` 21 }) 22 23 for (let i = 0; i < items.length; i++) { 24 try { 25 await processItem(items[i]) 26 results.success++ 27 } catch (err) { 28 results.failed++ 29 results.errors.push(`Item ${i}: ${err.message}`) 30 } 31 32 await sendSSE({ 33 status: 'progress', 34 progress: Math.floor(((i + 1) / items.length) * 100), 35 message: `Processed ${i + 1} of ${items.length}`, 36 results: { ...results } 37 }) 38 } 39 40 await sendSSE({ 41 status: 'complete', 42 progress: 100, 43 message: `Complete: ${results.success} succeeded, ${results.failed} failed`, 44 results 45 }) 46 } catch (error) { 47 await sendSSE({ 48 status: 'error', 49 message: error instanceof Error ? error.message : 'Batch failed', 50 results 51 }) 52 } finally { 53 await writer.close() 54 } 55 })() 56 57 return new Response(stream.readable, { 58 headers: { 59 'Content-Type': 'text/event-stream', 60 'Cache-Control': 'no-cache', 61 'Connection': 'keep-alive', 62 } 63 }) 64}

Quick Reference

typescript
1// Minimal SSE endpoint 2export async function POST(req: NextRequest) { 3 const stream = new TransformStream() 4 const writer = stream.writable.getWriter() 5 const encoder = new TextEncoder() 6 7 ;(async () => { 8 await writer.write(encoder.encode(`data: {"message":"Hello"}\n\n`)) 9 await writer.close() 10 })() 11 12 return new Response(stream.readable, { 13 headers: { 'Content-Type': 'text/event-stream' } 14 }) 15}
typescript
1// Minimal client consumption 2const res = await fetch('/api/sse', { method: 'POST' }) 3const reader = res.body!.getReader() 4const decoder = new TextDecoder() 5 6while (true) { 7 const { done, value } = await reader.read() 8 if (done) break 9 console.log(decoder.decode(value)) 10}

Resources

FAQ & Installation Steps

These questions and steps mirror the structured data on this page for better search understanding.

? Frequently Asked Questions

What is server-sent-events?

Ideal for Real-Time Update Agents requiring unidirectional server-to-client communication for progress updates and live data feeds. Server-Sent Events (SSE) for real-time progress updates in Next.js/Vercel. TransformStream patterns, client consumption, progress tracking.

How do I install server-sent-events?

Run the command: npx killer-skills add vuthuonghai-steve/KLTN/server-sent-events. It works with Cursor, Windsurf, VS Code, Claude Code, and 19+ other IDEs.

What are the use cases for server-sent-events?

Key use cases include: Streaming live updates to clients, Sending progress notifications for long-running tasks, Broadcasting real-time data feeds.

Which IDEs are compatible with server-sent-events?

This skill is compatible with Cursor, Windsurf, VS Code, Trae, Claude Code, OpenClaw, Aider, Codex, OpenCode, Goose, Cline, Roo Code, Kiro, Augment Code, Continue, GitHub Copilot, Sourcegraph Cody, and Amazon Q Developer. Use the Killer-Skills CLI for universal one-command installation.

Are there any limitations for server-sent-events?

Unidirectional communication only. Limited connections. Not suitable for bidirectional communication or complex stateful interactions.

How To Install

  1. 1. Open your terminal

    Open the terminal or command line in your project directory.

  2. 2. Run the install command

    Run: npx killer-skills add vuthuonghai-steve/KLTN/server-sent-events. The CLI will automatically detect your IDE or AI agent and configure the skill.

  3. 3. Start using the skill

    The skill is now active. Your AI agent can use server-sent-events immediately in the current project.

Related Skills

Looking for an alternative to server-sent-events or another community skill for your workflow? Explore these related open-source skills.

View All

widget-generator

Logo of f
f

Generate customizable widget plugins for the prompts.chat feed system

149.6k
0
Design

flags

Logo of vercel
vercel

The React Framework

138.4k
0
Browser

pr-review

Logo of pytorch
pytorch

Tensors and Dynamic neural networks in Python with strong GPU acceleration

98.6k
0
AI

antd-commit-msg

Logo of ant-design
ant-design

Generate a single-line commit message for ant-design by reading the projects git staged area and recent commit style. Use when the user asks for a commit message, says msg, commit msg, 写提交信息, or wants

97.8k
0
Design