# Opencode Sdk

> |

- **Type:** Skill
- **Install:** `agentstack add skill-timmy6942025-opencode-builder-skill-opencode-sdk`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Timmy6942025](https://agentstack.voostack.com/s/timmy6942025)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Timmy6942025](https://github.com/Timmy6942025)
- **Source:** https://github.com/Timmy6942025/opencode-builder-skill/tree/main/skills/opencode-sdk

## Install

```sh
agentstack add skill-timmy6942025-opencode-builder-skill-opencode-sdk
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# OpenCode JavaScript/TypeScript SDK (`@opencode-ai/sdk`)

> **📚 Official Docs:** For the latest information, always refer to the official documentation:
> [https://opencode.ai/docs/sdk/](https://opencode.ai/docs/sdk/)

Type-safe JavaScript/TypeScript client for the OpenCode server. Use it to build integrations, automate workflows, and control OpenCode programmatically.

All types are auto-generated from the server's OpenAPI 3.1 specification and available in the [types file](https://github.com/anomalyco/opencode/blob/dev/packages/sdk/js/src/gen/types.gen.ts).

---

## Installation

```bash
npm install @opencode-ai/sdk
# or
bun add @opencode-ai/sdk
```

---

## Two Client Modes

### Full Lifecycle (`createOpencode`)

Starts an OpenCode server and returns a connected client. Use this when you own the server lifecycle.

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

const { client, server } = await createOpencode({
  hostname: "127.0.0.1",
  port: 4096,
  timeout: 5000,
  config: {
    model: "anthropic/claude-3-5-sonnet-20241022",
  },
})

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

// Use the client...

server.close()
```

**Options:**

| Option | Type | Description | Default |
|---|---|---|---|
| `hostname` | `string` | Server hostname | `127.0.0.1` |
| `port` | `number` | Server port | `4096` |
| `signal` | `AbortSignal` | Abort signal for cancellation | `undefined` |
| `timeout` | `number` | Timeout in ms for server start | `5000` |
| `config` | `Config` | Configuration object | `{}` |

The instance still picks up your `opencode.json`, but inline config overrides or adds to it. The returned `server` object has a `.url` property and a `.close()` method to shut down the server process.

### Client Only (`createOpencodeClient`)

Connects to an already-running OpenCode server. Use this when the server is managed externally (e.g., `opencode serve` or a TUI instance).

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

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  throwOnError: true,
  responseStyle: "data",
})
```

**Options:**

| Option | Type | Description | Default |
|---|---|---|---|
| `baseUrl` | `string` | URL of the server | `http://localhost:4096` |
| `fetch` | `function` | Custom fetch implementation | `globalThis.fetch` |
| `parseAs` | `string` | Response parsing method | `auto` |
| `responseStyle` | `string` | Return style: `"data"` or `"fields"` | `"fields"` |
| `throwOnError` | `boolean` | Throw errors instead of returning them | `false` |

---

## Response Styles

The `responseStyle` option controls how API responses are returned:

- **`"fields"`** (default): Returns `{ data, error, response }` — you check `error` manually.
- **`"data"`**: Returns just the data payload directly. When combined with `throwOnError: true`, throws on error.

```typescript
// "fields" style — check error manually
const result = await client.session.get({ path: { id: "abc" } })
if (result.error) {
  console.error("Error:", result.error)
} else {
  console.log(result.data)
}

// "data" style — returns payload directly
const session = await client.session.get({ path: { id: "abc" } })
// session is the Session object directly
```

---

## Error Handling

### throwOnError

When `throwOnError` is `false` (default), errors are returned in the `error` field of the response object. When `true`, they throw and should be caught:

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

### Error Types

The SDK can return the following error types:

| Error Name | Description |
|---|---|
| `ProviderAuthError` | Authentication failure with a provider. Contains `providerID` and `message`. |
| `UnknownError` | An unexpected error occurred. Contains `message`. |
| `MessageOutputLengthError` | Model output exceeded the maximum length. |
| `MessageAbortedError` | The message generation was aborted. Contains `message`. |
| `ApiError` | An API-level error. Contains `message`, `statusCode`, `isRetryable`, `responseHeaders`, `responseBody`. |
| `BadRequest` | Invalid request parameters. Contains `message` and `kind` (`Params`, `Headers`, `Query`, `Body`, `Payload`). |
| `NotFoundError` | Resource not found. Contains `message`. |

### StructuredOutputError

If the model fails to produce valid structured output after all retries, the response will include a `StructuredOutputError`:

```typescript
if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("Failed to produce structured output:", result.data.info.error.message)
  console.error("Attempts:", result.data.info.error.retries)
}
```

---

## Types

Import TypeScript definitions directly from the SDK:

```typescript
import type {
  Session,
  Message,
  AssistantMessage,
  UserMessage,
  Part,
  TextPart,
  FilePart,
  ToolPart,
  ReasoningPart,
  Config,
  Project,
  Provider,
  Agent,
  Symbol,
  FileNode,
  FileContent,
  File,
  Command,
  Todo,
  AgentConfig,
  ProviderConfig,
  Auth,
  Event,
  GlobalEvent,
} from "@opencode-ai/sdk"
```

