Claude Memoryツールの6コマンド実装 — 返り値とエラーの仕様
Memoryツールのview/create/str_replace/insert/delete/renameが返す文字列とエラー処理を、公式仕様に沿って実装レベルで押さえます。
Memoryツールの返り値は自分の実装が決める
Memoryツールは、Claudeが /memories 配下のファイルにメモを書き残し、次のセッションで読み戻すための仕組みです。会話をまたいで知識を積み上げられるので、コンテキストウィンドウを圧迫せずに長期タスクの状態を保持できます。動作の主体はサーバーではなくクライアント側です。Claudeは「ファイルを作成したい」「この範囲を読みたい」という操作をtool_useブロックでリクエストするだけで、実際にファイルを読み書きするのは呼び出し元のアプリケーションです。
用途は「複数セッションにまたがるプロジェクトの文脈維持」「過去のやり取りやフィードバックを次のタスクに反映」「時間をかけて知識ベースを積み上げる」の3つが公式に挙げられています。いずれも、Claudeが/memories配下を正しく読み書きできて初めて成立する用途です。返り値の書式を誤ると、この3つの用途そのものが崩れます。
リクエストへの追加はシンプルで、tools配列に{"type": "memory_20250818", "name": "memory"}を1つ加えるだけです。入力スキーマを自分で定義する必要はありません。Claude 4以降の全モデルで利用でき、ベータヘッダーも不要です(公式SDKのヘルパークラスはベータ名前空間に置かれていますが、Memoryツール自体はベータ扱いではありません)。
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",
"max_tokens": 2048,
"messages": [{"role": "user", "content": "Help me respond to this ticket."}],
"tools": [{"type": "memory_20250818", "name": "memory"}]
}'構成はここまでで終わりです。残る作業は、view / create / str_replace / insert / delete / renameの6コマンドをディスパッチするハンドラを自分で書くことです。
実装の出発点はSDKによって用意が異なります。Python / C#ではBetaAbstractMemoryToolをサブクラス化、TypeScriptではbetaMemoryToolヘルパーに保存先の実装を渡す形、JavaではBetaMemoryToolHandlerを実装する形で、6コマンドの分岐そのものはSDK側が肩代わりします。Go / Rubyにはヘルパーが無く、ツール呼び出しのループ自体を自前で回す必要があります。どの言語でも、ファイルをディスクに置くかデータベースのキーにするか、暗号化するかは呼び出し元の判断に任されています。
公式ドキュメントは「Claudeはツール結果に含まれるテキストをそのまま読むので、必要なら異なる文字列を返してもよい」と明言しています。つまり下記の返り値仕様は強制ではありません。Memoryツールを有効にすると自動でシステムプロンプトに追加される「メモリプロトコル」の指示文自体は、viewでメモリディレクトリを最初に確認することを求めているだけで、ディレクトリ一覧やファイル内容の書式には触れていません。とはいえ独自の書式を一から設計するコストは小さくないので、まずは公式リファレンスの挙動をそのまま実装するのが実務上の出発点になります。
先に6コマンドの返り値を一覧にしておきます。
| コマンド | 成功時の返り値 | 代表的なエラー |
|---|---|---|
| view | 成功時の返り値ディレクトリ一覧、または行番号付きファイル内容 | 代表的なエラーパスが存在しない |
| create | 成功時の返り値File created successfully at: {path} | 代表的なエラー既に存在する(仕様上はエラー、上書きも許容) |
| str_replace | 成功時の返り値The memory file has been edited. + 編集箇所のスニペット | 代表的なエラー置換対象なし / 複数一致 |
| insert | 成功時の返り値The file {path} has been edited. | 代表的なエラー行番号が範囲外 |
| delete | 成功時の返り値Successfully deleted {path} | 代表的なエラーパスが存在しない / /memories直下の削除 |
| rename | 成功時の返り値Successfully renamed {old_path} to {new_path} | 代表的なエラー移動先が既に存在する |
view — ディレクトリ一覧とファイル内容の返り値仕様
viewはディレクトリの中身、またはファイルの内容を返します。view_rangeは省略可能で、[開始行, 終了行]を指定すればその範囲だけを、[開始行, -1]を指定すればその行から末尾までを返せます。
{
"command": "view",
"path": "/memories/notes.txt",
"view_range": [1, 10]
}対象がディレクトリのときは、階層2つ分までのファイルとディレクトリを、サイズ付きで一覧します。隠しファイル(.始まり)とnode_modulesは除外し、サイズと パスの間はタブ区切りにします。
Here're the files and directories up to 2 levels deep in /memories, excluding hidden items and node_modules:
4.0K /memories
1.5K /memories/notes.txt
2.0K /memories/progress.md空の/memoriesに対する最初のviewはエラーではありません。公式SDKのローカルファイルシステム実装(BetaLocalFilesystemMemoryTool)は、Claudeの最初の呼び出しより前にメモリのルートディレクトリを作成し、一覧のヘッダーと空ディレクトリ自身の1行だけを返します。
対象がファイルのときは、ヘッダーに続けて行番号付きの内容を返します。行番号の書式には細かい規定があります。
- 幅: 6文字、右詰め、空白パディング
- 区切り: 行番号と内容の間はタブ文字
- 起点: 1行目を1とする(0始まりではない)
- 上限: 999,999行を超えるファイルはエラーを返す(
File {path} exceeds maximum line limit of 999,999 lines.)
Here's the content of /memories/notes.txt with line numbers:
1 Meeting notes
2 - Discussed timeline
10 - Next steps definedClaudeへのツール説明文には「viewは.jpg / .jpeg / .pngの画像ファイルも表示する」「16,000文字を超えるテキストは切り詰めて表示する」とも書かれています。長いファイルにはview_rangeを使った追い読みのリクエストが続く前提で実装します。パスが存在しない場合は"The path {path} does not exist. Please provide a valid path."を返します。
create / str_replace / insert — ファイルを書き換える3コマンドの返り値
createは新規ファイルを作ります。成功時は"File created successfully at: {path}"を返します。仕様上、既存パスへのcreateはエラー("Error: File {path} already exists")ですが、Claudeへのツール説明文はcreateを「作成または上書き」と説明しているため、既存パスへの呼び出しは実際に起こり得ます。エラーを返すのが公式リファレンスの挙動ですが、上書きに倒す実装も有効な選択として認められています。
str_replaceはファイル内のテキストを置換します。new_strは省略可能で、省略するとold_strが削除だけされます。
{
"command": "str_replace",
"path": "/memories/preferences.txt",
"old_str": "Favorite color: blue",
"new_str": "Favorite color: green"
}成功時は"The memory file has been edited."に続けて、編集箇所を行番号付きで抜粋した文字列を返します。エラーは3種類あります。ファイルが存在しない場合、old_strが本文に一字一句一致しない場合(No replacement was performed, old_str ... did not appear verbatim in {path}.)、そしてold_strが複数箇所に一致する場合です。複数一致のときは該当行番号を列挙して一意にするよう促します(Multiple occurrences of old_str ... in lines: {line_numbers}. Please ensure it is unique)。パスがディレクトリを指していた場合は「ファイルが存在しない」エラーとして扱います。
insertは指定行の直後にテキストを挿入します。insert_lineに0を指定すると先頭挿入になります。
{
"command": "insert",
"path": "/memories/todo.txt",
"insert_line": 2,
"insert_text": "- Review memory tool spec\n"
}成功時は"The file {path} has been edited."、行番号が不正なときは"Error: Invalid insert_line parameter: {insert_line}. It should be within the range of lines of the file: [0, {n_lines}]"を返します。こちらもディレクトリ指定は「ファイルが存在しない」エラーで扱います。
insert_line: 2が指す位置は「2行目の直後」です。元のファイルが3行(1: 見出し、2: 項目A、3: 項目B)なら、挿入後は4行になり、新しい内容は3行目に入って項目Bが4行目へ繰り下がります。insert_line: 0だけが例外で、ファイルの一番先頭に挿入されます。この境界条件を取り違えると、Claudeが「指定した行の直後に入ったはず」の内容が1行ずれて見え、後続のviewで不整合に気づいて再編集を試みる、という無駄な往復が発生します。
delete / rename — 削除と移動の返り値、ディレクトリを渡したときの挙動
deleteはファイルまたはディレクトリを削除します。成功時は"Successfully deleted {path}"、パスが存在しない場合は"Error: The path {path} does not exist"を返します。対象がディレクトリのときは中身ごと再帰的に削除します。Claudeへのツール説明文は「/memoriesディレクトリ自体は削除できない」と伝えていますが、これはモデル側への指示にすぎません。old_pathまたはpathがメモリのルートそのものを指すリクエストを拒否する処理は、ハンドラ側で明示的に実装する必要があります。
renameはファイルまたはディレクトリの名前変更・移動を行います。
{
"command": "rename",
"old_path": "/memories/draft.txt",
"new_path": "/memories/final.txt"
}成功時は"Successfully renamed {old_path} to {new_path}"を返します。エラーは移動元が存在しない場合と、移動先が既に存在する場合の2種類です。後者は上書きせずにエラーを返すのが仕様です("Error: The destination {new_path} already exists")。deleteと同様、/memoriesルート自体のリネームを拒否するガードもハンドラ側の責任です。
実装で見落としやすい6つのポイント
-
返り値の完全一致は必須ではないという点です。Claudeはツール結果のテキストをそのまま読むだけなので、アプリケーションの都合で文言を変えても動作は壊れません。ただし独自の書式を一から作るコストと、Claudeが一覧やファイル内容を読み違えるリスクを考えると、まずは公式リファレンスの書式(タブ区切り・6桁の行番号など)に沿わせておくほうが実装は軽く済みます。
-
createの挙動は自分で決める設計判断です。「既存パスはエラー」という仕様と、「作成または上書き」というツール説明文の食い違いは、実装時に一度立ち止まって決めるべき分岐点です。上書きを許すなら、既存内容を保持したいケースの防止策(バックアップやバージョン管理)も合わせて検討します。テストで両方の挙動を確認しておくと、Claudeが既存ファイルへcreateを呼んだときの実際の反応を後から追いやすくなります。 -
str_replaceの複数一致エラーは、行番号を渡すところまでが仕様です。old_strが2箇所以上に一致したとき、単に「一意にしてください」と返すだけでは、Claudeはどこを直せばよいか分かりません。仕様どおり一致した行番号を列挙して返すことで、Claudeは次のstr_replaceで前後の文脈を広げて呼び出し直せます。行番号を省いた実装は動作こそしますが、Claudeが自力で該当箇所をviewし直す1往復を余計に発生させます。 -
エラーはメッセージ文字列だけでなく
is_errorフラグとセットで返します。Claudeにエラーだと伝えるには、tool_resultのis_errorをtrueにし、メッセージをcontentに入れます。この形式はText editorツールのエラー処理と共通です。 -
viewの返り値は文字数の上限を設けます。公式のSecurity considerationsは、ファイルサイズを追跡して上限を設けること、viewが返す文字数にキャップをかけて残りはview_rangeでページングさせることを挙げています。本記事のview仕様(タブ区切り・行番号)は上限なしの実装を前提にしていますが、実運用では長大なファイルを丸ごと返さない工夫が要ります。 -
機微情報の書き込みと、使われなくなったメモリファイルの扱いも運用側の責任です。Claudeは機微情報の書き込みを通常拒否しますが、確実に防ぎたいならハンドラ側で書き込み前にバリデーションをかけます。長期間アクセスされていないメモリファイルは定期的に削除する運用も公式が推奨しています。
{
"type": "tool_result",
"tool_use_id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",
"content": "Error: The path /memories/notes.txt does not exist",
"is_error": true
}まとめ
Memoryツール自体はツール定義1行をtoolsに足すだけで有効になりますが、実際に動かすには6コマンド分のハンドラをフルスクラッチで書く必要があります。返り値の書式(行番号の幅・タブ区切り・成功/エラー文言)は公式リファレンスの挙動に沿わせつつ、/memoriesルートの保護・返り値の文字数キャップ・機微情報のバリデーションはハンドラ側で自前のガードとして持たせます。6コマンドの分岐とこれらのガードさえ揃えば、あとはストレージの選定(ファイル・DB・暗号化ストレージ)を自分の運用に合わせて差し替えるだけです。
複数セッションをまたいだ運用パターンはMemoryツールをマルチセッションで運用するパターンにまとめています。ツール呼び出しループ全体の設計はTool RunnerでAnthropic APIのツール呼び出しループを自動化するを参照してください。