---
title: 메모리 도구
url: https://platform.claude.com/docs/ko/agents-and-tools/tool-use/memory-tool
description: 애플리케이션에서 메모리 도구의 파일 작업을 구현하여 Claude가 대화 간에 정보를 저장하고 검색할 수 있도록 합니다.
---

메모리 도구(memory tool)를 사용하면 Claude가 메모리 파일 디렉터리에 대화 간 정보를 저장하고 검색할 수 있습니다. Claude는 세션 간에 유지되는 파일을 생성, 읽기, 업데이트, 삭제할 수 있으며, 모든 것을 "context window"(컨텍스트 윈도우)에 보관하지 않고도 시간이 지남에 따라 지식을 축적할 수 있습니다.

메모리는 적시(just-in-time) 컨텍스트 검색을 지원합니다. 관련 정보를 모두 미리 로드하는 대신, 에이전트는 학습한 내용을 메모리 파일에 기록하고 필요할 때 다시 읽어옵니다. 이렇게 하면 활성 컨텍스트가 현재 작업에 집중된 상태로 유지되며, 이는 그렇지 않으면 컨텍스트 윈도우를 압도할 수 있는 장기 실행 세션에서 중요합니다. 더 넓은 패턴에 대해서는 [효과적인 컨텍스트 엔지니어링](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)을 참조하세요.

메모리 도구는 클라이언트 측에서 작동합니다. Claude가 파일 작업을 요청하면 애플리케이션이 이를 실행합니다. 자체 인프라를 통해 데이터가 어디에 어떻게 저장되는지를 직접 제어합니다.

