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

Open Libertyで簡単に実現するMCPサーバー 【2: コード解説編】

Posted at

はじめに

OpenLibertyで簡単に実現するMCPサーバー 【1: 動かす編】では、サンプルアプリケーションの起動手順とAIエージェントと接続して利用した例をご紹介しました。
この記事では、サンプルプログラムの実装解説を行い、MCPサーバーを構築する手順や難易度を理解していきます。

前提条件

Open Liberty / WebSphere Libertyでの開発知識がある想定で記載しています。

定義ファイル

server.xml

MCPサーバーの機能を持つfeatureとしてmcpServerを指定します。ベータ版で指定可能なバージョンは1.0です。

mcpServerを導入することで、依存関係から次のfeatureも自動的に導入されます。

  • cdi-4.0
  • jsonb-3.0
  • jsonp-2.1
  • servlet-6.0

その他のfeatureは今回のアプリケーションで使用しています。

server.xml
    <featureManager>
        <feature>mcpServer-1.0</feature>
        <feature>enterpriseBeansLite-4.0</feature>
        <feature>jdbc-4.3</feature>
        <feature>transportSecurity-1.0</feature>
    </featureManager>

MCPツールの実装

JAX-RSのようにアノテーションで宣言的に定義します

サンプルプログラムからのコード抜粋(一部読みやすいように整理しています)です。ポイントは次のとおりです。

  • メソッドをツールとして宣言する
  • メソッドの引数をツール引数として宣言する
LibraryTools.java
    @Tool(
        name = "get-user", 
        title = "ユーザー情報取得",
        description = "指定されたユーザー ID の名前とロールを取得します。")
    public String getUser(
        @ToolArg(
            name = "userid",
            description = "対象ユーザーのユーザーID。"
        ) String userId) {
                return service.getUser(userId).toString();
    }

この機能をJAX-RSでRESTAPIとして公開するとしたら、次のようなコードになるはずです。

    @GET
    @Path("/user")
    @Produces(MediaType.APPLICATION_JSON)
    public String getUser(@PathParam("userId") String userId) {
        return service.getUser(userId).toString();
    }
  • アノテーションで外部公開のための定義を記載する
  • メソッド内部はプレーンなJavaコードで実装する

という点で一致しているため、APIの開発経験があれば違和感なく実装できるでしょう。

SDKによる実装との比較

MCPの公式サイトにJava用のSDKが公開されています。

こちらを使用して実装した経験もあるのですが、次のようなコードを準備する必要があります。以下のコードは、上記サイトのサンプルコードから抜粋したものです。

1. 通信のインタフェースとなるTransportProviderを作成する

    @Bean
    public HttpServletStreamableServerTransportProvider servletStreamableMcpSessionTransport() {
        return HttpServletStreamableServerTransportProvider.builder()
          .objectMapper(new ObjectMapper())
          .contextExtractor(TEST_CONTEXT_EXTRACTOR)
          .mcpEndpoint(MESSAGE_ENDPOINT)
          .keepAliveInterval(Duration.ofSeconds(1))
          .build();
    }

2.サーバー定義を行う(1のTransportProviderはここで紐づける)

McpSyncServer syncServer = McpServer.sync(transportProvider)
    .serverInfo("my-server", "1.0.0")
    .capabilities(ServerCapabilities.builder()
        .tools(true)             // Enable tool support
        .build())
    .build();

3.ツールをサーバーに追加する

var syncToolSpecification = new McpServerFeatures.SyncToolSpecification(
    new Tool("calculator", "Basic calculator", schema),
    (exchange, arguments) -> {
        // Tool implementation
        return new CallToolResult(result, false);
    }
);

syncServer.addTool(syncToolSpecification);

このコードを実装するために、次のクラスの機能と役割を理解する必要があります。

  • HttpServletStreamableServerTransportProvider
  • McpSyncServer
  • McpServerFeatures.SyncToolSpecification
  • CallToolResult

大規模な開発に適用する場合は、難しい部分を隠ぺいして実装しやすくフレームワーク化する工夫もできるでしょう。ただ、独自の実装手順を準備するとなると保守性が気になります。
SDK固有の難しさを回避して、JAX-RSに似た形で宣言的、直感的に実装できることで開発の難易度が大きく下がります。

ツール定義

主な機能はOpenLibertyのブログに記載されています。ここではアノテーションで使用する代表的な要素についてご紹介します。
詳細はJavaDocにも記載されています。JavaDocはベータ版リソースのうち (展開フォルダ)\dev\api\ibm\javadoc\io.openliberty.mcp_1.0-javadoc.zip に存在します。

Tool アノテーション

項目 必須 説明
name 一意に設定するツール名
title AIエージェントで表示するユーザー向けのツール表記
description AIがツール利用判定に使用する説明文

ToolArg アノテーション

項目 必須 説明
name 一意に設定するツール名
description AIがツール利用判定に使用する説明文
required 項目指定が必須の場合trueを指定する
指定しない場合のデフォルトはtrue

ベータ版の制約

現時点で次の制約がある認識です。

日本語対応が今後の課題

ツールから出力するテキストの文字コードが適切に設定されていないようで、日本語が絡む出力は文字化けを起こします。そのため、サンプルプログラムではアノテーション定義やテストデータは英数字で統一しています。

requiredはまだ機能しない

ToolArgアノテーションにrequiredを指定できますが、1.0.108ではrequired=falseは機能していません。AIエージェント側はrequired=falseの定義に基づき、パラメータを渡さない挙動を取ることもありますが、この場合はサーバーサイドでランタイムエラーになります。
Issueとして管理されているためいずれ解消すると思いますが、現在はrequiredの指定は行わずにパラメータを全て埋める必要があります(サンプルプログラムでは設定不要なパラメータに * という文字列を渡すという仕様をDescriptionに記載し、サーバーロジックで * の場合は評価しないことで回避しています:sweat:)。

MCPエンドポイント

/mcpというパスがエンドポイントになります。
サンプルプログラムではコンテキストルートが librarymcp のため、librarymcp/mcp というパスがMCPサーバーのエンドポイントになります。

おわりに

MCPサーバーの実装としては、現状「RESTAPIで実装し、ブリッジを使いMCP化する」というパターンもあります。
「MCP ブリッジ」で検索すると、色々なブリッジのソリューションを見つけることができます。

ただ、Structured Content(ツールの出力構造を定義する)などの一部仕様は、ブリッジで対応可能な範囲を超えているようにみえます。
今後もMCP仕様は進化していく可能性があり、なるべくMCPサーバーを提供する場合にはネイティブ実装することを第一選択としたほうが無難であると考えます。
その際、Open Libertyが提供するMCPServerのFeatureはとても頼りになるといえるでしょう。

将来的にJakartaEEの仕様として採用され、Javaが得意とするエンタープライズ領域のアプリケーションでAIとの融合が幅広く展開されるといいですね。

...と半ば強引にJakartaEEのアドベントカレンダーに結びつけてみましたがいかがでしょうか:sweat:

「JavaとAI」というとAI駆動開発が注目されがちですが、アプリケーションの機能とAIのマッシュアップこそユーザーに価値を届ける組み合わせと考えます。MCPの機能などを駆使して、新しい世代のアプリケーションサービスを提供していきましょう。

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