ServiceNow を AI から自然言語で操作する MCP サーバーに Discovery 運用可視化ツール(実行履歴・デバイス単位の結果・ログ・MID ヘルス)を追加し、Docker Desktop 上に建てた MID Server で実スキャンを完走させて検証しました。
Discoveryで検出された CI からソフトウェアインストール・正規化(SAM)までのデータ連鎖を実データで追跡します。
GitHub: https://github.com/tedorigawa001/ServiceNow-MCP
何を作ったか
1. Discovery 運用可視化ツール(11 ツール)
これまで MCPでは list_discovery_schedules / list_mid_servers / run_discovery_scan の 3 つを実装していましたが、スキャンを起動できるのにDiscovery結果を追えない状態でした。
「昨夜の Discovery は成功した?」「このサーバーが発見されない原因は?」に答えるための新モジュールを追加:
| ツール | テーブル | 用途 |
|---|---|---|
list_discovery_runs / get_discovery_run
|
discovery_status |
実行履歴(DIS 番号でも sys_id でも) |
list_discovered_devices |
discovery_device_history |
デバイス単位の結果。with_issues_only で問題機だけ抽出 |
list_discovery_logs |
discovery_log |
センサー/プローブのログ(失敗原因の特定) |
list_discovery_ranges |
discovery_range_item |
スキャン対象 IP レンジ |
list_discovery_credentials |
discovery_credentials |
認証情報メタデータのみ(シークレット列は fields に含めない) |
list_mid_server_issues / get_mid_server_health
|
ecc_agent_issue / ecc_agent
|
MID の問題・健全性サマリ |
list_acc_agents / list_acc_policies / list_acc_checks
|
sn_agent_* |
ACC(プラグイン未導入なら PLUGIN_NOT_INSTALLED を明示) |
2. Mac上に検証環境を構築
MID Server が無いと Discovery を動かせません(エージェント型はACCを利用)。
MID Server は公式インストーラ ZIP から Dockerfile を書けばコンテナで動きます(Apple Silicon でも Rosetta で x86-64 動作)。
構成は以下。
┌─ Docker Desktop(Mac)── 172.XX.XX.0/24 ────────┐
│ ┌─────────────┐ ┌──────────────────────────┐ │
│ │ MID Server │→│ target1 (.21) SSH+正しい認証│ │
│ │ (.10) │→│ target2 (.22) SSH+認証未登録│ │
│ └──────┬──────┘ └──────────────────────────┘ │
└─────────┼───────────────────────────────────────┘
↓ HTTPS(アウトバウンドのみ、ポート開放不要)
<PDI>.service-now.com
ポイントは target2 をわざと「認証情報未登録」にしたこと。
認証失敗ケースを意図的に作ることで、with_issues_only やログ調査ツールの検証データになります。
落とし穴: Discovery はレンジ未リンクで「黙って」中断する
修正版でDiscoveryをトリガーしても、run が作られませんでした。
sys_trigger は発火して消えるのに、discovery_status もエラーログも何も残らない。
診断用の Scheduled Script Execution を仕込んで確認すると、スケジュール実行スクリプトの Discovery().discoveryStartJob() は正常に返っているのに run が無い。
そこで Discovery Script Include のソースを読みました。
discoveryStartJob: function() {
if (!this.isValidDiscoverySchedule(current))
return; // ← 黙って return
...
そして isValidDiscoverySchedule → isValidRange の中に決定打が:
isValidRange: function(schedule) {
...
var gr = new GlideRecord('discovery_range_item');
gr.addQuery('schedule', schedule.sys_id); // ← リンクは "schedule" フィールド
gr.addQuery('active', true);
...
私は IP レンジ(discovery_range_item)を parent フィールドでスケジュールに紐付けていました。parent の参照先は discovery_range(レンジセット)で、スケジュールとのリンクは別にある schedule フィールドが正解。
parent に入れてもバリデーションは 0 件 → 警告ログ 1 行だけ残して status も作らず中断します。
// ❌ parent に入れても Discovery からは見えない
{ parent: scheduleSysId, type: 'IP Network', network_ip: '172.XX.XX.0', netmask: '24' }
// ✅ schedule フィールドでリンクする
{ schedule: scheduleSysId, type: 'IP Network', network_ip: '172.XX.XX.0', netmask: '24' }
schedule に直したら即座に DIS0010002 が Starting で出現しました。
ツール側にも還元し、run_discovery_scan はトリガー後に schedule=<id>^active=true のレンジ数を確認して 0 件なら warning を返すようにしました。
教訓: サイレントアボートは Script Include のソースを読むのが最短。 sys_script_include は REST で読めるので、「なぜ動かない」は動くはずのコードの条件分岐を直接見に行けます。
実スキャンの結果(DIS0010002、4分で完走)
| 対象 | 結果 |
|---|---|
| 172.XX.XX.10(MID 自身) | 検出のみ |
| 172.XX.XX.1(Docker GW) | 検出のみ |
| 172.XX.XX.21(target1) | 「Linux Server」に分類 → CMDB に CI「disco-target-01」生成 |
| 172.XX.XX.22(target2) | issues=2(狙い通り認証失敗) |
MCPでは、with_issues_only: true で問題デバイスだけが抽出でき、そのデバイスのログを list_discovery_logs で辿ると:
"Could not find any valid credentials to authenticate the target
for types [SSH Password, SSH Private Key, Azure SSH Certificate]"
"Probe skipped due to failure of previous probe attempt"
**「なぜこのサーバーが発見されないのか」→「SSH 認証情報が無いから」**まで、AI が自力で答えられるデータが揃いました。
Discovery → CMDB → SAM のルートを追跡する
スキャンが完走すると、SAM Pro(前記事で追加したツール群)へのデータ連鎖が始まります。実データで 3 段を確認しました。
Discovery (DIS0010002)
↓ SSH probe → dpkg パッケージ収集
① CI: cmdb_ci_linux_server「disco-target-01」
os=Linux Ubuntu 22.04.5 / discovery_source=ServiceNow
↓ installed_on
② Software Installation: cmdb_sam_sw_install に 135 件
openssh-server / coreutils / util-linux / ...
↓ discovery_model
③ Software Discovery Model: cmdb_sam_sw_discovery_model に 142 件生成
正規化パイプラインへ投入
openssh-server で 3 段の連結を具体的に見ると:
- SoftwareInstallテーブルでは CI
disco-target-01をinstalled_onカラムでリンク - また、ソフトウェア検出モデルに
discovery_modelカラムでリンク
面白いのは、生成された Ubuntu パッケージの発見モデルが軒並み status「Match Not Found」(norm_publisher 空・unlicensed_install=true)だったこと。
これはバグではなく、ServiceNow のコンテンツライブラリに Ubuntu のシステムパッケージ(libwrap0 や ucf)が載っていないため正規化が成立しない、
SAM Pro 実運用でもおなじみの状態です。既存データの VMware / Microsoft 製品は「Normalized」でパブリッシャー突合済みなので、正規化済み/未正規化の両状態が同居する現実的なデータセットになりました。
SoftwareInstallテーブルに登録されたレコード
⭐️Discovery Source=ServiceNowとなる
⭐️ソフトウェア検出モデルには、ビジネスルールを介して自動的にコピーされる

結果として、こういう調査が MCP ツールだけで一気通貫に回ります:
「昨夜の Discovery の結果は?」 → list_discovery_runs
「発見に失敗したデバイスは? なぜ?」 → list_discovered_devices(with_issues_only) → list_discovery_logs
「新しく発見されたサーバーの中身は?」 → list_software_installs(installed_on=CI)
「未正規化のソフトは?」 → list_software_discovery_models(Match Not Found)
「ライセンスは足りてる?」 → list_license_positions / get_license_position_summary
ITOM(Discovery)と ITAM(SAM)は組織でも担当が分かれがちですが、データは一本の川です。
AI から見るとテーブルの垣根はないので、この縦断トレースは MCP と相性が良いと感じています。
まとめ
-
discovery_statusを直接 insert してもスキャンは起動しない。 Discovery エンジンはスケジューラ経由の run しか開始しない。REST API からの正攻法は「スケジュールをrun_type=once+run_start=nowに PATCH」 -
IP レンジとスケジュールのリンクは
discovery_range_item.scheduleフィールド。parent(レンジセット用)に入れると、Discovery は status も作らずサイレント中断する -
サイレントアボートの調査は Script Include のソースを読む。
sys_script_includeは REST で読める - MID Server は Docker で動く。 公式インストーラ ZIP + 数十行の Dockerfile。認証失敗ケース用のターゲットをわざと置くと検証データが充実する
- Discovery → CI → Software Installation → Discovery Model → ELP の ITOM/ITAM 縦断ルートが MCP ツールだけでトレースできる

