Kotlin でローカルに MCP サーバーを構築し、Claude Desktop と連携してみた
はじめに
2025年に入ってから急激に「MCP」という単語を目にするようになりました。少し前にキャッチアップのために公式のKotlin MCPクイックスタートを実践したところ理解を深めることができたので、やや遅くなりましたが、本記事ではその実践内容とMCPの概要について解説します。
MCPの技術概要
Model Context Protocol (MCP)は、LLM(大規模言語モデル)と外部ツールを連携させるための標準化された通信プロトコルです。技術的には以下の特徴があります。
MCP の主要用語
- ツール(Tool): LLMが呼び出せる機能のこと。例えば「天気予報を取得する」「カレンダーを検索する」などの機能単位。各ツールは名前、説明、入力スキーマ、処理ロジックで構成される。
- サーバー(Server): ツールの集合を管理し、LLMからのリクエストを受け付けて処理する役割を担うプログラム。
- MCPクライアント(Client): LLMとユーザーの間に立ち、ユーザーの質問をLLMに伝え、必要に応じてLLMからのツール呼び出しリクエストをサーバーに転送する役割を担うプログラム(Claude Desktopなど)。
- トランスポート(Transport): LLMとサーバー間の通信経路。MCPでは標準入出力(stdin/stdout)が使われる。
- メッセージ(Message): LLMとサーバー間でやり取りされるJSON形式のデータ。「hello」「listTools」「callTool」「callToolResult」などの種類がある。
- 入力スキーマ(Input Schema): 各ツールが受け付ける入力パラメータの定義。JSON Schemaベースで型や制約を指定する。
通信の流れ
Kotlin MCP クイックスタートで MCP を使った実際の通信フローを図示すると、以下のようになります。
この図からわかるように、MCPは単にAIモデルと外部ツールを繋ぐだけではなく、AIが「大谷翔平の所属チームは?」→「その本拠地は?」→「その場所の天気は?」という複数のステップを含む推論を行い、適切なタイミングで適切なツールを呼び出してくれます。
Kotlin MCP SDKについて
Kotlin MCP SDKは公式ライブラリで、特徴は以下です。
-
io.modelcontextprotocol:kotlin-sdk
というライブラリ名でMavenリポジトリから利用可能 - DSL風の構文でツール定義が可能
- 2025年4月時点ではバージョン0.4.0が最新
実装検証:気象情報ツールの構築
公式チュートリアルに沿って、National Weather Service(アメリカ国立気象局)のAPIを利用した気象情報ツールを構築しました。
環境構築
// build.gradle.kts
plugins {
kotlin("jvm") version "2.1.10"
kotlin("plugin.serialization") version "2.1.10"
id("com.github.johnrengelman.shadow") version "8.1.1"
application
}
val mcpVersion = "0.4.0"
val slf4jVersion = "2.0.9"
val ktorVersion = "3.1.1"
dependencies {
/* Kotlin MCP SDK */
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("org.slf4j:slf4j-nop:$slf4jVersion")
testImplementation(kotlin("test"))
}
MCPサーバーの初期化
MCPサーバーインスタンスを作成し、機能を設定する関数です。
// MCPサーバーを実行するメイン関数
fun `run mcp server`() {
// MCPサーバーのインスタンスを作成
val server = Server(
// 実装名とバージョンを指定
Implementation(
name = "kotlin-mcp-quickstart", // ツール名は「kotlin-mcp-quickstart」
version = "1.0.0" // 実装のバージョン
),
// サーバーオプションを設定
ServerOptions(
capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = true))
)
)
通信の確立
標準入出力を使ってAIモデルと通信するための設定です:
// 標準入出力を使用したサーバー通信用のトランスポートを作成
val transport = StdioServerTransport(
System.`in`.asInput(),
System.out.asSink().buffered()
)
// コルーチンスコープ内でサーバーを実行
runBlocking {
server.connect(transport)
val done = Job()
server.onClose {
done.complete()
}
done.join()
}
ツール定義と登録
緯度・軽度から天気予報を取得するツールを登録します。
// 緯度経度による天気予報取得ツールを登録
server.addTool(
// ツール名
name = "get_forecast",
// ツールの説明
description =
"""
特定の緯度/経度の天気予報を取得します。
""".trimIndent(),
// 入力スキーマの定義
inputSchema = Tool.Input(
// パラメータの定義
properties = buildJsonObject {
putJsonObject("latitude") { put("type", "number") }
putJsonObject("longitude") { put("type", "number") }
},
// 必須パラメータの指定
required = listOf("latitude", "longitude")
)
) { request ->
// リクエストから緯度経度を取得
val latitude = request.arguments["latitude"]?.jsonPrimitive?.doubleOrNull
val longitude = request.arguments["longitude"]?.jsonPrimitive?.doubleOrNull
// 緯度経度が提供されていない場合はエラーを返す
if (latitude == null || longitude == null) {
return@addTool CallToolResult(
content = listOf(TextContent("'latitude'と'longitude'パラメータが必要です。"))
)
}
// 天気予報を取得する処理
val forecast = httpClient.getForecast(latitude, longitude)
// 結果を返す
CallToolResult(content = forecast.map { TextContent(it) })
}
同様に、公式クイックスタートに記載のコードを実装していきます(コードは省略)。
MCP通信プロトコルの詳細
MCPは以下のようなフェーズとメッセージ構造を持っています。
初期化フェーズ
-
hello
メッセージ: バージョン情報、実装名、機能セットの交換 -
listTools
/tools
: 利用可能なツールとそのスキーマ情報の交換
ツール実行フェーズ
AIからのツール呼び出しリクエスト例:
{
"type": "callTool",
"id": "request-123",
"tool": "get_forecast",
"arguments": {
"latitude": 34.0522,
"longitude": -118.2437
}
}
ツールからの実行結果レスポンス例:
{
"type": "callToolResult",
"id": "response-123",
"requestId": "request-123",
"content": [
{
"type": "text",
"text": "Tonight:\nTemperature: 58 F\nWind: 5 mph W\nForecast: Mostly clear, with a low around 58. West wind around 5 mph becoming calm after midnight."
},
{
"type": "text",
"text": "Sunday:\nTemperature: 75 F\nWind: 5 mph W\nForecast: Sunny, with a high near 75. Calm wind becoming west around 5 mph in the afternoon."
}
// ... more forecast periods ...
]
}
Claude Desktopとの連携
実装したMCPサーバーは、公式ドキュメントにあるとおりにClaude Desktopと連携できます。
基本的には、生成された実行可能JARファイルや実行コマンドをClaude Desktopの設定画面で指定します。
動作確認
実装したMCPサーバーをClaude Desktopと連携し、いくつかの入力を試してみました。以下はその結果の例です。
入力 | 結果 |
---|---|
大谷翔平の本拠地の天気は? | LLMが推論 (大谷->LA->座標) し、get_forecast ツールで天気予報を取得 |
ロサンゼルスの天気は? | LLMが地名を認識し、get_forecast ツールで天気予報を取得 |
カリフォルニアの気象警報は? | LLMがツールを使用し、警報データを取得 |
不正な州コード "ZZ" の警報は? | ツールがエラーメッセージを返却 |
東京の天気は? | ツールが対応範囲外(米国のみ)のため、情報取得できず |
おわりに
公式チュートリアルから始めてツールを構築してみると、MCPの可能性をより深く理解できるかと思います。チュートリアルは時間もそれほどかからず、MCPの基本的な仕組みや定義を理解するのに適しているので、キャッチアップしてみたい方はオススメです。
Discussion