# CodeBuddy Agent SDK

> **Version Requirements**: This documentation is for CodeBuddy Agent SDK v0.1.0 and above.
> **Feature Status**: The SDK is currently in **Preview** stage, and interfaces and behaviors may be adjusted in future versions.

::: warning Important: Environment Isolation
The SDK **does not load any filesystem configurations by default**, including `settings.json`, `CODEBUDDY.md`, MCP servers, sub-agents, slash commands, Rules, and Skills. This is a key difference from direct CLI usage, ensuring SDK application behavior is completely controlled by code with predictability and consistency.

To load these configurations, use the `settingSources` option to explicitly specify. See [Environment Isolation](#environment-isolation-settingsources) section for details.
:::

CodeBuddy Agent SDK allows you to programmatically control CodeBuddy Agent in your applications. It supports TypeScript/JavaScript and Python, enabling scenarios such as automated task execution, custom permission control, and building AI-powered development tools.

## Why Use the SDK

CodeBuddy Agent SDK gives you programmatic access to all of CodeBuddy's capabilities, not just through command-line interaction.

### Beyond Command Line

- **Programmatic Control**: Embed AI programming assistants in your applications to implement automated workflows
- **Custom Interactions**: Build user interfaces and interaction methods that suit your needs
- **Batch Processing**: Perform batch AI operations on multiple files or projects
- **Integrate with Existing Systems**: Seamlessly integrate AI capabilities into CI/CD, IDE plugins, or other development tools

### Fine-grained Control

- **Permission Management**: Implement enterprise-level permission policies through `canUseTool` callbacks
- **Behavior Customization**: Use the Hook system to intercept and modify Agent behavior
- **Resource Limits**: Control token consumption, execution time, and budget
- **Session Management**: Persist and restore conversation contexts

### Extended Capabilities

- **Custom Agents**: Create specialized sub-agents to handle domain-specific tasks
- **MCP Integration**: Connect custom tools and services
- **Multi-model Support**: Flexibly switch and configure different AI models

## What You Can Build

### Development Tool Enhancements

- **IDE Plugins**: Build intelligent programming assistants for VS Code, JetBrains, and other IDEs
- **Code Review Tools**: Automate code quality checks and security scans
- **Documentation Generators**: Automatically generate API documentation, READMEs, and code comments

### Automated Workflows

- **CI/CD Integration**: Perform intelligent code analysis and fixes in pipelines
- **Test Generation**: Automatically generate unit tests and integration tests
- **Refactoring Assistants**: Batch execute code refactoring and migration tasks

### Enterprise Applications

- **Internal Development Platforms**: Build enterprise-level AI programming platforms
- **Knowledge Base Q&A**: Intelligent Q&A systems based on codebases
- **Training Tools**: Interactive programming learning and code review systems

## Feature Overview

- **Message Streaming**: Receive system messages, assistant responses, and tool call results in real-time
- **Multi-turn Conversations**: Support maintaining conversation context across multiple reasoning calls
- **Session Management**: Continue or resume existing conversations through session IDs
- **Permission Control**: Fine-grained tool access permission management
- **Hook System**: Insert custom logic before and after tool execution
- **Custom Agents**: Define specialized sub-agents to handle specific tasks
- **MCP Integration**: Support configuring custom MCP servers to extend functionality

## Installation

::: code-group

```bash [TypeScript]
npm install @tencent-ai/agent-sdk
# or
yarn add @tencent-ai/agent-sdk
# or
pnpm add @tencent-ai/agent-sdk
```

```bash [Python]
uv add codebuddy-agent-sdk
# or
pip install codebuddy-agent-sdk
```

:::

### Environment Requirements

| Language | Version Requirement |
|----------|---------------------|
| TypeScript/JavaScript | Node.js >= 18.20 |
| Python | Python >= 3.10 |

### Authentication Configuration

#### Using Existing Login Credentials

If you've already completed interactive login in the terminal via the `codebuddy` command, the SDK will automatically use that authentication information without additional configuration.

#### Using API Key

If not logged in or need to use different credentials, you can authenticate via API Key:

```bash
export CODEBUDDY_API_KEY="your-api-key"
```

**Get API Key:**

| Edition | Get API Key at |
|:--------|:---------------|
| International | <https://www.codebuddy.ai/profile/keys> |
| China | <https://copilot.tencent.com/profile/> |
| iOA | <https://tencent.sso.copilot.tencent.com/profile/keys> |

> **Note**: When using `CODEBUDDY_API_KEY`, you must correctly configure the `CODEBUDDY_INTERNET_ENVIRONMENT` environment variable based on your edition:
> - International: Do not set (default)
> - China: `export CODEBUDDY_INTERNET_ENVIRONMENT=internal`
> - iOA: `export CODEBUDDY_INTERNET_ENVIRONMENT=ioa`
>
> See [Identity and Access Management documentation](iam.md#personal-user-get-api-key) for details.

You can also pass it in code through the `env` option:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: '...',
  options: {
    env: {
      CODEBUDDY_API_KEY: process.env.MY_API_KEY,
      // China edition users need to set:
      // CODEBUDDY_INTERNET_ENVIRONMENT: 'internal'
      // iOA edition users need to set:
      // CODEBUDDY_INTERNET_ENVIRONMENT: 'ioa'
    }
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    env={
        "CODEBUDDY_API_KEY": os.environ.get("MY_API_KEY"),
        # China edition users need to set:
        # "CODEBUDDY_INTERNET_ENVIRONMENT": "internal"
        # iOA edition users need to set:
        # "CODEBUDDY_INTERNET_ENVIRONMENT": "ioa"
    }
)
```

:::

#### Enterprise Users: OAuth Client Credentials

> Currently only introducing the Client Credentials authorization method, suitable for server-side applications and CI/CD scenarios.

Enterprise users need to first obtain an access token through the OAuth 2.0 Client Credentials flow, then pass it to the SDK.

**Step 1: Create Application to Obtain Credentials**

Refer to [Enterprise Developer Quick Start](https://copilot.tencent.com/apiDocs/open-platform.html) to create an application and obtain Client ID and Client Secret.

**Step 2: Get Token and Call SDK**

::: code-group

```typescript [TypeScript]
async function getOAuthToken(clientId: string, clientSecret: string): Promise<string> {
  const response = await fetch('https://copilot.tencent.com/oauth2/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: clientId,
      client_secret: clientSecret,
    }),
  });
  const data = await response.json();
  return data.access_token;
}

