Skip to main content
Build applications on top of your deployed agents. The SDK handles streaming, sessions, and error handling.
npm install @superserve/sdk
import Superserve from "@superserve/sdk"

const client = new Superserve({ apiKey: "your-api-key" })
Get an API key from the Superserve dashboard or via superserve login.

Platform support

  • Node.js 18+ (uses native fetch and ReadableStream)
  • React 18+ (optional - only needed for @superserve/sdk/react)
  • Ships as both ESM and CJS - works with import and require

Run

Send a message, get a response. Creates a session, sends the message, tears down.
const result = await client.run("code-reviewer", {
  message: "Review the error handling in src/auth.ts",
})

console.log(result.text)
console.log(result.duration)    // 3800
console.log(result.toolCalls)   // [{ name: "read_file", input: {...}, duration: 120 }]
You can reuse an existing session instead of creating a new one:
const result = await client.run("code-reviewer", {
  message: "Now fix the issues you found",
  sessionId: "ses_abc123",
})
RunResult:
FieldTypeDescription
textstringFull response
toolCallsToolCall[]Tools invoked during the run
durationnumberMilliseconds
finishReasonstring"completed", "failed", or "cancelled"
maxTurnsReachedbooleanWhether the agent hit its maximum turn limit
RunOptions:
FieldTypeDescription
messagestringThe message to send (required)
sessionIdstring?Reuse an existing session instead of creating a new one
idleTimeoutnumber?Session idle timeout in seconds

Stream

Stream tokens as they arrive.
const stream = client.stream("code-reviewer", {
  message: "Refactor the database connection pool",
  onText: (text) => process.stdout.write(text),
  onToolStart: (tool) => console.log(`-> ${tool.name}`),
  onToolEnd: (tool) => console.log(`done in ${tool.duration}ms`),
  onFinish: (result) => console.log(`\nDone in ${result.duration}ms`),
})

await stream.result
Or iterate over text chunks:
const stream = client.stream("code-reviewer", {
  message: "Write tests for the auth module",
})

for await (const chunk of stream.textStream) {
  process.stdout.write(chunk)
}

Raw event iteration

The stream is an AsyncIterable<StreamEvent>. You can iterate over all events directly for full control:
const stream = client.stream("code-reviewer", {
  message: "Analyze this codebase",
})

for await (const event of stream) {
  switch (event.type) {
    case "text":
      process.stdout.write(event.content)
      break
    case "tool-start":
      console.log(`Using tool: ${event.name}`)
      break
    case "tool-end":
      console.log(`Tool finished in ${event.duration}ms`)
      break
    case "run-completed":
      console.log(`Run completed in ${event.duration}ms`)
      break
    case "run-failed":
      console.error(`Run failed: ${event.error}`)
      break
  }
}
StreamEvent types:
TypeFieldsDescription
TextEventcontent: stringA chunk of text from the agent
ToolStartEventname: string, input: unknownAgent started using a tool
ToolEndEventduration: numberTool execution completed
RunCompletedEventduration: number, maxTurnsReached: booleanRun finished successfully
RunFailedEventerror: stringRun failed
AgentStream:
PropertyTypeDescription
textStreamAsyncIterable<string>Text chunks as they arrive
resultPromise<RunResult>Resolves on completion
abort()() => voidCancel the stream
AgentStream can only be iterated once. If you access stream.result without iterating first, the stream is consumed automatically in the background.

Sessions

Multi-turn conversations. The agent’s workspace and memory persist between messages.
const session = await client.createSession("code-reviewer", {
  title: "Auth module review",
  idleTimeout: 3600, // 1 hour, in seconds
})

const r1 = await session.run("What files handle authentication?")
// "Authentication is handled in src/auth.ts and src/middleware/auth.ts..."

const r2 = await session.run("Add rate limiting to the login endpoint")
// Agent remembers context from r1

const r3 = session.stream("Now write tests for the rate limiter")
for await (const chunk of r3.textStream) {
  process.stdout.write(chunk)
}

await session.end()
Session streaming also supports callbacks:
const stream = session.stream("Refactor the auth module", {
  onText: (text) => process.stdout.write(text),
  onToolStart: (tool) => console.log(`-> ${tool.name}`),
  onToolEnd: (tool) => console.log(`done in ${tool.duration}ms`),
  onFinish: (result) => console.log(`\nDone in ${result.duration}ms`),
  onError: (err) => console.error(err),
})

