Google Workspace Admin Security-Audit MCP について
Google Workspace Admin Security-Audit MCPは、Workspace管理者がアカウントリスクやテナント変更を調査するための監査サーバーです。監査・ディレクトリ情報にGmailやDriveの確認を組み合わせ、複数ドメインの日次サマリーを提供します。ユーザーのサインアウトのみ、明示的な有効化が必要な状態変更です。
主な機能
- 01ログイン、アカウント活動、管理者操作の監査
- 02停止中アカウントとOAuth許可の確認
- 03Drive共有とドキュメント変更の追跡
- 04活動とメール設定による侵害調査
- 05複数ドメインのセキュリティ情報の集約
- 06オプションでのユーザーサインアウト
活用シーン
- 01不審なログインとアカウント保護設定の変更確認
- 02外部ファイル共有と共有ドライブのメンバー変更の調査
- 03侵害されたアカウントの活動と永続化設定の追跡
- 04テナント監査データとWorkspaceインシデントの照合
MCP 対応
MCPクライアントにGoogle Workspaceのセキュリティ監査ツールを提供します。Admin SDK Reports APIとDirectory APIを使い、指定された調査ではGmailなどのWorkspace APIも利用します。大半は読み取り専用で、ユーザーのサインアウトは明示的な有効化時のみ登録されます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
16掲載時のツール一覧 · 3 日前に更新
このツールにパラメーターはありません。
詳細
gwsadm-mcp
English | 日本語
Google Workspace のセキュリティ監査用 MCP(Model Context Protocol)サーバーです。 Admin SDK Reports API(監査アクティビティ)を使用し、アカウントのロック、不審なログイン、外部へのファイル共有を読み取り専用で可視化します。
管理コンソールの視点にちなんで名付けられました(gwsadm = Google Workspace admin)。boxadm-mcp の姉妹プロジェクトです。これは汎用 Workspace MCP ではありません。リスクを可視化しますが、変更は行いません。ただし、オプトインの例外として sign_out_user があります。これは、サーバーを GWSADM_ENABLE_SIGN_OUT=1 を設定して起動した場合にのみ利用できます。
機能
| ツール | 説明 |
|---|---|
health_check | サーバーのバージョン、設定パス、ドメインごとの認証プローブを確認します。セッション開始時またはタイムアウト後に呼び出します |
login_audit | Reports API の login をレポートします。Google によって自動的に無効化されたアカウント(account_disabled_*:パスワード漏えい、乗っ取り、スパム送信)、不審なログイン、失敗件数の上位 N 件 |
gmail_usage_report | Reports API の customerUsageReports をレポートします。ドメインごとの Gmail の日次送受信件数を、API 独自の UTC-8:00/PST 日付基準で、昨日までの各日について一日ずつ取得します。別途 admin.reports.usage.readonly DWD スコープが必要です(以下の認証モデルを参照)。どちらも Reports API 用ですが、admin.reports.audit.readonly とは異なる権限付与です |
suspended_accounts | Directory API:停止中アカウント(isSuspended=true)の現在のスナップショットです。下流の IdP(KeyCloak など)と照合し、停止されているのに引き続き有効なアカウントを見つけます |
get_user | Directory API の users().get:指定した 1 つのアカウントの現在の状態を確認します。suspended(理由と時刻を含む)、archived、last_login、2SV の登録/強制適用、組織部門、作成時刻、保留中のパスワード変更を確認できます。「このユーザーがサインインできない理由」を調べるためのものです。既知のメールアドレスに対して、ページネーションなしで 1 回のリクエストで確認できます。停止中のアカウントだけを列挙する suspended_accounts とは異なり、特定のアドレスが 停止されていない ことは確認できません(また、一覧がページ上限を超えると、どちらの意味でも不在は証拠になりません)。suspended_accounts がすでに使用しているもの以外にスコープは不要です |
user_oauth_tokens | Directory API の tokens().list:1 人のユーザーに付与されたサードパーティ OAuth アプリの権限です。以前に付与されたトークンは新たなログインを必要としないため、login_audit では検出できない侵害経路です。ドメインはユーザー名のサフィックスから解決されます。エイリアスやセカンダリドメインのアドレスには、任意で domain を指定して上書きできます |
sign_out_user | 状態を変更するオプトイン機能です。 Directory API の users().signOut を使い、users().get でアカウントの存在を確認した後、1 つのアカウントをすべてのウェブセッションとデバイスセッションからサインアウトさせ、サインイン Cookie をリセットします。サーバー起動時に GWSADM_ENABLE_SIGN_OUT=1 が指定されている場合にのみ登録されます。実行前に承認手順を設けてください。OAuth 権限やアプリパスワードの取り消し、登録済みの第 2 要素の削除、パスワードの変更は行いません(これらは not_covered に記載されています)。2 つの権限付与が必要です。アカウント確認用に admin.directory.user.readonly(get_user と同じ)、サインアウト自体に admin.directory.user.security(user_oauth_tokens と同じ)が必要です。偽装する管理者には、ユーザーセキュリティを管理する権限も必要です。毎回同じキーで、signed_out は true(Google が確認)、false(変更なし)、または null(結果不明:リクエスト送信済み、期限内に応答なし)を返し、GWSADM_SIGN_OUT_DEADLINE_SECONDS(既定値は 45)以内に終了します。アドレス自身のドメインを設定する必要があります。domain を指定する場合は、そのドメインと一致していなければなりません。 |
user_activity | Reports API の activities().list に userKey=<username> を指定し、期間内に1 つのアカウントが行ったすべての操作を、アプリケーションをまたいで新しい順にまとめて返します(既定では login、token、user_accounts、drive、groups、calendar、chat、meet、saml)。flags は件数にかかわらず、アカウントのセキュリティ状態を変えるイベントを表示します。2SV の登録/無効化、復旧用メールアドレス/電話番号の変更、パスワード変更、外部転送、Google による risky_sensitive_action_*/suspicious_login の判定、クライアントとスコープを含むすべての OAuth authorize が対象です。攻撃者が登録した第 2 要素はパスワードをリセットしても有効なままだからです。ips は、最初と最後に確認された時刻を含む送信元アドレスの一覧です。Google 自身による account_disabled_*/suspicious_login の判定は、アクターが Google であるため、アカウントの userKey では提供されません。そのため、login_audit と同様の方法で取得し、合成アプリケーション login_verdicts の下にレポートします。login_audit では答えられない侵害後の疑問に答えます。login_audit が確認するのは、Google 自身がドメイン全体に対して検出したイベントだけです |
ip_activity | Reports API の activities().list に actorIpAddress=<ip> を指定し、1 つの送信元アドレスが各ドメインおよびアプリケーションで操作したすべてのアカウントを、同じ flags とともに返します。user_activity で攻撃者のアドレスが判明した後の追跡調査に使います。同じ送信元から他のアカウントにもアクセスがあったかを確認できます。総当たり攻撃は、成功する前から login_failure の件数に現れます |
compromise_sweep | 1 つのアカウントの侵害が確認された後に Google 側で行う追跡調査を、1 回の呼び出しで実行します。activity(user_activity、フラグを優先)、state/grants(get_user。メール/Drive/連絡先/管理者スコープを持つ OAuth 権限は risky として明示)、ips(攻撃者のアドレスを指定するか、アカウント自身のフラグから取得し、アカウント自体を除外して全ドメインにわたり ip_activity を実行)、gmail(攻撃者がメールを読み続けるために残す設定:自動転送、転送先アドレス、転送/削除/アーカイブするフィルター、代理人、差出人名義と返信先。加えて、期間内にメールボックスから送信されたメールの件数と宛先ドメイン。件名や本文は取得しません)。gmail.kind は読み取りの可否を示します。ok/partial(設定セクションまたは送信概要のいずれかが失敗しましたが、残りの結果は有効です)/api_disabled(サービスアカウント自身の Cloud プロジェクトで Gmail API が無効です)/no_grant/error のいずれかです。失敗は必ず報告され、「何も見つからなかった」として扱われることはありません。highlights には対応すべき事実、checklist には手作業で残っている項目が記載されます。gmail_message_trace と同じ gmail.readonly DWD スコープを使用します(delegates.list を含むすべての設定読み取りに利用できることを実環境で確認済みです) |
drive_external_sharing | Reports API の drive:外部アドレスまたはドメインへの ACL の付与(取り消しは別途報告)と、リンク/公開範囲への移行を確認します |
drive_doc_activity | Reports API の drive にサーバー側の doc_id フィルターを指定し、1 つのドキュメントの所有者、ACL の変更、ライフサイクルイベントを確認します。drive_external_sharing のトリアージを補助します。所有者が個人か共有ドライブ名かによって、共有ドライブの誤検出を判別できます。共有ドライブ内で作成されたファイルにはメンバーの ACL が引き継がれるため、一括での外部共有のように見えることがあります |
shared_drive_membership_changes | Reports API の drive(shared_drive_membership_change):共有ドライブのメンバーを誰がいつ追加/削除/ロール変更したかを、対象メンバーが外部ユーザーかどうかの分類と、クライアント側のドライブ名フィルターとともに確認します |
admin_actions | Reports API の admin:管理者(ユーザー、または管理者として動作する同期処理や API クライアント)が Admin コンソールで行った操作を確認します。1 人のユーザーまたは 1 つのグループに対する操作(対象のドメインでサーバー側の USER_EMAIL/GROUP_EMAIL フィルターを使用)、またはテナント全体に対する操作が対象です。user_activity では確認できない「このアカウントを停止した/パスワードをリセットした/グループから削除したのは誰か」という疑問に答えます。管理者イベントのアクターは管理者であり、操作されたアカウントではないためです |
workspace_status | Google Workspace Status Dashboard(公開フィード、認証情報不要):現在発生中のインシデントと、期間内に終了したインシデントを確認します。任意で 1 つのサービス(Gmail、Drive、…)に絞り込めます。テナント側の調査に入る前に「Google 側の問題か」を確認できます |
gmail_message_trace | Gmail API:既知の Message-ID が特定のメールボックスに届いたか、またどこにあるか(受信トレイ/迷惑メール/ゴミ箱/アーカイブ済み)を確認します。受信者ごとに DWD を使ってそのユーザーになりすまし、ユーザー自身のメールボックスを検索します。別途 gmail.readonly DWD スコープが必要です(以下の認証モデルを参照)。この権限がないドメインでは、受信者ごとにエラーを報告し、誤って「見つからない」とは報告しません |
dmarc_rua_summary | Gmail API:ドメインごとの DMARC 集計(RUA)レポートについて、認証成功/失敗の概要と、拒否候補となる送信元 IP の上位を確認します。ドメインで設定された dmarc_rua_mailbox(実在するユーザー。既定値は postmaster@<domain>)になりすまし、dmarc_rua_recipient(公開されている rua= アドレス。例:postmaster+rua@。既定値はメールボックス)宛てのメールを検索し、それらのメールに添付された圧縮レポートを読み取ります。gmail_message_trace と同じ gmail.readonly DWD スコープを使用しますが、このツールはメタデータだけでなく添付ファイルの内容(レポート XML)も読み取ります。以下の認証モデルを参照してください |
group_delivery_policy | Groups Settings API:Google グループ独自の投稿/配信ポリシー(who_can_post、allow_external_members、モデレーションレベル)を確認します。グループのアクセス制御は Gmail の配信の前に適用されます。ドメイン内に限定した投稿ポリシーでは、外部送信者のメールが Gmail の配信イベントを生成する前に黙って破棄されます。そのため、ポリシーを直接読み取らなければ、配信失敗と区別できません。別途 apps.groups.settings DWD スコープが必要です(以下の認証モデルを参照) |
list_group_members | Directory API:Google グループの基本メタデータとメンバー一覧を、特定のメッセージをたまたま受信したユーザーから推測せず、直接取得します。別途 admin.directory.group.readonly と admin.directory.group.member.readonly の DWD スコープが必要です(以下の認証モデルを参照) |
daily_brief | 設定済みの全ドメインを対象とする 1 回の呼び出しでの概要です。アカウント保護に関する変更(2 段階認証、パスキー、Advanced Protection、パスワード、復旧用アドレス/電話番号、ドメイン外へのメール転送)を含み、自国(GWSADM_IP2ASN、GWSADM_HOME_COUNTRIES)以外から発生した場合、発信元を特定できない場合、または不審なログインに続いて発生した場合にフラグを付けます |
daily_brief_start / daily_brief_result | バックグラウンドで実行する daily_brief と同じ機能です。start はすぐに job_id を返し、その後 result(job_id) を done になるまでポーリングします。同期呼び出しではクライアントの約 60 秒のツール呼び出しタイムアウトに達するおそれがある、大規模なテナントで使用します |
予定:dlp_events(rules を報告します。DLP を利用できる Workspace エディションが必要です)、 | |
token_events。 |
認証モデル
監査権限を持つ管理者になりすます、**ドメイン全体の委任(DWD)**を設定したサービスアカウントを使用します。完全に非インタラクティブで、ブラウザーもトークンの更新ローテーションも必要ありません。そのため、サーバーを無人で実行できます(cron、MCP ゲートウェイ、CI)。
以下の DWD スコープをすべて、同じサービスアカウントのクライアント ID に対して、セットアップ時に一度に付与してください。ツールを作るたびに個別に追加すると、必要なツールで不足するまでスコープの欠落に気付かないことがあります。1 か所で一度に設定すれば、この問題を避けられます。
| スコープ | 必要とするツール | 不足している場合 |
|---|---|---|
https://www.googleapis.com/auth/admin.reports.audit.readonly | login_audit、user_activity、ip_activity、admin_actions、drive_external_sharing、drive_doc_activity、shared_drive_membership_changes、daily_brief* | これらのツールはドメインごとのエラーにより機能が低下します |
https://www.googleapis.com/auth/admin.directory.user.readonly | suspended_accounts、get_user、sign_out_user(オプトイン。そのアカウント確認に使用) | これらのツールはエラーにより機能が低下します(suspended_accounts はドメインごと)。その他は引き続き動作します |
https://www.googleapis.com/auth/admin.directory.user.security | user_oauth_tokens、sign_out_user(オプトイン) | これらのツールはドメインごとのエラーにより機能が低下します。その他は引き続き動作します |
health_check が応答するのにスコープは一切必要ありません。付与漏れが疑われる場合に呼び出すツールで、各ドメインを調査し、自身が失敗するのではなく、認証エラーをドメインごとの構造化された結果として報告します。
gmail_usage_report にも、上記の基本設定と同じ Admin SDK Reports API 配下にあるにもかかわらず、別個のスコープが必要です。「Usage report」ファミリー(customerUsageReports)と「Audit」アクティビティストリーム(activities().list。基本設定のその他すべて)は異なるスコープで制御されており、一方を持っていても、もう一方が付与されていることにはなりません。
| スコープ | 必要とするツール | 不足している場合 |
|---|---|---|
https://www.googleapis.com/auth/admin.reports.usage.readonly | gmail_usage_report | このツールはドメインごとのエラーにより機能が低下します。その他は引き続き動作します |
gmail_message_trace と dmarc_rua_summary には、さらにもう一つスコープが必要です。これは別の手順で付与してください。意図的に上記の設定には含めていません。
| スコープ | 必要とするツール | 不足している場合 |
|---|---|---|
https://www.googleapis.com/auth/gmail.readonly | gmail_message_trace、dmarc_rua_summary、compromise_sweep(その gmail セクション) | これらのツールは宛先ごと/ドメインごとのエラーを報告します。その他は引き続き動作します |
これは上記の3つよりも大幅に広い権限で、メタデータだけでなく、サービスアカウントがなりすますユーザーのメッセージ本文を読み取れます。gmail_message_trace が要求するのは常に format="metadata" で、メッセージ本文を読むことはありません。一方、dmarc_rua_summary は本文を読み取ります。各 RUA メッセージに含まれる圧縮 DMARC レポート添付ファイルを取得し(format="full" と attachments().get())、解析します。どちらも付与された権限の範囲内で動作しますが、「メタデータのみ」というより限定的な使い方を守るのは gmail_message_trace だけです。より狭い gmail.metadata スコープは検討しましたが、両ツールで却下しました。必要な rfc822msgid:/RUA メールボックスの検索で使う q= 検索パラメーターに対応していないためです。他のスコープと同じサービスアカウントのクライアント ID に対してこのスコープを付与し(Admin コンソール → セキュリティ → API の制御 → ドメイン全体の委任 → 既存のクライアント ID を検索 → そのリストにこのスコープを追加)、対象ドメインで有効にする前に、メッセージ追跡/DMARC レポートが実際にどれほど必要かと、権限範囲の広さを比較検討してください。
group_delivery_policy と list_group_members にも、それぞれ別個のスコープが必要です。基本設定に加えてさらに3つ付与しますが、相互にも、上記の gmail.readonly ともまとめて付与しません。
| スコープ | 必要な対象 | スコープがない場合 |
|---|---|---|
https://www.googleapis.com/auth/apps.groups.settings | group_delivery_policy | このツールはエラーになりますが、それ以外はすべて引き続き動作します |
https://www.googleapis.com/auth/admin.directory.group.readonly | list_group_members(グループメタデータ側) | その側は独自のエラーを報告します。下記の独自スコープが許可されていれば、メンバー一覧側は引き続き独立して動作します |
https://www.googleapis.com/auth/admin.directory.group.member.readonly | list_group_members(メンバー一覧側) | 上記のメタデータ側とは独立して同様に動作します。2 つの呼び出しが互いを妨げることはありません |
Groups Settings API は Directory API とは別の製品であるため、スコープも別です。
読み取り専用のスコープはありませんが、このサーバーが呼び出すのは
groups().get() のみで、変更を伴うメソッドは呼び出しません。
suspended_accounts、get_user、user_oauth_tokens は、顧客全体を対象とする
Reports ツールとは異なり、設定済みのドメインごとに動作します(Directory の
domain=/userKey=)。そのため、対象に含めたいドメイン(別の生徒用ドメインなど)
ごとに、専用の [domain.*] 設定セクションが必要です。なお、失敗時の動作は異なります。
suspended_accounts は未設定のドメインを結果から通知なく除外しますが、
get_user と user_oauth_tokens は不明なドメインのエラーを明示的に返します
(どちらも、末尾に対応するセクションがない別名/セカンダリアドレスに対して、
domain を上書き指定できます)。
セットアップ
# uv
uv pip install gwsadm-mcp
# pip
pip install gwsadm-mcp
または、ソースから:
git clone https://github.com/shigechika/gwsadm-mcp.git
cd gwsadm-mcp
# uv
uv sync
# pip
pip install -e .
設定
GWSADM_CONFIG に INI ファイルを指定します(デフォルトは ~/.config/gwsadm-mcp/config.ini、
パーミッションは 0600 にしてください)。
[gwsadm]
# optional; defaults to all [domain.*] section names
internal_domains = example.edu, mail.example.edu
[domain.example.edu]
service_account_file = /path/to/service-account.json
subject = [email protected]
customer_id = C0xxxxxxx
dmarc_rua_mailbox = [email protected] # optional, default: postmaster@<domain>; "none" opts out
dmarc_rua_recipient = [email protected] # optional, default: same as dmarc_rua_mailbox
監査対象の Workspace ドメインごとに、[domain.*] セクションを 1 つ設定します。
internal_domains は、共有先を内部と外部に分類するための許可リストです。
dmarc_rua_mailbox は、DMARC 集計レポートを読み取るために
dmarc_rua_summary が偽装する実在ユーザーです。ドメイン全体の委任で偽装できるのは
実在ユーザーのみで、グループやエイリアスにはできません。dmarc_rua_recipient は
レポートの送信先アドレス(ドメインの _dmarc レコードで公開されている
rua=mailto: の値)で、Gmail 検索を絞り込むためだけに使用されます(to:<recipient>)。
デフォルト値はメールボックスです。公開されているアドレスが postmaster+rua@ のような
Gmail のプラスサブアドレスの場合(このアドレスで検索することで、postmaster+ruf@ に
送信された ruf= の障害レポートが集計解析に含まれるのも防げます)、または偽装先の
受信トレイに転送するグループの場合に設定してください。dmarc_rua_mailbox = none を
設定すると、そのドメインの DMARC レポート読み取りを無効にできます。たとえば、
rua= が別ドメインのメールボックスを指しており、そのメールボックスを別の
[domain.*] セクションですでに読み取っている場合に使用します。レポートは各レポートに
記載されたポリシードメインごとに分類されるため、引き続きその別のセクションに表示されます。
環境変数(特に記載がない限り、すべて任意):
| 変数 | デフォルト | 意味 |
|---|---|---|
GWSADM_CONFIG | ~/.config/gwsadm-mcp/config.ini | 上記の INI ファイル |
GWSADM_MAX_WORKERS | 8 | ファンアウトで同時に実行する Reports API の取得数(1~32 の範囲に制限されます。整数以外を指定すると 8 に戻ります)。テナントでスロットリングが発生する場合は減らし、大規模なテナントのスキャンが遅すぎる場合は増やしてください |
GWSADM_SITES_INI | — | アカウント保護の変更元を特定するための、独自のアドレス範囲(セットアップドキュメントを参照) |
GWSADM_IP2ASN / GWSADM_HOME_COUNTRIES | — | 海外からの変更を検出するための iptoasn テーブルと本拠地の国(セットアップドキュメントを参照) |
GWSADM_ENABLE_SIGN_OUT | 未設定 | 1 を設定すると、状態を変更する sign_out_user が登録されます |
GWSADM_SIGN_OUT_DEADLINE_SECONDS | 45 | sign_out_user 1 回の呼び出しに割り当てる時間(10~300 秒の範囲に制限されます) |
使い方
Claude Code(プラグイン)
このリポジトリは単一プラグインのマーケットプレイスも兼ねているため、Claude Codeからサーバーをインストールできます。
/plugin marketplace add shigechika/gwsadm-mcp
/plugin install gwsadm-mcp@gwsadm-mcp
このプラグインは uvx gwsadm-mcp を起動し、GWSADM_CONFIG を読み込みます(設定されていない場合は ~/.config/gwsadm-mcp/config.ini を使用します)。これは設定で説明しているものと同じ変数です。/plugin install で設定されるのはサーバープロセスの起動のみです。設定INIや、参照先のGoogle CloudサービスアカウントJSONキーは作成できません。ツール呼び出しを成功させるには、プラグインを実行するマシン上に両方があらかじめ存在している必要があります。
Claude Codeを実行するプロセスの PATH に uvx が含まれている必要があります。通常、ログインシェルには含まれていますが、GUIから起動したアプリには含まれていない場合があります。プラグインが起動しない場合は、uvをシステム全体にインストールしてください。
Claude Code(手動)
.mcp.json に追加します(設定がデフォルトのパスにある場合、env は不要です。デフォルト以外の場所にある場合のみ "env": { "GWSADM_CONFIG": "..." } を追加してください)。
{
"mcpServers": {
"gwsadm-mcp": {
"type": "stdio",
"command": "gwsadm-mcp"
}
}
}
Claude Desktop
同じ項目を claude_desktop_config.json に追加します。
Codex
codex mcp add gwsadm-mcp -- gwsadm-mcp
設定INIがデフォルトのパス(~/.config/gwsadm-mcp/config.ini)にない場合のみ、--env GWSADM_CONFIG=... を -- の前に追加してください。
Gemini CLI
~/.gemini/settings.json(またはプロジェクトの .gemini/settings.json)に追加します。
{
"mcpServers": {
"gwsadm-mcp": {
"command": "gwsadm-mcp"
}
}
}
Codexと同様に、設定の場所がデフォルト以外の場合のみ "env": { "GWSADM_CONFIG": "..." } を追加してください。一部のツールだけを公開するには、includeTools にそれらを列挙します。
stdioサーバーを起動するその他のMCPクライアントも同様に、コマンドとして gwsadm-mcp を指定します。
直接実行
gwsadm-mcp
CLIオプション
gwsadm-mcp --version # Print version and exit
gwsadm-mcp --check # Config + auth + API smoke for every domain, then exit
gwsadm-mcp # Start MCP server (STDIO, default)
--check の終了コード:成功時は 0、設定または認証に失敗した場合はゼロ以外です。
DMARCレポートのアーカイブ(一括処理)
dmarc_rua_summary は、巡回用に一定期間の集計を行います。日ごとの履歴を保存するには、スケジュールされたジョブでレポート自体をダンプできます。
gwsadm-mcp dmarc-reports --domain example.edu --since 2026-09-20 --until 2026-09-23 > reports.json
dmarc_rua_summaryと同じRUAメールボックス(同じgmail.readonlyスコープ)から、[--since, --until)(UTCの日付、Gmailのafter:/before:)に到着したメッセージを対象に読み込みます。すべての添付ファイルとZIP内のすべてのエントリーを処理します(添付ファイルごとに最大20エントリー、展開後50 MBまで。それを超えるもの、または暗号化・破損したエントリーはnon_report_attachmentsにカウントされます)。- stdoutに1つのJSONドキュメントを出力します。各レポートの全データ(
org_name、report_id、報告対象期間のエポック秒であるbegin/end、公開されたpolicy(p、sp、pct、adkim、aspf)、およびreasonとauth_resultsを含む各レコード)を含みます。集計も重複排除も行いません。1つのレポートを複数のメッセージに分割し、同じreport_idで送信する報告元もあるため、IDではなく内容に基づいて重複排除してください。 - 上限に達したものがなく、メッセージ、添付ファイル、レコードのいずれもスキップされていない場合に限り、
fetch_completeはtrueになります(message_errors、non_report_attachments、dropped_records)。 - 引数または設定が無効な場合は
2、APIまたは認証に失敗した場合(例:スコープが取り消された場合)は1で終了します。どちらの場合もstdoutには何も出力されません。 - 出力には送信元IPアドレスとヘッダードメインが含まれます。公開するものには含めないでください。
注意事項
- 各結果セクションでは、ウィンドウがページ予算を超えた場合、またはプローブの取得が完全にエラーになった場合(
event_errorsを参照)にcapped: trueを報告します。部分的な結果を「該当なし」として提示することはありません。Drive スキャンでは、capped_events(途中で打ち切られた eventNames)も報告します。全件を取得するには、hoursを短くするかmax_pagesを増やしてください。大規模なテナントでは、学期中の平日にchange_user_accessイベントが 1 日あたり数千件発生することがあります。 - Google の
visibility=shared_externallyはファイル所有者のドメインを基準とするため、internal_domainsが複数ある場合、内部ドメイン間の共有権限(例:学生ドメインから職員ドメインへの共有)も該当します。そのため、外部かどうかは、共有先に基づいてinternal_domainsと照合して判定します。名前を指定した共有権限ではtarget_user、ドメインを指定した共有権限(例:「partner.edu の全員」)ではtarget_domainを使います(文字列のドメイン"all"は「リンクを知っている全員」を意味し、代わりに公開範囲で判定します)。risky_visibility_eventsが数えるのは、people_with_link/public_on_the_webへの遷移のみです(公開からリンク限定への範囲縮小は除外)。untargeted_external_transitionsは、分類対象となる宛先アドレスやドメインがないままshared_externallyに遷移したケースの残余バケットです。ドメイン指定の共有権限はすでに上記で数えているため、他で見落とした共有権限を照合するためのものではありません。external_samples/exposure_samples/untargeted_samplesには、それぞれの例が格納されます。 - Drive イベントは、監査に関係する eventName ごとに個別にクエリされるため、ページ予算が閲覧・編集などのノイズで消費されることはありません。API が拒否したイベント名は、ツール全体を失敗させるのではなく
event_errorsに記録されます。この API では、change_document_visibilityとchange_document_access_scopeは同じ変更を同時に発生する兄弟イベントとして報告します。分類に使うのは後者のみです(前者はacl_eventsの件数を得るためだけに取得します)。そのため、ドメイン指定の共有権限やリンク/ウェブ公開は、2 つのイベント間で二重計上されません。つまり、後者自身の取得に失敗した場合、前者で補うこともできません。event_errorsにchange_document_access_scopeがあれば、そのドメインのcapped: trueが設定されます。また、change_document_visibility(ひいてはacl_events)にデータが表示されていても、そのウィンドウの分類件数は下限値となります。 - あるドメインでの失敗は、そのドメインのセクション(
{"error": ...})のみに影響します。 gmail_message_traceは、同じ Message-ID のメッセージが受信者のメールボックスに複数ある場合(メーリングリストのコピーと直接 CC されたもの、隔離解除後の重複など)、その受信者にambiguous: true(match_countを含む)を設定します。その受信者の残りのフィールドに記述されるのは、最初に一致したメッセージのみであり、複数の結果をまとめたものではありません。メールボックスに一致するメッセージが十分にあり、match_countが正確な件数ではなく下限値となる場合は、あわせてmatch_count_cappedが設定されます(検索ではページネーションを行いません)。get_userは、「このアドレスに該当するアカウントがない」場合と「検索に失敗した」場合を区別します。通常の HTTP 404 の場合は、状態フィールドなしでfound: falseを返します。これは誤記されたアドレスや削除済みアドレスを示す診断結果であり、errorにはなりません。DWD スコープの不足や一時的な失敗の場合は、foundキーを含めず{"error": ...}を返すため、両者を取り違えることはありません。Google が返さないフィールドは、型を強制せずnullのままになります。suspendedがない場合に「アカウントに問題はない」と解釈してはなりません。group_delivery_policyは、Groups Settings API の文字列フィールド"true"/"false"(この API 固有の仕様であり、JSON のブール値ではありません)を、出力では実際のブール値に正規化します。Google のレスポンスにないフィールドはfalseに変換せず、nullのままにします。list_group_membersは、グループメタデータとメンバー一覧の検索を独立して実行します。DWD スコープが 2 つのうち一方のみのテナントでも、そのセクションは取得でき、もう一方は代わりに{"error": ...}として報告されます。メンバー一覧がページ予算(既定では 20 ページ × 200 件/ページ)を超えた場合と、メンバー検索に完全に失敗した場合(members_errorを参照)の両方でcapped: trueを報告します。どちらの場合も一覧は完全ではなく、cappedがtrueのときに空のmembersリストを、グループにメンバーがいないことの確証として解釈してはなりません。両方のグループツールは、「このアドレスはグループではない」場合(基盤となる 3 つすべての API 呼び出しで本番環境にて確認済みの通常の HTTP 404)と実際の失敗を区別します。group_delivery_policyはfound: falseを設定します。list_group_membersも、独立した 2 つの検索がどちらもエラーなく「見つからない」と判定した場合、または一方が「見つからない」と確定し、もう一方が独立して失敗した場合(その失敗は隠さずgroup_lookup_error/members_lookup_errorに記録されます)にfound: falseを設定します。存在しないことが確認された場合は、もう一方のスコープで発生した無関係なエラーより優先されます。一方が「見つからない」、もう一方でデータが見つかったという実際に状態が混在するケースのみ、通常のセクション形式で返されます。- 設計上は読み取り専用ですが、オプトインによる例外が 1 つあります。発行される読み取り呼び出しは、
activities().list()とcustomerUsageReports().get()(Reports API)、users().list()、users().get()、tokens().list()、groups().get()、members().list()(Directory API)、groups().get()(Groups Settings API)、および Gmail API のmessages().list()/messages().get(format="metadata")(gmail_message_traceとcompromise_sweep用。後者では To/Cc/Bcc/Date ヘッダーのみ)、messages().list()/messages().get(format="full")/messages().attachments().get()(dmarc_rua_summary用。メタデータだけでなく、レポートの添付ファイルも読み取ります)、compromise_sweep用の設定読み取りusers().settings().getAutoForwarding()/forwardingAddresses().list()/filters().list()/delegates().list()/sendAs().list()、およびhealth_checkの Gmail プローブ用users().getProfile()です。唯一の書き込みは、Directory API のusers().signOut()です。これはsign_out_userのみが実行し、このツールはサーバーの起動時にGWSADM_ENABLE_SIGN_OUT=1が設定されている場合に限り存在します。workspace_statusは、認証情報なしで Google の公開ステータスフィードにも GET リクエストを送信します。 - 出力にはアカウントアドレスが含まれます(それが監査ツールの目的です)。アクセスは、権限を持つセキュリティ担当者のみに制限してください。
gmail_message_traceは、一致したメッセージのスニペットとヘッダー(From/To/Cc/Subject/Date)も返します。出力は、元となったメールボックスの内容と同等の注意を払って扱ってください。
開発
git clone https://github.com/shigechika/gwsadm-mcp.git
cd gwsadm-mcp
# uv
uv sync --dev
uv run pytest -v
uv run ruff check .
# pip
python3 -m venv .venv
.venv/bin/pip install -e . && .venv/bin/pip install pytest ruff
.venv/bin/pytest -v
.venv/bin/ruff check .
ライブスモークテスト
ユニットテストは Google に一切接続しないため、高速です。一方で、ツールが実データを返さなくなっても検出できません。
scripts/smoke_test.py は、登録されているすべてのツールを設定済みのテナントに対して実行し、空の回答、不正な形式の回答、またはエラー回答があれば失敗します。
# uses the same config file as the server (GWSADM_CONFIG)
uv run python scripts/smoke_test.py
uv run python scripts/smoke_test.py --only oauth --traceback
- 読み取り専用です。 ここで実行するすべてのツールは、監査ログまたはディレクトリのスナップショットを読み取ります。Workspace の内容は変更されません(
sign_out_userはスキップされます)。daily_brief_startはプロセス内にジョブを作成しますが、ジョブは自動的に期限切れになります。 - レポートにペイロードは含まれません。 ツール名、ステータス、行数のみを記載します。これらのツールはアカウントのアドレスやドキュメントのタイトルを扱うため、サーバーが生成したエラーテキストも伏せられます。
- 範囲が制限されています。 ツールが指定できるすべての上限パラメーターを明示的に渡します。デフォルト値(5 ページ、180 日、200 イベント)は、人が一度問い合わせる場合を想定した規模であり、ソースコードから値を検出するテストによって適用されます。
- 仕様にテナント固有の情報は含まれません。 ユーザー単位およびドキュメント単位のツールが必要とするアカウントとドキュメントは、実行時に検出され、テナントに該当するものがなければスキップされます。この状態を維持するためのテストが 2 つあります。1 つはそれらのパラメーターをリテラルとして指定することを拒否し、もう 1 つはこのリポジトリが公開されているため、ファイル内のアドレス形式の文字列をすべて禁止します。
- 空の回答でも合格します。外部との共有がなく、アカウントがロックされていないことが望ましい状態だからです。代わりに検証するのは回答の形式です。また、ドメイン単位でキー付けされた回答の場合、ドメインのマップが空でないことも検証します。そうしないと、設定で解決されるドメインが 0 件の場合に、何も監査していないのにすべてのツールが正常に動作していると報告されてしまいます。
- CI は、コストの低い部分を検証します。プローブ仕様のないツールが登録されていると、ビルドは失敗します(
tests/test_smoke_probes.py)。そのため、ツールを追加する際には「動作をどう確認するか」という点も必ず検討することになります。 scripts/smoke_harness.pyはエンジンであり、Workspace に関する知識は持ちません。このファイルは共有しているサーバー間で同一に保たれているため、エンジンのバグは一度修正し、個別にパッチを当てるのではなくファイルを同期してください。
リリース
リリースは release-please で自動化されています。Conventional Commits(feat:、fix:、…)を main にマージすると、次のバージョンと変更履歴を含むリリース PR が開いた状態に保たれます。その PR をマージすると vX.Y.Z タグが付与され、GitHub Release が公開されます。この Release の release: published イベントを受けて release ワークフローが起動し、PyPI と MCP Registry にビルドを公開します。gwsadm_mcp/__init__.py と server.json のバージョンは release-please が管理します(手動で更新しないでください)。
重要
release-please ワークフローには、リポジトリシークレット
RELEASE_PLEASE_TOKEN(contents: writeとpull-requests: writeの権限を持つ PAT)を設定してください。 デフォルトのGITHUB_TOKENでは、後続のreleaseワークフローを起動する Release を作成できません(GitHub はGITHUB_TOKENによって起動されるワークフローの実行をブロックします)。そのため、PAT がないと何も公開されません。シークレットが設定されていない場合、ワークフローはGITHUB_TOKENにフォールバックするため、フォーク上でも PR の CI は引き続き動作します。
ライセンス
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。