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 に接続する ― 環境構築からローカル起動確認まで

0
Last updated at Posted at 2026-11-06

【前編】IBM OpenPages のMCP サーバーをIBM Bob に接続する ― 環境構築からローカル起動確認まで

はじめに

IBM が公式に公開している IBM/ibm-openpages-mcp-server を使うと、
AI エージェントからIBM OpenPages(GRC プラットフォーム)のオブジェクトを検索・作成・更新できるようになります。

この記事は2 本構成のシリーズの 前編 です。公式リポジトリをclone して、ローカルでMCP サーバーが起動するところまで を、コピペで再現できる形でまとめます。

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

IBM OpenPages とは

IBM OpenPages は、組織のリスク管理、コンプライアンス、監査といった業務を1 つの基盤にまとめる GRC(ガバナンス・リスク・コンプライアンス)プラットフォーム です。
IBM の製品ページでは、「スケーラブルなAI 搭載のGRC プラットフォーム」で、クラウドでもオンプレミスでも利用でき、必要なコンポーネントだけを選んで導入できるモジュール式の構成だと説明されています。また、オープンなAPI でBI ツールやデータ基盤と連携できるとされています。

この記事の手順で重要なのは、OpenPages が REST API を備えている 点です。MCP サーバーは、このREST API を呼び出してOpenPages のオブジェクト(Issue、Control、Risk など)を扱います。記事では、OpenPages のREST API にBasic 認証でアクセスします。

IBM Bob とは

IBM Bob は、IBM が提供する AI を活用したソフトウェア開発のパートナー(AI SDLC パートナー)です。
IBM の公式ドキュメントでは、既存のワークフローを補強し、実際のコードベースを理解・計画・改善する作業を支援するもの、と説明されています。
チャット形式で指示を出し、Bob が提案する操作を確認・承認しながら進めます。

Bob は、ファイルの読み書き、ターミナルでのコマンド実行といった操作に加えて、MCP を通じて外部のツールを使う ことができます。
この記事のシリーズでは、このMCP の仕組みを使って、Bob からOpenPages を操作できるようにします。

MCP(Model Context Protocol)とは

MCP は、AI システムが外部のツールやサービスとやり取りするための 標準化された通信プロトコル です。
MCP サーバーが「ツール」や「リソース」を公開し、Bob のようなMCP クライアントがそれを呼び出します。

今回のMCP サーバーは、OpenPages のREST API を、Bob が使える形のツールとリソースに包む役割を担います(全体の構成は後述の「全体構成」を参照してください)。

この記事で得られるもの

  • Python 仮想環境上でOpenPages MCP サーバーを Local(stdio)モード で起動できる
  • 私の環境で実際に遭遇した、公式リポジトリそのままでは動かない箇所2 つ(ログの stdout 汚染 / API パスのプレフィックス)と、その修正差分

全体構成

前編では、右側の main.py --mode local とOpenPages の接続までを作ります。run_local_bob.sh と Bob 側の登録は後編です。

公式リポジトリに 手を加える 手順を含みます(手順3)。これは私の検証環境で必要だった修正であり、すべてのOpenPages 環境で必要とは限りません。該当しない環境では手順3 の ② は不要です。

前提条件

必要な条件と、私の検証環境を次の表にまとめます。この記事の手順・出力は、すべて右列の環境で実施・取得したものです。
バージョンが異なる場合、挙動や出力が変わる可能性があります。