All types are generated from the server's OpenAPI specification.

---

## Structured Output

Request validated JSON from the model by specifying a `format` with a JSON schema. The model uses a `StructuredOutput` tool to return matching JSON.

### Basic Usage

```typescript
const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Research Anthropic and provide company info" }],
    format: {
      type: "json_schema",
      schema: {
        type: "object",
        properties: {
          company: { type: "string", description: "Company name" },
          founded: { type: "number", description: "Year founded" },
          products: {
            type: "array",
            items: { type: "string" },
            description: "Main products",
          },
        },
        required: ["company", "founded"],
      },
    },
  },
})

console.log(result.data.info.structured_output)
// { company: "Anthropic", founded: 2021, products: ["Claude", "Claude API"] }
```

### Output Format Types

| Type | Description |
|---|---|
| `"text"` | Default. Standard text response (no structured output) |
| `"json_schema"` | Returns validated JSON matching the provided schema |

### JSON Schema Format

When using `type: "json_schema"`, provide:

| Field | Type | Description |
|---|---|---|
| `type` | `"json_schema"` | Required. Specifies JSON schema mode |
| `schema` | `object` | Required. JSON Schema object defining the output structure |
| `retryCount` | `number` | Optional. Number of validation retries (default: 2) |

### Error Handling

If the model fails to produce valid structured output after all retries, the response will include a `StructuredOutputError`:

```typescript
if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("Failed to produce structured output:", result.data.info.error.message)
  console.error("Attempts:", result.data.info.error.retries)
}
```

### Best Practices

1. **Provide clear descriptions** in your schema properties to help the model understand what data to extract.
2. **Use `required`** to specify which fields must be present.
3. **Keep schemas focused** — complex nested schemas may be harder for the model to fill correctly.
4. **Set appropriate `retryCount`** — increase for complex schemas, decrease for simple ones.

---

## Core Workflows

### Session Management

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

const { client, server } = await createOpencode()

// Create a session
const session = await client.session.create({
  body: { title: "My session" },
})
console.log("Session ID:", session.id)

// Create a child session
const childSession = await client.session.create({
  body: { title: "Child session", parentID: session.id },
})

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

// Get a session by ID
const retrieved = await client.session.get({ path: { id: session.id } })

// Update session title
await client.session.update({
  path: { id: session.id },
  body: { title: "Updated Title" },
})

// List child sessions
const children = await client.session.children({ path: { id: session.id } })

// Get todo list for a session
const todos = await client.session.todo({ path: { id: session.id } })

// Delete a session
await client.session.delete({ path: { id: childSession.id } })
```

### Prompting

```typescript
// Send a basic prompt
const result = await client.session.prompt({
  path: { id: session.id },
  body: {
    parts: [{ type: "text", text: "Hello!" }],
  },
})
console.log(result.data.info) // AssistantMessage

// Send prompt with a specific model
const result2 = await client.session.prompt({
  path: { id: session.id },
  body: {
    model: { providerID: "anthropic", modelID: "claude-3-5-sonnet-20241022" },
    parts: [{ type: "text", text: "Explain this codebase" }],
  },
})

// Send prompt with a specific agent
const result3 = await client.session.prompt({
  path: { id: session.id },
  body: {
    agent: "plan",
    parts: [{ type: "text", text: "Create a plan for this feature" }],
  },
})

// Send prompt with system override
const result4 = await client.session.prompt({
  path: { id: session.id },
  body: {
    system: "You are a senior Go developer. Be concise.",
    parts: [{ type: "text", text: "Review this function" }],
  },
})

// Send prompt with tool control
const result5 = await client.session.prompt({
  path: { id: session.id },
  body: {
    tools: { read: true, write: false, bash: false },
    parts: [{ type: "text", text: "Read and analyze the code" }],
  },
})

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

// Send prompt with file parts
const result6 = await client.session.prompt({
  path: { id: session.id },
  body: {
    parts: [
      { type: "text", text: "Review this file" },
      {
        type: "file",
        mime: "text/plain",
        url: "file:///path/to/file.ts",
      },
    ],
  },
})

// Send prompt with agent invocation part
const result7 = await client.session.prompt({
  path: { id: session.id },
  body: {
    parts: [
      {
        type: "agent",
        name: "explore",
        source: { value: "Find all usages of createOpencode", start: 0, end: 30 },
      },
    ],
  },
})

// Send prompt asynchronously (fire and forget)
await client.session.prompt_async({
  path: { id: session.id },
  body: {
    parts: [{ type: "text", text: "Process this in the background" }],
  },
})
```

### Commands

```typescript
// Execute a slash command
const cmdResult = await client.session.command({
  path: { id: session.id },
  body: {
    command: "compact",
    arguments: "",
  },
})