<Note>
  이 기능에 대한 피드백을 공유하려면 [피드백 양식](https://forms.gle/YXC2EKGMhjN1c4L88)을 통해 문의하세요.
</Note>

<Note>
  "zero data retention"(제로 데이터 보존), 즉 ZDR이 이 기능에 어떻게 적용되는지 알아보려면 [API 및 데이터 보존](https://platform.claude.com/docs/ko/manage-claude/api-and-data-retention)을 참조하세요.
</Note>

## 사용 사례

* 여러 에이전트 세션에 걸쳐 프로젝트 컨텍스트 유지
* 과거 상호작용, 결정, 피드백에서 얻은 교훈을 새로운 작업에 적용
* 시간이 지남에 따라 지식 베이스 구축

## 작동 방식

메모리 도구가 활성화되면 Claude는 작업을 시작하기 전에 자동으로 메모리 디렉터리를 확인합니다. 작업하는 동안 Claude는 학습한 내용을 `/memories` 아래의 파일에 저장하고, 이후 대화에서 이를 다시 읽어 이전 작업을 이어갑니다.

메모리 도구는 클라이언트 측이므로 Claude는 메모리 작업을 요청하기만 합니다. 애플리케이션은 각 요청을 직접 제어하는 스토리지에 대해 실행하고 결과를 `tool_result` 블록으로 반환합니다([도구 호출 처리](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/handle-tool-calls) 참조). `/memories` 경로는 핸들러가 사용자별 디렉터리나 데이터베이스의 키와 같은 실제 스토리지에 매핑하는 접두사입니다. 메모리는 전적으로 애플리케이션 내에 존재합니다. 이후 대화가 동일한 `tools` 항목을 전송하고 핸들러가 동일한 저장소를 제공하면 동일한 메모리에서 이어집니다. 보안을 위해 모든 메모리 작업을 `/memories` 디렉터리로 제한하세요([경로 탐색 보호](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/memory-tool#path-traversal-protection) 참조).

### 예시: 메모리 도구 호출 작동 방식

일반적인 상호작용은 다음과 같습니다:

**1. 사용자 요청:**

```text wrap
"Help me respond to this customer service ticket."
```

**2. Claude가 메모리 디렉터리를 확인합니다:**

```text wrap
"I'll help you respond to the customer service ticket. Let me check my memory for any previous context."
```

Claude가 메모리 도구를 호출합니다:

```json
{
  "type": "tool_use",
  "id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",
  "name": "memory",
  "input": {
    "command": "view",
    "path": "/memories"
  }
}
```

**3. 애플리케이션이 디렉터리 내용을 반환합니다:**

```json
{
  "type": "tool_result",
  "tool_use_id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",
  "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"
}
```

**4. Claude가 관련 파일을 읽습니다:**

```json
{
  "type": "tool_use",
  "id": "toolu_01D5E6F7G8H9I0J1K2L3M4N5",
  "name": "memory",
  "input": {
    "command": "view",
    "path": "/memories/customer_service_guidelines.xml"
  }
}
```

**5. 애플리케이션이 파일 내용을 반환합니다:**

```json
{
  "type": "tool_result",
  "tool_use_id": "toolu_01D5E6F7G8H9I0J1K2L3M4N5",
  "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..."
}
```

**6. Claude가 메모리를 활용하여 도움을 줍니다:**

```text wrap
"Based on your customer service guidelines, I can help you craft a response. Please share the ticket details..."
```

메모리 도구는 모든 Claude 4 이상 모델에서 사용할 수 있습니다. Anthropic 제공 도구의 전체 목록은 [도구 레퍼런스](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-reference)를 참조하세요.

## 시작하기

메모리 도구 사용은 두 단계로 이루어집니다:

1. 요청에 메모리 도구를 추가합니다. `tools` 항목 `{"type": "memory_20250818", "name": "memory"}`가 전체 구성입니다. `name`은 반드시 `memory`여야 하며, Anthropic 제공 도구에 대해서는 입력 스키마를 정의하지 않습니다.
2. 각 메모리 명령에 대한 클라이언트 측 핸들러를 구현합니다. 핸들러는 `/memories` 외부의 경로를 거부해야 하므로, 작성하기 전에 [경로 탐색 보호](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/memory-tool#path-traversal-protection)를 읽어보세요.

## 기본 사용법

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 2048,
      "messages": [
        {
          "role": "user",
          "content": "Help me respond to this customer service ticket."
        }
      ],
      "tools": [{
        "type": "memory_20250818",
        "name": "memory"
      }]
    }'
  ```

  ```bash CLI
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 2048
  tools:
    - type: memory_20250818
      name: memory
  messages:
    - role: user
      content: Help me respond to this customer service ticket.
  YAML
  ```

  ```python Python
  client = anthropic.Anthropic()

  message = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=2048,
      messages=[
          {
              "role": "user",
              "content": "Help me respond to this customer service ticket.",
          }
      ],
      tools=[{"type": "memory_20250818", "name": "memory"}],
  )

  print(message)
  ```

  ```typescript TypeScript
  const anthropic = new Anthropic();

  const message = await anthropic.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 2048,
    messages: [
      {
        role: "user",
        content: "Help me respond to this customer service ticket."
      }
    ],
    tools: [{ type: "memory_20250818", name: "memory" }]
  });

  console.log(message);
  ```

  ```csharp C#
  var client = new AnthropicClient();

  var message = await client.Messages.Create(
      new()
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 2048,
          Messages =
          [
              new()
              {
                  Role = Role.User,
                  Content = "Help me respond to this customer service ticket.",
              },
          ],
          Tools = [new MemoryTool20250818()],
      }
  );

  Console.WriteLine(message);
  ```

  ```go Go
  client := anthropic.NewClient()

  message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 2048,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Help me respond to this customer service ticket.")),
  	},
  	Tools: []anthropic.ToolUnionParam{
  		{OfMemoryTool20250818: &anthropic.MemoryTool20250818Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(message)
  ```

  ```java Java
  import com.anthropic.models.messages.MemoryTool20250818;
  // ...
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(2048L)
      .addTool(MemoryTool20250818.builder().build())
      .addUserMessage("Help me respond to this customer service ticket.")
      .build();

    Message message = client.messages().create(params);
    IO.println(message);
  ```

  ```php PHP
  $client = new Client();

  $message = $client->messages->create(
      model: Model::CLAUDE_OPUS_5_5,
      maxTokens: 2048,
      messages: [
          [
              'role' => 'user',
              'content' => 'Help me respond to this customer service ticket.',
          ],
      ],
      tools: [new MemoryTool20250818],
  );

  echo $message;
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  message = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 2048,
    messages: [
      {
        role: "user",
        content: "Help me respond to this customer service ticket."
      }
    ],
    tools: [
      {
        type: "memory_20250818",
        name: "memory"
      }
    ]
  )
  puts message
  ```
</CodeGroup>

## 메모리 핸들러 구현

위와 같은 요청에 대한 Claude의 응답은 `view /memories`와 같은 메모리 작업을 요청하는 `tool_use` 블록으로 끝납니다. 애플리케이션은 해당 작업을 실행하고 결과를 `tool_result` 블록으로 반환한 다음, Claude가 계속할 수 있도록 대화를 다시 보냅니다. 이것이 표준 [도구 사용 루프](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/handle-tool-calls)입니다.

네 가지 SDK가 도구 인터페이스와 루프를 처리하는 메모리 도구 헬퍼를 제공합니다. 디스크의 파일, 데이터베이스, 클라우드 스토리지 또는 암호화된 파일과 같은 자체 스토리지로 메모리를 뒷받침하려면 `BetaAbstractMemoryTool`을 서브클래싱하거나(Python 및 C#), `betaMemoryTool`을 사용하거나(TypeScript), `BetaMemoryToolHandler`를 구현하세요(Java). Python과 TypeScript는 바로 사용할 수 있는 로컬 파일 시스템 구현인 `BetaLocalFilesystemMemoryTool`도 제공합니다. 메모리 도구 자체는 베타 헤더가 필요하지 않지만, 헬퍼와 "tool runner"(도구 러너) 인터페이스는 SDK의 베타 네임스페이스에 있습니다. Go 및 Ruby SDK에는 메모리 헬퍼가 없으므로 해당 예시는 도구 사용 루프를 직접 실행하며, PHP는 핸들러 클로저를 범용 `BetaRunnableTool`로 감쌉니다. 세 예시 모두 자체 스토리지로 교체해야 하는 인메모리 저장소를 사용합니다.

<CodeGroup exclude="shell">
  ```python Python
  import anthropic
  from anthropic.tools import BetaLocalFilesystemMemoryTool

  client = anthropic.Anthropic()
  memory = BetaLocalFilesystemMemoryTool(base_path="./memory")

  runner = client.beta.messages.tool_runner(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": "Remember that customer Acme Corp prefers email follow-ups.",
          }
      ],
      tools=[memory],
  )

  final_message = runner.until_done()
  print(final_message.content)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";
  import { betaMemoryTool } from "@anthropic-ai/sdk/helpers/beta/memory";
  import { BetaLocalFilesystemMemoryTool } from "@anthropic-ai/sdk/tools/memory/node";

  const client = new Anthropic();

  const backend = await BetaLocalFilesystemMemoryTool.init("./memory");
  const memory = betaMemoryTool(backend); // or pass your own handlers object

  const runner = client.beta.messages.toolRunner({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [
      {
        role: "user",
        content: "Remember that customer Acme Corp prefers email follow-ups."
      }
    ],
    tools: [memory],
    max_iterations: 10
  });

  const finalMessage = await runner;
  console.log(finalMessage.content);
  ```

  ```csharp C#
  using Anthropic;
  using Anthropic.Helpers.Beta;
  using Anthropic.Models.Beta.Messages;

  var client = new AnthropicClient();

  // BetaAbstractMemoryTool을 상속한 사용자 정의 서브클래스
  var memory = new FilesystemMemoryTool("./memories");

  var runner = client.Beta.Messages.ToolRunner(
      new MessageCreateParams
      {
          Model = Anthropic.Models.Messages.Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages =
          [
              new()
              {
                  Role = Role.User,
                  Content = "Remember that customer Acme Corp prefers email follow-ups.",
              },
          ],
      },
      [memory],
      maxIterations: 10
  );

  var finalMessage = await runner.RunUntilDoneAsync();
  Console.WriteLine(finalMessage);
  ```

  ```go Go
  package main

  import (
  	"context"
  	"encoding/json"
  	"fmt"
  	"log"
  	"slices"
  	"sort"
  	"strings"

  	"github.com/anthropics/anthropic-sdk-go"
  )

  // 메모리 파일 경로를 해당 내용에 매핑하는 인메모리 저장소입니다.
  // 프로덕션 환경에서는 자체 스토리지를 사용하세요.
  var store = map[string]string{}

  type memoryCommand struct {
  	Command    string `json:"command"`
  	Path       string `json:"path"`
  	FileText   string `json:"file_text"`
  	OldStr     string `json:"old_str"`
  	NewStr     string `json:"new_str"`
  	InsertLine int    `json:"insert_line"`
  	InsertText string `json:"insert_text"`
  	OldPath    string `json:"old_path"`
  	NewPath    string `json:"new_path"`
  }

  func executeMemory(raw json.RawMessage) string {
  	var cmd memoryCommand
  	if err := json.Unmarshal(raw, &cmd); err != nil {
  		return "Error: invalid memory command"
  	}
  	switch cmd.Command {
  	case "view":
  		if content, ok := store[cmd.Path]; ok {
  			lines := strings.Split(strings.TrimSuffix(content, "\n"), "\n")
  			for i, line := range lines {
  				lines[i] = fmt.Sprintf("%6d\t%s", i+1, line)
  			}
  			return fmt.Sprintf("Here's the content of %s with line numbers:\n%s", cmd.Path, strings.Join(lines, "\n"))
  		}
  		if cmd.Path == "/memories" {
  			listing := []string{"1.0K\t/memories"}
  			for path := range store {
  				listing = append(listing, "1.0K\t"+path)
  			}
  			sort.Strings(listing[1:])
  			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"))
  		}
  		return fmt.Sprintf("The path %s does not exist. Please provide a valid path.", cmd.Path)
  	case "create":
  		store[cmd.Path] = cmd.FileText
  		return "File created successfully at: " + cmd.Path
  	case "str_replace":
  		content, ok := store[cmd.Path]
  		if !ok || !strings.Contains(content, cmd.OldStr) {
  			return fmt.Sprintf("No replacement was performed, old_str `%s` did not appear verbatim in %s.", cmd.OldStr, cmd.Path)
  		}
  		store[cmd.Path] = strings.Replace(content, cmd.OldStr, cmd.NewStr, 1)
  		return "The memory file has been edited."
  	case "insert":
  		content, ok := store[cmd.Path]
  		if !ok {
  			return fmt.Sprintf("Error: The path %s does not exist", cmd.Path)
  		}
  		lines := strings.Split(content, "\n")
  		if cmd.InsertLine < 0 || cmd.InsertLine > len(lines) {
  			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))
  		}
  		lines = slices.Insert(lines, cmd.InsertLine, strings.TrimSuffix(cmd.InsertText, "\n"))
  		store[cmd.Path] = strings.Join(lines, "\n")
  		return fmt.Sprintf("The file %s has been edited.", cmd.Path)
  	case "delete":
  		if _, ok := store[cmd.Path]; !ok {
  			return fmt.Sprintf("Error: The path %s does not exist", cmd.Path)
  		}
  		delete(store, cmd.Path)
  		return "Successfully deleted " + cmd.Path
  	case "rename":
  		if _, ok := store[cmd.OldPath]; !ok {
  			return fmt.Sprintf("Error: The path %s does not exist", cmd.OldPath)
  		}
  		if _, ok := store[cmd.NewPath]; ok {
  			return fmt.Sprintf("Error: The destination %s already exists", cmd.NewPath)
  		}
  		store[cmd.NewPath] = store[cmd.OldPath]
  		delete(store, cmd.OldPath)
  		return fmt.Sprintf("Successfully renamed %s to %s", cmd.OldPath, cmd.NewPath)
  	default:
  		return "Error: unknown command " + cmd.Command
  	}
  }

  func main() {
  	client := anthropic.NewClient()
  	tools := []anthropic.ToolUnionParam{{OfMemoryTool20250818: &anthropic.MemoryTool20250818Param{}}}
  	messages := []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Remember that customer Acme Corp prefers email follow-ups.")),
  	}

  	for {
  		message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  			Model:     anthropic.ModelClaudeOpus5_5,
  			MaxTokens: 1024,
  			Messages:  messages,
  			Tools:     tools,
  		})
  		if err != nil {
  			log.Fatal(err)
  		}
  		if message.StopReason != anthropic.StopReasonToolUse {
  			for _, block := range message.Content {
  				if block.Type == "text" {
  					fmt.Println(block.Text)
  				}
  			}
  			break
  		}
  		results := []anthropic.ContentBlockParamUnion{}
  		for _, block := range message.Content {
  			if block.Type == "tool_use" {
  				results = append(results, anthropic.NewToolResultBlock(block.ID, executeMemory(block.Input), false))
  			}
  		}
  		messages = append(messages, message.ToParam(), anthropic.NewUserMessage(results...))
  	}
  }
  ```

  ```java Java
  import com.anthropic.client.AnthropicClient;
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.helpers.BetaMemoryToolHandler;
  import com.anthropic.helpers.BetaToolRunner;
  import com.anthropic.models.beta.messages.BetaMemoryTool20250818;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.MessageCreateParams; // beta package, not models.messages
  import com.anthropic.models.beta.messages.ToolRunnerCreateParams;
  import com.anthropic.models.messages.Model;
  import java.nio.file.Path;

  void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    // 여섯 가지 메모리 명령을 구현한 BetaMemoryToolHandler 구현체
    BetaMemoryToolHandler handler = new FileSystemMemoryToolHandler(Path.of("memories"));

    MessageCreateParams createParams = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024L)
      .addTool(BetaMemoryTool20250818.builder().build())
      .addUserMessage("Remember that customer Acme Corp prefers email follow-ups.")
      .build();

    ToolRunnerCreateParams runnerParams = ToolRunnerCreateParams.builder()
      .betaMemoryToolHandler(handler)
      .initialMessageParams(createParams)
      .maxIterations(10)
      .build();

    BetaToolRunner runner = client.beta().messages().toolRunner(runnerParams);
    for (BetaMessage message : runner) {
      IO.println(message);
    }
  }
  ```

  ```php PHP
  <?php

  use Anthropic\Beta\Messages\BetaMemoryTool20250818;
  use Anthropic\Client;
  use Anthropic\Lib\Tools\BetaRunnableTool;
  use Anthropic\Messages\Model;

  $client = new Client();

  // 메모리 파일 경로를 해당 내용에 매핑하는 인메모리 저장소입니다.
  // 프로덕션 환경에서는 자체 스토리지를 사용하세요.
  $store = [];

  $memory = new BetaRunnableTool(
      definition: new BetaMemoryTool20250818,
      run: function (array $input) use (&$store): string {
          $path = $input['path'] ?? '';
          switch ($input['command']) {
              case 'view':
                  if (isset($store[$path])) {
                      $numbered = [];
                      foreach (explode("\n", preg_replace('/\n\z/', '', $store[$path])) as $i => $line) {
                          $numbered[] = sprintf("%6d\t%s", $i + 1, $line);
                      }
                      return "Here's the content of {$path} with line numbers:\n" . implode("\n", $numbered);
                  }
                  if ($path === '/memories') {
                      $listing = ["1.0K\t/memories"];
                      foreach (array_keys($store) as $stored) {
                          $listing[] = "1.0K\t{$stored}";
                      }
                      return "Here're the files and directories up to 2 levels deep in {$path}, excluding hidden items and node_modules:\n" . implode("\n", $listing);
                  }
                  return "The path {$path} does not exist. Please provide a valid path.";
              case 'create':
                  $store[$path] = $input['file_text'];
                  return "File created successfully at: {$path}";
              case 'str_replace':
                  $position = strpos($store[$path] ?? '', $input['old_str']);
                  if ($position === false) {
                      return "No replacement was performed, old_str `{$input['old_str']}` did not appear verbatim in {$path}.";
                  }
                  $store[$path] = substr_replace($store[$path], $input['new_str'] ?? '', $position, strlen($input['old_str']));
                  return 'The memory file has been edited.';
              case 'insert':
                  if (!isset($store[$path])) {
                      return "Error: The path {$path} does not exist";
                  }
                  $lines = explode("\n", $store[$path]);
                  if ($input['insert_line'] < 0 || $input['insert_line'] > count($lines)) {
                      return "Error: Invalid `insert_line` parameter: {$input['insert_line']}. It should be within the range of lines of the file: [0, " . count($lines) . "]";
                  }
                  array_splice($lines, $input['insert_line'], 0, [preg_replace('/\n\z/', '', $input['insert_text'])]);
                  $store[$path] = implode("\n", $lines);
                  return "The file {$path} has been edited.";
              case 'delete':
                  if (!isset($store[$path])) {
                      return "Error: The path {$path} does not exist";
                  }
                  unset($store[$path]);
                  return "Successfully deleted {$path}";
              case 'rename':
                  if (!isset($store[$input['old_path']])) {
                      return "Error: The path {$input['old_path']} does not exist";
                  }
                  if (isset($store[$input['new_path']])) {
                      return "Error: The destination {$input['new_path']} already exists";
                  }
                  $store[$input['new_path']] = $store[$input['old_path']];
                  unset($store[$input['old_path']]);
                  return "Successfully renamed {$input['old_path']} to {$input['new_path']}";
              default:
                  return "Error: unknown command {$input['command']}";
          }
      },
  );

  $runner = $client->beta->messages->toolRunner(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Remember that customer Acme Corp prefers email follow-ups.']],
      model: Model::CLAUDE_OPUS_5_5,
      tools: [$memory],
      maxIterations: 10,
  );

  $finalMessage = $runner->runUntilDone();
  print_r($finalMessage->content);
  ```

  ```ruby Ruby
  require "anthropic"

  client = Anthropic::Client.new
  TOOLS = [{type: "memory_20250818", name: "memory"}].freeze

  # 메모리 파일 경로를 해당 내용에 매핑하는 인메모리 저장소입니다.
  # 프로덕션 환경에서는 자체 저장소를 사용하세요.
  STORE = {}

  def execute_memory(input)
    path = input[:path]
    case input[:command]
    when "view"
      if STORE.key?(path)
        lines = STORE[path].chomp.split("\n", -1)
        lines = [""] if lines.empty?
        numbered = lines.each_with_index.map { |line, i| format("%6d\t%s", i + 1, line) }
        "Here's the content of #{path} with line numbers:\n#{numbered.join("\n")}"
      elsif path == "/memories"
        listing = ["1.0K\t/memories"] + STORE.keys.map { |stored| "1.0K\t#{stored}" }
        "Here're the files and directories up to 2 levels deep in #{path}, excluding hidden items and node_modules:\n#{listing.join("\n")}"
      else
        "The path #{path} does not exist. Please provide a valid path."
      end
    when "create"
      STORE[path] = input[:file_text]
      "File created successfully at: #{path}"
    when "str_replace"
      unless STORE.key?(path) && STORE[path].include?(input[:old_str])
        return "No replacement was performed, old_str `#{input[:old_str]}` did not appear verbatim in #{path}."
      end
      STORE[path] = STORE[path].sub(input[:old_str]) { input[:new_str].to_s }
      "The memory file has been edited."
    when "insert"
      return "Error: The path #{path} does not exist" unless STORE.key?(path)
      lines = STORE[path].split("\n", -1)
      lines = [""] if lines.empty?
      if input[:insert_line] < 0 || input[:insert_line] > lines.length
        return "Error: Invalid `insert_line` parameter: #{input[:insert_line]}. It should be within the range of lines of the file: [0, #{lines.length}]"
      end
      lines.insert(input[:insert_line], input[:insert_text].chomp)
      STORE[path] = lines.join("\n")
      "The file #{path} has been edited."
    when "delete"
      return "Error: The path #{path} does not exist" unless STORE.key?(path)
      STORE.delete(path)
      "Successfully deleted #{path}"
    when "rename"
      return "Error: The path #{input[:old_path]} does not exist" unless STORE.key?(input[:old_path])
      return "Error: The destination #{input[:new_path]} already exists" if STORE.key?(input[:new_path])
      STORE[input[:new_path]] = STORE.delete(input[:old_path])
      "Successfully renamed #{input[:old_path]} to #{input[:new_path]}"
    else
      "Error: unknown command #{input[:command]}"
    end
  end

  messages = [{role: "user", content: "Remember that customer Acme Corp prefers email follow-ups."}]
  loop do
    message = client.messages.create(
      model: Anthropic::Model::CLAUDE_OPUS_5_5,
      max_tokens: 1024,
      messages: messages,
      tools: TOOLS
    )
    unless message.stop_reason == :tool_use
      puts message.content
      break
    end
    tool_results = message.content.filter_map do |block|
      next unless block.type == :tool_use
      {type: "tool_result", tool_use_id: block.id, content: execute_memory(block.input)}
    end
    messages << {role: "assistant", content: message.content} << {role: "user", content: tool_results}
  end
  ```
</CodeGroup>

Go, PHP, Ruby 예시의 인메모리 저장소는 예시를 독립적으로 실행할 수 있게 해 줍니다. 각 저장소는 `tool_use` 블록의 `input`에 있는 `command` 필드에 따라 분기하고 [도구 명령](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/memory-tool#tool-commands)에 설명된 문자열을 반환합니다. 프로덕션 핸들러에는 이러한 데모용 저장소가 생략한 [경로 검증](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/memory-tool#path-traversal-protection)도 필요합니다. SDK 자체의 전체 예시는 다음을 참조하세요:

* Python: [examples/memory/basic.py](https://github.com/anthropics/anthropic-sdk-python/blob/main/examples/memory/basic.py)
* TypeScript: [examples/tools-helpers-memory.ts](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/examples/tools-helpers-memory.ts)
* C#: [MemoryToolExample](https://github.com/anthropics/anthropic-sdk-csharp/tree/main/examples/MemoryToolExample)
* Java: [BetaMemoryToolExample.java](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/BetaMemoryToolExample.java)

## 도구 명령

클라이언트 측 구현은 다음 명령을 처리해야 합니다. 이 사양은 권장 동작과 반환 문자열을 설명합니다. Claude는 도구 결과에 포함된 텍스트를 그대로 읽으므로, 애플리케이션에 필요한 경우 다른 문자열을 반환할 수 있습니다.

### view

디렉터리 내용 또는 선택적 줄 범위와 함께 파일 내용을 표시합니다:

```json
{
  "command": "view",
  "path": "/memories/notes.txt",
  "view_range": [1, 10]
}
```

`view_range`는 선택 사항이며 텍스트 파일 보기에 적용됩니다. `[start_line, end_line]`은 해당 줄을 반환하고, `[start_line, -1]`은 `start_line`부터 파일 끝까지 모두 반환합니다.

#### 반환 값

**디렉터리의 경우:** 파일과 디렉터리를 크기와 함께 보여주는 목록을 반환합니다:

```text
Here're the files and directories up to 2 levels deep in {path}, excluding hidden items and node_modules:
{size}\t{path}
{size}\t{path}/{filename1}
{size}\t{path}/{filename2}
```

* 최대 2단계 깊이까지 파일을 나열합니다
* 사람이 읽기 쉬운 크기를 표시합니다(예: `5.5K`, `1.2M`)
* 숨김 항목(`.`으로 시작하는 파일)과 `node_modules`를 제외합니다
* 크기와 경로 사이에 탭 문자를 사용합니다

빈 저장소에서 `/memories`에 대한 첫 번째 `view`는 오류가 아닙니다. SDK가 로컬 파일 시스템 메모리 도구인 `BetaLocalFilesystemMemoryTool`을 제공하는 경우, 이 도구는 Claude의 첫 번째 호출 전에 메모리 루트를 생성하고, 목록 헤더와 함께 빈 디렉터리 자체에 대한 크기 및 경로 한 줄을 반환합니다.

**파일의 경우:** 헤더와 줄 번호와 함께 파일 내용을 반환합니다:

```text wrap
Here's the content of {path} with line numbers:
{line_numbers}{tab}{content}
```

줄 번호 형식:

* **너비:** 6자, 공백 패딩으로 오른쪽 정렬
* **구분자:** 줄 번호와 내용 사이에 탭 문자
* **인덱싱:** 1부터 시작(첫 번째 줄이 1번 줄)
* **줄 제한:** 999,999줄을 초과하는 파일은 오류를 반환해야 합니다: `"File {path} exceeds maximum line limit of 999,999 lines."`

**출력 예시:**

```text
Here's the content of /memories/notes.txt with line numbers:
     1	Hello World
     2	This is line two
    10	Line ten
   100	Line one hundred
