Claudeツール名に使える文字と日本語が弾かれる理由
Claude APIのツール定義でnameに使える文字は英数字・アンダースコア・ハイフンの4種類、長さは128文字までです。日本語や記号が400になる理由と、名前の衝突を防ぐ命名ルールをまとめます。
Claudeツール名に使える文字は4種類だけ
Claude APIにツールを渡すとき、nameフィールドは正規表現^[a-zA-Z0-9_-]{1,128}$で検証されます。使える文字はアルファベットの大文字・小文字、数字、アンダースコア(_)、ハイフン(-)の4種類です。長さは1〜128文字に収めます。
スペース、日本語、絵文字、ピリオド、コロンはすべて対象外です。get_weatherやcreate-issueは通りますが、天気を取得やget weather、get.weatherは400で拒否されます。
日本語のツール名は400になる。日本語は説明文に書く
日本語の文字はa-zA-Z0-9_-のどの文字クラスにも入りません。天気取得のような名前を送ると、リクエストが400(invalid_request_error)になります。
ツールの目的をチーム内で分かりやすくしたいときは、日本語をdescriptionに書きます。descriptionには文字種の制約がありません。定義の説明ページは、各ツールの説明を3〜4文以上で書くことも勧めています。名前は識別のため、説明文は読み手とモデルへの情報のために使い分けます。
通る名前と弾かれる名前
| 書き方 | 結果 | 理由 |
|---|---|---|
get_weather | 結果通る | 理由英小文字とアンダースコアのみ |
create-issue | 結果通る | 理由ハイフンは許可対象 |
github_list_prs | 結果通る | 理由サービス名を接頭辞にした形 |
天気取得 | 結果400 | 理由日本語は許可文字の外 |
get weather | 結果400 | 理由半角スペースは非対応 |
get.weather | 結果400 | 理由ピリオドは非対応 |
repo/create_issue | 結果400 | 理由スラッシュも許可文字に入っていない |
大文字と小文字はどちらも正規表現を満たすので、GetWeatherもget_weatherも文字種としては通ります。定義の説明ページのサンプルは、get_weatherやget_stock_priceのような小文字スネークケースで揃っています。
nameとプロパティ名で規則が違う
nameの規則はトップレベルの名前だけにかかります。input_schemaの中のプロパティ名には、別の上限と文字種があります。
名前の種類ごとの規則
ツールの name
英数字・アンダースコア・ハイフンのみ。ピリオドは使えません。
input_schema のトップレベルのプロパティ名
ASCIIの英数字・_・-に加えて.も使えます。日本語のプロパティ名は規則の外です。
Claude CodeのMCPの説明ページによると、APIはリクエスト内のすべてのツールの入力スキーマを検査し、1つでも違反すればリクエスト全体を400で拒否します。Claude Codeはこの検査のうちプロパティ名の規則とJSON Schema draft 2020-12への適合を、MCPサーバーのツールを読み込む時点で自前でも実行します。違反したツールは1本だけ除外され、同じサーバーの他のツールは使えるままです。
ただし除外はAnthropic側から取得するフィーチャーフラグで有効になる仕組みです。フラグの取得が無効な環境では検査と記録だけが走り、スキーマはそのままAPIへ送られて400になります。draft 2020-12の検査は、$schemaを宣言しないスキーマと2020-12を宣言したスキーマが対象で、別の方言を宣言したスキーマは含まれません。
つまり「日本語のプロパティ名でも構文エラーにならない」とは言えません。locationやunitのように、英数字とアンダースコアで揃えるのが安全です。意味の説明はdescriptionに書きます。
400とツールが呼ばれない症状を切り分ける
ツール関連の失敗は、エラーで止まるものと、静かに呼ばれなくなるものに分かれます。順に当たると原因を絞れます。
ツール関連の失敗を当たる順番
- 1
nameが正規表現に合っているか
nameが冒頭の正規表現を満たさないと、リクエスト全体が400です。動的に生成した名前は、送信前に1件ずつ確かめます。 - 2
プロパティ名とスキーマが規則に合っているか
名前の種類ごとの規則(上の比較)に加えて、スキーマ全体がJSON Schema draft 2020-12に適合しているかを見ます。
- 3
名前が重複していないか
エラーが出ないのにClaudeがツールを呼ばないときは、ツール一覧の中の重複名から確かめます。
3つ目は公式のトラブルシューティング表に載っている症状です。「Claudeがツールを呼ばない」の原因として、名前の衝突と汎用的すぎるスキーマが並び、対処の先頭は「ツールリスト内の重複名を確認する」です。
名前の重複は「ツールが呼ばれない」原因になる
文字種が正しくても、ツール一覧に同じ名前が重複していると、Claudeがツールを呼ばない原因になります。独自定義のsearchとMCPサーバー由来のsearchが同居すると、名前が重複します。症状は400ではなく「呼ばれない」なので、エラーメッセージからは見つけられません。
Claude Codeに接続したMCPサーバーのツールは、mcp__<サーバー名>__<ツール名>の形で呼ばれます。プラグイン同梱のMCPサーバーでは、mcp__plugin_<プラグイン名>_<サーバー名>__<ツール名>になります。プラグイン同梱サーバーの呼び名や/mcp__サーバー名__プロンプト名の形式のMCPプロンプトでは、A-Z・a-z・0-9・_・-以外の文字が_に置き換えられます。そのため、my.serverとmy_serverのように置換後に一致する名前は区別できなくなります。
命名で決める3つのルール
命名の失敗は実装の後半ほど直しにくくなります。公式の設計指針と合わせて、次の3つを先に決めておく方法があります。
ツール名を決めるときの3つの型
サービス名を接頭辞にする
github_list_prs、slack_send_messageのように由来を先頭に置きます。ツール検索を使うときは、github_のような接頭辞があると1回の検索で同じグループをまとめて拾えます。関連する操作は1つに集約する
create_pr・review_pr・merge_prを分けるより、actionパラメータを持つ1つのツールにまとめるほうが、選択のあいまいさが減ると公式が説明しています。命名規則をコードで強制する
ユーザー入力やDBの値を
nameにそのまま流さず、変換と重複の確認を1つの関数に通します。実装例は次の節です。
正規化関数は衝突も防ぐ
動的にツール定義を組み立てるアプリケーションでは、nameを作る箇所に検証関数を1つ挟みます。「許可されない文字を消す」だけの実装は、別々の名前を同じ文字列に潰すことがあります。
get weatherはget_weatherに、get.weatherはgetweatherに、天気取得は空文字になる、というのが単純な置換の結果です。空文字は検証で落ちますが、2つ目の衝突は検証を通り抜けます。次の実装は、変換した名前にもとの文字列のハッシュを付けて、別の入力が同じ名前にならないようにします。
import hashlib
import re
PATTERN = re.compile(r"[a-zA-Z0-9_-]{1,128}")
def to_safe_tool_name(raw: str, taken: set[str]) -> str:
# すでに有効で未使用なら、そのまま使う
if PATTERN.fullmatch(raw) and raw not in taken:
taken.add(raw)
return raw
# 許可されない文字の連続を _ に置き換え、元の文字列のハッシュを付ける
base = re.sub(r"[^a-zA-Z0-9_-]+", "_", raw).strip("_") or "tool"
suffix = hashlib.sha1(raw.encode()).hexdigest()[:8]
safe = f"{base[:119]}_{suffix}"
taken.add(safe)
return safePython 3で次の入力を順に通すと、出力は以下のとおりです。
'get_weather' -> get_weather
'get weather' -> get_weather_22575018
'get.weather' -> get_weather_6f45b27b
'get:weather' -> get_weather_c7985f0f
'天気取得' -> tool_ce84bbc5
'在庫確認' -> tool_09bc2585日本語の2件は、変換後はどちらもtool_にハッシュが付いた名前になります。日本語名はハッシュだけで区別できるので、もとの名称をdescriptionの先頭に添えると、人間にもモデルにも手がかりが残ります。
検証にはre.matchと$ではなく、fullmatchを使っています。Pythonの$は末尾の改行の手前にも一致するため、^[a-zA-Z0-9_-]{1,128}$にget_weather\nを通すとTrueになります。fullmatchなら同じ入力がNoneになります。
名前が参照される場所は4つある
一度決めたnameは、リクエストの複数の場所から同じ文字列で参照されます。変更すると、その全部を直すことになります。
tool_choiceの{"type": "tool", "name": "get_weather"}で、呼ばせたいツールを名前で指定する- Claudeが返す
tool_useブロックのnameで、どのツールが呼ばれたかを受け取る - MCPコネクタの
mcp_toolsetでは、configsのキーがツール名になる。サーバーに存在しない名前を書いてもエラーにならず、バックエンドに警告が残るだけ - ツール検索(
tool_search_tool_regexとbm25)では、ツール名・説明・引数名・引数の説明が検索対象になる
tool_choiceでanyやtoolを指定すると、Claude Opus 5.5、Claude Sonnet 5.5、Claude Fable 5.1、Claude Mythos 5.1では400になります。この場合の代わりの手段は、autoと厳密なツール利用の組み合わせです。エラーメッセージはtool_choice: type "tool" and "any" are not supported for this model.です。名前で強制呼び出しするコードを書く前に、使うモデルを見ておく必要があります。
組み込みツールとツールセットの名前
bash・text editorのようなAnthropic-schemaのクライアントツールは、nameではなく日付入りのtype(bash_20250124など)で宣言します。名前と入力の形はAnthropic側が固定しているため、文字種を考える場面はありません。
computer use toolとbrowser use toolは、1つのエントリが複数のメンバーツールを宣言するクライアントツールセットです。エントリ自体はnameを持ちません。Claudeはnameとtoolset_name(computerかbrowser)を持つtool_useブロックでメンバーを呼びます。
注意したいのは、自作ツールがメンバーと同じ名前を持てることです。公式はコンピュータ操作とブラウザ操作の両方にscreenshotがあることにも触れ、ディスパッチはtoolset_nameとnameの組で行うよう説明しています。名前の文字列だけで分岐するコードは、ここで取り違えます。
エラーレスポンスの形
400のエラーは、次の形式のJSONで返ります。invalid_request_errorの場合も同じ形です。トップレベルのerrorオブジェクトにtypeとmessageが入り、request_idも付きます。
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "..."
},
"request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}問い合わせるときは、このrequest_idを伝えると調査が早くなります。同じ値はレスポンスヘッダrequest-idにも入り、PythonとTypeScriptのSDKでは_request_idプロパティから読めます。
Tool RunnerやAgent SDKでも制約は同じ
Tool RunnerのようにSDK側でツール実行を肩代わりする仕組みを使っても、最終的にClaudeへ渡るのは上で見たツール定義です。
つまずきやすいのは、外部システムのメタデータ(日本語のAPI名やUI表示名)をnameに流し込んだときです。表示用の名前とname用の識別子は、最初から別の変数にしておくと安全です。エージェント向けのツール設計の原則でも、名前空間の設計は精度に関わる要素として扱われています。Anthropic API完全ガイドのモデル選択・料金とあわせて、命名規則は実装の早い段階で固定しておくと手戻りが減ります。
まとめ
日本語で伝えたい内容はdescriptionに書き、nameはサービス名を接頭辞にしたASCII識別子に揃えます。
文字種の違反は400で分かりますが、名前の重複はエラーにならず「呼ばれない」症状で現れます。変換関数は、変換後の名前が他と重ならないことまで確かめる形にしておくと安全です。