0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【後編】IBM OpenPages の MCP サーバーを IBM Bob に接続する ― Bob への登録から疎通確認・ハマりどころ総括まで

0
Last updated at Posted at 2026-11-06

【後編】IBM OpenPages の MCP サーバーを IBM Bob に接続する ― Bob への登録から疎通確認・ハマりどころ総括まで

はじめに

前編 では、IBM 公式の IBM/ibm-openpages-mcp-server をclone し、python main.py --mode local でMCP サーバーをstdio モードで起動できるところまで進めました。

後編では、これを IBM Bob から子プロセスとして起動させ、OpenPages のデータをBob から扱える状態 にします。

記事 内容
前編 環境構築 → .env 設定 → コード修正 → ローカル起動確認
後編(本記事) 起動ラッパー作成 → Bob への登録 → 疎通確認 → ハマりどころ総括

検証環境(要約)

詳細は 前編の「前提条件」 にまとめています。後編の内容に関わる主なバージョンは次のとおりです。

項目 バージョン
IBM OpenPages 9.2.1
IBM Bob 2.2.3(macOS 版)
OS / Python macOS 26.6.2(arm64) / Python 3.13.1
mcp(Python SDK) 2.3.0

Bob のMCP 設定や画面の表記、公開されるツールの一覧は、Bob のバージョンによって変わる可能性があります。

前編の到達点(この記事の前提)

後編は、次の状態になっていることを前提にします。

  • ibm-openpages-mcp-server/ に venv/ があり、pip install -r requirements.txt 済み
  • .env が作成済み(OPENPAGES_BASE_URL / OPENPAGES_USERNAME / OPENPAGES_PASSWORD など)
  • 前編の手順3 の修正(main.py の stdout 汚染対策、OPENPAGES_API_PREFIX の追加)が反映済み
  • python main.py --mode local が起動する

全体構成

本記事の表記ルールは前編と同じです。https://your-openpages-instance.com・your_username・your_password・/path/to/OpenPages-MCP はダミーです。

手順 5: 起動ラッパー run_local_bob.sh の作成

なぜラッパーが必要か

Bob はMCP サーバーを 子プロセスとして直接起動 します。前編では set -a; source .env; set +a で .env を手動で読み込んでから起動しましたが、
Bob の設定にそれを書くのは面倒です。そこで「.env を読み込んでから main.py を起動する」ラッパースクリプトを1 本用意しました。
(.bob/mcp.json の env に環境変数を直接書く方法もありますが、パスワードを設定ファイルに書くことになるため、私は .env に集約する方式にしました。)

スクリプト全文

ibm-openpages-mcp-server/run_local_bob.sh として保存します。

#!/bin/bash
# Bob の MCP サーバー起動スクリプト
# .env を読み込んでから main.py をローカルモードで起動する
# Bob は stdio 経由でこのスクリプトを子プロセスとして起動する

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# 既存の main.py --mode local プロセスを終了させる(プロセス蓄積防止)
# 複数タスクが起動するたびに古いプロセスが残存するのを防ぐ
pkill -f "main.py --mode local" 2>/dev/null || true
sleep 0.3

# .env ファイルが存在する場合に読み込む(既に環境変数が設定されている場合は上書きしない)
ENV_FILE="$SCRIPT_DIR/.env"
if [ -f "$ENV_FILE" ]; then
    # export しつつ読み込む(コメント行・空行はスキップ)
    set -a
    # shellcheck source=/dev/null
    source "$ENV_FILE"
    set +a
fi

# venv の Python で main.py をローカルモードで実行
exec "$SCRIPT_DIR/venv/bin/python" "$SCRIPT_DIR/main.py" --mode local

実行権限を付けます。

chmod +x /path/to/OpenPages-MCP/ibm-openpages-mcp-server/run_local_bob.sh

スクリプトがやっていることは次の4 つです。

  1. スクリプト自身の置き場所(ibm-openpages-mcp-server/)を SCRIPT_DIR として求める
  2. 既存の main.py --mode local プロセスを終了する(ハマりどころ ③。後述)
  3. .env を source して環境変数としてexport する
  4. exec でvenv のPython に置き換わり、main.py --mode local を起動する

exec を使うので、シェルのプロセスは残らず、Bob はPython プロセスと直接stdio でやり取りします。
Python は main.py のあるディレクトリをimport パスに加えるため、from src.app... のimport も解決されます。

この記事の .env の読み込みは、単純な KEY=value 形式を前提にしています。値に空白や $ などのシェルの特殊文字を含む場合は、引用符で囲むなど、source で正しく解釈される形にしてください。

ハマりどころ ③: Bob のタスクごとにプロセスが溜まる

症状

