AnimaWorks向けPython外部ツールモジュールを作成するメタスキル。core/tools連携・get_credential・permissionsを扱う。 Use when: core/toolsへ新規モジュール追加、Web APIラッパー実装、animaworks-toolから呼ぶカスタムツール開発が必要なとき。
Install
npx skillscat add xuiltul/animaworks/templates-ja-common-skills-tool-creator Install via the SkillsCat registry.
We need to produce a 2-3 sentence plain-text summary in English, objective, factual, no marketing language, no superlatives, no calls to action. At most 60 words. No quotes, no markdown, no bullet points, no headings. We need to summarize the skill: tool-creator meta skill for creating Python external tool modules for AnimaWorks, handling core/tools integration, get_credential, permissions. Use when adding new modules to core/tools, implementing web API wrappers, developing custom tools called from animaworks-tool.
tool-creator
概要
AnimaWorksのツールは3種類に分かれる:
| 種類 | 配置先 | 発見方法 |
|---|---|---|
| コアツール | core/tools/*.py(_ 接頭辞のファイルは除外) |
discover_core_tools() → TOOL_MODULES(パッケージ import) |
| 共有ツール | {data_dir}/common_tools/*.py |
discover_common_tools() |
| 個人ツール | {anima_dir}/tools/*.py |
discover_personal_tools() |
{data_dir} は通常 ~/.animaworks/。
- ディスパッチ:
ExternalToolDispatcherは_DISPATCH_TABLEを廃止し、各モジュールのdispatch(name, args)(またはスキーマ名と同名の関数)に統一している(core/tooling/dispatch.py)。 - マージ:
AgentCore起動時の_discover_personal_tools()とrefresh_toolsはいずれも 共通→個人 の順でマージし、個人が同名を上書き する({**common, **personal})。マージ結果はExternalToolDispatcherの_personal_toolsに保持される(名前は historical だが 共通ツールも含む)。 - コアとの衝突: コア
TOOL_MODULESと同名のファイルは、共通・個人の発見時に スキップ される(警告ログのみ)。 - ツールファイルの書き込み:
write_memory_fileでtools/*.pyに書くときはpermissions(permissions.json優先)の tool_creation.personal を満たす必要がある(core/tooling/handler_memory.py)。
実行パス(LLM からどう呼ばれるか)
| モード | 典型経路 |
|---|---|
| A(LiteLLM 等) | 統合ツール use_tool(tool_name, action, args) → モジュールの dispatch(core/tooling/handler.py)。詳細は read_memory_file で各ツールのスキルを読む設計(core/tooling/schemas/skill.py の USE_TOOL)。 |
| S(Agent SDK) | Claude Code 組み込み Bash で animaworks-tool <ツール> …、または MCP 経由(MCP に載るのは厳選サブセットのみ。下記「コアツールをリポジトリに追加する場合」参照)。 |
| Anthropic フォールバック等 | build_tool_list で include_use_tool=False の構成があり得る → 外部は Bash + animaworks-tool やスキル前提。 |
起動時は上記マージ済みマップが ToolHandler に渡るため、プロセス起動前に置いた 共通・個人ツールは最初から use_tool / ExternalToolDispatcher で参照できる。セッション中に新規追加した .py だけ、refresh_tools で再スキャンしないと use_tool がツール名を認識しない(マップ未更新のため)。
手順
Step 1: ツールの設計
- ツール名(モジュール名)を決める(スネークケース、例:
my_api_tool)。animaworks-tool my_api_tool …の第1引数になる。 - アクション(サブコマンド)を決める。スキーマ名は原則
{tool_name}_{action}(例:myapi_query)。use_toolではtool_name="myapi",action="query"。 - パラメータを JSON Schema で定義する(
input_schemaまたはparameters)。
Step 2: モジュールファイルの作成
単一アクションの例
from __future__ import annotations
import logging
from typing import Any
logger = logging.getLogger(__name__)
def get_tool_schemas() -> list[dict]:
"""ツールスキーマを返す。個人・共有ツールでは必須推奨(スキーマ読み込み・ログ用)。"""
return [
{
"name": "my_tool_action",
"description": "このツールが何をするかの説明",
"input_schema": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "パラメータの説明",
},
"param2": {
"type": "integer",
"description": "オプションパラメータ",
"default": 10,
},
},
"required": ["param1"],
},
}
]
def dispatch(name: str, args: dict[str, Any]) -> Any:
"""スキーマ名に応じた処理を実行する(推奨)。"""
args.pop("anima_dir", None) # フレームワークから注入。必要なら Path(anima_dir) で利用
if name == "my_tool_action":
return _do_action(
param1=args["param1"],
param2=args.get("param2", 10),
)
raise ValueError(f"Unknown tool: {name}")
def _do_action(param1: str, param2: int = 10) -> dict[str, Any]:
return {"result": f"Processed {param1} with {param2}"}animaworks-tool から叩く場合は、このあと cli_main を必ず実装する(下記「cli_main」節)。
複数アクション + 認証(API 連携)
get_credential(credential_name, tool_name, key_name="api_key", env_var=...) の解決順序は config.json の credentials.{credential_name}(api_key または keys[key_name])→ vault.json の shared セクション(キー名は引数 env_var で渡した文字列)→ shared/credentials.json(レガシー、キーは env_var) → 環境変数 env_var(core/tools/_base.py)。
from __future__ import annotations
import logging
from typing import Any
logger = logging.getLogger(__name__)
def get_tool_schemas() -> list[dict]:
return [
{
"name": "myapi_query",
"description": "APIにクエリを送信して結果を取得する",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "検索クエリ"},
"limit": {"type": "integer", "description": "最大件数", "default": 10},
},
"required": ["query"],
},
},
{
"name": "myapi_post",
"description": "APIにデータを送信する",
"input_schema": {
"type": "object",
"properties": {
"data": {"type": "string", "description": "送信データ"},
},
"required": ["data"],
},
},
]
class MyAPIClient:
def __init__(self) -> None:
from core.tools._base import get_credential
self._api_key = get_credential(
"myapi",
"myapi_tool",
env_var="MYAPI_KEY",
)
def query(self, query: str, limit: int = 10) -> list[dict]:
import httpx
resp = httpx.get(
"https://api.example.com/search",
params={"q": query, "limit": limit},
headers={"Authorization": f"Bearer {self._api_key}"},
timeout=30.0,
)
resp.raise_for_status()
return resp.json()["results"]
def post(self, data: str) -> dict:
import httpx
resp = httpx.post(
"https://api.example.com/data",
json={"data": data},
headers={"Authorization": f"Bearer {self._api_key}"},
timeout=30.0,
)
resp.raise_for_status()
return resp.json()
def dispatch(name: str, args: dict[str, Any]) -> Any:
args.pop("anima_dir", None)
client = MyAPIClient()
if name == "myapi_query":
return client.query(query=args["query"], limit=args.get("limit", 10))
if name == "myapi_post":
return client.post(data=args["data"])
raise ValueError(f"Unknown tool: {name}")Per-Anima 認証(Chatwork 等): args.get("anima_dir") から Anima 名を取り、CHATWORK_API_TOKEN__{anima_name} のような Anima 専用キーを resolve_env_style_credential(...) で解決するパターンがある(core/tools/_chatwork_identity.py の resolve_identity 等。未登録ならフォールバックせずエラーにする)。同様のキー命名をカスタムツールでも使える。
cli_main(animaworks-tool 用)
animaworks-tool <tool_name> … はコア・共通・個人いずれも モジュールに cli_main が無いと CLI 実行不可(core/tools/__init__.py の cli_dispatch)。argparse でサブコマンドをパースし、内部で dispatch(f"{tool}_{action}", args_dict) を呼ぶ形が一般的。スキーマから用法を生成したい場合は core/tools/_base.py の auto_cli_guide も参照。
Step 3: ファイルの保存
個人ツール:
write_memory_file(path="tools/my_tool.py", content=<コード>)tool_creation.personal が許可されていること。
Step 4: ツールの有効化(ホットリロード)
プロセス起動後に tools/*.py や common_tools/*.py を追加・変更した場合のみ:
refresh_tools()同一セッション内の ExternalToolDispatcher のファイルベースマップが再スキャンされ、use_tool から新しいモジュール名が解決される(起動前から存在するファイルは通常不要)。
Step 5: 共有(任意)
share_tool(tool_name="my_tool")~/.animaworks/common_tools/ にコピーされる。tool_creation.shared が必要。他 Anima は各自 refresh_tools(または再起動時の自動発見)が必要。
必須インターフェース
| 関数 / 定数 | 必須 | 説明 |
|---|---|---|
get_tool_schemas() |
個人・共有では 強く推奨 | スキーマ読み込み・ガイド生成用。コアでも空リストのモジュールがある(例: web_search は [])。重要: ExternalToolDispatcher.dispatch(tool_use でスキーマ名を直接渡す経路)は、コアについて get_tool_schemas() の name 一覧に含まれるスキーマだけモジュールにマッチする。空のモジュールはその経路ではコア側にヒットしない。一方 use_tool は TOOL_MODULES からモジュールを直接 import して dispatch を呼ぶため、スキーマ一覧が空でも dispatch があれば実行できる。カスタムツールは両経路を意識し、通常はスキーマを定義しておくのが安全。 |
dispatch(name, args) |
推奨 | ExternalToolDispatcher._call_module が優先利用。 |
| スキーマ名と同名の関数 | 代替 | dispatch が無い場合に getattr(mod, name)(**args)。 |
cli_main(argv) |
CLI 利用時は必須 | animaworks-tool エントリ。 |
EXECUTION_PROFILE |
任意 | expected_seconds, background_eligible、コアツールでは gated: True で送信系などを許可リスト必須にできる(core/tooling/permissions.py)。 |
呼び出しとスキーマ名
use_tool:schema_name = f"{tool_name}_{action}"でモジュールのdispatch(または同名関数)に渡る。許可判定は コア:tool_registry(get_permitted_toolsの結果にtool_nameが含まれること)、ファイルベース(共通・個人): マージ済み_personal_toolsにtool_nameがあること(core/tooling/handler.pyの_handle_use_tool)。拒否メッセージにpermissions.mdと出ることがあるが、実体はload_permissions(JSON 優先) のexternal_tools。animaworks-tool: 第1トークンがsubmitの場合はバックグラウンド投入(下記)。コアはTOOL_MODULESから import してcli_main、共通・個人はファイルからロードしてcli_main。未知の第1引数はメイン CLI(animaworks)へフォールバックする場合あり(core/tools/__init__.pyの_MAIN_CLI_COMMANDS/_ANIMA_SUBCOMMANDS)。- ゲート付きサブコマンド(コアのみ):
EXECUTION_PROFILEの該当アクションに"gated": Trueがあると、permissionsの許可集合に{tool_name}_{action}(例:gmail_send)が含まれていないと CLI / ディスパッチの両方でブロックされる。ファイルベースの個人・共有ツールはTOOL_MODULESに無いため、このゲート機構の対象外。
スキーマ正規化
core/tooling/schemas/loader.py の _normalise_schema が input_schema / parameters を受け取り、内部表現では parameters に統一する。
permissions(tool_creation・外部ツール)
- 読み込み:
load_permissions(anima_dir)(core/config/schemas.py)。permissions.jsonが優先。無い場合のみpermissions.mdをパースして JSON 生成・移行(migrate_permissions_md_to_json)。 - ツール作成(JSON の例):
{
"version": 1,
"tool_creation": {
"personal": true,
"shared": false
}
}Markdown の「ツール作成」セクション(個人ツール / 共有ツール 行)も移行時に同じ構造になる。
- 外部ツール(コア):
external_toolsはget_permitted_toolsで コアTOOL_MODULESのモジュール名 と、ゲート解除用の{tool}_{action}文字列(例:gmail_send)を集める。use_toolでは、コアツールはこの集合に入った名前がtool_registry側で使われ、個人・共有ツールは起動時マージまたはrefresh_tools後の_personal_toolsに名前があればコア集合外でも実行される(コアと同名ファイルは発見時にスキップされるため衝突しない)。
EXECUTION_PROFILE
background_eligible: True:animaworks-tool submit <tool> <subcommand> …でstate/background_tasks/pending/に JSON が書かれ、PendingTaskExecutorが拾う(core/tools/__init__.pyの_handle_submit)。プロファイル参照は import 可能なコアモジュールに対してのみ実施(ファイルツールは submit 時の警告対象外になりやすい)。gated: True: コアツールの該当アクションに対し、permissions でtool_actionの明示許可が必要。
EXECUTION_PROFILE: dict[str, dict[str, object]] = {
"pipeline": {"expected_seconds": 1800, "background_eligible": True},
"send": {"expected_seconds": 15, "background_eligible": False, "gated": True},
}コアツールをリポジトリに追加する場合
core/tools/{name}.pyを追加(_始まりはスキャン対象外)。TOOL_MODULESはdiscover_core_tools()で自動登録。core/tools/__init__.pyの手動リストは不要。- Mode S(MCP) に載せるのは
core/mcp/server.pyの_EXPOSED_TOOL_NAMESのみ(厳選)。2026-03 時点の例:search_memory,read_memory_file,write_memory_file,archive_memory_file,send_message,post_channel,call_human,delegate_task,submit_tasks,update_task,create_skill。Slack / Gmail /web_search等の外部サービス系コアツールは MCP に出ない — 通常はuse_tool/ Bash(animaworks-tool)/ スキル 経路。 - テストを
tests/に追加。スキーマやリファレンス文書を自動生成している場合はscripts/generate_reference.pyの対象も確認。 - 破壊的操作は
gated: Trueと permissions 側の説明更新を検討。
バリデーションチェックリスト
- ファイル名: スネークケース、
.py、先頭_なし(スキャン対象に入れるため) -
from __future__ import annotationsを先頭に付ける(プロジェクト規約) -
get_tool_schemas()が正しいスキーマ名を返す(個人・共有) -
dispatchまたはスキーマ名関数で全スキーマを処理 -
anima_dirを使わないならargs.pop("anima_dir", None)で副作用を避ける -
cli_mainを実装しanimaworks-toolで動作確認 - 外部 HTTP には
timeout=を付ける - 認証は
get_credential(またはコアと同型の per-anima 解決) - ログは
logging.getLogger(__name__)を推奨
セキュリティ
- 秘密情報をコードに埋め込まない。
get_credential/ vault / config を使う。 - 他 Anima のディレクトリに触れない。
- コアで「書き込み・送信」系は
gatedと permissions をセットで設計する。
参考実装
- 薄いエントリ +
_client/_cli分割:core/tools/chatwork.py,slack.py,discord.py - 認証・API:
core/tools/gmail.py,github.py,notion.py,google_calendar.py,google_tasks.py - 長時間・パイプライン:
core/tools/image_gen.py(ファサード、image/サブパッケージ +EXECUTION_PROFILE) - 検索・ローカル LLM:
core/tools/web_search.py(get_tool_schemasが空 →ExternalToolDispatcher.dispatchのコア経路ではマッチしない。use_toolはdispatchで可)、x_search.py,local_llm.py - ディスパッチャ・CLI エントリ:
core/tooling/dispatch.py,core/tools/__init__.py(cli_dispatch/_handle_submit)
注意事項
- ツールは実行可能な Python。スキル(Markdown)とは別物。
- 起動後に追加したツールだけ
refresh_toolsが必要(起動前から存在するファイルは起動時スキャン済み)。 - コアと同名の個人・共有ファイルは採用されない。
use_toolのスキーマ説明(core/tooling/schemas/skill.py)にpermissions.mdとある箇所があるが、実体はload_permissions(permissions.json優先)。