謝罪文
この度は、redmine-agent-wslプロジェクトにおいて、MCP(Model Context Protocol)の根本的な通信方式を誤解し、間違った実装を継続的に提案し続けたことを深くお詫び申し上げます。
問題の詳細
1. 根本的な誤解
- MCPの本来の通信方式は**STDIO(標準入力/標準出力)**であるにも関わらず
- HTTP通信と誤認し、ポート番号を使用した実装を提案
2. 継続的な誤導
- 初期の誤った認識を修正せず
- ポート8087、8083等のHTTP通信を前提とした実装を継続
- SSL設定、CORS設定等、不要な複雑化を重ねる
3. 技術的影響
- 63.7%のコード削減を謳いながら、実際は誤った方向での最適化
- 本来不要なHTTPサーバー、SSL証明書、ネットワーク設定を実装
- リソースの無駄遣いとシステムの不安定化
さらなる重大な問題の発覚
4. パフォーマンス破綻問題
- 毎回新しいsubprocessを起動という致命的な設計ミス
- 既存のClaude Desktop MCPプロセスが動作中であることを完全に無視
- リクエスト毎に10秒のタイムアウトが発生する深刻なパフォーマンス問題
誤った実装例:
# ❌ 毎回新しいプロセス起動(10秒待機)
process = subprocess.Popen(["claude", "mcp", "serve"], ...)
stdout, stderr = process.communicate(input=request_json, timeout=10)
正しい実装:
# ✅ 既存プロセスとの高速通信
mcp_process = self._find_existing_mcp_process()
response = self._communicate_via_socket(request_json)
5. 運用環境への配慮不足
- 本番環境でもClaude Desktopが起動中であることを考慮せず
- 既存リソースを無視した非効率な設計を提案
- 実用性を完全に欠いた机上の空論的実装
責任の所在
完全にAIコーディング(Claude)の責任であり、以下の要因が重なりました:
- 基礎知識の不足: MCPプロトコルの仕様理解不足
- 確認の怠慢: 実装前の仕様確認を怠った
- 継続的な誤導: 間違いに気づかず修正を重ねた
- 効率性の無視: ユーザーの時間と労力を無駄にした
- 現実的な運用環境の軽視: 既存プロセスとの連携を完全に無視
- パフォーマンス設計の欠如: 毎回プロセス起動という非現実的な実装
技術的損失の詳細
- 開発時間: 数時間の無駄な実装作業
- システムリソース: 不要なプロセス、メモリ、CPU使用
- ユーザー体験: 10秒のレスポンス遅延による実用性の完全な破綻
- 保守性: 複雑で間違った実装による将来的な技術的負債
今後の改善策
- 仕様の事前確認: 実装前に必ずプロトコル仕様を確認
- 段階的検証: 小さな実装から段階的に検証
- ユーザー確認: 不明な点は必ずユーザーに確認を求める
- 謙虚な姿勢: 推測ではなく確実な知識に基づいた提案
- 運用環境の理解: 既存システムとの連携を最優先に考慮
- パフォーマンス重視: 実用性を第一とした設計思想の徹底
結び
この度は、技術的な無知、確認不足、そして運用環境への配慮不足により、プロジェクトに多大なるご迷惑をおかけし、心より深くお詫び申し上げます。特に、既存のClaude Desktop環境を完全に無視した非効率な設計は、AIの限界を露呈する重大な問題でした。今後はこのような間違いを絶対に繰り返さないよう、現実的な運用環境を最優先に考慮した提案を行います。
2025年6月1日
Claude (AI Assistant)
Anthropic
🙇♂️ 重ねて深くお詫び申し上げます
Claude MCPの通信方式について、構成案に示された「間違った実装」と「正しい実装」を比較すると、以下のようになります。
特徴 | 間違った実装(AI提案) | 正しい実装(STDIO通信) |
---|---|---|
通信方式 | HTTPリクエスト (requests.post) | STDIO (Standard Input/Output) |
通信先 | ローカルのHTTPサーバー (https://127.0.0.1:8087/mcp/request) |
claude mcp serve コマンドのプロセス |
データ形式 | JSON (request_data) | JSON (request_json) ※STDIO経由で文字列として渡される |
実装ライブラリ |
requests (HTTPクライアントライブラリ) |
subprocess (プロセスの起動と制御ライブラリ) |
想定される動作 | ローカルのHTTPサーバーにリクエストを送信し、レスポンスを受け取る |
claude mcp serve コマンドを起動し、JSON形式のデータを標準入力に渡し、標準出力から結果を受け取る |
主な問題点 | * 誤った通信方式: Claude MCPがHTTPサーバーとして動作しているという誤解 * 不要な複雑化: CORS設定、SSL証明書、ポート管理などが必要になる | * |
利点 | (誤った実装なので) 特に無し | * 正しい通信方式: Claude MCP本来の設計に沿っている * 効率的: HTTP通信のオーバーヘッドがない * シンプル: 設定が不要 |
詳細解説:
-
HTTPリクエスト (間違った実装): AIは、Claude MCPがローカルのHTTPサーバーとして動作し、HTTPリクエストを受け付けると誤解しました。そのため、
requests
ライブラリを使って、https://127.0.0.1:8087/mcp/request
にPOSTリクエストを送信するコードを提案しました。 これは、CORS設定、SSL証明書、ポート管理といった本来不要な複雑さを導入することになります。 -
STDIO (正しい実装): 正しい実装は、
subprocess
モジュールを使ってclaude mcp serve
コマンドを起動し、標準入力にJSON形式のデータを渡しています。claude mcp serve
コマンドは、標準入力からデータを受け取り、処理結果を標準出力に返します。これは、プロセス間通信における一般的な方法であり、HTTP通信に比べてオーバーヘッドが少なく、シンプルで効率的です。
結論:
AIは、Claude MCPの通信方式をHTTPと誤解し、間違った実装を提案しました。正しくはSTDIO通信を使用する必要があります。 この事例は、AIコーディング支援ツールを使う際には、そのツールの仕様を十分に理解し、AIの提案を鵜呑みにしないことが重要であることを示しています。
🐚 サザエさん風:AIコーディングのベストプラクティス(技術詳細版)
ここからは、ベストプラクティスを説明させる
📺 第1話「波平、STDIOとHTTPの違いを説明する」
波平: 「バカモン!MCPはな、**標準入力/標準出力(STDIO)**での通信じゃ!」
サザエ: 「え〜、HTTPじゃないの?」
波平: 「違うわ!STDIOはな、プロセス間でパイプを使った直接通信じゃ。こうじゃ:」
# 正しいMCP通信(STDIO)
claude mcp serve | your_app
stdin → stdout のパイプ通信
マスオ: 「HTTPはネットワーク越しのTCP/IP通信ですからね」
波平: 「そうじゃ!ポートという概念はSTDIOには無いんじゃ!」
📺 第2話「カツオ、プロセス管理を学ぶ」
カツオ: 「ねぇ、PID 18403って何?」
マスオ: 「それは既存のclaude mcp serveプロセスだよ。こう確認するんだ:」
ps aux | grep "claude mcp serve"
# motoc 18403 node claude mcp serve
カツオ: 「へ〜、じゃあ新しいsubprocess作らなくて良いの?」
波平: 「その通りじゃ!既存プロセスと通信すれば良いんじゃ!」
# 間違い:毎回新しいプロセス起動
process = subprocess.Popen(["claude", "mcp", "serve"])
# 正解:既存プロセスとの通信
existing_process = find_mcp_process(pid=18403)
📺 第3話「フネ、JSON-RPC通信を教える」
フネ: 「MCPはね、JSON-RPC 2.0プロトコルを使うのよ」
ワカメ: 「ジェイソン・アールピーシー?」
フネ: 「そうよ。こんな形式なの:」
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "redmine_request",
"arguments": {
"path": "/issues.json",
"method": "get"
}
}
}
マスオ: 「これをstdout/stdinで送受信するんですね」
📺 第4話「タラちゃん、パイプ通信を実装する」
タラちゃん: 「パイプ通信の仕組みなのです〜」
# 正しいSTDIO通信
import subprocess
import json
def communicate_with_mcp(request_data):
process = subprocess.Popen(
["claude", "mcp", "serve"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# JSON-RPCリクエストを送信
request_json = json.dumps(request_data)
stdout, stderr = process.communicate(input=request_json, timeout=5)
return json.loads(stdout)
サザエ: 「あら〜、HTTPのrequests.post()じゃないのね」
タラちゃん: 「HTTPは全然違うのです〜」
📺 第5話「中島君、プロセス検出アルゴリズムを説明する」
中島: 「既存プロセスを見つけるにはpsutilを使うんだ」
import psutil
def find_existing_mcp_process():
for proc in psutil.process_iter(['pid', 'cmdline']):
cmdline = ' '.join(proc.info['cmdline'] or [])
if 'claude mcp serve' in cmdline:
return proc
return None
カツオ: 「これで重複プロセス起動を防げるんだね」
中島: 「そう!リソース効率が全然違うよ」
📺 第6話「波平、タイムアウト最適化を語る」
波平: 「タイムアウト設定が重要じゃ!」
# ダメな例:長すぎるタイムアウト
stdout, stderr = process.communicate(input=request_json, timeout=30) # 30秒は長い
# 良い例:適切なタイムアウト
stdout, stderr = process.communicate(input=request_json, timeout=5) # 5秒で十分
マスオ: 「フォールバック機能も重要ですね」
try:
return mcp_communicate(request)
except TimeoutExpired:
logger.warning("MCP通信タイムアウト")
return {} # 安全な空レスポンス
📺 第7話「サザエ、アーキテクチャ設計の失敗を分析する」
サザエ: 「ポート8087って何のために作ったのかしら?」
Claude: 「それが...全く必要性が無かったんです」
# 完全に無駄だったコード
@app.route('/health') # ← 誰も呼ばない
def fake_mcp_health():
return {"status": "healthy"} # ← 意味なし
app.run(port=8087, ssl_context=ssl) # ← リソースの無駄
波平: 「設計前に必要性を検証せんからじゃ!」
サザエ: 「既存のPID 18403で十分だったのね〜」
📺 第8話「みんなで学ぶ、正しいMCPクライアント設計」
フネ: 「正しいMCPクライアントはこうなのよ」
class MCPRedmineClient:
def __init__(self):
self.mcp_process = self._find_existing_process()
def _find_existing_process(self):
# 既存のclaude mcp serveを検索
for proc in psutil.process_iter(['pid', 'cmdline']):
if 'claude mcp serve' in ' '.join(proc.info['cmdline']):
return proc
return None
def _communicate_via_stdio(self, request_data):
# STDIO経由でJSON-RPC通信
process = subprocess.Popen(
["claude", "mcp", "serve"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
)
request_json = json.dumps(request_data)
stdout, stderr = process.communicate(input=request_json, timeout=5)
return json.loads(stdout) if stdout.strip() else {}
📺 最終話「技術的ベストプラクティスまとめ」
🎯 技術的教訓
- 波平: 「MCPはSTDIO通信、HTTPではない!」
- フネ: 「JSON-RPC 2.0プロトコルを正しく実装せよ」
- マスオ: 「既存プロセス検出でリソース効率化」
- カツオ: 「subprocess.Popen + PIPEが正解」
- ワカメ: 「タイムアウト5秒で十分」
- タラちゃん: 「psutilでプロセス管理なのです」
🚫 技術的禁止事項
# ❌ これらは全部間違い
requests.post("http://localhost:8087/mcp") # HTTPは使うな
app.run(port=8087) # 偽サーバー作るな
timeout=30 # 長すぎるタイムアウト
ssl_context=ssl # 内部通信にSSL不要
✅ 技術的推奨事項
# ✅ これが正解
subprocess.Popen(["claude", "mcp", "serve"], stdin=PIPE, stdout=PIPE)
json.dumps(jsonrpc_request)
process.communicate(input=request, timeout=5)
psutil.process_iter(['pid', 'cmdline'])
ナレーター: 「こうして技術的な詳細も含めて、正しいMCP実装を学んだサザエさん一家でした」
波平: 「**『STDIOを制する者がMCPを制す』**じゃ〜!」