// Get token and call SDK
const token = await getOAuthToken('your-client-id', 'your-client-secret');

for await (const msg of query({
  prompt: 'Hello',
  options: {
    env: { CODEBUDDY_AUTH_TOKEN: token },
  },
})) {
  console.log(msg);
}
```

```python [Python]
import httpx
from codebuddy_agent_sdk import query, CodeBuddyAgentOptions

async def get_oauth_token(client_id: str, client_secret: str) -> str:
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://copilot.tencent.com/oauth2/token",
            data={
                "grant_type": "client_credentials",
                "client_id": client_id,
                "client_secret": client_secret,
            },
        )
        return response.json()["access_token"]

# Get token and call SDK
token = await get_oauth_token("your-client-id", "your-client-secret")

options = CodeBuddyAgentOptions(
    env={"CODEBUDDY_AUTH_TOKEN": token}
)

async for msg in query(prompt="Hello", options=options):
    print(msg)
```

:::

For detailed authentication configuration instructions, please refer to [Identity Authentication](iam.md#authentication-methods).

### Other Environment Variables

| Variable Name | Description | Required |
|---------------|-------------|----------|
| `CODEBUDDY_CODE_PATH` | Path to CodeBuddy CLI executable | Optional |

If not set, the SDK will automatically attempt to locate the CLI.

## Basic Usage

### Simple Query

The most basic usage is to send a prompt and handle the response:

::: code-group

```typescript [TypeScript]
import { query } from '@tencent-ai/agent-sdk';

