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 Verify SaaS MCP Serverの構築手順

0
Posted at

IBM Verify SaaS の MCP サーバーをローカルに構築してみた

はじめに

IBM Verify SaaS は、IBM が提供するクラウドベースの IAM(Identity and Access Management)プラットフォームです。SCIM 2.0 準拠の REST API を持っており、ユーザー管理やアクセス制御を自動化できます。

もちろん公式ドキュメントをBobに入れて勝手にMCPサーバーを構築してもらうという手もありますが、今回はあえてドキュメントベースにMCPサーバー構築時のステップを解説します。

本記事では、IBM Verify SaaS の SCIM API を操作する MCP(Model Context Protocol)サーバー をローカル PC 上に構築する手順を解説します。MCP サーバーを作ることで、Claude Desktop や IBM Bob などの AI エージェントから自然言語で Verify のユーザー管理操作(一覧取得・作成・更新・削除・Excel 一括登録)ができるようになります。

参考公式ドキュメント


完成イメージ

AI エージェント(Claude / IBM Bob)
        ↓  MCP protocol (stdio)
mcp-verify-users(ローカル Node.js プロセス)
        ↓  SCIM 2.0 REST API + OAuth 2.0
IBM Verify SaaS テナント

構築後は以下のような自然言語命令が使えるようになります。

  • 「Verify のユーザー一覧を取得して」
  • 「yamada@example.com というユーザーを作成して」
  • 「この Excel ファイルのユーザーを一括登録して」

前提条件

項目 内容
OS Windows 10/11、macOS、Linux
Node.js v18 以上(node --version で確認)
npm v9 以上(Node.js に同梱)
IBM Verify SaaS テナント管理者権限があるアカウント

Step 1: IBM Verify SaaS の API クライアントを作成する

MCP サーバーが IBM Verify API を呼び出すためには、OAuth 2.0 クライアントクレデンシャルが必要です。

1-1. Verify 管理コンソールにログイン

https://<your-tenant>.verify.ibm.com にアクセスし、管理者アカウントでログインします。

1-2. API クライアントの作成

  1. 左メニューから 「セキュリティ」→「API アクセス」 を選択
  2. 「API クライアントの追加」 ボタンをクリック
  3. 以下を設定して保存
設定項目 値
名前 mcp-verify-users(任意)
クライアント ID 自動生成(後でコピー)
クライアントシークレット 自動生成(後でコピー)
権限 manageUsers、readUsers にチェック

⚠️ クライアントシークレットは作成時にしか表示されません。 必ずコピーして安全な場所に保存してください。

1-3. 取得した情報をメモ

以下の 3 つの値を控えておきます。

VERIFY_TENANT_URL   = https://<your-tenant>.verify.ibm.com
VERIFY_CLIENT_ID    = <クライアント ID>
VERIFY_CLIENT_SECRET = <クライアントシークレット>

Step 2: プロジェクトを作成する

2-1. ディレクトリ作成と初期化

mkdir mcp-verify-users
cd mcp-verify-users
npm init -y

2-2. 依存パッケージのインストール

npm install @modelcontextprotocol/sdk zod xlsx
npm install -D typescript @types/node
パッケージ 用途
@modelcontextprotocol/sdk MCP サーバー実装の SDK
zod ツールの入力スキーマ定義・バリデーション
xlsx Excel ファイルの読み込み(一括登録機能)
typescript TypeScript コンパイラ

2-3. TypeScript の設定

tsconfig.json をプロジェクトルートに作成します。

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "types": ["node"]
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

2-4. package.json を編集

package.json を以下のように修正します。

{
  "name": "mcp-verify-users",
  "version": "1.0.0",
  "description": "IBM Verify User Management MCP Server",
  "type": "module",
  "main": "build/index.js",
  "scripts": {
    "build": "tsc"
  },
  "bin": {
    "mcp-verify-users": "./build/index.js"
  }
}

"type": "module" は ES Modules を使用するために必須です。


Step 3: MCP サーバーの実装

src/index.ts を作成し、以下のコードを記述します。

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as XLSX from "xlsx";
import * as fs from "fs";
import * as path from "path";

// ── 環境変数から認証情報を読み込む ──────────────────────────
const TENANT_URL = process.env.VERIFY_TENANT_URL;
const CLIENT_ID = process.env.VERIFY_CLIENT_ID;
const CLIENT_SECRET = process.env.VERIFY_CLIENT_SECRET;

if (!TENANT_URL || !CLIENT_ID || !CLIENT_SECRET) {
  console.error(
    "Error: VERIFY_TENANT_URL, VERIFY_CLIENT_ID, VERIFY_CLIENT_SECRET are required"
  );
  process.exit(1);
}

const server = new McpServer({ name: "mcp-verify-users", version: "1.0.0" });