Bob は新しいタスクを開くたびにMCP サーバーのプロセスを起動します。私の環境では、古いプロセスが終了されないまま残り、
main.py --mode local が 複数並走 することがありました。蓄積するとCPU 使用率が上がり、OpenPages への接続でDNS 解決失敗([Errno 8])が出ました。

対処

ラッパーの先頭で、起動前に古いプロセスを終了させます(上のスクリプトの pkill と sleep 0.3)。

pkill -f "main.py --mode local" 2>/dev/null || true
sleep 0.3

蓄積が起きてしまった場合は、手動でも同じコマンドで掃除できます。

pgrep -a -f "main.py --mode local"   # 稼働中のプロセスを確認
pkill -f "main.py --mode local"      # まとめて終了

検証結果

pkill が効くかを、ラッパーを手動で3 回続けて起動して確認しました。
(起動前にBob 経由のプロセスが1 つ動いている状態から開始しました。)

for i in 1 2 3; do
  ( sleep 12 | ./run_local_bob.sh >/dev/null 2>&1 & )
  sleep 3
  echo "after launch $i: $(pgrep -f '[m]ain.py --mode local' | wc -l) process(es)"
done
タイミング main.py --mode local のプロセス数
起動前(Bob 経由の 1 つが稼働中) 1
1 回目の起動後 1
2 回目の起動後 1
3 回目の起動後 1

起動するたびに古いプロセスが終了し、プロセスが 1 つのまま増えない ことを確認できました。

確認できたこと・できていないこと

  • 確認できた: ラッパーを手動で連続起動しても、プロセスが蓄積しない
  • 確認できていない: Bob で複数のタスクを実際に開閉したときの挙動。上の検証は、ラッパーを手動で繰り返し起動する形で再現したものです

副作用に注意

pkill -f "main.py --mode local" は、コマンドラインに main.py --mode local を含むプロセスをすべて 終了します。
そのため次のような副作用があります。

  • 検証の際、動作中だったBob 用のプロセスも終了しました。その後Bob からクエリを実行すると、新しいプロセスが立ち上がって結果が返りました
  • Bob のウィンドウを複数開いて同じMCP サーバーを使っていると、片方の起動がもう片方のサーバーを終了させます
  • 無関係な同名のプロセスがあれば、それも終了します

複数ウィンドウで同時に使う場合や、他の用途で main.py --mode local を動かす場合は、この方式は向きません。
その場合はPID ファイルで管理するなど、終了対象を絞る方法を検討してください。

手順 6: .bob/mcp.json への登録

Bob のMCP 設定は2 階層あります(Bob 公式ドキュメントによる)。

範囲 場所 用途
グローバル ~/.bob/mcp.json すべてのワークスペースで有効
プロジェクト <プロジェクトルート>/.bob/mcp.json そのプロジェクトのみ。同名のサーバーはプロジェクト側が優先される

今回はグローバルに設定をします。

  1. Bob の設定画面を開きます
    op2.png

  2. MCP タブの MCP サーバーを追加 をクリックします
    op3.png

  3. 設定スコープ を グローバル に設定し、設定ファイルを開く をクリックすると、json ファイルが開きます
    op4.png

  4. json に次の内容を登録します

    {
      "mcpServers": {
        "openpages-mcp": {
          "command": "/path/to/OpenPages-MCP/ibm-openpages-mcp-server/run_local_bob.sh",
          "args": [],
          "env": {}
        }
      }
    }
    
  • command: STDIO トランスポートでは必須。実行するファイルを指定します。ここではラッパーの絶対パスを書きました
  • args: 引数の配列。ラッパー側で --mode local を付けているので空です
  • env: 環境変数。.env はラッパーが読むため空です

/path/to/... の部分は、お使いの環境の絶対パスに置き換えてください。保存すると、Bob のMCP パネルにサーバーが表示されます。
私の環境では、接続状態が Connected になりました。

手順 7: Bob からの疎通確認

以降は、Bob のチャットからOpenPages MCP のツールを使った結果です。数値は私のインスタンスのものです。

7-1. Bob からのリソース一覧の取得

Bob のチャットから自然言語で実行してみます。

openpages-mcpを使って、リソース一覧を取得して

Bob は list_resources を呼び出しました。このツールには引数が不要で、クエリの組み立ても発生しません。
op8.png

取得結果(抜粋)

op9.png

Bob は、この結果を次のように整理して返してくれました。

スキーマはさらに、SOX / 内部統制(SOXRisk、SOXControl、SOXIssue など)、オペレーショナルリスク、コンプライアンス・規制、監査、事業継続、モデルリスク、サードパーティ管理といった分野別に分類されて表示されました。

一覧に出てきた URI は、get_resource にそのまま渡すことで中身を取得できます。クエリを組み立てる前にスキーマを確認する流れの出発点になります。

