AI Agent Integration
Kovaは、AIエージェント(Claude Code、GPT、Gemini等)がウォレット機能を使えるよう設計されています。Agent Skills標準に準拠したスキル集を使用することで、自然言語でウォレット操作が可能になります。
AIエージェント統合の仕組み
AIエージェントとKovaの統合は、Skillsレイヤーを介して行われます:
Agent Skills標準とは
Agent Skills標準は、AIエージェントが外部ツールを呼び出すための標準仕様です。Kovaは、この標準に準拠したスキル集を提供しています。
主な特徴:
- 自然言語トリガー - ユーザーの発言から自動的にスキルを起動
- JSON出力 - 構造化されたデータで結果を返却
- エラーハンドリング - エラーを検出し、適切なメッセージを表示
- プラットフォーム非依存 - Claude Code、GPT、Gemini等で動作
インストール方法
Claude Code
kova skills コマンドでスキルをインストールできます:
# スキルをインストール
kova skills install
# インストール済みスキルを確認
kova skills listGPT/Gemini
各プラットフォームのスキルディレクトリに配置してください。詳細は、各プラットフォームのドキュメントを参照してください。
OpenClaw
kova skills install でインストールしたスキルをシンボリックリンクで参照:
mkdir -p ~/.openclaw/skills
cd ~/.openclaw/skills
ln -s ~/.kova/skill-repos/base-skills/skills/* .利用可能なスキル
Kovaは、以下のスキルを提供しています:
1. create-wallet
トリガー:
- “ウォレットをセットアップして”
- “kova の初期設定をして”
実行内容:
kova initウォレットは
kova initの対話フローで作成されます。v0.x はシングルウォレット固定です。
出力例:
{
"ok": true,
"data": {
"wallet": {
"id": "68cf6dba-e5f1-4c3a-9b2e-1234567890ab",
"accounts": [
{
"chainId": "eip155:8453",
"address": "0xB8EC761bf83B4374877e903d217222F2cd5512De"
}
]
}
}
}2. check-balance
トリガー:
- “残高を確認して”
- “いくら持ってる?”
- “my-wallet の Base での USDC 残高を確認して”
実行内容:
kova balance出力例:
{
"ok": true,
"data": {
"rows": [
{
"chain": "base",
"tokenKey": "USDC",
"symbol": "USDC",
"balance": "100.5",
"status": "ok"
}
]
}
}3. send-tokens
トリガー:
- “送金して”
- “XXX USDCを送って”
- “0x1234…abcd に 10 USDC を送信して”
実行内容:
kova send --name <name> --to <address> --amount <amount> --token <symbol> --broadcast出力例:
{
"ok": true,
"data": {
"dryRun": false,
"txHash": "0x1234567890abcdef...",
"explorerUrl": "https://basescan.org/tx/0x1234567890abcdef..."
}
}4. manage-config
トリガー:
- “デフォルトウォレットを設定して”
- “設定を変更して”
実行内容:
kova config set <key> <value>出力例:
{
"ok": true,
"data": {
"key": "defaultWallet",
"value": "my-wallet"
}
}エラーハンドリング
スキルは、CLIの出力JSONをパースしてエラーを検出します。
共通エラー
| エラーコード | 原因 | 対処法 |
|---|---|---|
WALLET_NOT_FOUND | ウォレットが存在しない | kova wallet info で確認 |
INSUFFICIENT_BALANCE | 残高不足 | balance で残高確認 |
INVALID_PARAMS | パラメータ不正(無効なアドレス・重複ウォレット名・型不一致など) | エラーメッセージで該当箇所を確認 |
CHAIN_NOT_SUPPORTED | チェーンが未対応 | サポートチェーン を確認 |
POLICY_DENIED | ポリシーに違反 | policy list で制限内容を確認 |
CREDENTIAL_REQUIRED | agent 経路で credential 未設定 | kova init で発行 |
エラーレスポンス例
{
"ok": false,
"error": {
"code": "WALLET_NOT_FOUND",
"message": "Wallet 'my-wallet' not found. Available wallets: []"
}
}スキルは、エラーコードに基づいて適切なメッセージをユーザーに表示します。
TypeScript統合例
AIエージェントをTypeScriptで実装する場合の例:
interface BalanceResult {
ok: boolean;
data?: {
rows: Array<{
chain: string;
tokenKey: string;
symbol: string;
balance: string | null;
status: 'ok' | 'empty' | 'error';
}>;
};
error?: {
code: string;
message: string;
};
}
async function checkBalance(): Promise<BalanceResult> {
try {
// kova balance は引数なし。全チェーン × 主要トークンのサマリーを返す。
// 非 TTY (パイプ) 経路なので JSON が返る。
const { execFileSync } = require('child_process');
const output = execFileSync('npx', [
'@komlock_lab/kova',
'balance',
], { encoding: 'utf-8' });
return JSON.parse(output);
} catch (error) {
return {
ok: false,
error: {
code: 'UNKNOWN_ERROR',
message: error.message,
},
};
}
}
// 使用例: 全チェーンのサマリーから Base の USDC を取り出す
async function main() {
const result = await checkBalance();
if (result.ok) {
const usdcOnBase = result.data.rows.find(
(r) => r.chain === 'base' && r.symbol === 'USDC',
);
console.log(`Base USDC 残高: ${usdcOnBase?.balance ?? '0'}`);
} else {
console.error(`エラー: ${result.error.code} - ${result.error.message}`);
}
}
main();dry-runとbroadcastの使い分け
送金スキルでは、dry-runとbroadcastの2段階フローを推奨しています。
推奨フロー
async function sendWithApproval(to: string, amount: string, token: string) {
const { execFileSync } = require('child_process');
// 1. dry-run(セキュリティ: 配列引数でコマンドインジェクション対策)
const dryRunResult = JSON.parse(
execFileSync('npx', [
'@komlock_lab/kova',
'send',
'--name',
'my-wallet',
'--to',
to,
'--amount',
amount,
'--token',
token,
], { encoding: 'utf-8' })
);
if (!dryRunResult.ok) {
console.error(`dry-runエラー: ${dryRunResult.error.message}`);
return;
}
// 2. ユーザー確認
console.log("送金内容を確認してください:");
console.log(JSON.stringify(dryRunResult.data.tx, null, 2));
const approved = await askUserApproval(); // ユーザーの承認を得る関数
if (!approved) {
console.log("送金をキャンセルしました");
return;
}
// 3. broadcast(セキュリティ: 配列引数でコマンドインジェクション対策)
const result = JSON.parse(
execFileSync('npx', [
'@komlock_lab/kova',
'send',
'--name',
'my-wallet',
'--to',
to,
'--amount',
amount,
'--token',
token,
'--broadcast',
], { encoding: 'utf-8' })
);
if (result.ok) {
console.log(`送金成功: ${result.data.explorerUrl}`);
} else {
console.error(`送金エラー: ${result.error.message}`);
}
}ポリシーの設定
AIエージェントに使わせる場合は、必ずポリシーを設定してください。
ポリシー作成
policy.json:
{
"name": "AIエージェント用ポリシー",
"maxAmount": "100",
"allowedChains": ["eip155:8453"],
"allowedTokens": ["USDC"],
"requireApproval": true
}# ポリシー作成
kova policy create --file policy.json
# API鍵ローテーション(初回は kova init で自動発行される)
kova key rotate --name agent-key
# 発行された credential を環境変数に設定
export KOVA_CREDENTIAL=kova_xxxxxエージェントへの認証情報の渡し方
kova init を実行すると、エージェントキーが ~/.kova/config.json に保存されます。Claude Code などから呼ばれる kova プロセスはこのファイルから credential を直接読み取るため、通常は環境変数を手動で設定する必要はありません。claude を普段どおり起動するだけで agent モードで動作します。
claude任意: 環境変数を sanitize して起動する
子プロセスへ渡す環境変数を明示的に制御したい場合は、kova launch ラッパを使えます(任意)。KOVA_CREDENTIAL / KOVA_API_KEY_ID を注入した状態でエージェントを起動します。
kova launch claude --model claude-sonnet-4-5プログラムから kova を呼び出す場合
スクリプトやサービスから kova を起動する場合は、環境変数で credential を渡します。⚠️ API key をコードにハードコードしないでください。
export KOVA_CREDENTIAL="cred_xxxxxxxxxxxxxxxxxxxxx"
export KOVA_API_KEY_ID="ak_1234567890abcdef"// ❌ BAD: ハードコードは避ける(コミットミスで漏洩リスク)
process.env.KOVA_CREDENTIAL = 'cred_xxxxx';
// ✅ GOOD: 環境変数から読み取る
const credential = process.env.KOVA_CREDENTIAL;
if (!credential) {
throw new Error('KOVA_CREDENTIAL not set');
}API key を環境変数に設定することで、CLIコマンドは自動的にポリシー評価を行います:
const { execFileSync } = require('child_process');
// ポリシー評価付きで送金(環境変数から credential 読み取り)
const result = execFileSync('npx', [
'@komlock_lab/kova',
'send',
'--name',
'my-wallet',
'--to',
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
'--amount',
'150',
'--token',
'USDC',
'--broadcast',
], { encoding: 'utf-8' });
// ポリシー違反の場合はエラー
// {
// "ok": false,
// "error": {
// "code": "POLICY_DENIED",
// "message": "Transaction exceeds policy maxAmount: 100 USDC"
// }
// }本番環境での運用
推奨事項
- dry-runの徹底 - 必ずdry-runで確認してから
--broadcast - ユーザー承認フロー - 高額送金は必ず人間の承認を得る
- ポリシー設定 - 必ずポリシーを設定し、最小権限の原則を適用
- ログの記録 - すべてのトランザクションをログに記録
- 異常検知 - 異常なトランザクションパターンをアラート
- プロンプト汚染対策 - 入力のバリデーション、出力の検証を実施
ログ記録の実装例
import * as fs from 'fs';
function logTransaction(tx: any) {
const log = {
timestamp: new Date().toISOString(),
tx,
};
fs.appendFileSync(
'./transaction-log.jsonl',
JSON.stringify(log) + '\n'
);
}
async function sendWithLogging(to: string, amount: string, token: string) {
const { execFileSync } = require('child_process');
// セキュリティ: 配列引数でコマンドインジェクション対策
const result = JSON.parse(
execFileSync('npx', [
'@komlock_lab/kova',
'send',
'--name',
'my-wallet',
'--to',
to,
'--amount',
amount,
'--token',
token,
'--broadcast',
], { encoding: 'utf-8' })
);
// ログ記録
logTransaction(result);
return result;
}異常検知の実装例
function detectAnomalies(tx: any): boolean {
// 高額送金の検出
if (parseFloat(tx.amount) > 1000) {
console.warn(`高額送金検出: ${tx.amount} ${tx.token}`);
return true;
}
// 短時間での連続送金の検出
const recentTxs = getRecentTransactions(60); // 直近60秒
if (recentTxs.length > 5) {
console.warn(`連続送金検出: ${recentTxs.length}件`);
return true;
}
return false;
}
async function sendWithAnomalyDetection(to: string, amount: string, token: string) {
const tx = { to, amount, token };
if (detectAnomalies(tx)) {
// 人間の承認を求める
const approved = await askHumanApproval(tx);
if (!approved) {
return { ok: false, error: { code: 'ANOMALY_DETECTED', message: 'Transaction blocked by anomaly detection' } };
}
}
// 通常の送金フロー
return sendWithApproval(to, amount, token);
}プロンプト汚染の防御
AIエージェントは、悪意のあるプロンプト注入攻撃に脆弱です。以下の対策を推奨します:
入力のバリデーション
function validateAddress(address: string): boolean {
// Ethereum アドレス形式チェック
const addressRegex = /^0x[a-fA-F0-9]{40}$/;
return addressRegex.test(address);
}
function validateAmount(amount: string): boolean {
// 正の数値チェック
const num = parseFloat(amount);
return num > 0 && !isNaN(num);
}
async function safeSend(to: string, amount: string, token: string) {
// バリデーション
if (!validateAddress(to)) {
return { ok: false, error: { code: 'INVALID_PARAMS', message: 'Invalid Ethereum address' } };
}
if (!validateAmount(amount)) {
return { ok: false, error: { code: 'INVALID_PARAMS', message: 'Invalid amount' } };
}
// 送金実行
return sendWithApproval(to, amount, token);
}出力の検証
function verifyOutput(output: any): boolean {
// JSON構造チェック
if (!output.ok || !output.data) {
return false;
}
// トランザクションハッシュ形式チェック
if (output.data.txHash && !/^0x[a-fA-F0-9]{64}$/.test(output.data.txHash)) {
return false;
}
return true;
}次のステップ
- Send and Receive - 送金・受取ガイド
- Core Concepts: Security - セキュリティのベストプラクティス
- Reference: Skills - スキルの詳細リファレンス