項目 必要な条件 私の検証環境
IBM OpenPages REST API にBasic 認証でアクセスできるインスタンス(URL・ユーザー名・パスワードが分かること) 9.2.1(検証用にホストされたインスタンス。HTTP・Basic 認証でアクセス)
IBM Bob 後編で使用します。前編では不要です 2.2.3(macOS 版アプリ。Info.plist の CFBundleShortVersionString)
OS Linux でも同様のはずですが未検証です macOS 26.6.2(arm64)
Python 公式の .python-version は 3.12 3.13.1(Homebrew 版。venv もこの版)
OpenPages MCP サーバー IBM/ibm-openpages-mcp-server clone 時点の最新コミット 3bf3fda(Vulnerabilty Fix (#12)、2026-07-24)
mcp(Python SDK) requirements.txt では >=1.9.4 2.3.0
fastapi / uvicorn >=0.104.0 / >=0.24.0 0.162.0 / 0.54.0
httpx >=0.24.0 0.28.1
pydantic / pydantic-settings >=2.5.0 / >=2.1.0 2.14.1 / 2.16.1

requirements.txt は mcp>=1.9.4 のように 下限のみの指定 なので、インストール時点の最新版が入ります。
バージョンを固定したい場合は、動作確認済みの pip freeze の結果を残しておくと再現しやすくなります。

OpenPages 9.2.1 は私が利用したインスタンスの版であり、他の版(たとえば SaaS 版や CP4D 版)での動作は確認していません。
手順3 の ② で触れる REST API のパス(/opgrc の要否)は、インスタンスの構成ごとに異なる 点にご注意ください。

表記について

OpenPages の接続情報は、この記事ではすべてダミー値で表記します。お手元の値に置き換えてください。

本文中の表記 意味
https://your-openpages-instance.com OpenPages の URL
your_username / your_password OpenPages のユーザー名 / パスワード
/path/to/OpenPages-MCP 作業ディレクトリ

手順 1: リポジトリの取得とPython 仮想環境の作成

mkdir -p /path/to/OpenPages-MCP && cd /path/to/OpenPages-MCP
git clone https://github.com/IBM/ibm-openpages-mcp-server.git
cd ibm-openpages-mcp-server

仮想環境を作成して依存パッケージをインストールします。

python3 --version        # 3.12 以上であることを確認
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

op1.png

主要な依存パッケージとバージョンは、冒頭の「前提条件」の表のとおりです。
Local モードではFastAPI / uvicorn は使いませんが、requirements.txt はRemote モードと共通なのでそのままインストールします。

インストール後、起動オプションを確認します。

python main.py --help

私の環境の出力(先頭にJSON 形式のログ行が1 行出ますが省略しています):

usage: main.py [-h] [--mode {remote,local}] [--host HOST] [--port PORT] [--debug]

GRC MCP Server

options:
  -h, --help            show this help message and exit
  --mode {remote,local}
                        Server mode: remote (HTTP) or local (stdio)
  --host HOST           Host to bind the server to (remote mode only)
  --port PORT           Port to bind the server to (remote mode only)
  --debug               Enable debug mode

--mode local がstdio で動くモードです。

手順 2: .env の設定

リポジトリには用途別の .env.example.* が用意されています。Basic 認証の最小構成をコピーして使います。

cp .env.example.minimal.basic .env

コピー直後の内容です。

# .env.example.minimal.basic の内容(コメントは一部省略)
OPENPAGES_BASE_URL=https://your-openpages-instance.com
OPENPAGES_AUTHENTICATION_TYPE=basic
OPENPAGES_USERNAME=your_username
OPENPAGES_PASSWORD=your_password
OPENPAGES_AUTH_MODE=server
SSL_VERIFY=true

これを、Local モードで動かすために次のように編集します。私が実際に使った項目は以下のとおりです(値はダミーです)。

# .env
OPENPAGES_BASE_URL=https://your-openpages-instance.com/openpages/
OPENPAGES_AUTHENTICATION_TYPE=basic
OPENPAGES_USERNAME=your_username
OPENPAGES_PASSWORD=your_password
OPENPAGES_AUTH_MODE=server
SSL_VERIFY=true

SERVER_MODE=local
LOG_LEVEL=INFO
LOG_FORMAT=json
DEBUG=False
HOST=0.0.0.0
PORT=8000

# 手順3 の ② で追加する設定(空文字にする)
OPENPAGES_API_PREFIX=

各項目の意味です。

変数 設定値 説明
OPENPAGES_BASE_URL OpenPages のURL 私の環境ではコンテキストパス /openpages/ を含めたURL でした
OPENPAGES_AUTHENTICATION_TYPE basic ユーザー名 + パスワードによるBasic 認証
OPENPAGES_AUTH_MODE server MCP サーバー自身の認証情報で全リクエストを処理する(ローカル・開発向け)。公式README では、user が既定でマルチテナント向け、server がオンプレ・ローカル・開発向けと説明されています
SSL_VERIFY true 自己署名証明書の開発環境では false にする
SERVER_MODE local --mode を省略した場合の既定モード
OPENPAGES_API_PREFIX (空) 公式リポジトリには存在しない設定。手順3 で追加します

.env にはパスワードが入ります。リポジトリの .gitignore に .env が含まれていることを確認し、コミットや記事・ログへの貼り付けをしないでください。

git check-ignore -v .env

手順 3: コードの修正(ハマりどころ ① ②)

ここまでの状態でそのまま動かすと、私の環境では2 点で問題が出ました。順に説明します。

ハマりどころ ①: ログがstdout に出力され、MCP プロトコルが壊れる

症状

Local(stdio)モードでは、stdout はJSON-RPC メッセージ専用です。クライアント(Bob)はstdout を1 行ずつJSON-RPC として解釈します。
ところが main.py は、モジュール読み込み時に use_stderr=False(= ログをstdout に出す) で setup_logging を呼んでいました。
--mode local が分かるのは argparse の後であり、それより前に出たログがstdout に混ざります。

実際に、修正前の main.py に initialize を送ってstdout の行を数えると、JSON-RPC 応答ではないログ行が1 行 混入していました(修正後は0 行。確認方法は手順4)。
このログは {"timestamp": ..., "message": "Logging configured: ..."} というJSON 形式なので、見た目はJSON でもJSON-RPC メッセージではありません。

原因

元のコードのコメントにも This will be reconfigured in local mode to use stderr(local モードでstderr に再設定される)とあるように、
再設定は後から行われます。しかし、再設定より前 のログ出力はstdout に出てしまいます。

対処

argparse を実行する前に、sys.argv と環境変数 SERVER_MODE からLocal モードかどうかを先に判定し、その結果で最初の setup_logging からstderr を使うようにします。

main.py の差分(git diff の実出力):

--- a/main.py
+++ b/main.py
@@ -17,14 +17,20 @@ if os.path.exists('/app'):
 from src.app.config.settings import settings
 from src.app.observability.logger import setup_logging, get_logger
 
-# Setup structured logging for remote mode (default)
-# This will be reconfigured in local mode to use stderr
+# Detect local mode early (before argparse) to avoid polluting stdout with log messages.
+# In local (stdio) mode stdout is reserved exclusively for JSON-RPC messages.
+_is_local_mode = (
+    "--mode" in sys.argv and sys.argv[sys.argv.index("--mode") + 1] == "local"
+    or os.getenv("SERVER_MODE", settings.SERVER_MODE) == "local"
+)
+
+# Setup structured logging — use stderr in local mode to keep stdout clean for MCP protocol
 setup_logging(
     level=settings.LOG_LEVEL,
     service_name=settings.APP_NAME,
     json_format=(settings.LOG_FORMAT == "json"),
-    log_file=settings.LOG_FILE,
-    use_stderr=False,  # Remote mode uses stdout
+    log_file=settings.LOG_FILE if not _is_local_mode else None,
+    use_stderr=_is_local_mode,
     log_max_bytes=settings.LOG_MAX_BYTES,
     log_backup_count=settings.LOG_BACKUP_COUNT,
 )

このコードを main.py に反映してください。差分を手で当てる場合は、git apply 用に上記を main.py.patch として保存して適用できます。

git apply --check main.py.patch && git apply main.py.patch

"--mode" in sys.argv and sys.argv[sys.argv.index("--mode") + 1] == "local" は、--mode が引数の末尾にある場合に IndexError になります。
--mode local の形で使う前提の簡易的な判定である点に注意してください。

ハマりどころ ②: /opgrc プレフィックスで認証画面にリダイレクトされる

症状

OpenPages へのリクエストが成功せず、認証が通りません。私の環境では、公式の既定パスである /opgrc/api/v2/... が HTTP 302 で
ログイン画面(logon.jsp)にリダイレクトされました。

原因

公式コードは、標準のOpenPages 向けにREST API のパスを /opgrc/api/v2/... と固定しています(_get_api_path)。
一方、私の環境は OPENPAGES_BASE_URL に /openpages/ が含まれており、REST API は /openpages/api/v2/... で直接公開されていました。

実際に curl で確認した結果です(URL のホスト部は伏せています)。

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

上記の結果は 私の環境での観測 です。/opgrc が必要なOpenPages 環境(標準構成)も存在します。
お手元の環境で同じ curl を実行し、どちらのパスが200 を返すか確認してから、この修正の要否を判断してください。

対処

API パスのプレフィックスを環境変数 OPENPAGES_API_PREFIX で上書きできるようにします。既定値は従来どおり /opgrc のままなので、
設定しない限り挙動は変わりません。

src/app/config/settings.py の差分:

--- a/src/app/config/settings.py
+++ b/src/app/config/settings.py
@@ -88,6 +88,7 @@ class Settings(BaseSettings):
     OPENPAGES_APIKEY_FILE: Optional[str] = None  # Path to file containing API key
     OPENPAGES_AUTHENTICATION_URL: str = ""
     OPENPAGES_INSTANCE_NAME: str = ""  # For CP4D deployments
+    OPENPAGES_API_PREFIX: str = "/opgrc"  # API path prefix; set to "" if base URL already includes context (e.g. /openpages/)
 
     # Cloud provider the server is provisioned on.
     # When set to "aws" the user-presented API key (auth type 4) must be exchanged

src/app/core/openpages_client.py の差分:

--- a/src/app/core/openpages_client.py
+++ b/src/app/core/openpages_client.py
@@ -235,7 +235,12 @@ class OpenPagesClient:
             return f"-opgrc{endpoint}"
         else:
             # Standard OpenPages uses: /opgrc/api/v2/...
-            return f"/opgrc{endpoint}"
+            # Allow override via OPENPAGES_API_PREFIX env var (e.g., "" for instances
+            # where the base URL already includes the context path and /api/v2/ is direct)
+            api_prefix = getattr(self.settings, 'OPENPAGES_API_PREFIX', '/opgrc')
+            if api_prefix is None:
+                api_prefix = '/opgrc'
+            return f"{api_prefix}{endpoint}"

この修正は「CP4D 以外(self.is_cp4d が偽)」の分岐にだけ効きます。CP4D 向けの -opgrc の分岐は変更していません。
そして .env に OPENPAGES_API_PREFIX=(空文字)を設定すると、パスは /api/v2/... になります。

手順 4: ローカル起動確認

仮想環境を有効化した状態で、.env を読み込んで起動します。

cd /path/to/OpenPages-MCP/ibm-openpages-mcp-server
source venv/bin/activate

set -a; source .env; set +a
python main.py --mode local

起動すると、サーバーはstdio で入力を待つ状態になり、プロンプトが戻ってこないのが正常 です。
stderr に次のようなログが出ます(私の環境の出力から抜粋)。

2026-10-26 17:22:55,162 - src.app.mcp.local.runner - INFO - Starting local MCP server with stdio transport...
2026-10-26 17:22:55,162 - src.app.mcp.local.stdio_runner - INFO - MCP server v1.0.0 starting in stdio mode...
2026-10-26 17:22:55,162 - src.app.mcp.local.stdio_runner - INFO - Server mode: local
2026-10-26 17:22:55,163 - src.app.mcp.mcp_server - INFO - === Initializing MCP Server ===

Starting local MCP server with stdio transport... が出ていれば、Local モードで起動できています。Ctrl+C で終了します。

stdout が汚れていないことの確認(任意)

ハマりどころ ① の修正が効いているかは、MCP の初期化メッセージを手で流し込み、stdout に JSON-RPC 応答以外の行 が混ざっていないかで確認できます。
ログもJSON 形式で出力されるため、「{ で始まるか」では見分けられません。"jsonrpc" を含まない行を数えます。

set -a; source .env; set +a
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke-test","version":"0.0.1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"resources/list"}' \
  | (cat; sleep 8) | timeout 20 python main.py --mode local >/tmp/stdout.txt 2>/tmp/stderr.txt

grep -vc '"jsonrpc"' /tmp/stdout.txt   # JSON-RPC 応答ではない行の数

私の環境では、修正後の main.py で 0 でした。同じ手順を修正前の main.py(git show HEAD:main.py で取り出したもの)で試すと 1 でした。
stdout の内容は、次のようなJSON-RPC 応答のみです(長いため途中を省略しています)。

{"jsonrpc": "2.0", "result": {"protocolVersion": "2025-03-26", "serverInfo": {"name": "local-mcp-server", "version": "1.0.0", ...

timeout コマンドはmacOS には標準で入っていません(brew install coreutils の gtimeout)。ない場合は timeout 20 を外し、数秒待って Ctrl+C で止めてください。
私の検証では、stdin が閉じてもサーバーが自動終了しなかったため、時間で打ち切る必要がありました。

前編のまとめ

前編では次のことを行いました。

  • 公式リポジトリをclone し、Python 仮想環境に依存パッケージをインストールした
  • Basic 認証用の .env を作成した
  • 私の環境で必要だった2 つの修正を入れた
    • ① main.py: Local モードで最初からログをstderr に出す(stdout をJSON-RPC 専用に保つ)
    • ② OPENPAGES_API_PREFIX: API パスのプレフィックスを設定で切り替えられるようにする
  • python main.py --mode local でstdio モードの起動を確認した

この時点で「.env 設定済み・コード修正済み・venv あり」の状態になりました。

後編 では、Bob から子プロセスとして起動するための run_local_bob.sh を作り、.bob/mcp.json に登録して、OpenPages のリソースやクエリがBob から使えることを確認します。あわせて、プロセスが溜まる問題(ハマりどころ ③)も扱います。

▶ 後編: https://qiita.com/mmmmmika/items/98e9008374b4e11fb264

参考

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?