or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

batches.mdbeta.mdclient-initialization.mderrors.mdindex.mdmessages.mdmodels.mdplatform-clients.mdstreaming.mdtools-builtin.mdtools-decorators.mdtools-function.mdtools-memory.mdtools-runners.mdtools.mdtypes.md

tools-builtin.mddocs/

0

# Builtin Tool Classes

1

2

Builtin tool classes provide abstract base classes for implementing built-in tool types with custom behavior. Unlike function tools that wrap Python functions, builtin tools define their own tool schema and execution logic, enabling integration with special tool types like computer use, bash, or memory.

3

4

## Capabilities

5

6

### Synchronous Builtin Tool

7

8

Abstract base class for implementing synchronous built-in tools with custom schemas and behavior.

9

10

```python { .api }

11

class BetaBuiltinFunctionTool(ABC):

12

"""

13

Abstract base class for synchronous built-in tools.

14

15

Subclass this to create custom implementations of built-in tool types

16

that require special handling beyond simple function wrapping.

17

"""

18

19

@abstractmethod

20

def to_dict(self) -> BetaToolUnionParam:

21

"""

22

Generate the tool definition for the API.

23

24

Must return a tool parameter dictionary that matches one of the

25

built-in tool schemas (e.g., computer_20250124, bash_20250124,

26

text_editor_20250728, memory_20250818).

27

28

Returns:

29

Tool parameter dictionary with type, name, and any tool-specific fields

30

"""

31

32

@abstractmethod

33

def call(self, input: object) -> str | Iterable[BetaContent]:

34

"""

35

Execute the tool with the given input.

36

37

Args:

38

input: Tool input parameters from Claude (format depends on tool type)

39

40

Returns:

41

Tool execution result as string or content blocks

42

"""

43

44

@property

45

def name(self) -> str:

46

"""

47

Get the tool name from the tool definition.

48

49

Returns:

50

The tool's name as it appears in the API

51

"""

52

```

53

54

#### Usage Examples

55

56

**Custom computer use tool:**

57

58

```python

59

from anthropic.lib.tools import BetaBuiltinFunctionTool

60

from anthropic.types.beta import BetaToolComputerUse20250124Param

61

62

class MyComputerTool(BetaBuiltinFunctionTool):

63

"""Custom computer use implementation."""

64

65

def to_dict(self) -> BetaToolComputerUse20250124Param:

66

return {

67

"type": "computer_20250124",

68

"name": "computer",

69

"display_width_px": 1920,

70

"display_height_px": 1080,

71

"display_number": 1,

72

}

73

74

def call(self, input: object) -> str:

75

"""Execute computer action."""

76

# input will be a dict with action, text, coordinate, etc.

77

action = input.get("action")

78

79

if action == "screenshot":

80

# Take and return screenshot

81

return self._capture_screenshot()

82

elif action == "mouse_move":

83

# Move mouse to coordinate

84

x, y = input["coordinate"]

85

return f"Moved mouse to ({x}, {y})"

86

elif action == "type":

87

# Type text

88

text = input["text"]

89

return f"Typed: {text}"

90

else:

91

return f"Unknown action: {action}"

92

93

def _capture_screenshot(self) -> str:

94

# Your screenshot logic

95

return "data:image/png;base64,..."

96

97

# Use the custom tool

98

client = Anthropic()

99

computer_tool = MyComputerTool()

100

101

message = client.beta.messages.create(

102

model="claude-3-5-sonnet-20241022",

103

max_tokens=1024,

104

tools=[computer_tool.to_dict()],

105

messages=[{"role": "user", "content": "Take a screenshot"}],

106

betas=["computer-use-2025-01-24"]

107

)

108

```

109

110

**Custom bash tool:**

111

112

```python

113

import subprocess

114

from anthropic.types.beta import BetaToolBash20250124Param

115

116

class SafeBashTool(BetaBuiltinFunctionTool):

117

"""Bash tool with safety restrictions."""

118

119

def __init__(self, allowed_commands: set[str]):

120

self.allowed_commands = allowed_commands

121

122

def to_dict(self) -> BetaToolBash20250124Param:

123

return {

124

"type": "bash_20250124",

125

"name": "bash"

126

}

127

128

def call(self, input: object) -> str:

129

"""Execute bash command if allowed."""

130

command = input.get("command", "")

131

132

# Check if command is in allowed list

133

cmd_name = command.split()[0] if command else ""

134

if cmd_name not in self.allowed_commands:

135

return f"Error: Command '{cmd_name}' not allowed"

136

137

try:

138

# Execute command with timeout

139

result = subprocess.run(

140

command,

141

shell=True,

142

capture_output=True,

143

text=True,

144

timeout=5

145

)

146

return result.stdout or result.stderr

147

except subprocess.TimeoutExpired:

148

return "Error: Command timed out"

149

except Exception as e:

150

return f"Error: {str(e)}"

151

152

# Use with restricted commands

153

bash_tool = SafeBashTool(allowed_commands={"ls", "pwd", "echo", "cat"})

154

155

runner = client.beta.messages.tool_runner(

156

model="claude-3-5-sonnet-20241022",

157

max_tokens=1024,

158

tools=[bash_tool],

159

messages=[{"role": "user", "content": "List files in current directory"}],

160

betas=["bash-2025-01-24"]

161

)

162

```

