Skip to content

Integrations

Stream Claude's responses directly into HTTP servers, SSE endpoints, and WebSocket connections.

HTTP Streaming (Express)

ts
import express from 'express'
import { Claude } from '@scottwalker/claude-connector'

const app = express()
const claude = new Claude({ useSdk: false })

app.get('/ai/stream', (req, res) => {
  const prompt = req.query.prompt as string

  res.writeHead(200, {
    'Content-Type': 'text/plain; charset=utf-8',
    'Transfer-Encoding': 'chunked',
    'Cache-Control': 'no-cache',
  })

  claude.stream(prompt).toReadable().pipe(res)
})

app.get('/ai/query', async (req, res) => {
  const text = await claude.stream(req.query.prompt as string).text()
  res.json({ text })
})

HTTP Streaming (Fastify)

ts
import Fastify from 'fastify'
import { Claude } from '@scottwalker/claude-connector'

const app = Fastify()
const claude = new Claude({ useSdk: false })

app.get('/ai/stream', async (req, reply) => {
  const prompt = req.query.prompt as string

  reply.raw.writeHead(200, {
    'Content-Type': 'text/plain; charset=utf-8',
    'Transfer-Encoding': 'chunked',
  })

  await claude.stream(prompt).pipe(reply.raw)
})

Server-Sent Events (SSE)

Stream structured events to the browser:

ts
import {
  EVENT_TEXT,
  EVENT_TOOL_USE,
  EVENT_RESULT,
  EVENT_ERROR,
} from '@scottwalker/claude-connector'

app.get('/ai/sse', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  })

  claude.stream(req.query.prompt as string)
    .on(EVENT_TEXT, (text) => {
      res.write(`data: ${JSON.stringify({ type: 'text', text })}\n\n`)
    })
    .on(EVENT_TOOL_USE, (event) => {
      res.write(`data: ${JSON.stringify({ type: 'tool', tool: event.toolName })}\n\n`)
    })
    .on(EVENT_RESULT, (event) => {
      res.write(`data: ${JSON.stringify({ type: 'done', usage: event.usage })}\n\n`)
      res.end()
    })
    .on(EVENT_ERROR, (event) => {
      res.write(`data: ${JSON.stringify({ type: 'error', message: event.message })}\n\n`)
      res.end()
    })
    .done()
})

Browser Consumer (SSE)

js
const source = new EventSource('/ai/sse?prompt=Explain%20auth')

source.onmessage = (e) => {
  const data = JSON.parse(e.data)

  if (data.type === 'text') {
    document.getElementById('output').textContent += data.text
  } else if (data.type === 'done') {
    source.close()
  }
}

WebSocket

Real-time bidirectional communication over WebSocket:

ts
import { WebSocketServer } from 'ws'
import {
  Claude,
  EVENT_TEXT,
  EVENT_TOOL_USE,
  EVENT_RESULT,
} from '@scottwalker/claude-connector'

const wss = new WebSocketServer({ port: 8080 })
const claude = new Claude({ useSdk: false })

wss.on('connection', (ws) => {
  const chat = claude.chat()
    .on(EVENT_TEXT, (text) => {
      ws.send(JSON.stringify({ type: 'text', text }))
    })
    .on(EVENT_TOOL_USE, (event) => {
      ws.send(JSON.stringify({ type: 'tool', name: event.toolName }))
    })
    .on(EVENT_RESULT, (event) => {
      ws.send(JSON.stringify({ type: 'result', usage: event.usage }))
    })

  ws.on('message', async (data) => {
    const { prompt } = JSON.parse(data.toString())
    await chat.send(prompt)
  })

  ws.on('close', () => chat.end())
})

Browser Consumer (WebSocket)

js
const ws = new WebSocket('ws://localhost:8080')

ws.send(JSON.stringify({ prompt: 'What does this project do?' }))

ws.onmessage = (e) => {
  const data = JSON.parse(e.data)
  if (data.type === 'text') {
    document.getElementById('output').textContent += data.text
  }
}

TIP

The WebSocket example uses ChatHandle for persistent multi-turn conversations. Each WebSocket connection gets its own chat process, so multiple clients can interact independently.

Released under the MIT License.