```

Claude의 도구 설명에는 `view`가 이미지 파일(`.jpg`, `.jpeg`, `.png`)을 표시하고 16,000자보다 긴 파일의 텍스트 보기를 잘라낸다고도 명시되어 있습니다. 이미지 경로에 대한 `view` 호출과 긴 파일에 대한 후속 범위 지정 보기를 예상하세요.

#### 오류 처리

* **파일 또는 디렉터리가 존재하지 않음:** `"The path {path} does not exist. Please provide a valid path."`

### create

새 파일을 생성합니다:

```json
{
  "command": "create",
  "path": "/memories/notes.txt",
  "file_text": "Meeting notes:\n- Discussed project timeline\n- Next steps defined\n"
}
```

#### 반환 값

* **성공:** `"File created successfully at: {path}"`

#### 오류 처리

* **파일이 이미 존재함:** `"Error: File {path} already exists"`

Claude의 도구 설명에는 `create`가 파일을 "생성하거나 덮어쓴다"고 되어 있으므로, 이미 존재하는 경로에 대한 `create` 호출을 예상하세요. 오류를 반환하는 것이 참조 동작이며, 대신 덮어쓰는 것도 유효한 구현 선택입니다.

### str\_replace

파일의 텍스트를 교체합니다:

```json
{
  "command": "str_replace",
  "path": "/memories/preferences.txt",
  "old_str": "Favorite color: blue",
  "new_str": "Favorite color: green"
}
```

`str_replace`에서 `new_str`은 선택 사항입니다. 생략하면 `old_str`이 대체 없이 삭제됩니다.

#### 반환 값

* **성공:** `"The memory file has been edited."`에 이어 줄 번호가 포함된 편집된 파일의 스니펫

#### 오류 처리

* **파일이 존재하지 않음:** `"Error: The path {path} does not exist. Please provide a valid path."`
* **텍스트를 찾을 수 없음:** ``"No replacement was performed, old_str `\{old_str}` did not appear verbatim in {path}."``
* **중복 텍스트:** `old_str`이 여러 번 나타나는 경우 다음을 반환합니다: ``"No replacement was performed. Multiple occurrences of old_str `\{old_str}` in lines: {line_numbers}. Please ensure it is unique"``

#### 디렉터리 처리

경로가 디렉터리인 경우 "파일이 존재하지 않음" 오류를 반환합니다.

### insert

특정 줄에 텍스트를 삽입합니다:

```json
{
  "command": "insert",
  "path": "/memories/todo.txt",
  "insert_line": 2,
  "insert_text": "- Review memory tool documentation\n"
}
```

`insert_text`는 `insert_line` 줄 뒤에 삽입되며, `0`은 파일의 시작 부분에 삽입합니다.

#### 반환 값

* **성공:** `"The file {path} has been edited."`

#### 오류 처리

* **파일이 존재하지 않음:** `"Error: The path {path} does not exist"`
* **잘못된 줄 번호:** ``"Error: Invalid `insert_line` parameter: {insert_line}. It should be within the range of lines of the file: [0, {n_lines}]"``

#### 디렉터리 처리

경로가 디렉터리인 경우 "파일이 존재하지 않음" 오류를 반환합니다.

### delete

파일 또는 디렉터리를 삭제합니다:

```json
{
  "command": "delete",
  "path": "/memories/old_file.txt"
}
```

#### 반환 값

* **성공:** `"Successfully deleted {path}"`

#### 오류 처리

* **파일 또는 디렉터리가 존재하지 않음:** `"Error: The path {path} does not exist"`

#### 디렉터리 처리

디렉터리와 그 모든 내용을 재귀적으로 삭제합니다. 도구 설명은 Claude에게 `/memories` 디렉터리 자체는 삭제할 수 없다고 알려주므로, 경로가 메모리 루트인 `delete`는 거부하세요.

### rename

파일 또는 디렉터리의 이름을 변경하거나 이동합니다:

```json
{
  "command": "rename",
  "old_path": "/memories/draft.txt",
  "new_path": "/memories/final.txt"
}
```

#### 반환 값

* **성공:** `"Successfully renamed {old_path} to {new_path}"`

#### 오류 처리

* **원본이 존재하지 않음:** `"Error: The path {old_path} does not exist"`
* **대상이 이미 존재함:** 오류를 반환합니다(덮어쓰지 않음): `"Error: The destination {new_path} already exists"`

#### 디렉터리 처리

디렉터리의 이름을 변경합니다. 도구 설명은 Claude에게 `/memories` 디렉터리 자체의 이름은 변경할 수 없다고 알려주므로, `old_path`가 메모리 루트인 `rename`은 거부하세요.

## 프롬프트 가이드

요청의 `tools`에 메모리 도구가 있으면 API가 자동으로 이 지침을 "system prompt"(시스템 프롬프트)에 추가합니다. 직접 전송할 필요가 없습니다:

```text wrap
IMPORTANT: ALWAYS VIEW YOUR MEMORY DIRECTORY BEFORE DOING ANYTHING ELSE.
MEMORY PROTOCOL:
1. Use the `view` command of your `memory` tool to check for earlier progress.
2. ... (work on the task) ...
   - As you make progress, record status / progress / thoughts etc in your memory.