7-2. Bob からのクエリ実行例(SOXIssue の取得)

Bob のチャットから自然言語で次のように指示を出します。

openpages-mcpを使って、OpenPagesのSOXIssueを5件取得して

指示を受けた Bob は、以下のステップを自律的に行いました。

  1. スキーマの取得 (get_resource)
    クエリ構築前に openpages://schema/SOXIssue(compact モード)を参照し、SOXIssue の正確なフィールド名([SOXIssue].[Resource ID], [SOXIssue].[OPSS-Iss:Status] などプレフィックス付きフィールドを含む)を確認
    op5.png

  2. クエリ実行 (execute_openpages_query)
    確認したスキーマ定義に基づいてクエリを生成・実行
    op6.png

実行されたクエリ

SELECT
  [SOXIssue].[Resource ID],
  [SOXIssue].[Name],
  [SOXIssue].[Description],
  [SOXIssue].[OPSS-Iss:Status],
  [SOXIssue].[OPSS-Iss:Priority],
  [SOXIssue].[OPSS-Iss:Due Date]
FROM [SOXIssue]

取得結果

op7.png

OpenPages 上の Issue データ(ID、名前、ステータス、優先度、期日、説明)が正常に 5 件取得され、Bob の画面上にフォーマットされて返ってきました。

OpenPages のクエリ言語には、フィールド名を [オブジェクト型].[フィールド名] の形式で完全修飾する、AS によるエイリアスは使えない、といったルールがあります。
フィールド名はインスタンスの構成ごとに異なるため、MCP サーバーが提供する openpages://schema/<型名> で事前にフィールド定義を取得して組み立てる必要があります。Bob はこの仕様を理解し、自動的にスキーマを取得してからクエリを実行します。

7-3. REST API へのアクセスの確認(任意)

Bob を介さずに、OpenPages のREST API へ直接アクセスできているかは、前編で使った curl でも確認できます。

curl -s -o /dev/null -u "your_username:your_password" \
  -w '%{http_code}\n' \
  "https://your-openpages-instance.com/openpages/api/v2/types"
200

ハマりどころのまとめ

前編・後編を通して遭遇した 3 点を一覧にします。

# 症状 原因 対処 該当
① Local(stdio)モードで、stdout にJSON-RPC 以外のログ行が混ざる main.py が最初の setup_logging で use_stderr=False を使い、stderr への切り替えが argparse の後だった argparse の前に --mode / SERVER_MODE からLocal モードを判定し、最初から use_stderr=True にする 前編 手順3
② /opgrc/api/v2/... がHTTP 302 で logon.jsp にリダイレクトされ、認証が通らない(私の環境) API パスが /opgrc に固定されていた。私の環境は /openpages/api/v2/... が直接のREST API だった OPENPAGES_API_PREFIX を追加し、.env で空文字を設定する 前編 手順3
③ Bob のタスクごとに main.py --mode local が溜まり、CPU 使用率の上昇とDNS 解決失敗 [Errno 8] が出た 古いプロセスが終了されずに残る run_local_bob.sh の先頭で pkill する 後編 手順5

注意事項

  • stdout はJSON-RPC 専用です。Local モードで print() などをstdout に出力すると、Bob との通信が壊れます。ログや調査用の出力はstderr に出してください
  • 認証情報の管理: .env は .gitignore に含め、コミットやログ、記事・スクリーンショットに値を出さないでください
  • 通信の暗号化: 私が使ったインスタンスはHTTP(非SSL)でBasic 認証が使えましたが、これは検証用途の構成です。本番環境ではHTTPS を使い、SSL_VERIFY=true にしてください
  • ラッパーの pkill の副作用: 手順5 の「副作用に注意」を確認してください
  • 公式リポジトリへの修正: 今回の修正は、公式リポジトリへの変更として取り込まれたものではなく、私の環境で入れたローカルの変更です。公式リポジトリを更新(git pull)する際は、競合に注意してください

おわりに

2 本の記事で、IBM OpenPages のMCP サーバーをIBM Bob にLocal(stdio)モードで接続し、Bob からOpenPages のリソース取得とクエリ実行ができるところまで確認しました。

今回は次の点を扱っていません。公式README には記載があるので、必要に応じて参照してください。

  • Remote(HTTP)モード(streamable-http)での接続。複数ユーザーでの共有に向きます
  • Bearer 認証(IAM / MCSP / CP4D)
  • Docker での実行、監視スタック

公式のREADME では、Local モードは「IBM Bob のようなMCP クライアントから、1 ユーザーで使う場合」の用途として説明されています。

▶ 前編: https://qiita.com/mmmmmika/items/fc67a362df2c928f0e49

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?