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-memory.mddocs/

0

# Memory Tool Classes

1

2

Memory tools enable Claude to store and retrieve information across conversations by providing a file-system-like interface for persistent memory. Implement custom memory backends by subclassing the abstract memory tool classes.

3

4

## Capabilities

5

6

### Synchronous Memory Tool

7

8

Abstract base class for implementing synchronous memory backends that Claude can use to create, view, edit, and delete memory files.

9

10

```python { .api }

11

class BetaAbstractMemoryTool(BetaBuiltinFunctionTool):

12

"""

13

Abstract base class for synchronous memory tool implementations.

14

15

Subclass this to create custom memory storage solutions such as:

16

- File system storage

17

- Database-backed memory

18

- Cloud storage (S3, GCS, etc.)

19

- Encrypted memory stores

20

- In-memory data structures

21

22

The memory tool provides a file-system-like interface with commands

23

for viewing, creating, editing, deleting, and renaming memory files.

24

"""

25

26

def __init__(self, *, cache_control: BetaCacheControlEphemeralParam | None = None) -> None:

27

"""

28

Initialize the memory tool.

29

30

Args:

31

cache_control: Optional cache control for prompt caching

32

"""

33

34

def execute(self, command: BetaMemoryTool20250818Command) -> str | Iterable[BetaContent]:

35

"""

36

Execute a memory command and return the result.

37

38

This method dispatches to the appropriate handler method based on

39

the command type. You typically don't need to override this method.

40

41

Args:

42

command: Memory command to execute (view, create, str_replace, insert, delete, rename)

43

44

Returns:

45

Command result as string or content blocks

46

47

Raises:

48

NotImplementedError: If command type is unknown

49

"""

50

51

@abstractmethod

52

def view(self, command: BetaMemoryTool20250818ViewCommand) -> str | Iterable[BetaContent]:

53

"""

54

View the contents of a memory path.

55

56

Args:

57

command: View command with path to view

58

59

Returns:

60

File contents or directory listing

61

"""

62

63

@abstractmethod

64

def create(self, command: BetaMemoryTool20250818CreateCommand) -> str | Iterable[BetaContent]:

65

"""

66

Create a new memory file with the specified content.

67

68

Args:

69

command: Create command with path and content

70

71

Returns:

72

Success message or error

73

"""

74

75

@abstractmethod

76

def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str | Iterable[BetaContent]:

77

"""

78

Replace text in a memory file.

79

80

Args:

81

command: Replace command with path, old_str, and new_str

82

83

Returns:

84

Success message or error

85

"""

86

87

@abstractmethod

88

def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str | Iterable[BetaContent]:

89

"""

90

Insert text at a specific line number in a memory file.

91

92

Args:

93

command: Insert command with path, line number, and content

94

95

Returns:

96

Success message or error

97

"""

98

99

@abstractmethod

100

def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str | Iterable[BetaContent]:

101

"""

102

Delete a memory file or directory.

103

104

Args:

105

command: Delete command with path to delete

106

107

Returns:

108

Success message or error

109

"""

110

111

@abstractmethod

112

def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str | Iterable[BetaContent]:

113

"""

114

Rename or move a memory file or directory.

115

116

Args:

117

command: Rename command with old_path and new_path

118

119

Returns:

120

Success message or error

121

"""

122

123

def clear_all_memory(self) -> str | Iterable[BetaContent]:

124

"""

125

Clear all memory data.

126

127

Optional method for clearing entire memory store.

128

129

Returns:

130

Success message

131

132

Raises:

133

NotImplementedError: If not implemented by subclass

134

"""

135

```

136

137

#### Usage Examples

138

139

**Simple in-memory storage:**

140

141

