Claude共有会 / 2026.05.22

AIに自制を
求めるのは
無理ゲー。

じゃあどうやって安全に使い倒すか。現場でやってる事故防止の仕組みと、これからモカの基幹システム(moz)をAIに開放するときの設計の話。専門用語は出てきたら都度説明します。

01 / 仕組みで止める

プロンプトで縛るな、
ハーネスで止めろ

ハーネス = AIを動かしている外側の仕組み(Claude Codeそのもの)。AIに「危険なコマンドは慎重に」とお願いするより、ハーネス側で物理的にブロックする方が確実です。ATMに引き出し上限があるのと同じ発想。

具体的にやってること

Claude Codeには PreToolUse Hook(コマンド実行直前に自動チェックを挟む機能)があります。私の環境では、以下のコマンドは正規表現で問答無用にブロック:

AIが「実行します」と判断しても、フックが {"decision": "block"} を返すと実行はキャンセルされる。プロンプトは破れるが、フックは破れない

実際のhook(~/.claude/settings.json)

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command');
if echo \"$CMD\" | grep -qiE '(rm\\s+-rf\\s+/|drop\\s+database|drop\\s+table|truncate\\s+table|--force\\s+push.*main)'; then
  echo '{\"decision\":\"block\",\"reason\":\"危険なコマンドを検出\"}'
else
  echo '{\"decision\":\"approve\"}'
fi"
      }]
    }]
  }
}

Auto Mode × フックの二段構え

① Auto Mode

defaultMode: auto で許可プロンプトを最小化。Claudeが止まらないので生産性は最大化。

② PreToolUse Hook

Auto Modeで自由に動いても、本当に危険なものだけは正規表現で叩き落とす。速度と安全の両立

AIに自制させるな。
ハーネスで止めろ。 // プロンプトは破れる、フックは破れない
02 / その場で実演

自分の環境に
シークレット
平文で残ってないか

シークレット = APIキー・パスワード・トークン等の認証情報。本来は暗号化された場所か環境変数に置くべきものが、設定ファイルやシェル履歴に そのまま生の文字列で残ってないか を全員でチェックします。たぶん1人1個は出ます。私自身、調査して2件見つけて冷や汗かきました。

3ステップでできる

  1. 下のプロンプトをコピー
  2. 自分のClaude Codeにペースト
  3. 結果をシェア(マスク表示されるので画面共有しても安全)

配布プロンプト

配布ファイル: ~/Downloads/secret_scan_prompt.txt

私の環境にAPIキー・パスワード・トークン・秘密鍵が
平文で残っていないかをチェックしてください。

【点検する場所】
- ~/.claude/settings.json
- ~/.claude/settings.local.json
- ~/.zshrc / ~/.bashrc / ~/.bash_profile
- ~/.zsh_history(最後の3000行)

【検出パターン】
- Anthropic API Key (sk-ant-api...)
- OpenAI / Google / AWS / GitHub / Slack のAPIキー
- WordPress App Password(4文字×6スペース区切り)
- 秘密鍵(-----BEGIN ... PRIVATE KEY-----)
- password= / passwd= の平文

【報告ルール】
- 実際の値は絶対に全文出力しない(先頭8文字+...でマスク)
- ファイルパス:行番号:種類:マスク値 の形式
- 各ヒットに修復アクション併記
- ヒットゼロなら「クリーンです」と1行で報告

こんな結果が返ってくる

シークレットスキャン結果: 2件ヒット

1. ~/.claude/settings.local.json:24
   種類: Anthropic API Key
   値: sk-ant-a...(マスク)
   対応: console.anthropic.com で revoke
         → 環境変数 ANTHROPIC_API_KEY に退避

2. ~/.zshrc:42
   種類: WordPress App Password
   値: tgln EOX...(マスク)
   対応: WP管理画面で再発行 → ~/.netrc に退避

なぜ漏れるのか(2大漏洩源)

① 許可ボタンの盲点