ASSUME INTERRUPTION: Your context window might be reset at any moment, so you risk losing any progress that is not recorded in your memory directory.
```

Claude의 도구 설명은 이미 메모리 디렉터리를 정리된 상태로 유지하라고 지시하므로, 해당 지침을 반복할 필요가 없습니다. 그래도 Claude가 어수선한 메모리 파일을 생성한다면 프롬프트에서 이를 강화할 수 있습니다:

```text wrap
Note: 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.
```

Claude가 메모리에 무엇을 기록할지 안내할 수도 있습니다. 예: "Only write down information relevant to \<topic> in your memory system."

## 보안 고려 사항

애플리케이션은 Claude가 요청하는 모든 파일 작업을 실행하므로, 다음 안전 장치는 사용자의 책임입니다:

### 민감한 정보

Claude는 일반적으로 민감한 정보를 메모리 파일에 기록하는 것을 거부합니다. 더 강력한 보장을 위해, 핸들러가 파일을 쓰기 전에 민감한 데이터를 제거하는 검증을 추가하세요.

### 파일 저장 크기

메모리 파일 크기를 추적하고 파일이 커질 수 있는 한도를 설정하세요. `view` 명령이 반환하는 문자 수에 한도를 두고, Claude가 `view_range`로 나머지를 페이지 단위로 탐색하도록 하는 것을 고려하세요.

### 메모리 만료

오랫동안 접근되지 않은 메모리 파일을 주기적으로 삭제하세요.

### 경로 탐색 보호

<Warning>
  `/memories/../../secrets.env`와 같은 악의적인 경로는 `/memories` 디렉터리 외부의 파일에 도달할 수 있습니다. 구현은 디렉터리 탐색 공격을 방지하기 위해 모든 명령의 모든 경로를 검증해야 합니다.
</Warning>

다음 안전 장치를 고려하세요:

* 모든 경로가 `/memories`로 시작하는지 검증
* 경로를 정규 형식으로 해석하고 메모리 디렉터리 내에 있는지 확인
* `../`, `..\\` 또는 기타 탐색 패턴과 같은 시퀀스를 포함하는 경로 거부
* URL 인코딩된 탐색 시퀀스(`%2e%2e%2f`) 주의
* 언어의 내장 경로 보안 유틸리티 사용(예: Python의 `pathlib.Path.resolve()` 및 `relative_to()`)

## 오류 처리

메모리 도구는 [텍스트 편집기 도구](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/text-editor-tool#handle-errors)와 유사한 오류 처리 패턴을 사용합니다. 각 명령의 오류 메시지는 [도구 명령](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/memory-tool#tool-commands)에 나열되어 있습니다. Claude에게 오류를 반환하려면 도구 결과에서 `is_error`를 `true`로 설정하고 메시지를 `content`에 넣으세요:

```json
{
  "type": "tool_result",
  "tool_use_id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",
  "content": "Error: The path /memories/notes.txt does not exist",
  "is_error": true
}
```

## 컨텍스트 편집 통합

메모리 도구는 컨텍스트 편집과 함께 사용하여 장기 실행 대화를 관리합니다. 자세한 내용은 [컨텍스트 편집](https://platform.claude.com/docs/ko/build-with-claude/context-editing)을 참조하세요.

## 컴팩션과 함께 사용

메모리 도구는 서버 측에서 오래된 대화 컨텍스트를 요약하는 [컴팩션](https://platform.claude.com/docs/ko/build-with-claude/compaction)과도 함께 사용할 수 있습니다. 컨텍스트 편집은 클라이언트에서 특정 도구 결과를 지웁니다. 컴팩션은 대화가 컨텍스트 윈도우 한도에 가까워지면 서버에서 전체 대화를 자동으로 요약합니다.

장기 실행 에이전트의 경우 두 가지를 모두 사용하는 것을 고려하세요. 컴팩션은 클라이언트 측 관리 작업 없이 활성 컨텍스트를 작게 유지하고, 메모리는 요약 후에도 살아남아야 하는 정보를 보존합니다.

## 다중 세션 소프트웨어 개발 패턴

여러 에이전트 세션에 걸친 소프트웨어 프로젝트의 경우, 작업이 진행되면서 임시로 메모리 파일을 작성하는 대신 의도적으로 설정하세요. 다음 패턴은 메모리를 복구 메커니즘으로 전환합니다. 각 새 세션은 마지막 세션이 기록한 상태에서 재개됩니다.

### 패턴 작동 방식

1. **초기화 세션:** 첫 번째 세션은 실질적인 작업이 시작되기 전에 메모리 파일을 설정합니다. 여기에는 진행 로그(완료된 작업과 다음 작업 추적), 기능 체크리스트(작업 범위 정의), 프로젝트에 필요한 시작 또는 초기화 스크립트에 대한 참조가 포함됩니다.

2. **후속 세션:** 각 새 세션은 해당 메모리 파일을 읽는 것으로 시작합니다. 이를 통해 코드베이스를 다시 탐색하거나 이전 결정을 되짚지 않고도 프로젝트 상태를 복원합니다.

3. **세션 종료 시 업데이트:** 세션이 끝나기 전에 완료된 작업과 남은 작업으로 진행 로그를 업데이트합니다. 이를 통해 다음 세션이 정확한 시작점을 갖도록 보장합니다.

### 핵심 원칙

한 번에 하나의 기능만 작업하세요. 코드가 작성되었을 때가 아니라 엔드투엔드 검증으로 작동이 확인된 후에만 기능을 완료로 표시하세요. 이렇게 하면 세션 간에 진행 로그가 정확하게 유지됩니다.

<Tip>
  초기화 스크립트, 진행 파일 구조, git 기반 복구를 포함하여 이 패턴의 실제 적용에 대한 자세한 사례 연구는 [장기 실행 에이전트를 위한 효과적인 하네스](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents)를 참조하세요.
</Tip>

## 다음 단계

<CardGroup cols={2}>
  <Card title="Bash 도구" icon="terminal" href="https://platform.claude.com/docs/ko/agents-and-tools/tool-use/bash-tool">
    지속적인 bash 세션에서 셸 명령을 실행합니다.
  </Card>

  <Card title="컨텍스트 편집" icon="edit" href="https://platform.claude.com/docs/ko/build-with-claude/context-editing">
    컨텍스트 편집으로 대화 컨텍스트가 커짐에 따라 자동으로 관리합니다.
  </Card>

  <Card title="컴팩션" icon="stack" href="https://platform.claude.com/docs/ko/build-with-claude/compaction">
    컨텍스트 윈도우 한도에 가까워지는 긴 대화를 관리하기 위한 서버 측 컨텍스트 컴팩션입니다.
  </Card>

  <Card title="도구 레퍼런스" icon="book" href="https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-reference">
    Anthropic 제공 도구 디렉터리 및 선택적 도구 정의 속성에 대한 레퍼런스입니다.
  </Card>
</CardGroup>