```python

142

from anthropic import Anthropic

143

from anthropic.lib.tools import BetaAbstractMemoryTool

144

from anthropic.types.beta import (

145

BetaMemoryTool20250818ViewCommand,

146

BetaMemoryTool20250818CreateCommand,

147

BetaMemoryTool20250818StrReplaceCommand,

148

BetaMemoryTool20250818InsertCommand,

149

BetaMemoryTool20250818DeleteCommand,

150

BetaMemoryTool20250818RenameCommand,

151

)

152

153

class InMemoryTool(BetaAbstractMemoryTool):

154

"""Store memory in Python dictionaries."""

155

156

def __init__(self):

157

super().__init__()

158

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

159

160

def view(self, command: BetaMemoryTool20250818ViewCommand) -> str:

161

path = command["path"]

162

if path not in self.files:

163

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

164

return self.files[path]

165

166

def create(self, command: BetaMemoryTool20250818CreateCommand) -> str:

167

path = command["path"]

168

content = command["file_text"]

169

170

if path in self.files:

171

return f"Error: File already exists: {path}"

172

173

self.files[path] = content

174

return f"Created file: {path}"

175

176

def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str:

177

path = command["path"]

178

old_str = command["old_str"]

179

new_str = command["new_str"]

180

181

if path not in self.files:

182

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

183

184

content = self.files[path]

185

if old_str not in content:

186

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

187

188

self.files[path] = content.replace(old_str, new_str, 1)

189

return f"Replaced text in: {path}"

190

191

def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str:

192

path = command["path"]

193

insert_line = command["insert_line"]

194

new_str = command["new_str"]

195

196

if path not in self.files:

197

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

198

199

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

200

if insert_line < 0 or insert_line > len(lines):

201

return f"Error: Invalid line number: {insert_line}"

202

203

lines.insert(insert_line, new_str)

204

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

205

return f"Inserted text at line {insert_line} in: {path}"

206

207

def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str:

208

path = command["path"]

209

if path not in self.files:

210

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

211

212

del self.files[path]

213

return f"Deleted: {path}"

214

215

def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str:

216

old_path = command["old_path"]

217

new_path = command["new_path"]

218

219

if old_path not in self.files:

220

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

221

if new_path in self.files:

222

return f"Error: Destination already exists: {new_path}"

223

224

self.files[new_path] = self.files[old_path]

225

del self.files[old_path]

226

return f"Renamed {old_path} to {new_path}"

227

228

# Use with Claude

229

client = Anthropic()

230

memory = InMemoryTool()

231

232

runner = client.beta.messages.tool_runner(

233

model="claude-sonnet-4-5",

234

max_tokens=1024,

235

tools=[memory],

236

messages=[{"role": "user", "content": "Remember that my favorite color is blue"}],

237

betas=["memory-2025-08-18"]

238

)

239

240

final_message = runner.until_done()

241

print(final_message.content[0].text)

242

243

# Later conversation

244

runner2 = client.beta.messages.tool_runner(

245

model="claude-sonnet-4-5",

246

max_tokens=1024,

247

tools=[memory],

248

messages=[{"role": "user", "content": "What's my favorite color?"}],

249

betas=["memory-2025-08-18"]

250

)

251

252

response = runner2.until_done()

253

print(response.content[0].text) # "Your favorite color is blue."

254

```

255

256

**File system backed memory:**

257

258