async function main() {
  const q = query({
    prompt: 'Please explain what a recursive function is',
    options: {
      permissionMode: 'bypassPermissions'
    }
  });

  for await (const message of q) {
    if (message.type === 'assistant') {
      for (const block of message.message.content) {
        if (block.type === 'text') {
          console.log(block.text);
        }
      }
    }
  }
}

main();
```

```python [Python]
import asyncio
from codebuddy_agent_sdk import query, CodeBuddyAgentOptions
from codebuddy_agent_sdk import AssistantMessage, TextBlock

async def main():
    options = CodeBuddyAgentOptions(
        permission_mode="bypassPermissions"
    )

    async for message in query(prompt="Please explain what a recursive function is", options=options):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text)

asyncio.run(main())
```

:::

### Extracting Results

After the query completes, you'll receive a `result` message containing execution statistics:

::: code-group

```typescript [TypeScript]
for await (const message of q) {
  if (message.type === 'result') {
    if (message.subtype === 'success') {
      console.log('Complete! Duration:', message.duration_ms, 'ms');
      console.log('Cost:', message.total_cost_usd, 'USD');
    } else {
      console.log('Execution error');
    }
  }
}
```

```python [Python]
from codebuddy_agent_sdk import ResultMessage

async for message in query(prompt="...", options=options):
    if isinstance(message, ResultMessage):
        if message.subtype == "success":
            print(f"Complete! Duration: {message.duration_ms} ms")
            print(f"Cost: {message.total_cost_usd} USD")
        else:
            print("Execution error")
```

:::

### Message Type Handling

The SDK returns multiple types of messages:

::: code-group

```typescript [TypeScript]
for await (const message of q) {
  switch (message.type) {
    case 'system':
      // Session initialization message
      console.log('Session ID:', message.session_id);
      console.log('Available tools:', message.tools);
      break;

    case 'assistant':
      // AI assistant response
      for (const block of message.message.content) {
        if (block.type === 'text') {
          console.log('[Text]', block.text);
        } else if (block.type === 'tool_use') {
          console.log('[Tool Call]', block.name, block.input);
        } else if (block.type === 'tool_result') {
          console.log('[Tool Result]', block.content);
        }
      }
      break;

    case 'result':
      // Query complete
      console.log('Execution complete, duration:', message.duration_ms, 'ms');
      break;
  }
}
```

```python [Python]
from codebuddy_agent_sdk import (
    SystemMessage, AssistantMessage, ResultMessage,
    TextBlock, ToolUseBlock, ToolResultBlock
)

async for message in query(prompt="...", options=options):
    if isinstance(message, SystemMessage):
        # Session initialization message
        print(f"Session ID: {message.data.get('session_id')}")
        print(f"Available tools: {message.data.get('tools')}")

    elif isinstance(message, AssistantMessage):
        # AI assistant response
        for block in message.content:
            if isinstance(block, TextBlock):
                print(f"[Text] {block.text}")
            elif isinstance(block, ToolUseBlock):
                print(f"[Tool Call] {block.name}: {block.input}")
            elif isinstance(block, ToolResultBlock):
                print(f"[Tool Result] {block.content}")

    elif isinstance(message, ResultMessage):
        # Query complete
        print(f"Execution complete, duration: {message.duration_ms} ms")
```

:::

## Configuration Options

### Permission Mode

Control tool invocation permission behavior through `permissionMode`:

| Mode | Description |
|------|-------------|
| `default` | Default mode, all operations require confirmation |
| `acceptEdits` | Automatically approve file edits, Bash still requires confirmation |
| `plan` | Plan mode, only read operations allowed |
| `bypassPermissions` | Skip all permission checks (use with caution) |

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: 'Analyze project structure',
  options: {
    permissionMode: 'plan'  // Read-only mode
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    permission_mode="plan"  # Read-only mode
)
async for msg in query(prompt="Analyze project structure", options=options):
    pass
```

