Skip to content

5.10a SDK Basics ​

TL;DR: Use the JavaScript/TypeScript SDK to control OpenCode programmatically for automated workflows and custom integrations.

Course Notes ​

Key takeaways from this lesson:

5.10a SDK Basics Notes


What You'll Learn ​

  • Install and configure the OpenCode SDK
  • Create server and client instances
  • Launch the TUI interface
  • Manage sessions and send messages
  • Listen to real-time events

Your Current Challenges ​

  • Want to call OpenCode from your own application
  • Want to batch process tasks programmatically
  • Want to build custom integrations (IDE plugins, CI/CD tools, etc.)
  • Want to automate OpenCode operations in scripts

SDK Architecture Overview ​

┌─────────────────────────────────────────────────────────┐
│                    Your Application                       │
├─────────────────────────────────────────────────────────┤
│                   @opencode-ai/sdk                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
│  │createOpencode│  │createOpencode│  │createOpencode│    │
│  │             │  │   Client    │  │    Tui      │      │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘      │
│         │                │                │              │
│  Server+Client     Client Only      Launch TUI          │
└─────────┼────────────────┼────────────────┼─────────────┘
          │                │                │
          ▼                ▼                ▼
┌─────────────────────────────────────────────────────────┐
│                  OpenCode Server                         │
│            HTTP API (default port 4096)                  │
└─────────────────────────────────────────────────────────┘

Install SDK ​

bash
npm install @opencode-ai/sdk

V1 and V2 Entry Points ​

The examples in this lesson use the V1 entry point, @opencode-ai/sdk. As of v1.18.22, V1 has not been removed. The same package also exports @opencode-ai/sdk/v2, which provides V2 extensions for sessions, questions, the current location, event streams, paginated history, runtime operations, and permission requests. The two entry points use different parameter structures—do not simply change the import and continue using V1-style { path, body } calls.

typescript
// V1: used by the remaining examples in this lesson
import { createOpencodeClient } from "@opencode-ai/sdk"

// V2: flat parameters; see Lesson 5.10c
import { createOpencodeClient as createV2Client } from "@opencode-ai/sdk/v2"

Source: packages/sdk/js/package.json:12-20, V1 sdk.gen.ts:431-700, V2 location:5038-5058, V2 session:5171-5793


Three Usage Modes ​

Start both server and client, ideal for standalone scripts and automation:

typescript
import { createOpencode } from "@opencode-ai/sdk"

const { client, server } = await createOpencode()

// Use client to call APIs
const sessions = await client.session.list()
console.log(`Currently ${sessions.data?.length} sessions`)

// Close server when done
server.close()

ServerOptions Parameters ​

ParameterTypeDescriptionDefault
hostnamestringServer hostname127.0.0.1
portnumberServer port4096
signalAbortSignalAbort signal for cancellationundefined
timeoutnumberServer startup timeout (ms)5000
configConfigConfig object to override opencode.json{}

Source: packages/sdk/js/src/server.ts:5-11

Configuration Override Example ​

typescript
import { createOpencode } from "@opencode-ai/sdk"

const opencode = await createOpencode({
  hostname: "127.0.0.1",
  port: 4097,  // Use different port to avoid conflicts
  timeout: 10000,
  config: {
    model: "anthropic/claude-opus-4-5-thinking",
    logLevel: "DEBUG",
  },
})

console.log(`Server running at ${opencode.server.url}`)

// Close when done
opencode.server.close()

2. Client-Only Mode ​

Connect to a running OpenCode instance, ideal for plugin development:

typescript
import { createOpencodeClient } from "@opencode-ai/sdk"

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
})

// Use client directly
const sessions = await client.session.list()

ClientOptions Parameters ​

ParameterTypeDescriptionDefault
baseUrlstringServer URLhttp://localhost:4096
fetchfunctionCustom fetch implementationglobalThis.fetch
parseAsstringResponse parsing: auto, json, text, blob, arrayBuffer, stream, formDataauto
responseStyle"data" | "fields"Return style: data returns only data, fields returns full responsefields
throwOnErrorbooleanThrow on error instead of returningfalse
directorystringProject directory (passed via X-Opencode-Directory header)undefined