```python

259

import os

260

import json

261

from pathlib import Path

262

263

class FileSystemMemoryTool(BetaAbstractMemoryTool):

264

"""Store memory in the file system."""

265

266

def __init__(self, base_dir: str = "./memory"):

267

super().__init__()

268

self.base_dir = Path(base_dir)

269

self.base_dir.mkdir(exist_ok=True)

270

271

def _resolve_path(self, path: str) -> Path:

272

"""Resolve and validate memory path."""

273

full_path = (self.base_dir / path).resolve()

274

# Ensure path is within base_dir

275

if not str(full_path).startswith(str(self.base_dir)):

276

raise ValueError(f"Invalid path: {path}")

277

return full_path

278

279

def view(self, command: BetaMemoryTool20250818ViewCommand) -> str:

280

path = self._resolve_path(command["path"])

281

282

if not path.exists():

283

return f"Error: Path not found: {command['path']}"

284

285

if path.is_dir():

286

# List directory contents

287

entries = [p.name for p in path.iterdir()]

288

return "\n".join(sorted(entries))

289

else:

290

# Read file content

291

return path.read_text()

292

293

def create(self, command: BetaMemoryTool20250818CreateCommand) -> str:

294

path = self._resolve_path(command["path"])

295

296

if path.exists():

297

return f"Error: Path already exists: {command['path']}"

298

299

# Create parent directories

300

path.parent.mkdir(parents=True, exist_ok=True)

301

302

# Write content

303

path.write_text(command["file_text"])

304

return f"Created: {command['path']}"

305

306

def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str:

307

path = self._resolve_path(command["path"])

308

309

if not path.exists():

310

return f"Error: File not found: {command['path']}"

311

312

content = path.read_text()

313

old_str = command["old_str"]

314

new_str = command["new_str"]

315

316

if old_str not in content:

317

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

318

319

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

320

path.write_text(new_content)

321

return f"Replaced text in: {command['path']}"

322

323

def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str:

324

path = self._resolve_path(command["path"])

325

326

if not path.exists():

327

return f"Error: File not found: {command['path']}"

328

329

lines = path.read_text().split("\n")

330

insert_line = command["insert_line"]

331

332

if insert_line < 0 or insert_line > len(lines):

333

return f"Error: Invalid line number: {insert_line}"

334

335

lines.insert(insert_line, command["new_str"])

336

path.write_text("\n".join(lines))

337

return f"Inserted text at line {insert_line}"

338

339

def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str:

340

path = self._resolve_path(command["path"])

341

342

if not path.exists():

343

return f"Error: Path not found: {command['path']}"

344

345

if path.is_dir():

346

import shutil

347

shutil.rmtree(path)

348

else:

349

path.unlink()

350

351

return f"Deleted: {command['path']}"

352

353

def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str:

354

old_path = self._resolve_path(command["old_path"])

355

new_path = self._resolve_path(command["new_path"])

356

357

if not old_path.exists():

358

return f"Error: Source not found: {command['old_path']}"

359

if new_path.exists():

360

return f"Error: Destination exists: {command['new_path']}"

361

362

new_path.parent.mkdir(parents=True, exist_ok=True)

363

old_path.rename(new_path)

364

return f"Renamed {command['old_path']} to {command['new_path']}"

365

366

# Use file-based memory

367

memory = FileSystemMemoryTool("./claude_memory")

368

```

369

370

**Database-backed memory:**

371

372

```python

373

import sqlite3

374

375

class DatabaseMemoryTool(BetaAbstractMemoryTool):

376

"""Store memory in SQLite database."""

377

378

def __init__(self, db_path: str = "memory.db"):

379

super().__init__()

380

self.db_path = db_path

381

self._init_db()

382

383

def _init_db(self):

384

"""Initialize database schema."""

385

with sqlite3.connect(self.db_path) as conn:

386

conn.execute("""

387

CREATE TABLE IF NOT EXISTS memory_files (

388

path TEXT PRIMARY KEY,

389

content TEXT NOT NULL,

390

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

391

updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

392

)

393

""")

394

395

def view(self, command: BetaMemoryTool20250818ViewCommand) -> str:

396

with sqlite3.connect(self.db_path) as conn:

397

cursor = conn.execute(

398

"SELECT content FROM memory_files WHERE path = ?",

399

(command["path"],)

400

)

401

row = cursor.fetchone()

402

403

if row is None:

404

return f"Error: File not found: {command['path']}"

405

406

return row[0]

407

408

def create(self, command: BetaMemoryTool20250818CreateCommand) -> str:

409

try:

410

with sqlite3.connect(self.db_path) as conn:

411

conn.execute(

412

"INSERT INTO memory_files (path, content) VALUES (?, ?)",

413

(command["path"], command["file_text"])

414

)

415

return f"Created: {command['path']}"

416

except sqlite3.IntegrityError:

417

return f"Error: File already exists: {command['path']}"

418

419

def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str:

420

with sqlite3.connect(self.db_path) as conn:

421

cursor = conn.execute(

422

"SELECT content FROM memory_files WHERE path = ?",

423

(command["path"],)

424

)

425

row = cursor.fetchone()

426

427

if row is None:

428

return f"Error: File not found: {command['path']}"

429

430

content = row[0]

431

old_str = command["old_str"]

432

433

if old_str not in content:

434

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

435

436

new_content = content.replace(old_str, command["new_str"], 1)

437

conn.execute(

438

"UPDATE memory_files SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE path = ?",

439

(new_content, command["path"])

440

)

441

442

return f"Replaced text in: {command['path']}"

443

444

def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str:

445

with sqlite3.connect(self.db_path) as conn:

446

cursor = conn.execute(

447

"SELECT content FROM memory_files WHERE path = ?",

448

(command["path"],)

449

)

450

row = cursor.fetchone()

451

452

if row is None:

453

return f"Error: File not found: {command['path']}"

454

455

lines = row[0].split("\n")

456

insert_line = command["insert_line"]

457

458

if insert_line < 0 or insert_line > len(lines):

459

return f"Error: Invalid line number: {insert_line}"

460

461

lines.insert(insert_line, command["new_str"])

462

new_content = "\n".join(lines)

463

464

conn.execute(

465

"UPDATE memory_files SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE path = ?",

466

(new_content, command["path"])

467

)

468

469

return f"Inserted text at line {insert_line}"

470

471

def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str:

472

with sqlite3.connect(self.db_path) as conn:

473

cursor = conn.execute(

474

"DELETE FROM memory_files WHERE path = ?",

475

(command["path"],)

476

)

477

478

if cursor.rowcount == 0:

479

return f"Error: File not found: {command['path']}"

480

481

return f"Deleted: {command['path']}"

482

483

def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str:

484

with sqlite3.connect(self.db_path) as conn:

485

# Check old path exists

486

cursor = conn.execute(

487

"SELECT 1 FROM memory_files WHERE path = ?",

488

(command["old_path"],)

489

)

490

if cursor.fetchone() is None:

491

return f"Error: Source not found: {command['old_path']}"

492

493

# Check new path doesn't exist

494

cursor = conn.execute(

495

"SELECT 1 FROM memory_files WHERE path = ?",

496

(command["new_path"],)

497

)

498

if cursor.fetchone() is not None:

499

return f"Error: Destination exists: {command['new_path']}"

500

501

# Rename

502

conn.execute(

503

"UPDATE memory_files SET path = ?, updated_at = CURRENT_TIMESTAMP WHERE path = ?",

504

(command["new_path"], command["old_path"])

505

)

506

507

return f"Renamed {command['old_path']} to {command['new_path']}"

508

509

def clear_all_memory(self) -> str:

510

"""Clear all memory entries."""

511

with sqlite3.connect(self.db_path) as conn:

512

conn.execute("DELETE FROM memory_files")

513

return "All memory cleared"

514

515

# Use database memory

516

memory = DatabaseMemoryTool()

517

```