:::

### Working Directory

Specify the Agent's working directory:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: 'Read package.json',
  options: {
    cwd: '/path/to/project'
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    cwd="/path/to/project"
)
```

:::

### Model Selection

Specify the AI model to use:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: '...',
  options: {
    model: 'deepseek-v3.1',
    fallbackModel: 'deepseek-v3.1'
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    model="deepseek-v3.1",
    fallback_model="deepseek-v3.1"
)
```

:::

### Resource Limits

Limit execution scope:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: '...',
  options: {
    maxTurns: 20         // Maximum conversation turns
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    max_turns=20,        # Maximum conversation turns
)
```

:::

## Environment Isolation (settingSources)

### Design Philosophy

The SDK **does not load any filesystem configurations by default**, providing a completely clean runtime environment. This is a key difference from direct CLI usage.

### Why This Design?

1. **Predictability**: SDK application behavior is completely controlled by code, unaffected by user or project configuration files
2. **Isolation**: Avoid user preferences or project settings interfering with SDK application logic
3. **Security**: Sensitive configurations (such as hooks, permission rules) won't accidentally leak into the SDK environment
4. **Consistency**: Behavior remains consistent when running on different machines

### Default Behavior Comparison

| Scenario | Settings | Memory | MCP | Subagent | Commands | Rules | Skills |
|----------|----------|--------|-----|----------|----------|-------|--------|
| SDK Call (default) | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded |
| CLI Direct Run | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all |

**Configuration File Location Reference**:

| Config Type | User Level Location | Project Level Location | Description |
|-------------|--------------------|-----------------------|-------------|
| Settings | `~/.codebuddy/settings.json` | `.codebuddy/settings.json` | Permissions, hooks, environment variables, etc. |
| Memory | `~/.codebuddy/CODEBUDDY.md` | `CODEBUDDY.md` | Project instructions and context |
| MCP | `~/.codebuddy/.mcp.json` | `.mcp.json` | MCP server configuration |
| Subagent | `~/.codebuddy/agents/` | `.codebuddy/agents/` | Custom sub-agents |
| Commands | `~/.codebuddy/commands/` | `.codebuddy/commands/` | Custom slash commands |
| Rules | `~/.codebuddy/rules/` | `.codebuddy/rules/` | Modular rule files |
| Skills | `~/.codebuddy/skills/` | `.codebuddy/skills/` | AI auto-invoked skills |

### Explicitly Loading Configuration

To load filesystem configurations, use `settingSources` to explicitly specify:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: '...',
  options: {
    // Load project configuration (.codebuddy/settings.json, CODEBUDDY.md)
    settingSources: ['project'],

    // Or load all configurations
    // settingSources: ['user', 'project', 'local']
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    # Load project configuration
    setting_sources=["project"],

    # Or load all configurations
    # setting_sources=["user", "project", "local"]
)
```

:::

### Configuration Source Descriptions

| Value | Description | Location |
|-------|-------------|----------|
| `'user'` | Global user settings | `~/.codebuddy/settings.json`, `~/.codebuddy/CODEBUDDY.md` |
| `'project'` | Project shared settings | `.codebuddy/settings.json`, `CODEBUDDY.md` |
| `'local'` | Project local settings | `.codebuddy/settings.local.json`, `CODEBUDDY.local.md` |

### Typical Use Cases

**CI/CD Environment**:

::: code-group

```typescript [TypeScript]
// Only load project configuration, ignore user and local configurations
const q = query({
  prompt: 'Run tests',
  options: {
    settingSources: ['project'],
    permissionMode: 'bypassPermissions'
  }
});
```

```python [Python]
# Only load project configuration, ignore user and local configurations
options = CodeBuddyAgentOptions(
    setting_sources=["project"],
    permission_mode="bypassPermissions"
)
```