Source: packages/sdk/js/src/gen/client/types.gen.ts:10-52, packages/sdk/js/src/client.ts:33

Switching Project Directories ​

typescript
// Connect to different projects
const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  directory: "/path/to/my-project",
})

Remote Connection (with Authentication) ​

When connecting to a remote OpenCode server with OPENCODE_SERVER_PASSWORD set, pass Basic Auth via headers:

typescript
import { createOpencodeClient } from "@opencode-ai/sdk"

// Remote connection (with auth)
const client = createOpencodeClient({
  baseUrl: "http://192.168.1.100:4096",
  headers: {
    // Basic Auth format: Base64(username:password)
    // Use btoa() in browser/Edge Runtime
    Authorization: `Basic ${btoa("opencode:your-password")}`
  },
  directory: "/projects/my-app"  // Specify remote project directory
})
Use Buffer in Node.js
typescript
// Node.js doesn't have btoa, use Buffer instead
Authorization: `Basic ${Buffer.from("opencode:password").toString("base64")}`
ScenarioUsernameDescription
DefaultopencodeServer default username
CustomValue of OPENCODE_SERVER_USERNAME env varIf server has custom username

Source: packages/opencode/src/server/auth.ts:36-42 (Basic Auth header generation), packages/opencode/src/server/routes/instance/httpapi/middleware/authorization.ts (request parsing)


3. Launch TUI Interface ​

Programmatically start OpenCode's terminal interface:

typescript
import { createOpencodeTui } from "@opencode-ai/sdk"

const tui = createOpencodeTui({
  project: "/path/to/my-project",
  model: "anthropic/claude-opus-4-5-thinking",
  session: "abc123",  // Resume specific session
  agent: "build",
})

// User can interact in TUI
// ...

// Close TUI
tui.close()

TuiOptions Parameters ​

ParameterTypeDescription
projectstringProject directory path
modelstringModel to use (format: provider/model)
sessionstringSession ID to resume
agentstringAgent to use (e.g., build, plan)
signalAbortSignalAbort signal for cancellation
configConfigConfiguration object

Source: packages/sdk/js/src/server.ts:13-20


Basic API Usage ​

Session Management ​

typescript
// Create new session
const session = await client.session.create({
  body: { title: "My Task" },
})
console.log(`Created session: ${session.data?.id}`)

// List all sessions
const sessions = await client.session.list()

// Get single session
const detail = await client.session.get({
  path: { id: session.data!.id },
})

// Delete session
await client.session.delete({
  path: { id: session.data!.id },
})

Send Messages ​

typescript
// Send prompt and wait for AI response
const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    model: { providerID: "anthropic", modelID: "claude-opus-4-5-thinking" },
    parts: [{ type: "text", text: "Please analyze the performance issues in this code" }],
  },
})

// Inject context (without triggering AI response)
await client.session.prompt({
  path: { id: sessionId },
  body: {
    noReply: true,
    parts: [{ type: "text", text: "You are a professional code review assistant." }],
  },
})

Async Send (Don't Wait for Response) ​

typescript
// Returns immediately, ideal for long tasks
await client.session.promptAsync({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Please refactor the entire module" }],
  },
})

// Get response via event listener

File Operations ​

typescript
// Search text content
const textResults = await client.find.text({
  query: { pattern: "function.*opencode" },
})

// Find files (supports glob patterns)
const files = await client.find.files({
  query: { query: "*.ts" },
})

// Find directories only
const dirs = await client.find.files({
  query: { query: "src", dirs: "true" },
})

// Read file content
const content = await client.file.read({
  query: { path: "src/index.ts" },
})

// Get file status (git changes)
const status = await client.file.status()

TUI Control ​

typescript
// Append text to input box
await client.tui.appendPrompt({
  body: { text: "Please check this file" },
})

// Submit current input
await client.tui.submitPrompt()

// Clear input
await client.tui.clearPrompt()

// Show notification
await client.tui.showToast({
  body: { 
    message: "Task completed!", 
    variant: "success",
    duration: 3000,  // Show for 3 seconds
  },
})

// Open dialogs
await client.tui.openHelp()
await client.tui.openSessions()
await client.tui.openThemes()
await client.tui.openModels()