518

519

### Asynchronous Memory Tool

520

521

Abstract base class for implementing asynchronous memory backends with async/await support.

522

523

```python { .api }

524

class BetaAsyncAbstractMemoryTool(BetaAsyncBuiltinFunctionTool):

525

"""

526

Abstract base class for asynchronous memory tool implementations.

527

528

Identical to BetaAbstractMemoryTool but with async methods for

529

non-blocking I/O operations.

530

"""

531

532

def __init__(self, *, cache_control: BetaCacheControlEphemeralParam | None = None) -> None:

533

"""

534

Initialize the async memory tool.

535

536

Args:

537

cache_control: Optional cache control for prompt caching

538

"""

539

540

async def execute(self, command: BetaMemoryTool20250818Command) -> str | Iterable[BetaContent]:

541

"""

542

Execute a memory command asynchronously.

543

544

Args:

545

command: Memory command to execute

546

547

Returns:

548

Command result as string or content blocks

549

"""

550

551

@abstractmethod

552

async def view(self, command: BetaMemoryTool20250818ViewCommand) -> str | Iterable[BetaContent]:

553

"""View the contents of a memory path."""

554

555

@abstractmethod

556

async def create(self, command: BetaMemoryTool20250818CreateCommand) -> str | Iterable[BetaContent]:

557

"""Create a new memory file."""

558

559

@abstractmethod

560

async def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str | Iterable[BetaContent]:

561

"""Replace text in a memory file."""

562

563

@abstractmethod

564

async def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str | Iterable[BetaContent]:

565

"""Insert text at a specific line number."""

566

567

@abstractmethod

568

async def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str | Iterable[BetaContent]:

569

"""Delete a memory file or directory."""

570

571

@abstractmethod

572

async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str | Iterable[BetaContent]:

573

"""Rename or move a memory file or directory."""

574

575

async def clear_all_memory(self) -> str | Iterable[BetaContent]:

576

"""Clear all memory data."""

577

```

578

579

#### Usage Examples

580

581

**Async file system memory:**

582

583