// List available commands
const commands = await client.command.list()
```

### Shell

```typescript
// Run a shell command
const shellResult = await client.session.shell({
  path: { id: session.id },
  body: {
    agent: "build",
    command: "ls -la",
  },
})

// Run shell with specific model
const shellResult2 = await client.session.shell({
  path: { id: session.id },
  body: {
    agent: "build",
    model: { providerID: "anthropic", modelID: "claude-3-5-sonnet-20241022" },
    command: "git status",
  },
})
```

### Messages

```typescript
// List messages in a session
const messages = await client.session.messages({ path: { id: session.id } })
for (const msg of messages) {
  console.log(msg.info.role, msg.parts.map(p => p.type))
}

// List messages with limit
const recentMessages = await client.session.messages({
  path: { id: session.id },
  query: { limit: 10 },
})

// Get a single message
const msg = await client.session.message({
  path: { id: session.id, messageID: "msg-123" },
})
```

### Abort

```typescript
// Abort a running session
await client.session.abort({ path: { id: session.id } })
```

### Share / Unshare

```typescript
// Share a session (generates a shareable URL)
const shared = await client.session.share({ path: { id: session.id } })
console.log("Share URL:", shared.share?.url)

// Unshare a session
const unshared = await client.session.unshare({ path: { id: session.id } })
```

### Diff

```typescript
// Get the diff for a session
const diffs = await client.session.diff({ path: { id: session.id } })
for (const diff of diffs) {
  console.log(`${diff.file}: +${diff.additions} -${diff.deletions}`)
}

// Get diff for a specific message
const msgDiffs = await client.session.diff({
  path: { id: session.id },
  query: { messageID: "msg-123" },
})
```

### Summarize

```typescript
// Summarize a session
await client.session.summarize({
  path: { id: session.id },
  body: {
    providerID: "anthropic",
    modelID: "claude-3-5-sonnet-20241022",
  },
})
```

### Revert / Unrevert

```typescript
// Revert a specific message
await client.session.revert({
  path: { id: session.id },
  body: {
    messageID: "msg-123",
    partID: "part-456", // optional — revert a specific part
  },
})

// Restore all reverted messages
await client.session.unrevert({ path: { id: session.id } })
```

### Permissions Response

```typescript
// Respond to a permission request
await client.postSessionByIdPermissionsByPermissionId({
  path: {
    id: session.id,
    permissionID: "perm-123",
  },
  body: {
    response: "always", // "once" | "always" | "reject"
  },
})
```

### Fork

```typescript
// Fork a session at a specific message
const forked = await client.session.fork({
  path: { id: session.id },
  body: {
    messageID: "msg-123", // optional — fork from the beginning if omitted
  },
})
console.log("Forked session:", forked.id)
```

### Init

```typescript
// Analyze the current app and create AGENTS.md
await client.session.init({
  path: { id: session.id },
  body: {
    messageID: "msg-123",
    providerID: "anthropic",
    modelID: "claude-3-5-sonnet-20241022",
  },
})
```

### Session Status

```typescript
// Get status for all sessions
const statuses = await client.session.status()
// Returns: { [sessionID: string]: SessionStatus }
// SessionStatus is: { type: "idle" } | { type: "busy" } | { type: "retry", attempt: number, message: string, next: number }
```

---

### File Operations

```typescript
// Read a file
const content = await client.file.read({
  query: { path: "src/index.ts" },
})
console.log(content.content) // file content string

// List files and directories at a path
const fileList = await client.file.list({
  query: { path: "src" },
})
// Returns FileNode[] with { name, path, absolute, type, ignored }

// Search for text in files (ripgrep-style)
const textResults = await client.find.text({
  query: { pattern: "function.*opencode" },
})
for (const match of textResults) {
  console.log(`${match.path.text}:${match.line_number}: ${match.lines.text}`)
}

// Find files by name (fuzzy match)
const files = await client.find.files({
  query: { query: "*.ts" },
})

// Find directories
const dirs = await client.find.files({
  query: { query: "packages", type: "directory", limit: 20 },
})

// Find files with directory override
const customDirFiles = await client.find.files({
  query: { query: "*.json", directory: "/some/other/path" },
})

// Find workspace symbols
const symbols = await client.find.symbols({
  query: { query: "createOpencode" },
})

// Get file status (tracked files with git status)
const status = await client.file.status()
// Returns File[] with { path, added, removed, status: "added" | "deleted" | "modified" }
```

**`find.files` query parameters:**

| Parameter | Type | Description |
|---|---|---|
| `query` | `string` | Required. Search string (fuzzy match) |
| `type` | `"file"` or `"directory"` | Optional. Limit results to files or directories |
| `directory`

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Timmy6942025](https://github.com/Timmy6942025)
- **Source:** [Timmy6942025/opencode-builder-skill](https://github.com/Timmy6942025/opencode-builder-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-timmy6942025-opencode-builder-skill-opencode-sdk
- Seller: https://agentstack.voostack.com/s/timmy6942025
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
