コンテンツにスキップ

ロギング

機械翻訳

このページは英語版ドキュメントから自動翻訳されたものであり、正式な版は英語版のページです。不自然な箇所があれば、翻訳についてで報告の方法を説明しています。

ツールからのログ出力は、他のどの Python 関数でも同じやり方です。標準ライブラリを使います。

MCP にはプロトコルレベルのロギングのケイパビリティがあります。サーバーは Context オブジェクトのメソッドを通じて、ログメッセージを通知としてクライアントへ送り出せました。仕様の 2026-07-28 版ではこのケイパビリティが非推奨となり、代わりのものは用意されていません。そのため、このドキュメントでは扱いません。非推奨になったものと、代わりにどうすればよいかの一覧は、非推奨の機能にあります。

代わりにやることは、他のどの Python プログラムでもやっていることと同じです。標準ライブラリを使います。

ログを出すツール

server.py
import logging

from mcp.server import MCPServer

logger = logging.getLogger(__name__)

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str) -> str:
    """Search the catalog by title or author."""
    logger.info("Searching for %r", query)
    return f"Found 3 books matching {query!r}."
  • logging.getLogger(__name__) は、モジュール名にちなんだ名前のロガーを返します。冒頭で一度だけ作成してください。
  • ツールの中では、他の関数と同じように logger.info(...) を呼び出します。注入するものも、await するものも、MCP 固有のものも何もありません。

Check

ツールを呼び出して、結果全体を見てみましょう。

result.content             # [TextContent(text="Found 3 books matching 'dune'.")]
result.structured_content  # {'result': "Found 3 books matching 'dune'."}

ログの行はどこにもありません。ロギングはサーバーを運用する人のためのものです。モデルがそれを見ることはありません。モデルに何かを読ませたいなら、return してください。

出力先

stdio サーバーでは、この問いがいつも以上に重要です。ホストはサーバーをサブプロセスとして起動し、その stdout から MCP メッセージを読み取っています。標準エラーは自由に使えます。

標準ライブラリは最初から正しく動作します。ログ出力はデフォルトで sys.stderr に送られます。logger.info(...) の行はターミナル(またはホストがサブプロセスの stderr を集める場所)に届き、プロトコルのストリームはきれいなまま保たれます。

Tip

stdio サーバーで print() を使わないでください。printstdout に書き込みますが、stdout はプロトコルのものです。サーバーの稼働中、SDK は実際に「フラッシュされた」stdout を stderr へ振り向けるので、通信路を壊すことはありません。しかし、ブロックバッファリングされたプロセスでの print() は、たいていフラッシュされないまま sys.stdout のバッファに残り、終了時にインタープリターがそれを吐き出すと、そのままプロトコルのストリームに流れ込みます。振り向けられた場合でも、その行はレベルもロガー名もなく、フィルターする手段もないまま、生の状態でログ出力の中に紛れ込みます。

logger.debug("got here") なら同じ 1 行の手間で、正しい場所に出力されます。

レベル

logging.basicConfig() を自分で呼び出す必要はありません。MCPServer を構築した時点で、すでに呼び出されています。標準エラーに向けたハンドラーが、log_level= で渡したレベルで設定されます。つまり MCPServer("Bookshop", log_level="DEBUG") と書くだけで、logger.debug(...) の行が見えるようになります。

デフォルトは "INFO" です。

logging.basicConfig() は、すでに存在するハンドラーを置き換えることはありません。サーバーを作成する前に自分でロギングを設定していれば、その設定が優先されます。

試してみる

MCP Inspector でサーバーを実行してください。

uv run mcp dev server.py

Tools タブから search_books を呼び出してください。Inspector に表示される結果は、戻り値だけです。次の行は、

Searching for 'dune'

標準エラー、つまりターミナルに出力されました。通信上には現れません。

Info

本当に欲しいものが「トレーシング」(すべてのリクエスト、かかった時間、失敗したかどうか)なら、必要なのはログ行ではなくスパンです。サーバーはすでにスパンを出力しています。SDK はデフォルトで、すべてのメッセージを OpenTelemetry でトレースします。OpenTelemetry を参照してください。

まとめ

  • MCP プロトコルのロギングのケイパビリティは 2026-07-28 版の仕様で非推奨となり、代わりのものはありません。これを土台にしないでください。
  • モジュールレベルで logger = logging.getLogger(__name__)、ツールの中で logger.info(...)。パターンはこれだけです。
  • ログ出力がモデルに届くことはありません。届くのは return した値だけです。
  • 標準エラーは自由に使えますが、stdout はプロトコルのものです。SDK は稼働中、フラッシュされた紛れ込みの stdout 出力を stderr へ振り向けますが、フラッシュされていない print() は終了時に通信路へ流れ込むことがあり、振り向けられた行もラベルなしで届きます。すべてのレコードをフラッシュするハンドラーを持つ logging を使ってください。
  • MCPServer(..., log_level="DEBUG") でレベルを設定でき、先に行ったロギングの設定はそのまま残されます。

サーバー上で何か(ツール一覧やリソース)が変わったことを接続中のクライアントに伝える方法は、サブスクリプションにあります。