Claude Codeが「このコマンド許可しますか?」と聞いてくる時、コマンドにシークレットが含まれた状態で許可ボタンを押すと、そのまま settings.local.json の許可リストに焼き付く。次から聞かれず便利だけど、鍵も一緒に保存される。

② シェル履歴

ターミナルで curl -u user:pass ...export KEY=... をワンライナーで流すと、~/.zsh_history永続的に残る。Gitに上げてないからセーフ、ではない。

シークレットは
「うっかり」でしか漏れない。 // だから定期スキャンを儀式にする
03 / moz MCP化計画

基幹DBをAIに開放する
多層防御の設計

いま、モカの基幹システム(取引先・案件・財務・クリエイター65万件)を MCP(AIアプリから外部システムを操作するための共通プロトコル)で公開する計画を進めてます。便利な反面、AIが暴走したらデータが消える。だから設計時点で防御を4層重ねます。

全体アーキテクチャ

[Claude Code / Claude Desktop]
        │  stdio
        ▼
[ブリッジプロセス (Node.js)]
        │  HTTP SSE + Bearer PAT
        ▼
[Heroku: moz-mocha Laravel]
        │  /mcp エンドポイント
        │  Sanctum認証 → ability → is_admin → 実行
        ▼
[McpToolService]
        │
        ├─ PostgreSQL(DB操作)
        ├─ Redis Queue(bulk処理)
        └─ mcp_audit_logs(全書き込み記録)

防御① 認証・認可の3層構造

ユーザーごとに PAT(Personal Access Token = 個人専用トークン)を発行。トークンには ability(権限ラベル)を付けて、できる操作を限定します。

仕組み役割
1. 認証 Sanctum PAT(90日有効・ハッシュ保存) 誰のリクエストか特定
2. 認可(権限) ability制 resource:action 例: projects:write = 案件の書き込み権
3. 認可(管理者) destructive操作は is_admin=true 必須 トークン漏洩時の被害を局所化

PATが万が一漏れても、abilityが付いてなければ何もできない。さらに削除系は 管理者ユーザーしか実行できない。abilityチェックを通過しても is_admin=false なら403。

防御② 取り返しのつかない操作は2段階フロー

削除・失注マーク・入金済みマークは、いわゆる destructive 操作(戻せない操作)。AIに一発で実行させず、dry-run → 人間確認 → 本番実行を強制します。

Step 1 AIがツール呼び出し(confirmなし) dry-run実行(DBは変更しない) 「23件の関連メモが道連れで消えます」など影響範囲を返す Step 2 AIがユーザー(人間)に確認を求める ユーザー承認 Step 3 AIが confirm=true で再呼び出し 本番実行 + 監査ログ記録

AIのループ誤動作で取引先テーブルが空になる事故を、構造的に 防ぐ。

防御③ レート制限(暴走対策)

操作分類上限狙い
SAFE 読み取り300 req/分サーバー負荷防止
NORMAL 書き込み60 req/分大量誤登録の防止
DESTRUCTIVE20 req/分(固定窓)削除ループの早期遮断
bulk_create(一括)5 req/分キュー詰まり防止

防御④ 全書き込みを監査ログに残す

「誰が・いつ・どのツールを・どの引数で・dry-runか実行か」を mcp_audit_logs テーブルに全件記録。事故が起きた時に巻き戻せます。

CREATE TABLE mcp_audit_logs (
    id          BIGSERIAL PRIMARY KEY,
    user_id     BIGINT REFERENCES users(id),
    tool_name   VARCHAR(100) NOT NULL,   -- 例: projects.create
    params      JSONB,                    -- 引数(PAT・パスワードは除外)
    result      JSONB,
    dry_run     BOOLEAN DEFAULT FALSE,
    confirmed   BOOLEAN DEFAULT FALSE,
    ip          VARCHAR(45),
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);
設計の原則: MCPはあくまで既存ビジネスロジックの上に乗る薄い層。フォロー日計算は ScheduleCalculatorService 経由必須、ステータス変更メモは既存ルールを継承、クリエイター指標更新はCSVインポートのみ。バリデーションをバイパスする抜け道は作らない
04 / それで、動くの?