:::

**Fully Programmatic Control**:

::: code-group

```typescript [TypeScript]
// Default behavior: Don't load any configuration
// All behavior explicitly defined through options
const q = query({
  prompt: '...',
  options: {
    agents: { /* custom agent */ },
    mcpServers: { /* custom MCP */ },
    allowedTools: ['Read', 'Grep', 'Glob']
  }
});
```

```python [Python]
# Default behavior: Don't load any configuration
# All behavior explicitly defined through options
options = CodeBuddyAgentOptions(
    agents={"reviewer": AgentDefinition(...)},
    mcp_servers={"db": {...}},
    allowed_tools=["Read", "Grep", "Glob"]
)
```

:::

## Permission Control

### canUseTool Callback

Implement fine-grained permission control through the `canUseTool` callback:

::: code-group

```typescript [TypeScript]
import { query } from '@tencent-ai/agent-sdk';

const q = query({
  prompt: 'Analyze project structure',
  options: {
    canUseTool: async (toolName, input, options) => {
      // Only allow read-only tools
      const readOnlyTools = ['Read', 'Glob', 'Grep'];

      if (readOnlyTools.includes(toolName)) {
        return {
          behavior: 'allow',
          updatedInput: input
        };
      }

      // Deny other tools
      return {
        behavior: 'deny',
        message: `Tool ${toolName} is not allowed`
      };
    }
  }
});
```

```python [Python]
from codebuddy_agent_sdk import (
    query, CodeBuddyAgentOptions,
    CanUseToolOptions, PermissionResultAllow, PermissionResultDeny
)

async def can_use_tool(
    tool_name: str,
    input_data: dict,
    options: CanUseToolOptions
):
    # Only allow read-only tools
    read_only_tools = ["Read", "Glob", "Grep"]

    if tool_name in read_only_tools:
        return PermissionResultAllow(updated_input=input_data)

    # Deny other tools
    return PermissionResultDeny(
        message=f"Tool {tool_name} is not allowed"
    )

options = CodeBuddyAgentOptions(can_use_tool=can_use_tool)
```

:::

### Intercepting Dangerous Operations

Combine permission callbacks to intercept dangerous commands:

::: code-group

```typescript [TypeScript]
const dangerousCommands = ['rm -rf', 'sudo', 'chmod 777'];

const q = query({
  prompt: 'Clean temporary files',
  options: {
    canUseTool: async (toolName, input) => {
      if (toolName === 'Bash') {
        const command = input.command as string;
        for (const dangerous of dangerousCommands) {
          if (command.includes(dangerous)) {
            return {
              behavior: 'deny',
              message: `Dangerous command intercepted: ${dangerous}`,
              interrupt: true  // Interrupt the entire session
            };
          }
        }
      }
      return { behavior: 'allow', updatedInput: input };
    }
  }
});
```

```python [Python]
dangerous_commands = ["rm -rf", "sudo", "chmod 777"]

async def can_use_tool(tool_name, input_data, options):
    if tool_name == "Bash":
        command = input_data.get("command", "")
        for dangerous in dangerous_commands:
            if dangerous in command:
                return PermissionResultDeny(
                    message=f"Dangerous command intercepted: {dangerous}",
                    interrupt=True  # Interrupt the entire session
                )
    return PermissionResultAllow(updated_input=input_data)
```

:::

## Multi-turn Conversations

### Using Session/Client API

For scenarios requiring multi-turn interactions, use the Session (TypeScript) or Client (Python) API:

::: code-group

```typescript [TypeScript]
import { unstable_v2_createSession } from '@tencent-ai/agent-sdk';

async function main() {
  const session = unstable_v2_createSession({
    model: 'deepseek-v3.1'
  });

  // First turn
  await session.send('Analyze this project\'s architecture');
  for await (const message of session.stream()) {
    console.log(message);
  }

  // Second turn (maintaining context)
  await session.send('Please explain point three in detail');
  for await (const message of session.stream()) {
    console.log(message);
  }

  session.close();
}
```