// ── OAuth 2.0 アクセストークン取得 ──────────────────────────
async function getAccessToken(): Promise<string> {
  const url = `${TENANT_URL}/oauth2/token`;
  const body = new URLSearchParams({
    grant_type: "client_credentials",
    client_id: CLIENT_ID!,
    client_secret: CLIENT_SECRET!,
  });

  const res = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: body.toString(),
  });

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Token request failed: ${res.status} ${text}`);
  }

  const data = (await res.json()) as { access_token: string };
  return data.access_token;
}

// ── SCIM API 共通リクエスト ──────────────────────────────────
async function verifyRequest(
  method: string,
  path: string,
  body?: unknown
): Promise<unknown> {
  const token = await getAccessToken();
  const url = `${TENANT_URL}${path}`;

  const res = await fetch(url, {
    method,
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/scim+json",
      Accept: "application/scim+json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  const text = await res.text();
  if (!res.ok) {
    throw new Error(`Verify API error: ${res.status} ${text}`);
  }
  return text ? JSON.parse(text) : {};
}

ツール登録

続いて、AI エージェントが呼び出せる各ツールを registerTool で登録します。

① ユーザー一覧取得

server.registerTool(
  "list_users",
  {
    description: "IBM Verify テナントのユーザー一覧を取得します",
    inputSchema: z.object({
      count: z.number().optional().describe("取得件数(デフォルト: 20)"),
      startIndex: z.number().optional().describe("開始インデックス(デフォルト: 1)"),
      filter: z.string().optional().describe('SCIM フィルター例: "userName eq \\"john\\""'),
    }),
  },
  async ({ count = 20, startIndex = 1, filter }) => {
    const params = new URLSearchParams({
      count: String(count),
      startIndex: String(startIndex),
    });
    if (filter) params.set("filter", filter);
    const data = await verifyRequest("GET", `/v2.0/Users?${params}`);
    return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
  }
);

② ユーザー作成

server.registerTool(
  "create_user",
  {
    description: "IBM Verify に新しいユーザーを作成します",
    inputSchema: z.object({
      userName: z.string().describe("ユーザー名(メールアドレス推奨)"),
      givenName: z.string().describe("名(First name)"),
      familyName: z.string().describe("姓(Last name)"),
      email: z.string().describe("メールアドレス"),
      userPassword: z.string().optional().describe("初期パスワード(省略時は招待メール送信)"),
    }),
  },
  async ({ userName, givenName, familyName, email, userPassword }) => {
    const body: Record<string, unknown> = {
      schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
      userName,
      name: { givenName, familyName },
      emails: [{ value: email, type: "work", primary: true }],
    };
    if (userPassword) body["password"] = userPassword;
    const data = await verifyRequest("POST", "/v2.0/Users", body);
    return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
  }
);

③ ユーザー更新(SCIM PATCH)

server.registerTool(
  "update_user",
  {
    description: "既存ユーザーの情報を更新します(SCIM PATCH)",
    inputSchema: z.object({
      userId: z.string().describe("更新対象のユーザー ID"),
      givenName: z.string().optional().describe("新しい名(First name)"),
      familyName: z.string().optional().describe("新しい姓(Last name)"),
      email: z.string().optional().describe("新しいメールアドレス"),
      active: z.boolean().optional().describe("アカウントの有効/無効"),
    }),
  },
  async ({ userId, givenName, familyName, email, active }) => {
    const operations: unknown[] = [];
    if (givenName !== undefined)
      operations.push({ op: "replace", path: "name.givenName", value: givenName });
    if (familyName !== undefined)
      operations.push({ op: "replace", path: "name.familyName", value: familyName });
    if (email !== undefined)
      operations.push({ op: "replace", path: 'emails[type eq "work"].value', value: email });
    if (active !== undefined)
      operations.push({ op: "replace", path: "active", value: active });

    const body = {
      schemas: ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
      Operations: operations,
    };
    const data = await verifyRequest("PATCH", `/v2.0/Users/${userId}`, body);
    return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
  }
);

④ Excel 一括ユーザー登録

Excel ファイルの列構成(1 行目はヘッダー):

userName givenName familyName email userPassword
john@example.com John Doe john@example.com Pass1234!
server.registerTool(
  "bulk_create_users",
  {
    description: "ExcelファイルからIBM Verifyにユーザーを一括登録します",
    inputSchema: z.object({
      filePath: z.string().describe("Excelファイルのパス(絶対パスまたは相対パス)"),
    }),
  },
  async ({ filePath }) => {
    const resolvedPath = path.resolve(filePath);
    if (!fs.existsSync(resolvedPath)) {
      throw new Error(`ファイルが見つかりません: ${resolvedPath}`);
    }
    const workbook = XLSX.readFile(resolvedPath);
    const sheet = workbook.Sheets[workbook.SheetNames[0]];
    const rows = XLSX.utils.sheet_to_json<Record<string, string>>(sheet, { defval: "" });
    // ... 各行をループして verifyRequest("POST", "/v2.0/Users", body) を呼び出す
  }
);

⑤ ユーザー削除

server.registerTool(
  "delete_user",
  {
    description: "指定した ID のユーザーを削除します",
    inputSchema: z.object({
      userId: z.string().describe("削除対象のユーザー ID"),
    }),
  },
  async ({ userId }) => {
    await verifyRequest("DELETE", `/v2.0/Users/${userId}`);
    return { content: [{ type: "text" as const, text: `ユーザー ${userId} を削除しました` }] };
  }
);

サーバー起動処理

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("mcp-verify-users running on stdio");
}

main().catch((error) => {
  console.error("Fatal error:", error);
  process.exit(1);
});

Step 4: ビルドする

npm run build

build/index.js が生成されれば成功です。

mcp-verify-users/
├── build/
│   └── index.js   ← コンパイル済みファイル
├── src/
│   └── index.ts
├── package.json
└── tsconfig.json

Step 5: AI エージェントに MCP サーバーを登録する

IBM Bob の場合(.bob/mcp.json)

プロジェクトルートの .bob/mcp.json、またはグローバル設定 ~/.bob/mcp.json に以下を追記します。

{
  "mcpServers": {
    "mcp-verify-users": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-verify-users/build/index.js"],
      "env": {
        "VERIFY_TENANT_URL": "https://<your-tenant>.verify.ibm.com",
        "VERIFY_CLIENT_ID": "<クライアント ID>",
        "VERIFY_CLIENT_SECRET": "<クライアントシークレット>"
      }
    }
  }
}

ポイント: args のパスは 絶対パス を指定してください。Windows では C:\\Users\\... のようにバックスラッシュをエスケープするか、フォワードスラッシュ C:/Users/... を使用します。

Claude Desktop の場合(claude_desktop_config.json)

{
  "mcpServers": {
    "mcp-verify-users": {
      "command": "node",
      "args": ["C:/path/to/mcp-verify-users/build/index.js"],
      "env": {
        "VERIFY_TENANT_URL": "https://<your-tenant>.verify.ibm.com",
        "VERIFY_CLIENT_ID": "<クライアント ID>",
        "VERIFY_CLIENT_SECRET": "<クライアントシークレット>"
      }
    }
  }
}

設定ファイルの場所:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Step 6: 動作確認

AI エージェントに以下のように話しかけてみましょう。

Verify のユーザー一覧を 5 件取得して

正常に動作していれば、SCIM API のレスポンスが JSON 形式で返ってきます。

{
  "totalResults": 42,
  "itemsPerPage": 5,
  "startIndex": 1,
  "Resources": [
    {
      "id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "userName": "yamada@example.com",
      "name": {
        "givenName": "太郎",
        "familyName": "山田"
      },
      ...
    }
  ]
}

実装のポイント解説

なぜ stdio トランスポートを使うのか

MCP には stdio(標準入出力)と HTTP の 2 つのトランスポートがあります。ローカル PC での構築には stdio が推奨されます。

stdio HTTP
セットアップ 簡単(HTTP サーバー不要) HTTP サーバーが必要
セキュリティ 高い(ネットワーク露出なし) 別途 認証が必要
遅延 低い ネットワーク分のオーバーヘッド

OAuth 2.0 クライアントクレデンシャルフロー

IBM Verify SaaS の API 認証には OAuth 2.0 の Client Credentials フロー を使用します。ユーザー介在なしにサービス間認証ができるため、自動化・バックエンド連携に適しています。

MCP サーバー → POST /oauth2/token (client_id + client_secret)
             ← access_token(有効期限あり)
MCP サーバー → GET /v2.0/Users (Authorization: Bearer <token>)
             ← SCIM レスポンス

アクセストークンには有効期限があります。本実装では各 API 呼び出しの都度トークンを取得しているため、期限切れの心配は不要ですが、高頻度な呼び出しではトークンをキャッシュする最適化も検討してください。

SCIM 2.0 のエンドポイント

IBM Verify は SCIM 2.0 標準に準拠しており、以下のエンドポイントを利用しています。

操作 メソッド エンドポイント
一覧取得 GET /v2.0/Users
詳細取得 GET /v2.0/Users/{id}
作成 POST /v2.0/Users
更新 PATCH /v2.0/Users/{id}
削除 DELETE /v2.0/Users/{id}

トラブルシューティング

Error: VERIFY_TENANT_URL, VERIFY_CLIENT_ID, VERIFY_CLIENT_SECRET are required

環境変数が設定されていません。MCP 設定ファイルの env セクションを確認してください。

Token request failed: 401

クライアント ID またはクライアントシークレットが誤っています。Verify 管理コンソールで再確認してください。

Verify API error: 403

API クライアントに必要な権限(manageUsers、readUsers)が付与されていません。Step 1-2 を再確認してください。

build/index.js が見つからない

npm run build を実行してビルドを完了させてください。tsconfig.json の outDir が ./build になっているか確認してください。


まとめ

IBM Verify SaaS の SCIM API を操作する MCP サーバーをローカル PC 上に構築することがそこまで難しいことではないですが、MCP側のツールインストールや、Verify API側の権限設計が重要です。

VerifyにおけるMCPサーバーの権限管理について次回の記事に開設したいと思います。

参考リンク

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?