設計だけ語っても
説得力が弱いので、
今日 moz に実装した。

設計書を眺める会じゃ意味がない。共有会のために、最小スコープ(読み取り3ツール)で実装してきました。約3時間の作業。これから皆さんの前で動かします。

追加・変更したファイル

ファイル役割規模
app/Services/McpToolService.php3ツールのビジネスロジック約140行
app/Http/Controllers/McpController.phpJSON-RPC 2.0ハンドラ + ability check約100行
app/Console/Commands/McpIssueToken.phpPAT発行 artisanコマンド約60行
routes/api.phpPOST /api/mcp 追加+3行
app/Models/User.phpHasApiTokens trait追加+2行
composer.jsonlaravel/sanctum 追加+1依存

合計 約300行 のコードと、Sanctumライブラリ1個。これだけで基幹DBがAIから操作可能になる。

PAT発行はartisanコマンド一発

$ docker compose exec app php artisan mcp:issue-token \
    --user=1 \
    --abilities=clients:read,projects:read,financials:read

============================================================
MCP PAT を発行しました
============================================================
User       : 岡崎耕平 (id=1, is_admin=true)
Abilities  : clients:read, projects:read, financials:read
Expires    : 2026-08-20 11:11

Token (平文・1回限り表示):
1|reXabZH4GZ2iZuvuYWoOSh0xzua1zxNI3uMQCHHgb...

デモ① 経営サマリーを聞く

Question

「moz経由で、今月の売上目標と実績、達成率を教えて」

Claude の動き

[financials.get_summary を呼んだ]
{
  "year_month": "2026-05",
  "revenue_target": 19600000,
  "revenue_actual": 25270000,
  "achievement_rate_pct": 128.9,
  "gross_profit_actual": 7220000,
  "gross_profit_rate_pct": 28.6
}

→ 今月は目標1,960万に対し実績2,527万、
  達成率128.9%です(粗利率28.6%)。

デモ② 優先取引先を聞く

Question

「優先度Sの取引先を見せて」

Claude の動き

[clients.list を priority=S で呼んだ]
{
  "total": 1,
  "clients": [{
    "id": 254,
    "company_name": "ナッシュ株式会社",
    "type": "client",
    "priority": "S",
    "status": "brief_received",
    "next_follow_up_at": "2026-03-26",
    "user": { "id": 3, "name": "栗原龍司" }
  }]
}

デモ③ Ability制御の実演(ハイライト)

「鍵に権限ラベルをつける」設計が本当に効くか、その場で見せます。financials:read のみの制限トークンを発行し直して、わざと clients.list を呼ばせます。

✅ 許可された呼び出し

financials.get_summary
→ { "achievement_rate_pct": 128.9, ... }
  正常に返却

❌ ブロックされた呼び出し

clients.list
→ {
  "error": {
    "code": -32003,
    "message": "Forbidden:
      このトークンには
      clients:read の権限が
      ありません"
  }
}

これが「PAT × Ability × is_admin の3層」の実物。トークンが漏れても、abilityがなければ何もできない。

今日の実装に入ってないもの(次フェーズ)

05 / Takeaway

持ち帰り4行

明日から自分のClaude Code環境に効く話。

01

AIに自制させるな、ハーネスで止めろ

PreToolUse hookで危険コマンドを正規表現ブロック。プロンプトは破れるがフックは破れない。

02

シークレットスキャンを儀式にする

月1で配布プロンプトを流すだけ。許可ボタンとシェル履歴という2大漏洩源を定期点検。

03

PAT × Ability × is_admin の3層認可

destructive操作だけ管理者必須にすれば、トークン漏洩時の被害が「読み取り+一部書き込み」で止まる。

04

Dry-run → Confirm の2段階フロー

削除系はAIに一発で実行させない。「23件が消えます」を人間に見せてから実行する。