MCPサーバーのPython実装:エラーハンドリングの設計と実装 Link to heading

はじめに Link to heading

前回の記事で基本的な動作を確認したMCPサーバーに、エラーハンドリング機能を追加しました。この記事では、エラーハンドリングの設計と実装について説明します。

エラーハンドリングの必要性 Link to heading

MCPサーバーでは、以下のような様々なエラーが発生する可能性があります:

  1. リクエスト関連のエラー

    • 不正なJSONフォーマット
    • 必須フィールドの欠落
    • 不正なメソッド名
    • 不正なパラメータ型
  2. サーバー内部のエラー

    • 処理中の例外
    • リソースの不足
    • タイムアウト
  3. プロトコル関連のエラー

    • 未実装のメソッド
    • 不正なプロトコルバージョン
    • 互換性の問題

エラーレスポンスの実装 Link to heading

errors.pyでエラー関連のクラスを定義しました:

 1class ErrorCode(IntEnum):
 2    # リクエスト関連のエラー (-32000 から -32099)
 3    INVALID_REQUEST = -32000
 4    METHOD_NOT_FOUND = -32001
 5    INVALID_PARAMS = -32002
 6    
 7    # サーバー内部のエラー (-32100 から -32199)
 8    INTERNAL_ERROR = -32100
 9    RESOURCE_EXHAUSTED = -32101
10    TIMEOUT = -32102
11    
12    # プロトコル関連のエラー (-32200 から -32299)
13    PROTOCOL_ERROR = -32200
14    VERSION_MISMATCH = -32201
15
16class ErrorInfo(BaseModel):
17    code: int
18    message: str
19    data: Optional[Dict[str, Any]] = None
20
21class ErrorResponse(BaseModel):
22    error: ErrorInfo
23    id: Optional[str] = None

エラーハンドリングの実装 Link to heading

  1. カスタム例外クラス

     1class MCPError(Exception):
     2    def __init__(self, code: ErrorCode, message: str, data: Optional[Dict] = None):
     3        self.code = code
     4        self.message = message
     5        self.data = data
     6
     7class RequestError(MCPError):
     8    pass
     9
    10class ServerError(MCPError):
    11    pass
    12
    13class ProtocolError(MCPError):
    14    pass
    
  2. グローバルエラーハンドラ

     1@app.exception_handler(MCPError)
     2async def mcp_exception_handler(request: Request, exc: MCPError) -> JSONResponse:
     3    return JSONResponse(
     4        status_code=200,
     5        content=ErrorResponse(
     6            error=ErrorInfo(
     7                code=exc.code,
     8                message=exc.message,
     9                data=exc.data
    10            )
    11        ).model_dump()
    12    )
    

テストの実装 Link to heading

test_error_handling.pyで以下のテストケースを実装しました:

  1. 不正なJSONフォーマット

    1def test_invalid_json():
    2    response = client.post("/", content=b"invalid json")
    3    assert response.status_code == 200
    4    data = response.json()
    5    assert data["error"]["code"] == -32000
    6    assert "Invalid JSON format" in data["error"]["message"]
    
  2. メソッドの欠落

    1def test_missing_method():
    2    response = client.post("/", json={})
    3    assert response.status_code == 200
    4    data = response.json()
    5    assert data["error"]["code"] == -32000
    6    assert "Method is required" in data["error"]["message"]
    
  3. 不正なメソッド

    1def test_invalid_method():
    2    response = client.post("/", json={
    3        "method": "nonexistent",
    4        "params": {}
    5    })
    6    assert response.status_code == 200
    7    data = response.json()
    8    assert data["error"]["code"] == -32001
    9    assert "Method 'nonexistent' not found" in data["error"]["message"]
    
  4. 不正な初期化パラメータ

     1def test_invalid_initialize_params():
     2    # capabilitiesが辞書でない場合
     3    response = client.post("/", json={
     4        "method": "initialize",
     5        "params": {
     6            "capabilities": "not a dict",
     7            "clientInfo": {
     8                "name": "Test Client",
     9                "version": "1.0.0"
    10            }
    11        }
    12    })
    13    assert response.status_code == 200
    14    data = response.json()
    15    assert data["error"]["code"] == -32002
    16    assert "Client capabilities must be a dictionary" in data["error"]["message"]
    
  5. GETリクエストの処理

    1def test_get_request():
    2    response = client.get("/")
    3    assert response.status_code == 200
    4    data = response.json()
    5    assert data["error"]["code"] == -32000
    6    assert "This server only accepts POST requests" in data["error"]["message"]
    

実装のポイント Link to heading

  1. エラーの階層化

    • 基本エラー(MCPError
    • リクエストエラー(RequestError
    • サーバーエラー(ServerError
    • プロトコルエラー(ProtocolError
  2. エラーレスポンスの標準化

    • MCPプロトコルに準拠した形式
    • 一貫したエラーコード体系
    • 詳細なエラーメッセージ
  3. テストの網羅性

    • 各エラーケースのテスト
    • エラーレスポンスの形式確認
    • エンドツーエンドのテスト

次のステップ Link to heading

  1. ログ機能の実装

    • エラーログの構造化
    • デバッグ情報の付加
    • ログレベルの設定
  2. エラーハンドリングの拡張

    • より詳細なエラー情報
    • エラーの追跡機能
    • エラー統計の収集

まとめ Link to heading

エラーハンドリングの実装では、以下の点に注意を払いました:

  • MCPプロトコルに準拠したエラーレスポンス
  • 階層化されたエラー処理
  • 網羅的なテストケース
  • 拡張可能な設計

次の記事では、ログ機能の実装について説明します。

注記 本記事はエラーハンドリングの設計と実装にフォーカスしていますが、現状の実装では logger.py(ログ機能)も既に存在します。 ログ機能の詳細については、次回以降の記事で順次解説していきます。