// Execute TUI commands
await client.tui.executeCommand({
  body: { command: "agent_cycle" },
})

Real-time Event Listening ​

Subscribe to Event Stream ​

typescript
const events = await client.event.subscribe()

for await (const event of events.stream) {
  console.log(`Event type: ${event.type}`)
  console.log(`Event data:`, event.properties)
  
  // Handle by event type
  switch (event.type) {
    case "message.updated":
      console.log("Message updated:", event.properties.info)
      break
    case "session.idle":
      console.log("Session idle:", event.properties.sessionID)
      break
    case "permission.updated":
      console.log("Permission request:", event.properties)
      break
  }
}

Common Event Types ​

Event TypeDescription
message.updatedMessage content updated
message.part.updatedMessage part updated (includes delta)
session.statusSession status changed (idle/busy/retry)
session.idleSession entered idle state
permission.updatedPermission request pending
file.editedFile was edited
todo.updatedTodo list updated

For complete event types, see 5.10b API Reference


Type Imports ​

SDK provides complete TypeScript type definitions:

typescript
import type { 
  // Core types
  Session,
  Message,
  Part,
  
  // Event types
  Event,
  EventMessageUpdated,
  EventSessionIdle,
  
  // Config types
  Config,
  AgentConfig,
  ProviderConfig,
  
  // Others
  Todo,
  Permission,
  Agent,
  Provider,
  Model,
} from "@opencode-ai/sdk"

Error Handling ​

Standard Error Handling ​

typescript
try {
  const session = await client.session.get({ 
    path: { id: "invalid-id" } 
  })
} catch (error) {
  console.error("Failed to get session:", (error as Error).message)
}

Using throwOnError Option ​

typescript
// Global configuration
const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  throwOnError: true,  // Throw on all request errors
})

// Or use in single request
const result = await client.session.get({
  path: { id: sessionId },
  throwOnError: true,
})

Check Return Value ​

typescript
const result = await client.session.get({
  path: { id: sessionId },
})

if (result.error) {
  console.error("Error:", result.error)
} else {
  console.log("Session:", result.data)
}

Practical Example: Batch Code Review ​

typescript
import { createOpencode } from "@opencode-ai/sdk"
import { readdir } from "fs/promises"

async function batchCodeReview(directory: string) {
  const { client, server } = await createOpencode({
    config: {
      model: "anthropic/claude-opus-4-5-thinking",
    },
  })

  try {
    // Create session
    const session = await client.session.create({
      body: { title: `Batch Code Review - ${directory}` },
    })
    const sessionId = session.data!.id

    // Find all TypeScript files
    const files = await client.find.files({
      query: { query: "*.ts", directory },
    })

    console.log(`Found ${files.data?.length} files`)

    // Review each file
    for (const file of files.data ?? []) {
      console.log(`Reviewing: ${file}`)
      
      await client.session.prompt({
        path: { id: sessionId },
        body: {
          parts: [{ 
            type: "text", 
            text: `Please review file ${file} for potential issues and improvement suggestions.` 
          }],
        },
      })
    }

    console.log("Review complete!")
  } finally {
    server.close()
  }
}

batchCodeReview("./src")

Common Pitfalls ​

IssueCauseSolution
SDK connection failedServer not runningRun opencode serve first or use createOpencode()
Port conflictDefault port 4096 in useSpecify different port port: 4097
Type errorsSDK version mismatchUpdate to latest npm update @opencode-ai/sdk
Timeout errorSlow server startup or network issuesIncrease timeout value
Event stream interruptedConnection droppedImplement reconnection logic
Response format confusionresponseStyle configDefault is fields, returns { data, error, request, response }

Lesson Summary ​

You learned:

  1. Install SDK: npm install @opencode-ai/sdk
  2. Three usage modes:
    • createOpencode() - Server + Client
    • createOpencodeClient() - Client only
    • createOpencodeTui() - Launch TUI
  3. Basic APIs: Session management, message sending, file operations, TUI control
  4. Event listening: Real-time status change notifications


Next Lesson Preview ​

5.10b API Reference covers all 20 API modules plus one permission-response method, complete type definitions, and 32 event types in detail.