await stream.result
SessionOptions:
FieldTypeDescription
titlestring?A label for the session (shown in superserve sessions list)
idleTimeoutnumber?Idle timeout in seconds before the session is cleaned up
Session:
Property / MethodTypeDescription
idstringSession ID
agentIdstringThe agent’s ID
infoSessionInfoFull session metadata
run(message)Promise<RunResult>Send and wait
stream(message, options?)AgentStreamSend and stream (accepts callback options)
end()Promise<void>End the session
SessionInfo:
FieldTypeDescription
idstringSession ID
agentIdstringAgent ID
agentNamestring?Agent name
statusstring"active", "idle", "completed", or "failed"
titlestring?Session title
messageCountnumberNumber of messages in the session
createdAtstringISO 8601 timestamp

Agents

const agents = await client.agents.list()
const agent = await client.agents.get("code-reviewer")
Agent:
FieldTypeDescription
idstringAgent ID (e.g. agt_7c9e...)
namestringAgent name
commandstring | nullStart command (e.g. python agent.py)
depsStatusstringDependency installation status
depsErrorstring | nullDependency error message, if any
requiredSecretsstring[]Secrets the agent needs
environmentKeysstring[]Environment variables configured
createdAtstringISO 8601 timestamp
updatedAtstringISO 8601 timestamp

React

Build chat interfaces with useAgent. Manages messages, streaming, and sessions automatically.
npm install @superserve/sdk react
import { SuperserveProvider, useAgent } from "@superserve/sdk/react"

function App() {
  return (
    <SuperserveProvider apiKey="your-api-key">
      <Chat />
    </SuperserveProvider>
  )
}

function Chat() {
  const { messages, sendMessage, isStreaming, stop } = useAgent({
    agent: "code-reviewer",
    onFinish: (msg) => console.log("Agent responded:", msg.content),
    onError: (err) => console.error("Error:", err),
  })

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>
          <strong>{msg.role}:</strong> {msg.content}
        </div>
      ))}

      {isStreaming && <button onClick={stop}>Stop</button>}

      <form onSubmit={(e) => {
        e.preventDefault()
        const input = e.currentTarget.elements.namedItem("msg") as HTMLInputElement
        sendMessage(input.value)
        input.value = ""
      }}>
        <input name="msg" placeholder="Send a message..." />
      </form>
    </div>
  )
}
useAgent options:
OptionTypeDescription
agentstringAgent name or ID
apiKeystring?API key (can also be provided via SuperserveProvider)
baseUrlstring?Base URL for the Superserve API
initialMessagesMessage[]?Initial messages to populate the chat
onFinish(message: Message) => voidCalled when the agent finishes responding
onError(error: Error) => voidCalled on errors
useAgent return:
FieldTypeDescription
messagesMessage[]All messages
sendMessage(text: string) => voidSend a message
statusAgentStatus"ready", "streaming", or "error"
isStreamingbooleanResponse in progress
errorError | nullLast error
stop() => voidStop the current stream
reset() => voidClear all messages and end the session
Message:
FieldTypeDescription
idstringUnique message ID
role"user" | "assistant"Who sent the message
contentstringMessage text
toolCallsToolCall[]?Tools the agent used (assistant messages only)
createdAtDateWhen the message was created
The hook creates the client and session lazily on the first sendMessage call. Calling sendMessage while a response is streaming is a no-op - the message is silently ignored. Calling reset() ends the underlying session and clears all messages.

Configuration

const client = new Superserve({
  apiKey: "your-api-key",     // required
  baseUrl: "https://...",     // default: https://api.superserve.ai
  timeout: 30000,             // ms, default: 30000
})

Errors

All errors extend SuperserveError. Network failures and API errors throw APIError with a status code.
import { APIError, SuperserveError } from "@superserve/sdk"

try {
  await client.run("my-agent", { message: "Hello" })
} catch (e) {
  if (e instanceof APIError) {
    console.error(e.status)   // HTTP status code (401, 404, 500, etc.)
    console.error(e.message)  // Error message from the API
    console.error(e.details)  // Field-level error details, if any
  }

  if (e instanceof SuperserveError) {
    // Covers APIError and other SDK errors (e.g. aborted requests)
    console.error(e.message)
  }
}
Error classes:
ClassDescription
SuperserveErrorBase error class for all SDK errors
APIErrorHTTP error from the API. Has status, message, and details properties
Common APIError status codes:
StatusMeaning
401Invalid or missing API key
404Agent or session not found
422Invalid request (check details for field-level info)
0Network failure or request timeout

TypeScript types

All public types are exported from @superserve/sdk:
import type {
  SuperserveOptions,
  RunOptions,
  StreamOptions,
  SessionOptions,
  RunResult,
  ToolCall,
  StreamEvent,
  TextEvent,
  ToolStartEvent,
  ToolEndEvent,
  RunCompletedEvent,
  RunFailedEvent,
  Agent,
  SessionInfo,
} from "@superserve/sdk"
React types are exported from @superserve/sdk/react:
import type {
  Message,
  AgentStatus,
  UseAgentOptions,
  UseAgentReturn,
  SuperserveProviderProps,
} from "@superserve/sdk/react"