```python [Python]
from codebuddy_agent_sdk import CodeBuddySDKClient, CodeBuddyAgentOptions

async def main():
    options = CodeBuddyAgentOptions(model="deepseek-v3.1")

    async with CodeBuddySDKClient(options=options) as client:
        # First turn
        await client.query("Analyze this project's architecture")
        async for message in client.receive_response():
            print(message)

        # Second turn (maintaining context)
        await client.query("Please explain point three in detail")
        async for message in client.receive_response():
            print(message)

asyncio.run(main())
```

:::

### Interrupting Execution

Interrupt execution during runtime:

::: code-group

```typescript [TypeScript]
const q = query({ prompt: 'Execute long-running task...' });

let count = 0;
for await (const message of q) {
  if (message.type === 'assistant') {
    for (const block of message.message.content) {
      if (block.type === 'tool_use') {
        count++;
        if (count >= 10) {
          await q.interrupt();  // Interrupt execution
          break;
        }
      }
    }
  }
}
```

```python [Python]
async with CodeBuddySDKClient(options=options) as client:
    await client.query("Execute long-running task...")

    count = 0
    async for message in client.receive_messages():
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, ToolUseBlock):
                    count += 1
                    if count >= 10:
                        await client.interrupt()  # Interrupt execution
                        break
```

:::

## Hook System

Hooks allow you to insert custom logic before and after tool execution.

### PreToolUse Hook

Intercept and handle before tool execution:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: 'Clean temporary files',
  options: {
    hooks: {
      PreToolUse: [{
        matcher: 'Bash',  // Only match Bash tool
        hooks: [
          async (input, toolUseId) => {
            console.log('About to execute command:', input.command);

            // Can block execution
            if (input.command.includes('rm')) {
              return {
                decision: 'block',
                reason: 'Delete command blocked'
              };
            }

            return { continue: true };
          }
        ]
      }]
    }
  }
});
```

```python [Python]
from codebuddy_agent_sdk import HookMatcher, HookContext

async def pre_tool_hook(input_data, tool_use_id, context: HookContext):
    print(f"About to execute command: {input_data.get('command')}")

    # Can block execution
    if "rm" in input_data.get("command", ""):
        return {"continue_": False, "reason": "Delete command blocked"}

    return {"continue_": True}

options = CodeBuddyAgentOptions(
    hooks={
        "PreToolUse": [
            HookMatcher(matcher="Bash", hooks=[pre_tool_hook])
        ]
    }
)
```

:::

### Hook Event Types

| Event | Trigger Timing |
|-------|----------------|
| `PreToolUse` | Before tool execution |
| `PostToolUse` | After successful tool execution |
| `PostToolUseFailure` | After failed tool execution |
| `UserPromptSubmit` | User submits prompt |
| `SessionStart` | Session starts |
| `SessionEnd` | Session ends |
| `WorktreeCreate` | When creating an isolated `worktree` |
| `WorktreeRemove` | When removing an isolated `worktree` |

## Extended Capabilities

### Custom Agents

Define specialized sub-agents:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: 'Use code-reviewer to review code',
  options: {
    agents: {
      'code-reviewer': {
        description: 'Professional code review assistant',
        tools: ['Read', 'Glob', 'Grep'],  // Read-only
        disallowedTools: ['Bash', 'Write', 'Edit'],
        prompt: `You are a code review expert. Please check:
1. Code standards
2. Potential bugs
3. Performance issues
4. Security vulnerabilities`,
        model: 'deepseek-v3.1'
      }
    }
  }
});
```