163

164

**Custom text editor tool:**

165

166

```python

167

from anthropic.types.beta import BetaToolTextEditor20250728Param

168

169

class InMemoryEditorTool(BetaBuiltinFunctionTool):

170

"""Text editor that stores files in memory."""

171

172

def __init__(self):

173

self.files: dict[str, list[str]] = {}

174

175

def to_dict(self) -> BetaToolTextEditor20250728Param:

176

return {

177

"type": "text_editor_20250728",

178

"name": "str_replace_editor"

179

}

180

181

def call(self, input: object) -> str:

182

"""Execute editor command."""

183

command = input.get("command")

184

path = input.get("path", "")

185

186

if command == "view":

187

if path not in self.files:

188

return f"Error: File {path} not found"

189

return "\n".join(self.files[path])

190

191

elif command == "create":

192

content = input.get("file_text", "")

193

self.files[path] = content.split("\n")

194

return f"Created {path} with {len(self.files[path])} lines"

195

196

elif command == "str_replace":

197

if path not in self.files:

198

return f"Error: File {path} not found"

199

old_str = input.get("old_str", "")

200

new_str = input.get("new_str", "")

201

content = "\n".join(self.files[path])

202

if old_str not in content:

203

return f"Error: String not found: {old_str}"

204

content = content.replace(old_str, new_str, 1)

205

self.files[path] = content.split("\n")

206

return f"Replaced text in {path}"

207

208

return f"Unknown command: {command}"

209

210

# Use the in-memory editor

211

editor_tool = InMemoryEditorTool()

212

```

213

214

**Logging tool wrapper:**

215

216

```python

217

class LoggingToolWrapper(BetaBuiltinFunctionTool):

218

"""Wraps another tool and logs all calls."""

219

220

def __init__(self, wrapped_tool: BetaBuiltinFunctionTool):

221

self.wrapped_tool = wrapped_tool

222

self.call_log = []

223

224

def to_dict(self):

225

return self.wrapped_tool.to_dict()

226

227

def call(self, input: object) -> str | Iterable[BetaContent]:

228

# Log the call

229

self.call_log.append({

230

"timestamp": datetime.now(),

231

"tool": self.wrapped_tool.name,

232

"input": input

233

})

234

235

# Execute wrapped tool

236

try:

237

result = self.wrapped_tool.call(input)

238

self.call_log[-1]["result"] = result

239

self.call_log[-1]["success"] = True

240

return result

241

except Exception as e:

242

self.call_log[-1]["error"] = str(e)

243

self.call_log[-1]["success"] = False

244

raise

245

246

def get_log(self) -> list[dict]:

247

"""Get the call log."""

248

return self.call_log

249

250

# Wrap any tool with logging

251

original_tool = MyComputerTool()

252

logged_tool = LoggingToolWrapper(original_tool)

253

254

# Use and inspect log

255

runner = client.beta.messages.tool_runner(

256

model="claude-3-5-sonnet-20241022",

257

max_tokens=1024,

258

tools=[logged_tool],

259

messages=[{"role": "user", "content": "Take a screenshot"}]

260

)

261

result = runner.until_done()

262

263

# Check what was called

264

for call in logged_tool.get_log():

265

print(f"{call['timestamp']}: {call['tool']} - {call['success']}")

266

```

267

268

### Asynchronous Builtin Tool

269

270

Abstract base class for implementing asynchronous built-in tools with custom schemas and behavior.

271

272