```python

584

import aiofiles

585

from pathlib import Path

586

from anthropic import AsyncAnthropic

587

from anthropic.lib.tools import BetaAsyncAbstractMemoryTool

588

589

class AsyncFileSystemMemoryTool(BetaAsyncAbstractMemoryTool):

590

"""Async file system memory storage."""

591

592

def __init__(self, base_dir: str = "./memory"):

593

super().__init__()

594

self.base_dir = Path(base_dir)

595

self.base_dir.mkdir(exist_ok=True)

596

597

def _resolve_path(self, path: str) -> Path:

598

full_path = (self.base_dir / path).resolve()

599

if not str(full_path).startswith(str(self.base_dir)):

600

raise ValueError(f"Invalid path: {path}")

601

return full_path

602

603

async def view(self, command: BetaMemoryTool20250818ViewCommand) -> str:

604

path = self._resolve_path(command["path"])

605

606

if not path.exists():

607

return f"Error: File not found: {command['path']}"

608

609

async with aiofiles.open(path, 'r') as f:

610

content = await f.read()

611

return content

612

613

async def create(self, command: BetaMemoryTool20250818CreateCommand) -> str:

614

path = self._resolve_path(command["path"])

615

616

if path.exists():

617

return f"Error: File already exists: {command['path']}"

618

619

path.parent.mkdir(parents=True, exist_ok=True)

620

621

async with aiofiles.open(path, 'w') as f:

622

await f.write(command["file_text"])

623

624

return f"Created: {command['path']}"

625

626

async def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str:

627

path = self._resolve_path(command["path"])

628

629

if not path.exists():

630

return f"Error: File not found: {command['path']}"

631

632

async with aiofiles.open(path, 'r') as f:

633

content = await f.read()

634

635

old_str = command["old_str"]

636

if old_str not in content:

637

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

638

639

new_content = content.replace(old_str, command["new_str"], 1)

640

641

async with aiofiles.open(path, 'w') as f:

642

await f.write(new_content)

643

644

return f"Replaced text in: {command['path']}"

645

646

async def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str:

647

path = self._resolve_path(command["path"])

648

649

if not path.exists():

650

return f"Error: File not found: {command['path']}"

651

652

async with aiofiles.open(path, 'r') as f:

653

content = await f.read()

654

655

lines = content.split("\n")

656

insert_line = command["insert_line"]

657

658

if insert_line < 0 or insert_line > len(lines):

659

return f"Error: Invalid line number: {insert_line}"

660

661

lines.insert(insert_line, command["new_str"])

662

663

async with aiofiles.open(path, 'w') as f:

664

await f.write("\n".join(lines))

665

666

return f"Inserted text at line {insert_line}"

667

668

async def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str:

669

path = self._resolve_path(command["path"])

670

671

if not path.exists():

672

return f"Error: File not found: {command['path']}"

673

674

path.unlink()

675

return f"Deleted: {command['path']}"

676

677

async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str:

678

old_path = self._resolve_path(command["old_path"])

679

new_path = self._resolve_path(command["new_path"])

680

681

if not old_path.exists():

682

return f"Error: Source not found: {command['old_path']}"

683

if new_path.exists():

684

return f"Error: Destination exists: {command['new_path']}"

685

686

new_path.parent.mkdir(parents=True, exist_ok=True)

687

old_path.rename(new_path)

688

return f"Renamed {command['old_path']} to {command['new_path']}"

689

690

# Use async memory

691

client = AsyncAnthropic()

692

memory = AsyncFileSystemMemoryTool()

693

694

runner = client.beta.messages.tool_runner(

695

model="claude-sonnet-4-5",

696

max_tokens=1024,

697

tools=[memory],

698

messages=[{"role": "user", "content": "Remember my API key is xyz123"}],

699

betas=["memory-2025-08-18"]

700

)

701

702

final_message = await runner.until_done()

703

```

704

705

## Types

706

707

### Memory Command Types

708

709

```python { .api }

710

BetaMemoryTool20250818Command = (

711

BetaMemoryTool20250818ViewCommand

712

| BetaMemoryTool20250818CreateCommand

713

| BetaMemoryTool20250818StrReplaceCommand

714

| BetaMemoryTool20250818InsertCommand

715

| BetaMemoryTool20250818DeleteCommand

716

| BetaMemoryTool20250818RenameCommand

717

)

718

```

719

720

Union of all memory command types.

721

722

### Memory Tool Parameter

723

724

```python { .api }

725

class BetaMemoryTool20250818Param(TypedDict):

726

"""

727

Memory tool definition for the API.

728

"""

729

type: Literal["memory_20250818"]

730

name: Literal["memory"]

731

cache_control: BetaCacheControlEphemeralParam | None # optional

732

```

733