Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring AI × MCP 入門〜AIエージェントへのツール公開、境界設計から始める最小構成 〜

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Spring AI × MCP 入門〜AIエージェントへのツール公開、境界設計から始める最小構成 〜

Avatar for 宮本裕也

宮本裕也

May 29, 2026

Other Decks in Technology

Transcript

  1. 1 / 54 S P R I N G A

    I × M C P J J U G C C C 2 0 2 6 S p r i n g Spring AI × MCP 入門 〜 AIエージェントへのツール公開、境界設計から始める最小構成 〜 宮本 裕也 / 2026-05-30
  2. 自己紹介 宮本 裕也 Yuya Miyamoto 得意領域 Java Spring TypeScript CI

    バックエンドエンジニア 大阪 関心ごと Web開発 AI活用 自動化 気になった技術は自分の手で触ってみないと気が済まない性格です。 今日お話しする MCP も、そのひとつ。 はじめに 2 / 54 S P R I N G A I × M C P 登壇のきっかけを最初に ▶
  3. きっかけ 〜 AI がツールを使ってくれない 〜 01 期待していたこと Todo アプリ用の MCP

    を作成 02 実際に起きたこと AI がツールを使いこなせていない 03 辿り着いた答え 原因はモデルではなく ツール設計 の問題 ツールは AI ファースト で設計すべ き 具体的に AI ファーストで設計するとはどういうことか —— 本日はそれを共有します。 はじめに 3 / 54 S P R I N G A I × M C P
  4. アジェンダ 01 はじめに 02 MCP 概要 03 MCP Server Boot

    Starter 概要 04 境界設計×実装 本日の核 05 補足 06 まとめ 重要なのは、ハイライトしている 「境界設計×実装」 です。 はじめに 5 / 54 S P R I N G A I × M C P
  5. 6 / 54 02 S P R I N G

    A I × M C P S E C T I O N 0 2 / 0 6 MCP 概要 入門セッションのため、簡単に説明します。
  6. MCP とは Model Context Protocol — AI エージェントとツールを繋ぐための標準規格。 目指すもの: あらゆる

    AI エージェントとあらゆるツールが、 ひとつのインターフェースで繋がれること。 MCP 概要 7 / 54 S P R I N G A I × M C P MCP の生い立ち ▶
  7. N×M 問題 N 個の AI エージェント × M 個のツール =

    最大 N×M 個の個別実装が必要。 保守地獄 1 つの仕様変更が、繋がっていた実装すべての修正 を必要とする 利用者の負担 選択と接続の手間が膨らむ MCP 概要 9 / 54 S P R I N G A I × M C P
  8. MCP 登場 — N×M問題の解消 MCP は AI エージェントとツールの接続方法を統一・標準化しました。 エージェント側・ツール側それぞれMCPに対応する実装を 1

    つ。 MCPという規格を通すことで、すべての組み合わせが接続可能になった。 MCP 概要 10 / 54 S P R I N G A I × M C P
  9. 11 / 54 03 S P R I N G

    A I × M C P S E C T I O N 0 3 / 0 6 MCP Server Boot Starter 概要 Spring AI MCP Server Boot Starter の説明をします。
  10. Spring AI が提供する 2 つの Starter MCP のサーバー / クライアントの役割を、Spring

    AI がそれぞれ Starter として提供。 サーバー — 機能を提供する側 例: Gmail / Slack / PostgreSQL MCP Server クライアント — 機能を利用する側 例: Claude Desktop / Codex / GitHub Copilot ▼ ▼ Spring AI Spring AI MCP Server Boot Starter Spring AI MCP Client Boot Starter Starter 概要 12 / 54 S P R I N G A I × M C P
  11. Spring AI MCP Server Boot Starter とは MCP の仕様部分を Starter

    側が負担し、 開発者はビジネスロジックの実装だけに 専念できるようにした Spring のフレームワーク。 Starter 概要 13 / 54 S P R I N G A I × M C P
  12. 担当分離 STARTER が担う 仕様・通信・統合 JSON-RPC 2.0 の解析・組み立て MCP クライアント↔サーバー間の通信基盤 Transport

    系の通信制御 リクエスト → メソッド引数のバインディング Spring Boot ライフサイクルへの統合 開発者が書く 設計とビジネスロジック ツールのビジネスロジック AIエージェント向けインターフェース設計 name / description の設計 返却値・エラー設計 監査ログ 役割分担は Starter に限らず、どの言語のフレームワークでも共通。 Starter 概要 14 / 54 S P R I N G A I × M C P
  13. Starter の特徴 — 3 つ 01 Spring エコシステムとの統合 Spring Boot

    のスキルセットがその まま使える。 02 アノテーションを用いた宣言的 定義 @McpTool / @McpToolParam を 付けるだけ。 03 Spring AI フレームワークとの連 携 ChatClient / VectorStore / RAG と同じ世界で構築できる。 — 本日は省略 Starter 概要 15 / 54 S P R I N G A I × M C P
  14. 特徴 ① Spring エコシステムとの統合 Spring Boot のスキルセットがそのまま使える。 @Component で Bean

    登録 application.yml で設定 既存業務ロジックを DI で注入(書き直し不要) → 既存アプリの「もう一つの入り口」として実装できる Starter 概要 16 / 54 S P R I N G A I × M C P
  15. 特徴 ② アノテーションを用いた宣言的定義 アノテーションをつけるだけで、 自動検出 → JSON Schema 生成 →

    ルーティングまで自動。 @McpTool(name = "search_tasks", description = "タスクを検索"...) public List<TaskDto> searchTasks( @McpToolParam(description = "検索キーワード") String keyword, ... ) { ... } Starter 概要 17 / 54 S P R I N G A I × M C P
  16. 18 / 54 04 S P R I N G

    A I × M C P S E C T I O N 0 4 / 0 6 境界設計×実装 ここから本題。ツールの設計は AI ファーストでなければならない。
  17. 人間向け API 設計と AI 向けツール設計は受け手の前提が違う 人間向け API 設計 提供側 利用側

    API 決定論的 ↔ 開発者 決定論的 前提が一致 AI 向けツール設計 提供側 利用側 ツール 決定論的 ↔ AI 確率的 前提が片側だけずれる API・ツール・開発者は挙動・解釈が安定。 一方 AI は出力が確率的。 結果、AI 向けツール設計は前提が片側だけずれる。 境界設計×実装 19 / 54 S P R I N G A I × M C P
  18. 前提差は具体的に 4 つの関心事に表れる 関心事 人間向け API 設計 AI 向けツール設計 公開機能の分類軸

    副作用の有無 回復可能性 説明文の目的 開発者への仕様説明 モデルへのプロンプト 返却値の設計 クライアントが使いやすい形 次の推論を壊さない形 ログの目的 障害診断 AI の行動の事後検証 境界設計×実装 20 / 54 S P R I N G A I × M C P この 4 つを設計原則として整理する ▶
  19. 境界設計の 4 原則 4 つの関心事を、設計原則として言い換える。 01 公開機能は 可逆 02 説明文は

    プロンプト 03 返却値は 次の推論インプット 04 ログは 行動の証跡 境界設計の由来は、前提の違う 2 者がぶつかる面 = 境界 を意識的に設計するから 境界設計×実装 21 / 54 S P R I N G A I × M C P
  20. 22 / 54 04 S P R I N G

    A I × M C P S E C T I O N 0 4 / 0 6 原 則 ① / ④ 公開機能は可逆
  21. 公開するツールは可逆な操作に限定する 設計軸の転換 読み取り / 書き込み ではなく 可逆 / 不可逆。 ①

    公開機能は可逆 結論 23 / 54 S P R I N G A I × M C P 読み取り/書き込みの分類では限界がある ▶
  22. 不可逆操作の扱い — 原則と例外 公開してよい — 元に戻せる 論理削除(フラグで隠す) アーカイブ化 公開しない —

    元に戻せない 物理削除 外部通知(メール送信・Webhook) どうしても不可逆を公開するなら、ツール側で Human-in-the-loop を強制する。 ① 公開機能は可逆 方針 25 / 54 S P R I N G A I × M C P
  23. 実装 — @McpTool.McpAnnotations でツールの性質を宣言する Starter が用意する宣言は計 4 つ。注目すべきは destructiveHint。 ヒント

    意味 デフォルト readOnlyHint 読み取り専用か false destructiveHint 破壊的操作(=不可逆)か true idempotentHint 冪等か false openWorldHint サーバの外側と相互作用するか true ① 公開機能は可逆 実装 26 / 54 S P R I N G A I × M C P
  24. 実装 — 可逆 / 読み取り専用は明示的に打ち消す ヒントを書き忘れたツールは 「破壊的・書き込みあり = 危険」 として扱われる。

    = 読み取り専用や可逆なツールは、デフォルトを明示的に打ち消す。 annotations = @McpTool.McpAnnotations( readOnlyHint = true, // 読み取り専用であることを宣言 destructiveHint = false, // デフォルト(true)を打ち消す idempotentHint = true, openWorldHint = false ) ※ Tool Annotations はMCP仕様上 「信頼できないヒント」 という位置づけ AIエージェントにとって、安全性を保証する根拠にはならないが、実行判断に影響するため正確な記述は必要 ① 公開機能は可逆 実装 27 / 54 S P R I N G A I × M C P
  25. 28 / 54 04 S P R I N G

    A I × M C P S E C T I O N 0 4 / 0 6 原 則 ② / ④ 説明文はプロンプト
  26. 説明文はモデルへの指示 — プロンプトとして設計する REST API の仕様書 読み手は人間。 網羅性・正確性が求められる。 ツールの description

    読み手はモデル。 いつ使うかの判断が決まる。 description に仕様を書いても、 モデルが「いつ使うべきか」判断できなければ適切に呼ばれない。 ② 説明文はプロンプト 結論/理由 29 / 54 S P R I N G A I × M C P
  27. description に書くべき 3 要素 01 使用条件 いつ使うかを書く 02 否定的境界 類似ツールとの使い分けを書く

    03 副作用宣言 副作用は明記する モデルは書かれていないことを補完しません。 書かれた通りに、書かれていない通りに判断します。 ② 説明文はプロンプト 方針 30 / 54 S P R I N G A I × M C P
  28. 実装 — description に 3 要素を仕込む ① 使用条件 ② 否定的境界

    ③ 副作用宣言 @McpTool( name = "archive_task", description = "タスク ID を指定して、タスクを論理アーカイブ(非表示化)します。" // ① 使用条件 + " 完全に削除(物理削除)したい場合は delete_task_with_elicit を使ってください。" // ② 否定的境界 + " 副作用: 担当者にアーカイブの通知メールが送信されます。", // ③ 副作用宣言 annotations = @McpTool.McpAnnotations(...) ) public String archiveTask( @McpToolParam(description = "1 以上の整数", required = true) final int taskId ) { ... } 「書き方」そのものが設計。 ② 説明文はプロンプト 実装 31 / 54 S P R I N G A I × M C P
  29. 32 / 54 04 S P R I N G

    A I × M C P S E C T I O N 0 4 / 0 6 原 則 ③ / ④ 返却値は次の推論インプット
  30. 返却値はモデルの推論インプット 返却値は API の「出力」ではなく、 モデルのコンテキストへ流れ込む 「入力」 として設計する。 01 ツールが値を返す ▼

    02 モデルのコンテキストに流入 ▼ 03 次の推論 = 次のツール選択 / 次のユーザー応答 ③ 返却値は次の推論インプット 結論 33 / 54 S P R I N G A I × M C P
  31. 返却値を整形する 2 つの観点 01 ノイズは最初から削る 重複フィールドは削除 次の推論で使わない値は削除 モデルはノイズ耐性が弱い — コンテキストに一度入った情報は取り除けない

    02 先回りで詰め込まない 検索と詳細取得はツールで分ける フルドキュメントはまず要約する 必要かはケースバイケース — 取得するかどうかは AI が判断すべき ③ 返却値は次の推論インプット 方針 34 / 54 S P R I N G A I × M C P
  32. エラーレスポンスもモデルへの分岐指示 人間向けエラー Error: タスクが見つかりません → モデルは諦めるか、同じリクエストをループする。 AI 向けエラー Error: タスク

    999 は存在しません search_tasks で類似名のタスクを検索してください → モデルは正しい次アクションに誘導される。 「何が起きたか」だけでなく「次に何をすべきか」まで返し、AI の暴走・迷走を防ぐ。 ③ 返却値は次の推論インプット 方針 35 / 54 S P R I N G A I × M C P
  33. 実装 — POJO / Record で返す 最もシンプルな書き方。データクラスをそのまま return するだけ。 @McpTool(name

    = "get_task_detail", description = "タスク ID からタスクを取得します。...") public TaskDto getTaskDetail( @McpToolParam(...) final int taskId, @McpToolParam(...) final List<String> fields ) { if (!taskService.isValidFields(fields)) { throw new IllegalArgumentException( "不正なフィールドが含まれています。" + "指定可能なフィールド一覧は list_task_fields で確認してください。"); } return taskService.selectFieldsById(taskId, fields) .orElseThrow(() -> new IllegalArgumentException( "タスク " + taskId + " は存在しません。一覧から探す場合は search_tasks を使ってください。")); } ③ 返却値は次の推論インプット 実装 36 / 54 S P R I N G A I × M C P
  34. 実装 — CallToolResult で画像などのメディアを返す POJO 返却と違い、画像などを単なる文字列ではなく型付きコンテンツとして返せる。 マルチモーダル対応モデルは画像を画像として認識・解釈できる。 @McpTool(name = "get_task_attachment",

    description = "タスクに添付された画像を取得します。...") public CallToolResult getTaskAttachment( @McpToolParam(...) int taskId ) { Attachment file = taskService.findAttachment(taskId); String base64 = Base64.getEncoder().encodeToString(file.bytes()); return CallToolResult.builder() .addTextContent("タスク " + taskId + " の添付画像です。") // 自然言語の補足 .addContent(new ImageContent(null, base64, file.mimeType())) // 型付きコンテンツとして返す .build(); } ③ 返却値は次の推論インプット 実装 37 / 54 S P R I N G A I × M C P
  35. 38 / 54 04 S P R I N G

    A I × M C P S E C T I O N 0 4 / 0 6 原 則 ④ / ④ ログは行動の証跡
  36. 実装 — AOP で監査ログを共通化 @Aspect @Component public class McpToolAuditAspect {

    @Around("@annotation(mcpTool)") public Object auditToolCall( ProceedingJoinPoint jp, McpTool mcpTool) throws Throwable { String tool = mcpTool.name(); log.info("[AUDIT] {} called args={}", tool, jp.getArgs()); try { Object result = jp.proceed(); log.info("[AUDIT] {} success", tool); return result; } catch (Throwable t) { log.error("[AUDIT] {} error: {}", tool, t.getMessage(), t); throw t; } } } AOP で全ツールの 監査ログを共通化できる @McpTool 付きメソッドを対象にする 呼ばれた時と結果を返す時にログを 書く Aspect 1 つで個別に書かなくて済む ④ ログは行動の証跡 実装 41 / 54 S P R I N G A I × M C P
  37. 42 / 54 05 S P R I N G

    A I × M C P S E C T I O N 0 5 / 0 6 補足 MCP ならではの仕様と、Starter での実装を紹介します。 ① Elicitation / ② Sampling
  38. 実装Step1 — McpSyncRequestContext の注入とフォールバック @McpTool(name = "delete_task_with_elicit", ...) public String

    deleteTaskWithElicit( final McpSyncRequestContext context, // ① @McpToolParam(description = "1 以上の整数。") final int taskId ) { if (!context.elicitEnabled()) { // ① return "Elicitation 未対応のため削除しません"; } ... } ① 第一引数に McpSyncRequestContext ツール実行中に MCP クライアントと同期でやり取り するときに付ける。Starter が自動注入。 未対応クライアントへのフォールバック Elicitation は比較的新しい機能。未対応なら 「実 行しない」に倒す。 補足 ① Elicitation 44 / 54 S P R I N G A I × M C P
  39. 実装Step2 — 確認リクエストの定義 record ApprovalInput( @McpToolParam(description = "削除する場合は true") boolean

    approveDeletion ) {} @McpTool(name = "delete_task_with_elicit", ...) public String deleteTaskWithElicit(...) { ... String elicitMessage = "タスク " + taskId + " を削除しますか?"; StructuredElicitResult<ApprovalInput> result = context.elicit( s -> s.message(elicitMessage), ApprovalInput.class ); ... } 回答受け取り型 ユーザーの回答をマッピングする Java の型を .class で指定する。 確認メッセージ ユーザーに送る文言。操作の重大さに合わせて丁寧 に書く。雑に書くと意味がない。 補足 ① Elicitation 45 / 54 S P R I N G A I × M C P
  40. 実装Step3 — ユーザーの返答に応じて処理を分岐 @McpTool(name = "delete_task_with_elicit", ...) public String deleteTaskWithElicit(...)

    { ... StructuredElicitResult<ApprovalInput> result = ... if (result.action() != Action.ACCEPT || !result.structuredContent().approveDeletion()) { return "削除はキャンセルされました"; } return taskService.delete(taskId) ? "削除しました" : "..."; } action() 確認ダイアログへの応答(承認 / キャンセル / 拒 否)。 structuredContent() Step ② で指定した回答型に入ったユーザーの回答 内容。 両方の確認が必要 ー 承認かつ確認済みのときだけ実行する 補足 ① Elicitation 46 / 54 S P R I N G A I × M C P
  41. Sampling 活用例 — 削除理由を文脈から添える Sampling が効くのは Elicitation と組み合わせるとき。 単なる確認メッセージ タスク

    ID-1 を削除しますか? → なぜ削除するのかが分からないと、 ユーザーは安心して判断できない。 Sampling で理由を補強 タスク ID-1 を削除しますか? 削除背景(AI推定): 他タスクと重複して いるため → なぜ呼ばれたのかを、 クライアント側モデルに問い合わせる。 補足 ② Sampling 48 / 54 S P R I N G A I × M C P
  42. Sampling の実装サンプル private String sampleDeletionReason( final McpSyncRequestContext context, final int

    taskId) { if (!context.sampleEnabled()) { return ""; } CreateMessageResult res = context.sample(spec -> spec .message("タスク " + taskId + " の削除が要求されました。" + "会話の文脈から理由を1文で説明してください。") .includeContextStrategy( ContextInclusionStrategy.ALL_SERVERS) .maxTokens(100)); if (res != null && res.content() instanceof TextContent tc) { return "削除背景(AI推定): " + tc.text(); } return ""; } 留意点 ① クライアント未対応の場合があるため、 sampleEnabled() でフォールバックする。 留意点 ② クライアント側モデルの利用枠を消費するた め、maxTokens でコストを制御する。 補足 ② Sampling 49 / 54 S P R I N G A I × M C P
  43. 50 / 54 06 S P R I N G

    A I × M C P S E C T I O N 0 6 / 0 6 まとめ
  44. 今日のまとめ AI 向けツール設計と人間向け API 設計は考え方が異なる。 なぜならツールは決定論的に動作するのに対し、AI は確率的に動作するから。 MCP はこの境界を 接続する仕組み

    を提供してくれる。 しかし、境界を どう設計するか は開発者の責任。 本セッションでは、境界設計の要点を 「境界設計の4原則」として整理した。 まとめ 51 / 54 S P R I N G A I × M C P
  45. 4 原則は AI にツールを任せても事故にならないようにする工夫 01 公開機能は可逆 被害を取り返しのつく範囲に閉じ込める 02 説明文はプロンプト AI

    の判断の質を上げる誘導を仕込む 03 返却値は次の推論インプット AI のコンテキストに入る情報を制御する 04 ログは行動の証跡 AI の行動を後から検証可能にする AI エージェントとツールの境界が存在する限り、普遍的に問われる考え方。 まとめ 52 / 54 S P R I N G A I × M C P
  46. Spring AI 公式ドキュメント • MCP Server Boot Starter docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html •

    MCP Annotations Server (@McpTool / Elicitation / Sampling のサンプル実装) docs.spring.io/spring-ai/reference/api/mcp/mcp-annotations-server.html MCP 仕様 • MCP 公式サイト modelcontextprotocol.io • Tool Annotations の定義 (仕様 — Schema Reference) modelcontextprotocol.io/specification/2025-11-25/schema#toolannotations 参考リンク 53 / 54 S P R I N G A I × M C P