2
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 Bob × WAS Liberty】既存Java アプリにMCP Toolを追加するハンズオン

2
Last updated at Posted at 2026-10-02

GitHub から 備品管理アプリを取得し、既存の Service を変えずに MCP Tool を追加して IBM Bob から呼び出します。

「既存の Java システムを Bob から使えるようにする」といっても、Servlet、JSP、Service、DAO を AI 用に作り直す必要はありません。既存の業務ロジックを呼ぶ MCP 専用の入口 を追加すればよい構成にできます。

この記事では、Java 25・Jakarta EE 10・IBM WebSphere Application Server Liberty / Open Liberty へ移行済みの備品管理アプリを出発点に、MCP Tool を二つ追加します。

  • searchEquipment:備品一覧と在庫状況を返す
  • getActiveLoanSummary:貸出中の備品と期限超過件数を返す

完成後、Bob に「利用可能な備品一覧を教えて」「返却期限を過ぎた備品はある?」と入力すると、既存 Java の Service が DB を照会した結果を Bob が説明します。

この記事で分かること

  1. GitHub の Java 25 移行済みアプリを起点にする方法
  2. Liberty の mcp-1.0 を有効にする変更
  3. 既存 ItemService / LoanService を再利用する Tool の作り方
  4. Bob への接続と確認方法
  5. 既存 Web 画面を置き換えない、並行追加の考え方

MCP の概念と最小サンプルは、先にこちらを参照してください。

前提

必要なもの 用途
Podman と podman-compose アプリケーションの起動
IBM Bob MCP Client として Tool を呼び出す
Git サンプルの取得

サンプルの実行イメージは OSS の Open Liberty です。同じ Liberty 機能を使うため、IBM WebSphere Application Server Liberty でも動作する構成です。

mcp-1.0 は Liberty 26.0.0.9 で GA(正式提供)になりました。ベータ版の mcpServer-1.0 / <mcpServer> ではなく、GA 版の mcp-1.0 / <mcp> を使います。


1. 出発点のアプリを取得して起動する

まず、MCP を含まない Java 25 移行済みアプリを取得します。

git clone https://github.com/TSA0001/equipment-management-java25.git
cd equipment-management-java25
BUILDAH_FORMAT=docker podman-compose up --build -d

ブラウザで次を開きます。

http://localhost:9083/equipment-management/items

ログインには、サンプルの一般利用者 user1 / user1234 または管理者 admin / admin123 を使えます。

ここで備品一覧の画面が表示できれば、既存アプリの確認は完了です。

このリポジトリーのアプリ固有コードは Apache-2.0 で公開しています。H2 や Liberty などの外部コンポーネントについては THIRD-PARTY-NOTICES.md を参照してください。


2. MCP は既存機能の置き換えではなく、入口の並行追加

今回の変更後も、既存利用者は従来の Web 画面を使い続けます。Bob 向けにだけ EquipmentTools というクラスを追加し、既存の Service を呼びます。

既存利用者                                  IBM Bob
    ↓                                           ↓
Web画面 → AuthFilter → Servlet / JSP      MCP → EquipmentTools  ← 今回追加
                         │                    │
                         └───────┬────────────┘
                                 ↓
                    ItemService / LoanService  ← 既存の業務ロジック
                                 ↓
                                DAO → H2

この例では、既存の次のメソッドをそのまま再利用します。

// 既存の ItemService
public List<Item> search(ItemSearchCriteria criteria) throws ServiceException

// 既存の LoanService
public List<Loan> findActiveLoans() throws ServiceException

Servlet、JSP、DAO は変更しません。MCP Tool に DB の SQL を直接書くのでもなく、既存 Service の業務ルールを迂回するのでもありません。


3. Maven 依存関係を追加する

pom.xml の <properties> に、次を追加します。

<cdi.api.version>4.0.1</cdi.api.version>
<mcp.server.api.version>1.0.0</mcp.server.api.version>
<liberty.mcp.api.version>1.0.117</liberty.mcp.api.version>

続いて <dependencies> に追加します。Liberty が実行時に提供する API なので、スコープは provided です。

<dependency>
    <groupId>jakarta.enterprise</groupId>
    <artifactId>jakarta.enterprise.cdi-api</artifactId>
    <version>${cdi.api.version}</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.mcpjava</groupId>
    <artifactId>mcp-server-api</artifactId>
    <version>${mcp.server.api.version}</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>io.openliberty.api</groupId>
    <artifactId>io.openliberty.mcp</artifactId>
    <version>${liberty.mcp.api.version}</version>
    <scope>provided</scope>
</dependency>

4. Liberty に MCP エンドポイントを追加する