```python { .api }

273

class BetaAsyncBuiltinFunctionTool(ABC):

274

"""

275

Abstract base class for asynchronous built-in tools.

276

277

Identical to BetaBuiltinFunctionTool but with async execution support.

278

"""

279

280

@abstractmethod

281

def to_dict(self) -> BetaToolUnionParam:

282

"""

283

Generate the tool definition for the API.

284

285

Returns:

286

Tool parameter dictionary with type, name, and any tool-specific fields

287

"""

288

289

@abstractmethod

290

async def call(self, input: object) -> str | Iterable[BetaContent]:

291

"""

292

Execute the tool asynchronously with the given input.

293

294

Args:

295

input: Tool input parameters from Claude

296

297

Returns:

298

Tool execution result as string or content blocks

299

"""

300

301

@property

302

def name(self) -> str:

303

"""

304

Get the tool name from the tool definition.

305

306

Returns:

307

The tool's name as it appears in the API

308

"""

309

```

310

311

#### Usage Examples

312

313

**Async computer use tool:**

314

315

```python

316

class AsyncComputerTool(BetaAsyncBuiltinFunctionTool):

317

"""Async computer use implementation."""

318

319

def to_dict(self) -> BetaToolComputerUse20250124Param:

320

return {

321

"type": "computer_20250124",

322

"name": "computer",

323

"display_width_px": 1920,

324

"display_height_px": 1080,

325

}

326

327

async def call(self, input: object) -> str:

328

"""Execute computer action asynchronously."""

329

action = input.get("action")

330

331

if action == "screenshot":

332

# Async screenshot capture

333

screenshot = await self._async_capture_screenshot()

334

return screenshot

335

elif action == "type":

336

# Async typing

337

text = input["text"]

338

await asyncio.sleep(len(text) * 0.01) # Simulate typing delay

339

return f"Typed: {text}"

340

341

return f"Action complete: {action}"

342

343

async def _async_capture_screenshot(self) -> str:

344

# Async screenshot logic

345

await asyncio.sleep(0.1)

346

return "data:image/png;base64,..."

347

```

348

349

**Async bash tool with process management:**

350

351

```python

352

class AsyncBashTool(BetaAsyncBuiltinFunctionTool):

353

"""Async bash execution with proper process handling."""

354

355

def to_dict(self) -> BetaToolBash20250124Param:

356

return {"type": "bash_20250124", "name": "bash"}

357

358

async def call(self, input: object) -> str:

359

"""Execute bash command asynchronously."""

360

command = input.get("command", "")

361

362

try:

363

# Use async subprocess

364

process = await asyncio.create_subprocess_shell(

365

command,

366

stdout=asyncio.subprocess.PIPE,

367

stderr=asyncio.subprocess.PIPE

368

)

369

370

# Wait with timeout

371

stdout, stderr = await asyncio.wait_for(

372

process.communicate(),

373

timeout=10

374

)

375

376

return stdout.decode() or stderr.decode()

377

378

except asyncio.TimeoutError:

379

return "Error: Command timed out"

380

except Exception as e:

381

return f"Error: {str(e)}"

382

```

383

384

**Async API-backed tool:**

385

386

```python

387

import httpx

388

389

class AsyncWebSearchTool(BetaAsyncBuiltinFunctionTool):

390

"""Web search using external API."""

391

392

def to_dict(self):

393

return {

394

"type": "custom_search",

395

"name": "web_search",

396

"description": "Search the web for information"

397

}

398

399

async def call(self, input: object) -> str:

400

"""Perform web search."""

401

query = input.get("query", "")

402

403

async with httpx.AsyncClient() as client:

404

response = await client.get(

405

"https://api.search.com/search",

406

params={"q": query}

407

)

408

results = response.json()

409

410

# Format results

411

formatted = []

412

for result in results["results"][:5]:

413

formatted.append(f"- {result['title']}: {result['url']}")

414

415

return "\n".join(formatted)

416

417

# Use with async client

418

client = AsyncAnthropic()

419

search_tool = AsyncWebSearchTool()

420

421

runner = client.beta.messages.tool_runner(

422

model="claude-3-5-sonnet-20241022",

423

max_tokens=1024,

424

tools=[search_tool],

425

messages=[{"role": "user", "content": "Search for Python tutorials"}]

426

)

427

final_message = await runner.until_done()

428

```

429

430

## Types

431

432

### Built-in Tool Union Type

433

434

```python { .api }

435

BetaToolUnionParam = (

436

ToolParam

437

| BetaToolBash20250124Param

438

| BetaToolTextEditor20250728Param

439

| BetaToolComputerUse20250124Param

440

| BetaWebSearchTool20250305Param

441

| BetaMemoryTool20250818Param

442

# ... and other built-in tool types

443

)

444

```

445

446

Union of all possible tool parameter types that can be used with the API.

447