SPB Git

Repository Public

memory-tool.md 38.7 KB 1,026 lines markdown

1---2title: Memory tool3url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool4description: Let Claude store and retrieve information across conversations by implementing the memory tool's file operations in your application.5---67The memory tool lets Claude store and retrieve information across conversations in a directory of memory files. Claude can create, read, update, and delete files that persist between sessions, building up knowledge over time without keeping everything in the context window.89Memory supports just-in-time context retrieval. Rather than loading all relevant information up front, an agent records what it learns in memory files and reads them back on demand. This keeps the active context focused on the current task, which matters for long-running sessions that would otherwise overwhelm the context window. See [Effective context engineering](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) for the broader pattern.1011The memory tool operates client-side: Claude requests file operations, and your application executes them. You control where and how the data is stored through your own infrastructure.1213<Note>14  Reach out through the [feedback form](https://forms.gle/YXC2EKGMhjN1c4L88) to share your feedback on this feature.15</Note>1617<Note>18  To learn how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).19</Note>2021## Use cases2223* Maintain project context across multiple agent sessions24* Apply lessons from past interactions, decisions, and feedback to new tasks25* Build up a knowledge base over time2627## How it works2829When the memory tool is enabled, Claude automatically checks its memory directory before starting a task. As it works, Claude stores what it learns in files under `/memories` and reads them back in later conversations to continue earlier work.3031Because the memory tool is client-side, Claude only requests memory operations. Your application executes each request against storage you control and returns the result in a `tool_result` block (see [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls)). The `/memories` path is a prefix that your handler maps onto real storage, such as a per-user directory or keys in a database. Memory lives entirely in your application. A later conversation continues from the same memory when it sends the same `tools` entry and your handler serves the same store. For security, restrict all memory operations to the `/memories` directory (see [Path traversal protection](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#path-traversal-protection)).3233### Example: How memory tool calls work3435A typical interaction looks like this:3637**1. User request:**3839```text wrap40"Help me respond to this customer service ticket."41```4243**2. Claude checks the memory directory:**4445```text wrap46"I'll help you respond to the customer service ticket. Let me check my memory for any previous context."47```4849Claude calls the memory tool:5051```json52{53  "type": "tool_use",54  "id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",55  "name": "memory",56  "input": {57    "command": "view",58    "path": "/memories"59  }60}61```6263**3. Your application returns the directory contents:**6465```json66{67  "type": "tool_result",68  "tool_use_id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",69  "content": "Here're the files and directories up to 2 levels deep in /memories, excluding hidden items and node_modules:\n4.0K\t/memories\n1.5K\t/memories/customer_service_guidelines.xml\n2.0K\t/memories/refund_policies.xml"70}71```7273**4. Claude reads relevant files:**7475```json76{77  "type": "tool_use",78  "id": "toolu_01D5E6F7G8H9I0J1K2L3M4N5",79  "name": "memory",80  "input": {81    "command": "view",82    "path": "/memories/customer_service_guidelines.xml"83  }84}85```8687**5. Your application returns the file contents:**8889```json90{91  "type": "tool_result",92  "tool_use_id": "toolu_01D5E6F7G8H9I0J1K2L3M4N5",93  "content": "Here's the content of /memories/customer_service_guidelines.xml with line numbers:\n     1\t<guidelines>\n     2\t<addressing_customers>\n     3\t- Always address customers by their first name\n     4\t- Use empathetic language\n..."94}95```9697**6. Claude uses the memory to help:**9899```text wrap100"Based on your customer service guidelines, I can help you craft a response. Please share the ticket details..."101```102103The memory tool is available on all Claude 4 and later models. For the full list of Anthropic-provided tools, see the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference).104105## Getting started106107Using the memory tool takes two steps:1081091. Add the memory tool to your request. The `tools` entry `{"type": "memory_20250818", "name": "memory"}` is the entire configuration: the `name` must be `memory`, and you don't define an input schema for an Anthropic-provided tool.1102. Implement a client-side handler for each memory command. Your handler must reject paths outside `/memories`, so read [Path traversal protection](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#path-traversal-protection) before you write it.111112## Basic usage113114<CodeGroup>115  ```bash cURL116  curl https://api.anthropic.com/v1/messages \117    -H "x-api-key: $ANTHROPIC_API_KEY" \118    -H "anthropic-version: 2023-06-01" \119    -H "content-type: application/json" \120    -d '{121      "model": "claude-opus-5",122      "max_tokens": 2048,123      "messages": [124        {125          "role": "user",126          "content": "Help me respond to this customer service ticket."127        }128      ],129      "tools": [{130        "type": "memory_20250818",131        "name": "memory"132      }]133    }'134  ```135136  ```bash CLI137  ant messages create <<'YAML'138  model: claude-opus-5139  max_tokens: 2048140  tools:141    - type: memory_20250818142      name: memory143  messages:144    - role: user145      content: Help me respond to this customer service ticket.146  YAML147  ```148149  ```python Python150  client = anthropic.Anthropic()151152  message = client.messages.create(153      model="claude-opus-5",154      max_tokens=2048,155      messages=[156          {157              "role": "user",158              "content": "Help me respond to this customer service ticket.",159          }160      ],161      tools=[{"type": "memory_20250818", "name": "memory"}],162  )163164  print(message)165  ```166167  ```typescript TypeScript168  const anthropic = new Anthropic();169170  const message = await anthropic.messages.create({171    model: "claude-opus-5",172    max_tokens: 2048,173    messages: [174      {175        role: "user",176        content: "Help me respond to this customer service ticket."177      }178    ],179    tools: [{ type: "memory_20250818", name: "memory" }]180  });181182  console.log(message);183  ```184185  ```csharp C#186  var client = new AnthropicClient();187188  var message = await client.Messages.Create(189      new()190      {191          Model = Model.ClaudeOpus5,192          MaxTokens = 2048,193          Messages =194          [195              new()196              {197                  Role = Role.User,198                  Content = "Help me respond to this customer service ticket.",199              },200          ],201          Tools = [new MemoryTool20250818()],202      }203  );204205  Console.WriteLine(message);206  ```207208  ```go Go209  client := anthropic.NewClient()210211  message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{212  	Model:     anthropic.ModelClaudeOpus5,213  	MaxTokens: 2048,214  	Messages: []anthropic.MessageParam{215  		anthropic.NewUserMessage(anthropic.NewTextBlock("Help me respond to this customer service ticket.")),216  	},217  	Tools: []anthropic.ToolUnionParam{218  		{OfMemoryTool20250818: &anthropic.MemoryTool20250818Param{}},219  	},220  })221  if err != nil {222  	log.Fatal(err)223  }224  fmt.Println(message)225  ```226227  ```java Java228  import com.anthropic.models.messages.MemoryTool20250818;229  // ...230    AnthropicClient client = AnthropicOkHttpClient.fromEnv();231232    MessageCreateParams params = MessageCreateParams.builder()233      .model(Model.CLAUDE_OPUS_5)234      .maxTokens(2048L)235      .addTool(MemoryTool20250818.builder().build())236      .addUserMessage("Help me respond to this customer service ticket.")237      .build();238239    Message message = client.messages().create(params);240    IO.println(message);241  ```242243  ```php PHP244  $client = new Client();245246  $message = $client->messages->create(247      model: Model::CLAUDE_OPUS_5,248      maxTokens: 2048,249      messages: [250          [251              'role' => 'user',252              'content' => 'Help me respond to this customer service ticket.',253          ],254      ],255      tools: [new MemoryTool20250818],256  );257258  echo $message;259  ```260261  ```ruby Ruby262  client = Anthropic::Client.new263264  message = client.messages.create(265    model: Anthropic::Model::CLAUDE_OPUS_5,266    max_tokens: 2048,267    messages: [268      {269        role: "user",270        content: "Help me respond to this customer service ticket."271      }272    ],273    tools: [274      {275        type: "memory_20250818",276        name: "memory"277      }278    ]279  )280  puts message281  ```282</CodeGroup>283284## Implement the memory handler285286Claude's reply to a request like the previous one ends with a `tool_use` block that requests a memory operation, such as `view /memories`. Your application executes the operation and returns the result in a `tool_result` block, then sends the conversation back so Claude can continue: the standard [tool-use loop](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).287288Four SDKs provide memory tool helpers that handle the tool interface and the loop. Subclass `BetaAbstractMemoryTool` (Python and C#), use `betaMemoryTool` (TypeScript), or implement `BetaMemoryToolHandler` (Java) to back memory with your own storage, such as files on disk, a database, cloud storage, or encrypted files. Python and TypeScript also ship a ready-made local-filesystem implementation, `BetaLocalFilesystemMemoryTool`. The helper and tool-runner surfaces live in each SDK's beta namespace even though the memory tool itself doesn't require a beta header. The Go and Ruby SDKs have no memory helper, so those examples run the tool-use loop themselves, and PHP wraps your handler closure in its generic `BetaRunnableTool`. All three use an in-memory store that you replace with your own storage.289290<CodeGroup exclude="shell">291  ```python Python292  import anthropic293  from anthropic.tools import BetaLocalFilesystemMemoryTool294295  client = anthropic.Anthropic()296  memory = BetaLocalFilesystemMemoryTool(base_path="./memory")297298  runner = client.beta.messages.tool_runner(299      model="claude-opus-5",300      max_tokens=1024,301      messages=[302          {303              "role": "user",304              "content": "Remember that customer Acme Corp prefers email follow-ups.",305          }306      ],307      tools=[memory],308  )309310  final_message = runner.until_done()311  print(final_message.content)312  ```313314  ```typescript TypeScript315  import Anthropic from "@anthropic-ai/sdk";316  import { betaMemoryTool } from "@anthropic-ai/sdk/helpers/beta/memory";317  import { BetaLocalFilesystemMemoryTool } from "@anthropic-ai/sdk/tools/memory/node";318319  const client = new Anthropic();320321  const backend = await BetaLocalFilesystemMemoryTool.init("./memory");322  const memory = betaMemoryTool(backend); // or pass your own handlers object323324  const runner = client.beta.messages.toolRunner({325    model: "claude-opus-5",326    max_tokens: 1024,327    messages: [328      {329        role: "user",330        content: "Remember that customer Acme Corp prefers email follow-ups."331      }332    ],333    tools: [memory],334    max_iterations: 10335  });336337  const finalMessage = await runner;338  console.log(finalMessage.content);339  ```340341  ```csharp C#342  using Anthropic;343  using Anthropic.Helpers.Beta;344  using Anthropic.Models.Beta.Messages;345346  var client = new AnthropicClient();347348  // Your subclass of BetaAbstractMemoryTool349  var memory = new FilesystemMemoryTool("./memories");350351  var runner = client.Beta.Messages.ToolRunner(352      new MessageCreateParams353      {354          Model = Anthropic.Models.Messages.Model.ClaudeOpus5,355          MaxTokens = 1024,356          Messages =357          [358              new()359              {360                  Role = Role.User,361                  Content = "Remember that customer Acme Corp prefers email follow-ups.",362              },363          ],364      },365      [memory],366      maxIterations: 10367  );368369  var finalMessage = await runner.RunUntilDoneAsync();370  Console.WriteLine(finalMessage);371  ```372373  ```go Go374  package main375376  import (377  	"context"378  	"encoding/json"379  	"fmt"380  	"log"381  	"slices"382  	"sort"383  	"strings"384385  	"github.com/anthropics/anthropic-sdk-go"386  )387388  // An in-memory store that maps memory file paths to their contents.389  // Use your own storage in production.390  var store = map[string]string{}391392  type memoryCommand struct {393  	Command    string `json:"command"`394  	Path       string `json:"path"`395  	FileText   string `json:"file_text"`396  	OldStr     string `json:"old_str"`397  	NewStr     string `json:"new_str"`398  	InsertLine int    `json:"insert_line"`399  	InsertText string `json:"insert_text"`400  	OldPath    string `json:"old_path"`401  	NewPath    string `json:"new_path"`402  }403404  func executeMemory(raw json.RawMessage) string {405  	var cmd memoryCommand406  	if err := json.Unmarshal(raw, &cmd); err != nil {407  		return "Error: invalid memory command"408  	}409  	switch cmd.Command {410  	case "view":411  		if content, ok := store[cmd.Path]; ok {412  			lines := strings.Split(strings.TrimSuffix(content, "\n"), "\n")413  			for i, line := range lines {414  				lines[i] = fmt.Sprintf("%6d\t%s", i+1, line)415  			}416  			return fmt.Sprintf("Here's the content of %s with line numbers:\n%s", cmd.Path, strings.Join(lines, "\n"))417  		}418  		if cmd.Path == "/memories" {419  			listing := []string{"1.0K\t/memories"}420  			for path := range store {421  				listing = append(listing, "1.0K\t"+path)422  			}423  			sort.Strings(listing[1:])424  			return fmt.Sprintf("Here're the files and directories up to 2 levels deep in %s, excluding hidden items and node_modules:\n%s", cmd.Path, strings.Join(listing, "\n"))425  		}426  		return fmt.Sprintf("The path %s does not exist. Please provide a valid path.", cmd.Path)427  	case "create":428  		store[cmd.Path] = cmd.FileText429  		return "File created successfully at: " + cmd.Path430  	case "str_replace":431  		content, ok := store[cmd.Path]432  		if !ok || !strings.Contains(content, cmd.OldStr) {433  			return fmt.Sprintf("No replacement was performed, old_str `%s` did not appear verbatim in %s.", cmd.OldStr, cmd.Path)434  		}435  		store[cmd.Path] = strings.Replace(content, cmd.OldStr, cmd.NewStr, 1)436  		return "The memory file has been edited."437  	case "insert":438  		content, ok := store[cmd.Path]439  		if !ok {440  			return fmt.Sprintf("Error: The path %s does not exist", cmd.Path)441  		}442  		lines := strings.Split(content, "\n")443  		if cmd.InsertLine < 0 || cmd.InsertLine > len(lines) {444  			return fmt.Sprintf("Error: Invalid `insert_line` parameter: %d. It should be within the range of lines of the file: [0, %d]", cmd.InsertLine, len(lines))445  		}446  		lines = slices.Insert(lines, cmd.InsertLine, strings.TrimSuffix(cmd.InsertText, "\n"))447  		store[cmd.Path] = strings.Join(lines, "\n")448  		return fmt.Sprintf("The file %s has been edited.", cmd.Path)449  	case "delete":450  		if _, ok := store[cmd.Path]; !ok {451  			return fmt.Sprintf("Error: The path %s does not exist", cmd.Path)452  		}453  		delete(store, cmd.Path)454  		return "Successfully deleted " + cmd.Path455  	case "rename":456  		if _, ok := store[cmd.OldPath]; !ok {457  			return fmt.Sprintf("Error: The path %s does not exist", cmd.OldPath)458  		}459  		if _, ok := store[cmd.NewPath]; ok {460  			return fmt.Sprintf("Error: The destination %s already exists", cmd.NewPath)461  		}462  		store[cmd.NewPath] = store[cmd.OldPath]463  		delete(store, cmd.OldPath)464  		return fmt.Sprintf("Successfully renamed %s to %s", cmd.OldPath, cmd.NewPath)465  	default:466  		return "Error: unknown command " + cmd.Command467  	}468  }469470  func main() {471  	client := anthropic.NewClient()472  	tools := []anthropic.ToolUnionParam{{OfMemoryTool20250818: &anthropic.MemoryTool20250818Param{}}}473  	messages := []anthropic.MessageParam{474  		anthropic.NewUserMessage(anthropic.NewTextBlock("Remember that customer Acme Corp prefers email follow-ups.")),475  	}476477  	for {478  		message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{479  			Model:     anthropic.ModelClaudeOpus5,480  			MaxTokens: 1024,481  			Messages:  messages,482  			Tools:     tools,483  		})484  		if err != nil {485  			log.Fatal(err)486  		}487  		if message.StopReason != anthropic.StopReasonToolUse {488  			for _, block := range message.Content {489  				if block.Type == "text" {490  					fmt.Println(block.Text)491  				}492  			}493  			break494  		}495  		results := []anthropic.ContentBlockParamUnion{}496  		for _, block := range message.Content {497  			if block.Type == "tool_use" {498  				results = append(results, anthropic.NewToolResultBlock(block.ID, executeMemory(block.Input), false))499  			}500  		}501  		messages = append(messages, message.ToParam(), anthropic.NewUserMessage(results...))502  	}503  }504  ```505506  ```java Java507  import com.anthropic.client.AnthropicClient;508  import com.anthropic.client.okhttp.AnthropicOkHttpClient;509  import com.anthropic.helpers.BetaMemoryToolHandler;510  import com.anthropic.helpers.BetaToolRunner;511  import com.anthropic.models.beta.messages.BetaMemoryTool20250818;512  import com.anthropic.models.beta.messages.BetaMessage;513  import com.anthropic.models.beta.messages.MessageCreateParams; // beta package, not models.messages514  import com.anthropic.models.beta.messages.ToolRunnerCreateParams;515  import com.anthropic.models.messages.Model;516  import java.nio.file.Path;517518  void main() {519    AnthropicClient client = AnthropicOkHttpClient.fromEnv();520521    // Your BetaMemoryToolHandler implementation of the six memory commands522    BetaMemoryToolHandler handler = new FileSystemMemoryToolHandler(Path.of("memories"));523524    MessageCreateParams createParams = MessageCreateParams.builder()525      .model(Model.CLAUDE_OPUS_5)526      .maxTokens(1024L)527      .addTool(BetaMemoryTool20250818.builder().build())528      .addUserMessage("Remember that customer Acme Corp prefers email follow-ups.")529      .build();530531    ToolRunnerCreateParams runnerParams = ToolRunnerCreateParams.builder()532      .betaMemoryToolHandler(handler)533      .initialMessageParams(createParams)534      .maxIterations(10)535      .build();536537    BetaToolRunner runner = client.beta().messages().toolRunner(runnerParams);538    for (BetaMessage message : runner) {539      IO.println(message);540    }541  }542  ```543544  ```php PHP545  <?php546547  use Anthropic\Beta\Messages\BetaMemoryTool20250818;548  use Anthropic\Client;549  use Anthropic\Lib\Tools\BetaRunnableTool;550  use Anthropic\Messages\Model;551552  $client = new Client();553554  // An in-memory store that maps memory file paths to their contents.555  // Use your own storage in production.556  $store = [];557558  $memory = new BetaRunnableTool(559      definition: new BetaMemoryTool20250818,560      run: function (array $input) use (&$store): string {561          $path = $input['path'] ?? '';562          switch ($input['command']) {563              case 'view':564                  if (isset($store[$path])) {565                      $numbered = [];566                      foreach (explode("\n", preg_replace('/\n\z/', '', $store[$path])) as $i => $line) {567                          $numbered[] = sprintf("%6d\t%s", $i + 1, $line);568                      }569                      return "Here's the content of {$path} with line numbers:\n" . implode("\n", $numbered);570                  }571                  if ($path === '/memories') {572                      $listing = ["1.0K\t/memories"];573                      foreach (array_keys($store) as $stored) {574                          $listing[] = "1.0K\t{$stored}";575                      }576                      return "Here're the files and directories up to 2 levels deep in {$path}, excluding hidden items and node_modules:\n" . implode("\n", $listing);577                  }578                  return "The path {$path} does not exist. Please provide a valid path.";579              case 'create':580                  $store[$path] = $input['file_text'];581                  return "File created successfully at: {$path}";582              case 'str_replace':583                  $position = strpos($store[$path] ?? '', $input['old_str']);584                  if ($position === false) {585                      return "No replacement was performed, old_str `{$input['old_str']}` did not appear verbatim in {$path}.";586                  }587                  $store[$path] = substr_replace($store[$path], $input['new_str'] ?? '', $position, strlen($input['old_str']));588                  return 'The memory file has been edited.';589              case 'insert':590                  if (!isset($store[$path])) {591                      return "Error: The path {$path} does not exist";592                  }593                  $lines = explode("\n", $store[$path]);594                  if ($input['insert_line'] < 0 || $input['insert_line'] > count($lines)) {595                      return "Error: Invalid `insert_line` parameter: {$input['insert_line']}. It should be within the range of lines of the file: [0, " . count($lines) . "]";596                  }597                  array_splice($lines, $input['insert_line'], 0, [preg_replace('/\n\z/', '', $input['insert_text'])]);598                  $store[$path] = implode("\n", $lines);599                  return "The file {$path} has been edited.";600              case 'delete':601                  if (!isset($store[$path])) {602                      return "Error: The path {$path} does not exist";603                  }604                  unset($store[$path]);605                  return "Successfully deleted {$path}";606              case 'rename':607                  if (!isset($store[$input['old_path']])) {608                      return "Error: The path {$input['old_path']} does not exist";609                  }610                  if (isset($store[$input['new_path']])) {611                      return "Error: The destination {$input['new_path']} already exists";612                  }613                  $store[$input['new_path']] = $store[$input['old_path']];614                  unset($store[$input['old_path']]);615                  return "Successfully renamed {$input['old_path']} to {$input['new_path']}";616              default:617                  return "Error: unknown command {$input['command']}";618          }619      },620  );621622  $runner = $client->beta->messages->toolRunner(623      maxTokens: 1024,624      messages: [['role' => 'user', 'content' => 'Remember that customer Acme Corp prefers email follow-ups.']],625      model: Model::CLAUDE_OPUS_5,626      tools: [$memory],627      maxIterations: 10,628  );629630  $finalMessage = $runner->runUntilDone();631  print_r($finalMessage->content);632  ```633634  ```ruby Ruby635  require "anthropic"636637  client = Anthropic::Client.new638  TOOLS = [{type: "memory_20250818", name: "memory"}].freeze639640  # An in-memory store that maps memory file paths to their contents.641  # Use your own storage in production.642  STORE = {}643644  def execute_memory(input)645    path = input[:path]646    case input[:command]647    when "view"648      if STORE.key?(path)649        lines = STORE[path].chomp.split("\n", -1)650        lines = [""] if lines.empty?651        numbered = lines.each_with_index.map { |line, i| format("%6d\t%s", i + 1, line) }652        "Here's the content of #{path} with line numbers:\n#{numbered.join("\n")}"653      elsif path == "/memories"654        listing = ["1.0K\t/memories"] + STORE.keys.map { |stored| "1.0K\t#{stored}" }655        "Here're the files and directories up to 2 levels deep in #{path}, excluding hidden items and node_modules:\n#{listing.join("\n")}"656      else657        "The path #{path} does not exist. Please provide a valid path."658      end659    when "create"660      STORE[path] = input[:file_text]661      "File created successfully at: #{path}"662    when "str_replace"663      unless STORE.key?(path) && STORE[path].include?(input[:old_str])664        return "No replacement was performed, old_str `#{input[:old_str]}` did not appear verbatim in #{path}."665      end666      STORE[path] = STORE[path].sub(input[:old_str]) { input[:new_str].to_s }667      "The memory file has been edited."668    when "insert"669      return "Error: The path #{path} does not exist" unless STORE.key?(path)670      lines = STORE[path].split("\n", -1)671      lines = [""] if lines.empty?672      if input[:insert_line] < 0 || input[:insert_line] > lines.length673        return "Error: Invalid `insert_line` parameter: #{input[:insert_line]}. It should be within the range of lines of the file: [0, #{lines.length}]"674      end675      lines.insert(input[:insert_line], input[:insert_text].chomp)676      STORE[path] = lines.join("\n")677      "The file #{path} has been edited."678    when "delete"679      return "Error: The path #{path} does not exist" unless STORE.key?(path)680      STORE.delete(path)681      "Successfully deleted #{path}"682    when "rename"683      return "Error: The path #{input[:old_path]} does not exist" unless STORE.key?(input[:old_path])684      return "Error: The destination #{input[:new_path]} already exists" if STORE.key?(input[:new_path])685      STORE[input[:new_path]] = STORE.delete(input[:old_path])686      "Successfully renamed #{input[:old_path]} to #{input[:new_path]}"687    else688      "Error: unknown command #{input[:command]}"689    end690  end691692  messages = [{role: "user", content: "Remember that customer Acme Corp prefers email follow-ups."}]693  loop do694    message = client.messages.create(695      model: Anthropic::Model::CLAUDE_OPUS_5,696      max_tokens: 1024,697      messages: messages,698      tools: TOOLS699    )700    unless message.stop_reason == :tool_use701      puts message.content702      break703    end704    tool_results = message.content.filter_map do |block|705      next unless block.type == :tool_use706      {type: "tool_result", tool_use_id: block.id, content: execute_memory(block.input)}707    end708    messages << {role: "assistant", content: message.content} << {role: "user", content: tool_results}709  end710  ```711</CodeGroup>712713The in-memory stores in the Go, PHP, and Ruby examples keep them self-contained: each one dispatches on the `command` field in the `tool_use` block's `input` and returns the strings described under [Tool commands](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#tool-commands). A production handler also needs the [path validation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#path-traversal-protection) these demonstration stores skip. For the SDKs' own complete examples, see:714715* Python: [examples/memory/basic.py](https://github.com/anthropics/anthropic-sdk-python/blob/main/examples/memory/basic.py)716* TypeScript: [examples/tools-helpers-memory.ts](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/examples/tools-helpers-memory.ts)717* C#: [MemoryToolExample](https://github.com/anthropics/anthropic-sdk-csharp/tree/main/examples/MemoryToolExample)718* Java: [BetaMemoryToolExample.java](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/BetaMemoryToolExample.java)719720## Tool commands721722Your client-side implementation must handle the following commands. These specifications describe the recommended behaviors and return strings: Claude reads whatever text your tool result contains, so you can return different strings if your application needs to.723724### view725726Shows directory contents or file contents with optional line ranges:727728```json729{730  "command": "view",731  "path": "/memories/notes.txt",732  "view_range": [1, 10]733}734```735736`view_range` is optional and applies to text-file views: `[start_line, end_line]` returns those lines, and `[start_line, -1]` returns everything from `start_line` to the end of the file.737738#### Return values739740**For directories:** Return a listing that shows files and directories with their sizes:741742```text743Here're the files and directories up to 2 levels deep in {path}, excluding hidden items and node_modules:744{size}\t{path}745{size}\t{path}/{filename1}746{size}\t{path}/{filename2}747```748749* Lists files up to 2 levels deep750* Shows human-readable sizes (for example, `5.5K`, `1.2M`)751* Excludes hidden items (files starting with `.`) and `node_modules`752* Uses a tab character between the size and the path753754The first `view` of `/memories` on an empty store is not an error. The SDKs' local-filesystem memory tools (`BetaLocalFilesystemMemoryTool`) create the memory root before Claude's first call and return the listing header followed by a single size-and-path line for the empty directory itself.755756**For files:** Return file contents with a header and line numbers:757758```text wrap759Here's the content of {path} with line numbers:760{line_numbers}{tab}{content}761```762763Line number formatting:764765* **Width:** 6 characters, right-aligned with space padding766* **Separator:** Tab character between line number and content767* **Indexing:** 1-indexed (first line is line 1)768* **Line limit:** Files with more than 999,999 lines should return an error: `"File {path} exceeds maximum line limit of 999,999 lines."`769770**Example output:**771772```text773Here's the content of /memories/notes.txt with line numbers:774     1	Hello World775     2	This is line two776    10	Line ten777   100	Line one hundred778```779780Claude's tool description also says that `view` displays image files (`.jpg`, `.jpeg`, and `.png`) and truncates the text view of files longer than 16,000 characters. Expect `view` calls on image paths and follow-up ranged views of long files.781782#### Error handling783784* **File or directory does not exist:** `"The path {path} does not exist. Please provide a valid path."`785786### create787788Creates a new file:789790```json791{792  "command": "create",793  "path": "/memories/notes.txt",794  "file_text": "Meeting notes:\n- Discussed project timeline\n- Next steps defined\n"795}796```797798#### Return values799800* **Success:** `"File created successfully at: {path}"`801802#### Error handling803804* **File already exists:** `"Error: File {path} already exists"`805806Claude's tool description says `create` "creates or overwrites" a file, so expect `create` calls on paths that already exist. Returning the error is the reference behavior, and overwriting instead is a valid implementation choice.807808### str\_replace809810Replaces text in a file:811812```json813{814  "command": "str_replace",815  "path": "/memories/preferences.txt",816  "old_str": "Favorite color: blue",817  "new_str": "Favorite color: green"818}819```820821`new_str` is optional for `str_replace`: when it's omitted, `old_str` is deleted without a replacement.822823#### Return values824825* **Success:** `"The memory file has been edited."` followed by a snippet of the edited file with line numbers826827#### Error handling828829* **File does not exist:** `"Error: The path {path} does not exist. Please provide a valid path."`830* **Text not found:** ``"No replacement was performed, old_str `\{old_str}` did not appear verbatim in {path}."``831* **Duplicate text:** When `old_str` appears multiple times, return: ``"No replacement was performed. Multiple occurrences of old_str `\{old_str}` in lines: {line_numbers}. Please ensure it is unique"``832833#### Directory handling834835If the path is a directory, return a "file does not exist" error.836837### insert838839Inserts text at a specific line:840841```json842{843  "command": "insert",844  "path": "/memories/todo.txt",845  "insert_line": 2,846  "insert_text": "- Review memory tool documentation\n"847}848```849850`insert_text` is inserted after line `insert_line`, and `0` inserts at the beginning of the file.851852#### Return values853854* **Success:** `"The file {path} has been edited."`855856#### Error handling857858* **File does not exist:** `"Error: The path {path} does not exist"`859* **Invalid line number:** ``"Error: Invalid `insert_line` parameter: {insert_line}. It should be within the range of lines of the file: [0, {n_lines}]"``860861#### Directory handling862863If the path is a directory, return a "file does not exist" error.864865### delete866867Deletes a file or directory:868869```json870{871  "command": "delete",872  "path": "/memories/old_file.txt"873}874```875876#### Return values877878* **Success:** `"Successfully deleted {path}"`879880#### Error handling881882* **File or directory does not exist:** `"Error: The path {path} does not exist"`883884#### Directory handling885886Deletes the directory and all its contents recursively. The tool description tells Claude it cannot delete the `/memories` directory itself, so reject a `delete` whose path is the memory root.887888### rename889890Renames or moves a file or directory:891892```json893{894  "command": "rename",895  "old_path": "/memories/draft.txt",896  "new_path": "/memories/final.txt"897}898```899900#### Return values901902* **Success:** `"Successfully renamed {old_path} to {new_path}"`903904#### Error handling905906* **Source does not exist:** `"Error: The path {old_path} does not exist"`907* **Destination already exists:** Return an error (do not overwrite): `"Error: The destination {new_path} already exists"`908909#### Directory handling910911Renames the directory. The tool description tells Claude it cannot rename the `/memories` directory itself, so reject a `rename` whose `old_path` is the memory root.912913## Prompting guidance914915When the memory tool is present in your request's `tools`, the API automatically adds this instruction to the system prompt. You don't need to send it yourself:916917```text wrap918IMPORTANT: ALWAYS VIEW YOUR MEMORY DIRECTORY BEFORE DOING ANYTHING ELSE.919MEMORY PROTOCOL:9201. Use the `view` command of your `memory` tool to check for earlier progress.9212. ... (work on the task) ...922   - As you make progress, record status / progress / thoughts etc in your memory.923ASSUME INTERRUPTION: Your context window might be reset at any moment, so you risk losing any progress that is not recorded in your memory directory.924```925926Claude's tool description already tells it to keep the memory directory organized, so you don't need to repeat that instruction. If Claude still creates cluttered memory files, you can reinforce it in your prompt:927928```text wrap929Note: when editing your memory folder, always try to keep its content up-to-date, coherent and organized. You can rename or delete files that are no longer relevant. Do not create new files unless necessary.930```931932You can also guide what Claude writes to memory. For example: "Only write down information relevant to \<topic> in your memory system."933934## Security considerations935936Your application executes every file operation Claude requests, so these safeguards are your responsibility:937938### Sensitive information939940Claude usually refuses to write sensitive information to memory files. For stronger guarantees, add validation that strips sensitive data before your handler writes the file.941942### File storage size943944Track memory file sizes and cap how large a file can grow. Consider capping how many characters the `view` command returns, and let Claude page through the rest with `view_range`.945946### Memory expiration947948Periodically delete memory files that haven't been accessed in a long time.949950### Path traversal protection951952<Warning>953  A malicious path such as `/memories/../../secrets.env` can reach files outside the `/memories` directory. Your implementation must validate every path in every command to prevent directory traversal attacks.954</Warning>955956Consider these safeguards:957958* Validate that all paths start with `/memories`959* Resolve paths to their canonical form and verify they remain within the memory directory960* Reject paths containing sequences such as `../`, `..\\`, or other traversal patterns961* Watch for URL-encoded traversal sequences (`%2e%2e%2f`)962* Use your language's built-in path security utilities (for example, Python's `pathlib.Path.resolve()` and `relative_to()`)963964## Error handling965966The memory tool uses similar error-handling patterns to the [text editor tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool#handle-errors). Each command's error messages are listed under [Tool commands](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#tool-commands). To return an error to Claude, set `is_error` to `true` on the tool result and put the message in `content`:967968```json969{970  "type": "tool_result",971  "tool_use_id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",972  "content": "Error: The path /memories/notes.txt does not exist",973  "is_error": true974}975```976977## Context editing integration978979The memory tool pairs with context editing to manage long-running conversations. For details, see [Context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing).980981## Using with compaction982983The memory tool can also be paired with [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction), which summarizes older conversation context server-side. Context editing clears specific tool results on the client. Compaction automatically summarizes the whole conversation on the server when the conversation approaches the context window limit.984985For long-running agents, consider using both: compaction keeps the active context small without client-side bookkeeping, and memory preserves the information that must survive summarization.986987## Multisession software development pattern988989For software projects that span multiple agent sessions, set up memory files deliberately instead of writing them ad hoc as work progresses. The following pattern turns memory into a recovery mechanism: each new session resumes from the state the last one recorded.990991### How the pattern works9929931. **Initializer session:** The first session sets up the memory files before any substantive work begins. This includes a progress log (tracking what has been done and what comes next), a feature checklist (defining the scope of work), and a reference to any startup or initialization script the project needs.9949952. **Subsequent sessions:** Each new session opens by reading those memory files. This restores the project state without re-exploring the code base or retracing earlier decisions.9969973. **End-of-session update:** Before a session ends, it updates the progress log with what was completed and what remains. This ensures the next session has an accurate starting point.998999### Key principle10001001Work on one feature at a time. Mark a feature complete only after end-to-end verification confirms it works, not when the code is written. This keeps the progress log accurate from session to session.10021003<Tip>1004  For a detailed case study of this pattern in practice, including the initializer script, progress file structure, and git-based recovery, see [Effective harnesses for long-running agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents).1005</Tip>10061007## Next steps10081009<CardGroup cols={2}>1010  <Card title="Bash tool" icon="terminal" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool">1011    Execute shell commands in a persistent bash session.1012  </Card>10131014  <Card title="Context editing" icon="edit" href="https://platform.claude.com/docs/en/build-with-claude/context-editing">1015    Automatically manage conversation context as it grows with context editing.1016  </Card>10171018  <Card title="Compaction" icon="stack" href="https://platform.claude.com/docs/en/build-with-claude/compaction">1019    Server-side context compaction for managing long conversations that approach context window limits.1020  </Card>10211022  <Card title="Tool reference" icon="book" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference">1023    Directory of Anthropic-provided tools and reference for optional tool definition properties.1024  </Card>1025</CardGroup>1026
Files main
Menu

Theme