src/main/liberty/config/server.xml の <featureManager> に、CDI と MCP を追加します。

<featureManager>
    <feature>pages-3.1</feature>
    <feature>cdi-4.0</feature>
    <feature>mcp-1.0</feature>
    <feature>monitor-1.0</feature>
</featureManager>

既存の <application /> を開始・終了タグ形式に変更して、その内側へ MCP のパスと説明を追加します。

<application id="equipment-management"
             location="equipment-management.war"
             type="war">
    <mcp path="/mcp">
        <info name="Equipment Management"
              version="1.0.0"
              description="備品管理システムの読み取り専用照会ツール" />
    </mcp>
</application>

このアプリでは、コンテキストパスと合わせた MCP URL は次です。

http://localhost:9082/equipment-management/mcp

以降は Web 画面用のポートを 9083 から 9082 に変えます。compose.yaml の公開ポートを次のように変更してください。

ports:
  - "9082:9080"

5. AI に返す項目だけを持つ戻り値を追加する

DB の Item や Loan をそのまま返さず、AI に必要な項目だけを定義します。src/main/java/com/example/equipment/mcp を作り、次の 4 ファイルを追加します。

// EquipmentItem.java
package com.example.equipment.mcp;

public record EquipmentItem(String managementNo, String itemName,
        String categoryName, String storageLocation,
        String status, String statusLabel) {
}

// EquipmentSummary.java
package com.example.equipment.mcp;

import java.util.List;

public record EquipmentSummary(int itemCount, String statusFilter,
        List<EquipmentItem> items) {
}
// ActiveLoan.java
package com.example.equipment.mcp;

public record ActiveLoan(String managementNo, String itemName,
        String userName, String plannedReturnDate, boolean overdue) {
}

// ActiveLoanSummary.java
package com.example.equipment.mcp;

import java.util.List;

public record ActiveLoanSummary(int activeLoanCount, int overdueLoanCount,
        List<ActiveLoan> activeLoans) {
}

ここが、LLM へ渡す量を抑える境界です。たとえば Item に原価や社内メモがあっても、EquipmentItem に含めなければ Tool の結果には入りません。


6. 既存 Service を呼ぶ EquipmentTools を追加する

同じ mcp パッケージへ EquipmentTools.java を追加します。@Tool が付いた public メソッドだけが、Bob から発見・実行できる Tool になります。

package com.example.equipment.mcp;

import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

import com.example.equipment.model.Item;
import com.example.equipment.model.ItemSearchCriteria;
import com.example.equipment.model.ItemStatus;
import com.example.equipment.model.Loan;
import com.example.equipment.service.ItemService;
import com.example.equipment.service.LoanService;
import com.example.equipment.service.ServiceException;

import jakarta.enterprise.context.ApplicationScoped;
import org.mcpjava.server.tools.Tool;
import org.mcpjava.server.tools.ToolArg;

@ApplicationScoped
public class EquipmentTools {
    // 既存 Service は CDI Bean ではないため、このサンプルでは new する。
    private final ItemService itemService = new ItemService();
    private final LoanService loanService = new LoanService();

    @Tool(name = "searchEquipment", title = "備品一覧・在庫状況",
            description = "備品の一覧を返します。利用可能な在庫・保管中備品、貸出中、修理中、廃棄の状況を確認するときに使います。status を省略すると全件、AVAILABLE を指定すると利用可能な備品だけを返します。",
            structuredContent = true)
    public EquipmentSummary searchEquipment(
            @ToolArg(name = "status", required = false,
                    description = "AVAILABLE=利用可能(保管中・在庫)、LOANED=貸出中、REPAIRING=修理中、DISPOSED=廃棄。省略時は全状態。") String status)
            throws ServiceException {
        ItemSearchCriteria criteria = new ItemSearchCriteria();
        ItemStatus itemStatus = normalizeStatus(status);
        if (itemStatus != null) {
            criteria.setStatus(itemStatus.name());
        }

        List<Item> items = itemService.search(criteria); // 既存処理を再利用
        List<EquipmentItem> equipment = new ArrayList<EquipmentItem>();
        for (Item item : items) {
            equipment.add(new EquipmentItem(item.getManagementNo(), item.getItemName(),
                    item.getCategoryName(), item.getStorageLocation(),
                    item.getStatus().name(), item.getStatusLabel()));
        }
        return new EquipmentSummary(equipment.size(),
                itemStatus == null ? null : itemStatus.name(), equipment);
    }

