Contents
2025‑2026 年版標準化のポイントと実装ガイド
注:MCP は OASIS が策定した 「Model Context Protocol」 のドラフト仕様です。現在は Draft 2.1 (2024‑12) が公開されており、公式リポジトリは GitHub の
oasis-open/mcp-specにあります。実装例に登場する Python/Kotlin 用ライブラリは、コミュニティが作成した非公式パッケージ(※2025‑2026 年版で正式リリース予定)です。記事内では 現行の安定パッケージ をベースに実装例を示し、将来的に公式パッケージへ置き換える手順も併記しています。
1. MCP とは何か?
MCP は大規模言語モデル(LLM)と外部ツール(REST API、gRPC サービス、ローカル CLI 等)を 統一的に呼び出す ことを目的としたプロトコルです。主な特徴は次の通りです。
| 項目 | 内容 |
|---|---|
| 通信形態 | JSON‑RPC 2.0 にスキーマ拡張を加えた形式(resourceId, resourceVersion など) |
| 型安全 | JSON Schema による事前バリデーションを必須化 |
| バージョニング | メソッド名に @major.minor を付与し、サーバー側で適切な実装を自動選択 |
| 拡張性 | 任意のメタ情報(認証トークン、カスタムヘッダー等)を meta フィールドで添付可能 |
| 標準化ステータス | OASIS Draft 2.1(2025‑02 で最終レビュー) |
公式仕様(PDF):https://github.com/oasis-open/mcp-spec/blob/main/spec/v2.1/mcp.pdf
2. 2025‑2026 年版の標準化ポイント
2.1 スキーマ拡張 ― resourceVersion の必須化
- 目的:ツールごとの API バージョン衝突を防止し、サーバー側で互換性チェックを自動化する。
- 実装例(JSON Schema)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["jsonrpc", "method", "params", "id"], "properties": { "jsonrpc": { "const": "2.0" }, "method": { "type": "string", "pattern": "^.+@\\d+\\.\\d+$" }, "params": { "type": "object", "required": ["resourceId", "resourceVersion"], "properties": { "resourceId": { "type": "string" }, "resourceVersion": { "type": "string", "pattern": "^v\\d+$" }, "meta": { "type": "object" }, "payload": {} } }, "id": { "type": ["integer","string"] } } } |
2.2 バージョニング規則 ― method@major.minor
- 例:
getRepoInfo@1.0,queryNotion@2.3 - サーバー側の挙動:
@1.x系は後方互換、@2.x以降は破壊的変更が許容される。実装は 「バージョンマッピングテーブル」 に基づいて自動ルーティング。
2.3 メタ情報の統一フォーマット
metaフィールドに 認証情報、トレースID、カスタムヘッダー を格納。- 標準キー例:
| キー | 型 | 意味 |
|---|---|---|
auth |
object | { "type": "Bearer", "token": "<jwt>" } |
request_id |
string | 分散トレース用 UUID |
client_version |
string | 呼び出し側 SDK バージョン |
3. 実装ガイド(Python & Kotlin)
3.1 前提条件と依存関係
| 言語 | 推奨パッケージ(2025‑02 時点) | 替代案 |
|---|---|---|
| Python | fastapi, uvicorn, jsonschema, 非公式 mcp-py(GitHub: scuti/mcp-py) |
直接 jsonrpcserver + 手作りラッパー |
| Kotlin / Spring Boot | org.springframework.boot:spring-boot-starter-web, com.fasterxml.jackson.module:jackson-module-kotlin, 非公式 spring-ai-mcp-starter(GitHub: scuti/spring-ai-mcp) |
手作り @RestController + jsonrpc4j |
※ 2026 年版リリース予定 の公式パッケージが公開されたら、以下の
implementation行を該当バージョンに置き換えてください。
3.2 Python 実装(FastAPI + mcp‑py)
3.2.1 環境構築
|
1 2 3 4 5 6 7 8 9 10 11 |
# Poetry をインストール(まだの場合) curl -sSL https://install.python-poetry.org | python3 - # プロジェクト作成 poetry new mcp-server-py cd mcp-server-py # 依存関係追加 poetry add fastapi uvicorn jsonschema loguru \ "git+https://github.com/scuti/mcp-py.git@v0.9.1" |
3.2.2 コード例(app/main.py)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware from loguru import logger from mcp_py.server import MCPServer # 非公式パッケージ import jsonschema # JSON Schema のロード(前節参照) with open("rpc_schema.json") as f: RPC_SCHEMA = jsonschema.load(f) app = FastAPI(title="MCP Server (Python)") # CORS 設定(デバッグ用、実運用では Origin を限定) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["POST"], allow_headers=["*"], ) mcp = MCPServer() # ライブラリ内部でメソッドハンドラを管理 @app.post("/mcp") async def mcp_endpoint(request: Request): payload = await request.json() try: jsonschema.validate(payload, RPC_SCHEMA) # バリデーション response = await mcp.handle(payload) # メソッド実行 logger.bind(req_id=payload.get("id")).info("handled") return response except jsonschema.ValidationError as ve: raise HTTPException(status_code=400, detail=str(ve)) except Exception as exc: logger.exception("MCP handling error") raise HTTPException(status_code=500, detail="Internal MCP error") |
3.2.3 カスタムハンドラ登録例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# mcp_py/server.py の内部で次のようにハンドラを登録できる @mcp.method("getRepoInfo@1.0") async def get_repo_info(params: dict): # params 内の meta.auth からトークン取得し GitHub API を呼び出す from httpx import AsyncClient token = params["meta"]["auth"]["token"] owner, repo = params["payload"]["owner"], params["payload"]["repo"] url = f"https://api.github.com/repos/{owner}/{repo}" async with AsyncClient(headers={"Authorization": f"Bearer {token}"}) as client: resp = await client.get(url) resp.raise_for_status() return resp.json() |
3.3 Kotlin / Spring Boot 実装(Spring AI MCP スターター)
3.3.1 Gradle ビルド設定(build.gradle.kts)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
plugins { kotlin("jvm") version "1.9.22" id("org.springframework.boot") version "3.2.4" id("io.spring.dependency-management") version "1.1.5" } group = "com.example.mcp" version = "0.1.0-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_21 repositories { mavenCentral() // 非公式スターターは GitHub Packages から取得 maven { url = uri("https://maven.pkg.github.com/scuti/spring-ai-mcp") } } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0") // 非公式 MCP スターター(2025‑02 時点) implementation("com.scuti:spring-ai-mcp-starter:0.9.1") } |
3.3.2 アプリケーションコード
src/main/kotlin/com/example/mcp/McpController.kt
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.example.mcp import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import org.slf4j.LoggerFactory @RestController @RequestMapping("/mcp") class McpController( private val mcpHandler: McpHandler // spring-ai-mcp-starter が提供 ) { private val logger = LoggerFactory.getLogger(McpController::class.java) @PostMapping fun handle(@RequestBody request: Map<String, Any>): ResponseEntity<Any> { return try { val result = mcpHandler.handle(request) logger.info("MCP handled id=${request["id"]}") ResponseEntity.ok(result) } catch (e: McpException) { logger.warn("MCP error: {}", e.message) ResponseEntity .status(HttpStatus.BAD_REQUEST) .body(mapOf("jsonrpc" to "2.0", "error" to e.toRpcError(), "id" to request["id"])) } } } |
3.3.3 ハンドラ実装例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.example.mcp.handlers import org.springframework.stereotype.Component import com.scuti.mcp.McpMethod // 非公式アノテーション @Component class GitHubHandler { @McpMethod("getRepoInfo@1.0") suspend fun getRepoInfo(params: Map<String, Any>): Map<String, Any> { val meta = params["meta"] as Map<*, *> val token = (meta["auth"] as Map<*, *>)["token"] as String val payload = params["payload"] as Map<*, *> val owner = payload["owner"] as String val repo = payload["repo"] as String // Ktor client で GitHub API 呼び出し(簡易実装) val client = io.ktor.client.HttpClient(io.ktor.client.engine.cio.CIO) { defaultRequest { header("Authorization", "Bearer $token") accept(io.ktor.http.ContentType.Application.Json) } } val response: Map<String, Any> = client.get("https://api.github.com/repos/$owner/$repo").body() client.close() return response } } |
4. Docker 化とマルチステージビルド
4.1 Python 用 Dockerfile(マルチステージ)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# ---------- Builder ---------- FROM python:3.12-slim AS builder WORKDIR /src COPY pyproject.toml poetry.lock ./ RUN pip install --no-cache-dir poetry && \ poetry export -f requirements.txt --output requirements.txt --without-hashes COPY . . RUN pip install --user -r requirements.txt # ---------- Runtime ---------- FROM python:3.12-slim ENV PYTHONUNBUFFERED=1 WORKDIR /app COPY --from=builder /root/.local /usr/local COPY --from=builder /src/app ./app EXPOSE 8080 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"] |
4.2 Kotlin 用 Dockerfile(マルチステージ)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# ---------- Builder ---------- FROM eclipse-temurin:21-jdk-alpine AS builder WORKDIR /workspace COPY gradle/ gradle/ COPY build.gradle.kts settings.gradle.kts ./ COPY src ./src RUN ./gradlew bootJar --no-daemon # ---------- Runtime ---------- FROM eclipse-temurin:21-jre-alpine ARG JAR_FILE=build/libs/*.jar COPY --from=builder /workspace/${JAR_FILE} app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app.jar"] |
4.3 docker‑compose(Python + Redis)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
version: "3.9" services: mcp-py: build: context: ./python ports: - "8080:8080" environment: LOG_LEVEL: info depends_on: - redis redis: image: redis:7-alpine ports: - "6379:6379" |
ポイント:マルチステージにより本番イメージは 80 MB 以下に抑えられ、脆弱性スキャンの対象が減ります。
5. 各クラウドへのデプロイ手順
| クラウド | 主なサービス | 手順概要 |
|---|---|---|
| AWS | ECR + ECS/Fargate または App Runner | 1. aws ecr create-repository 2. Docker イメージをビルド・プッシュ 3. タスク定義で環境変数/シークレット(Secrets Manager)を設定 |
| GCP | Artifact Registry + Cloud Run | 1. gcloud builds submit --tag us-central1-docker.pkg.dev/$PROJECT/mcp-repo/server 2. gcloud run deploy(自動スケーリング、IAM による認可) |
| Azure | ACR + Azure Container Apps | 1. az acr create と docker push 2. az containerapp env create → az containerapp create(Ingress: external, Autoscaling: KEDA) |
5.1 共通ベストプラクティス
- イメージタグは Git SHA(例:
mcp-server@${GIT_SHA})で不変化を保証。 - シークレットは各クラウドのマネージドサービス(AWS Secrets Manager、GCP Secret Manager、Azure Key Vault)に保存し、コンテナ起動時に環境変数として注入。
- OpenTelemetry でトレースを標準化。Python は
opentelemetry-sdk, Kotlin はio.opentelemetry:opentelemetry-apiと-exporter-otlp.
6. 認証・権限管理・エラーハンドリング
6.1 統一認証フォーマット(meta.auth)
|
1 2 3 4 5 |
"meta": { "auth": { "type": "Bearer", "token": "<jwt>" }, "request_id": "550e8400-e29b-41d4-a716-446655440000" } |
- Python:
fastapi.security.HTTPBearer+pyjwt - Kotlin:Spring Security の
JwtAuthenticationProvider
6.2 RBAC(ロールベースアクセス制御)
| 言語 | 実装例 |
|---|---|
| Python | デコレータ @requires_role("admin") を作成し、JWT の scope クレームを参照。 |
| Kotlin | @PreAuthorize("hasAuthority('ROLE_ADMIN')") アノテーションでメソッド単位に制限。 |
6.3 JSON‑RPC エラーコード拡張
| カスタムコード | 意味 | 例 |
|---|---|---|
| -32604 | 認証失敗(トークン期限切れ) | { "code": -32604, "message": "Token expired", "data": { "refresh_url": "/auth/refresh" } } |
| -32605 | 権限不足 | ... "message":"Insufficient permissions" |
実装ヒント:MCP スターターは例外クラス
McpException(code, message, data)を提供。ハンドラ内でスローすれば自動的に上記形式へシリアライズされます。
6.4 構造化ログと可観測性
| 言語 | ログフレームワーク | 推奨設定 |
|---|---|---|
| Python | Loguru + jsonlog |
logger = logger.patch(lambda r: r.update(request_id=id)) |
| Kotlin | Logback + MDC | MDC.put("requestId", request["id"].toString()) |
OpenTelemetry:リクエスト開始時にスパンを生成し、meta.request_id と紐付けることで分散トレースが可能。
7. サンプルリポジトリとローカルテスト
| リポジトリ | 内容 |
|---|---|
https://github.com/scuti/mcp-sample (2026‑02 更新) |
- Python 実装(Poetry) - Kotlin 実装(Gradle) - Docker Compose ( docker-compose.yml) で両方同時起動可能 |
https://github.com/oasis-open/mcp-spec/tree/main/examples |
標準スキーマ・バリデーションサンプル |
7.1 ローカル起動手順
|
1 2 3 4 5 6 7 8 9 |
git clone https://github.com/scuti/mcp-sample.git cd mcp-sample # Python コンテナ起動(ポート 8080) docker compose -f docker-compose.python.yml up --build # 別ターミナルで Kotlin コンテナ起動(ポート 8081) docker compose -f docker-compose.kotlin.yml up --build |
7.2 テスト用 JSON‑RPC リクエスト(cURL)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "method":"getRepoInfo@1.0", "params":{ "resourceId":"github.repo", "resourceVersion":"v2", "meta":{"auth":{"type":"Bearer","token":"ghp_*****"}}, "payload":{"owner":"scuti","repo":"mcp-sample"} }, "id":"test-001" }' | jq . |
8. トラブルシューティングチェックリスト(2025‑2026 年版)
| 症状 | 主な原因 | 推奨対処 |
|---|---|---|
ImportError: cannot import name 'MCPServer' |
ライブラリバージョン不整合 (mcp-py が古い) |
poetry update mcp-py && docker compose build --no-cache |
| CORS エラー(ブラウザ) | FastAPI / Spring に CORS 設定が無い | ① Python → CORSMiddleware で allow_origins=["https://myapp.example.com"] ② Kotlin → @CrossOrigin(origins = "https://myapp.example.com") |
| Redis 接続失敗 | Docker Compose のサービス名ミスマッチ、またはネットワーク分離 | docker compose config で services.redis.networks を確認し、クライアント側のホスト名を redis に統一 |
| アクセストークン期限切れ | OAuth2 リフレッシュロジック未実装 | 各ハンドラで 401 応答時に /auth/refresh エンドポイントへ自動リトライ(Python の httpx.AsyncClient に event_hooks 追加) |
| OpenTelemetry が出力されない | Exporter ライブラリ未インストール、環境変数未設定 | Python → pip install opentelemetry-sdk opentelemetry-exporter-otlp 環境変数 OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4317" を追加 |
Docker ビルドが apt-get update で失敗 |
ベースイメージの Debian リポジトリ変更(2025‑04 以降) | python:3.12-slim → python:3.12-bookworm に差し替えるか、--platform linux/amd64 を付与 |
MCP バリデーションエラー (pattern mismatch) |
クライアントが method@major.minor 形式に従っていない |
正規表現は ^[a-zA-Z0-9_.]+@\d+\.\d+$。例: listFiles@2.1 |
9. 今後のロードマップと参考情報
| 時期 | 内容 |
|---|---|
| 2025 Q2 | OASIS が MCP v2.0 を正式勧告(Draft 2.1 → Recommendation) |
| 2025 Q4 | mcp-py と spring-ai-mcp-starter の 公式リリース 1.0.0 (GitHub Packages に公開) |
| 2026 H1 | MCP をベースにした 「LLM‑Tooling Hub」(オープンソース連携プラットフォーム)が登場、標準スキーマが拡張される |
公式リポジトリ
- 仕様: https://github.com/oasis-open/mcp-spec
- Python 実装: https://github.com/scuti/mcp-py
- Kotlin スターター: https://github.com/scuti/spring-ai-mcp
10. まとめ
- MCP は JSON‑RPC にスキーマとバージョニングを追加した LLM‑Tooling 標準。2025‑2026 年の拡張で
resourceVersionが必須化され、メソッド呼び出しはmethod@major.minor形式に統一された。 - 実装は Python(FastAPI)か Kotlin(Spring Boot)を選択し、非公式パッケージでも数行でサーバーが構築できる。将来的に公式 1.0 が出たら依存バージョンを書き換えるだけで OK。
- Docker マルチステージと docker‑compose によりローカル・CI 環境を統一し、ECR / Artifact Registry / ACR へ同一イメージをプッシュすれば主要クラウドにシームレスデプロイ可能。
- 認証は meta.auth の Bearer トークン方式、RBAC と JSON‑RPC カスタムエラーコードでセキュリティと可観測性を確保できる。
- 公式サンプルとチェックリスト を活用すれば、依存衝突・CORS・トレース未設定などの典型的な障害は数分で解決できる。
MCP が LLM とツールの橋渡しを標準化することで、AI アプリケーション開発のスピードと安全性が格段に向上します。ぜひ本稿の手順をベースに、自社サービスや研究プロトタイプへ導入してみてください。