```python [Python]
from codebuddy_agent_sdk import AgentDefinition

options = CodeBuddyAgentOptions(
    agents={
        "code-reviewer": AgentDefinition(
            description="Professional code review assistant",
            tools=["Read", "Glob", "Grep"],  # Read-only
            disallowed_tools=["Bash", "Write", "Edit"],
            prompt="""You are a code review expert. Please check:
1. Code standards
2. Potential bugs
3. Performance issues
4. Security vulnerabilities""",
            model="deepseek-v3.1"
        )
    }
)
```

:::

### MCP Server Configuration

Integrate custom MCP servers:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: 'Query database',
  options: {
    mcpServers: {
      'database': {
        type: 'stdio',
        command: 'node',
        args: ['./mcp-servers/db-server.js'],
        env: {
          DB_HOST: 'localhost',
          DB_PORT: '5432'
        }
      }
    }
  }
});
```

```python [Python]
options = CodeBuddyAgentOptions(
    mcp_servers={
        "database": {
            "type": "stdio",
            "command": "node",
            "args": ["./mcp-servers/db-server.js"],
            "env": {
                "DB_HOST": "localhost",
                "DB_PORT": "5432"
            }
        }
    }
)
```

:::

### Handling AskUserQuestion

AI may ask users questions through the `AskUserQuestion` tool, which can be handled in the permission callback:

::: code-group

```typescript [TypeScript]
const q = query({
  prompt: 'Configure database connection',
  options: {
    canUseTool: async (toolName, input) => {
      if (toolName === 'AskUserQuestion') {
        const questions = input.questions as any[];
        const answers: Record<string, string> = {};

        for (const q of questions) {
          console.log(`Question: ${q.question}`);
          // Can integrate actual user interaction here
          answers[q.question] = q.options[0].label;
        }

        return {
          behavior: 'allow',
          updatedInput: { ...input, answers }
        };
      }
      return { behavior: 'allow', updatedInput: input };
    }
  }
});
```

```python [Python]
async def can_use_tool(tool_name, input_data, options):
    if tool_name == "AskUserQuestion":
        questions = input_data.get("questions", [])
        answers = {}

        for q in questions:
            print(f"Question: {q['question']}")
            # Can integrate actual user interaction here
            answers[q["question"]] = q["options"][0]["label"]

        return PermissionResultAllow(
            updated_input={**input_data, "answers": answers}
        )

    return PermissionResultAllow(updated_input=input_data)
```

:::

## Error Handling

::: code-group

```typescript [TypeScript]
import { query, AbortError } from '@tencent-ai/agent-sdk';

try {
  const q = query({ prompt: '...' });
  for await (const message of q) {
    // ...
  }
} catch (error) {
  if (error instanceof AbortError) {
    console.log('Operation aborted');
  } else {
    console.error('Error occurred:', error);
  }
}
```

```python [Python]
from codebuddy_agent_sdk import (
    query, CodeBuddySDKError,
    CLIConnectionError, CLINotFoundError
)

try:
    async for message in query(prompt="..."):
        pass
except CLINotFoundError as e:
    print(f"CLI not found: {e}")
except CLIConnectionError as e:
    print(f"Connection failed: {e}")
except CodeBuddySDKError as e:
    print(f"SDK error: {e}")
```

:::

## Best Practices

1. **Permission Control**: In production environments, use `canUseTool` for fine-grained permissions, avoid using `bypassPermissions`
2. **Resource Limits**: Use `maxTurns` to limit execution scope and prevent unexpected resource consumption
3. **Error Handling**: Always handle error states in `result` messages
4. **Hook Timeouts**: Set reasonable timeout values for Hooks

## Related Documentation

- [TypeScript SDK Reference](sdk-typescript.md) - Detailed TypeScript API reference
- [Python SDK Reference](sdk-python.md) - Detailed Python API reference
- [Hook Reference Guide](hooks.md) - Detailed Hook configuration instructions
- [MCP Integration](mcp.md) - MCP server configuration guide
- [Sub-Agent System](sub-agents.md) - Detailed sub-agent explanation

*CodeBuddy Agent SDK - Integrate AI programming capabilities into your applications*