    @Tool(name = "getActiveLoanSummary", title = "貸出中備品の状況",
            description = "現在貸出中の備品を一覧で返します。備品名、管理番号、利用者、返却予定日、期限超過かどうかを確認したいときに使います。",
            structuredContent = true)
    public ActiveLoanSummary getActiveLoanSummary() throws ServiceException {
        List<Loan> loans = loanService.findActiveLoans(); // 既存処理を再利用
        List<ActiveLoan> activeLoans = new ArrayList<ActiveLoan>();
        int overdueCount = 0;
        for (Loan loan : loans) {
            if (loan.isOverdue()) {
                overdueCount++;
            }
            activeLoans.add(new ActiveLoan(loan.getManagementNo(), loan.getItemName(),
                    loan.getUserName(), formatDate(loan.getPlannedReturnDate()), loan.isOverdue()));
        }
        return new ActiveLoanSummary(activeLoans.size(), overdueCount, activeLoans);
    }

    private String formatDate(Date date) {
        return date == null ? null : date.toString();
    }

    private ItemStatus normalizeStatus(String status) throws ServiceException {
        if (status == null || status.trim().isEmpty()) {
            return null;
        }
        String normalized = status.trim().toUpperCase();
        if ("利用可能".equals(status) || "保管中".equals(status) || "在庫".equals(status)) {
            normalized = ItemStatus.AVAILABLE.name();
        }
        try {
            return ItemStatus.fromCode(normalized);
        } catch (IllegalArgumentException e) {
            throw new ServiceException("status は AVAILABLE、LOANED、REPAIRING、DISPOSED のいずれかを指定してください。");
        }
    }
}

既存アプリの Service がすでに CDI Bean の場合は、new ItemService() / new LoanService() の代わりに @Inject を使います。


7. getActiveLoanSummary 用のデータを用意する

searchEquipment は初期データだけで確認できます。一方、getActiveLoanSummary で「貸出中」「期限超過」を試すには、貸出中のデータが必要です。

最も分かりやすい方法は、既存の Web 画面から 2 件を貸し出すことです。

  1. 管理者 admin でログインする
  2. 備品一覧から 2 件を貸し出す
  3. 片方の返却予定日を過去日に、もう片方を未来日にする

これで既存 Web 画面が作成した LOANS レコードを、MCP Tool が LoanService.findActiveLoans() 経由で読むことを確認できます。MCP 専用の DB や SQL は不要です。

初期データとして組み込みたい場合は、最初の起動前に src/main/resources/db/seed.sql へ、状態が LOANED の備品と対応する LOANS レコードを追加しても構いません。このサンプルは DB が未作成のときだけ seed を投入するため、すでに起動済みなら Web 画面から登録する方法が安全です。


8. デモ用に /mcp を認証フィルタから通す

既存の AuthFilter が未ログイン利用者を /login へリダイレクトする場合、Bob の通信も止まります。デモでは isPublicPath に /mcp を追加します。

return "/login".equals(path)
        || "/logout".equals(path)
        || "/health".equals(path)
        || "/mcp".equals(path) // デモ用
        || path.startsWith("/css/");

この認証除外はデモ限定です。 本番では /mcp を匿名公開せず、Liberty / Jakarta Security で認証し、利用者のロールごとに Tool の利用可否を制御してください。最初は読み取り専用 Tool だけを公開するのが安全です。


9. 起動して Bob から確認する

変更後のアプリを起動します。

BUILDAH_FORMAT=docker podman-compose up --build --force-recreate -d

Bob の MCP 設定に、次を追加します。

{
  "mcpServers": {
    "liberty-equipment": {
      "type": "streamable-http",
      "url": "http://localhost:9082/equipment-management/mcp"
    }
  }
}

Bob 側でサーバーが「接続済み」になったら、新しいチャットを開いて次のように入力します。

Bob への入力 実行される Tool
利用可能な備品一覧を教えて searchEquipment
現在貸出中の備品を教えて getActiveLoanSummary
返却期限を過ぎた備品はある? getActiveLoanSummary

Tool を追加・修正した後は、Bob の MCP サーバーを再接続し、新しいチャットで試してください。古いチャットには以前取得した Tool 一覧が残ることがあります。

まとめ

このハンズオンで加えたのは、MCP の設定、Tool クラス、AI に返す専用の戻り値、デモ用の経路設定です。

既存の Web 画面、Servlet、JSP、Service、DAO を MCP 専用に置き換えてはいません。EquipmentTools を並行して追加し、既存 ItemService / LoanService を使うため、従来の業務ロジックを保ちながら Bob 向けの入口を増やせます。

次の段階では、Tool の戻り値を用途ごとにさらに絞り、認証・認可と監査ログを追加して本番利用へ近づけます。

2
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
2
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?