MCPサーバーは、ローカルでToolが動くだけなら比較的簡単に作れます。しかし本番へ公開すると、「Toolが急に遅くなった」「特定のユーザーだけ401になる」「外部APIのTimeoutが増えた」「どのToolがエラーを出したのか分からない」といった問題が起きます。このときprint("error")だけでは原因を追えません。
本番のMCPでは、ログ・メトリクス・トレースを分けて設計します。ログは「何が起きたか」、メトリクスは「どのくらい起きているか」、トレースは「1回のRequestがどこを通り、どこで時間を使ったか」を追います。
この記事では、障害時に「どのToolが・どこで・なぜ遅いか」まで追える構成を、MCP Python SDKの公式ドキュメントとOpenTelemetryの仕様に沿って整理します。ログの出し先の基本はMCPのProtocol Loggingが非推奨になった理由|stderrとOpenTelemetryでのログ設計、リモート公開の認証やHTTPSはMCPサーバーをローカルではなくリモート公開する方法|認証・HTTPSの基本で解説しているため、この記事ではメトリクス・アラート・トレース・運用の設計に絞ります。
なお、この記事はStreamable HTTPで公開するRemote MCPの運用を想定しています。stdioでHostが子Processとして起動するServerは、Process管理やログの扱いがHost側に依存するため、前提が異なります。
- 本番MCPではログ・メトリクス・トレースを分ける
- Protocol Loggingは非推奨だが、すぐ動かなくなるわけではない
- 構造化ログにtool_nameとtrace_idを入れる
- Tool ArgumentとResultを丸ごとログへ出さない
- MCP Python SDKは受信メッセージごとにSpanを作る
- ExporterをInstallしてOTLPで送る
- Trace ContextはClientからServerへつながる
- Tool単位のメトリクスを作る
- メトリクスはp95・p99とTool別のエラー率を見る
- MCP Method別・依存先別にも分けて見る
- 401・403・421もMonitoringの対象にする
- Health Checkは認証されない前提で作る
- LivenessとReadinessを分ける
- MCPServerはアプリケーションサーバーではない
- Alertはログの行ではなく症状から作る
- Tool Failureをすべて「error」にしない
- Trace Samplingはhead samplingとtail samplingを区別する
- Deploy VersionをLogとTraceに入れる
- 複数ReplicaではServer名とInstance IDを分ける
- 監査ログとApplication Logを分ける
- 障害調査はMetric、Trace、Logの順に絞る
- MCPサーバーの本番運用に関するよくある質問
- 本番MCPは「どのToolが・どこで・なぜ遅いか」を追える状態にする
本番MCPではログ・メトリクス・トレースを分ける
たとえばsearch_documentsというToolが5秒かかったとします。3つは同じ出来事を別の角度から記録します。
{
"level": "INFO",
"tool_name": "search_documents",
"duration_ms": 5123,
"result_count": 8
}
mcp.tool.calls mcp.tool.duration → search_documentsのp95が2.8秒、エラー率が3.2%
tools/call search_documents 5.1秒 ├─ Embedding API 4.6秒 ├─ PostgreSQL 120ms └─ その他の処理 380ms
メトリクスで異常に気付き、トレースで遅い箇所を特定し、ログで原因の詳細を確認する、という順番で使います。
Protocol Loggingは非推奨だが、すぐ動かなくなるわけではない
MCP 2026-07-28では、Logging・Sampling・Rootsが非推奨になりました。移行先として公式が示しているのは、stdioならstderr、構造化されたObservabilityにはOpenTelemetryです。
ただしこれは「注釈だけの非推奨」で、メソッド・型・Capability Flagは、このRevisionと、1年以内に公開される仕様でも引き続き動作するとされています。削除には別途SEPが必要です。既存のLogging Notificationが突然壊れるわけではありません。ただし、同じ2026-07-28でlogging/setLevelは削除され、ログレベルはリクエストごとに_metaのio.modelcontextprotocol/logLevelで指定する形に変わりました。Serverは、この項目を含まないリクエストにnotifications/messageを送ってはいけません。新しく作るServerの監視設計はOpenTelemetryと通常のApplication Logを中心にします。
stdio ServerのstdoutはProtocolの通信路なので、ログはstderrへ出します(MCPのstdioサーバーが接続できない原因|stdoutにログを出してはいけない理由)。Remote MCPなら、通常のWeb APIと同じようにApplication Logを出せます。
構造化ログにtool_nameとtrace_idを入れる
本番では、”Tool failed”のような文字列より、JSONなどの構造化ログのほうが検索しやすくなります。tool_nameやerror_typeで絞り込めるため、Tool別のエラー分析もできます。さらにtrace_idを入れておけば、ログからTraceへ移動できます。次のFormatterは、現在有効なSpanがあればtrace_idとspan_idを付けます。
import json
import logging
import sys
from opentelemetry import trace
class JsonFormatter(logging.Formatter):
EXTRA_KEYS = (
"tool_name",
"duration_ms",
"error_type",
"request_id",
)
def format(self, record):
data = {
"time": self.formatTime(record),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
for key in self.EXTRA_KEYS:
value = getattr(record, key, None)
if value is not None:
data[key] = value
context = trace.get_current_span().get_span_context()
if context.is_valid:
data["trace_id"] = format(context.trace_id, "032x")
data["span_id"] = format(context.span_id, "016x")
if record.exc_info:
data["exception"] = self.formatException(
record.exc_info
)
return json.dumps(data, ensure_ascii=False)
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(JsonFormatter())
root = logging.getLogger()
root.setLevel(logging.INFO)
root.handlers = [handler]
logger = logging.getLogger(__name__)
logger.info(
"tool finished",
extra={
"tool_name": "search_documents",
"duration_ms": 5123,
},
)
trace_idが出力されるのは、OpenTelemetry SDKを設定して有効なSpanがある場合だけです。Exporterを入れていない段階では、trace_idは付きません。
Tool ArgumentとResultを丸ごとログへ出さない
Debug目的で引数やResultを丸ごとlogger.infoしたくなりますが、Tool Argumentにはメールアドレス、顧客情報、検索Keyword、ファイル内容、Access Tokenなどが含まれる可能性があります。Resultも同様で、Customer DataやDocument全文がLogging BackendやBackupへ複製されます。
本番では、Tool名、引数の件数、安全なIdentifier、処理結果、所要時間、result_countやresult_size_bytesのようなMetadataだけを記録します。Authorization: Bearer ...のようなHeaderは、ログへ出してはいけません。Result側の設計はMCPで巨大なレスポンスを返してはいけない理由|トークン消費を減らす設計、OAuthのTokenの扱いはMCPでOAuth認証を実装する方法|アクセストークンを安全に扱う設計を参照してください。
MCP Python SDKは受信メッセージごとにSpanを作る
MCP Python SDKの公式ドキュメントによると、受信した各メッセージは、メソッドとその対象にちなんだ名前のSERVER Spanになります。たとえばtools/call search_booksやtools/listです。Spanにはmcp.method.name、mcp.protocol.version、jsonrpc.request.idが付きます(通知にはRequest IDは付きません)。
Tool実行ではOpenTelemetryのGenAI Semantic Conventionに合わせて、gen_ai.operation.nameがexecute_tool、gen_ai.tool.nameがTool名になります。Handlerが例外を投げた場合、そしてis_error=TrueのTool Resultも、SpanのStatusはErrorになります。そのため、Tool単位のエラー率をTrace Backendから分析できます。
SDKが依存するのはopentelemetry-apiだけで、OpenTelemetry SDKとExporterを入れるまでは、Spanは何もしないNo-opとして動きます。入れていなければコストはかかりません。
ExporterをInstallしてOTLPで送る
Traceを収集したくなったら、OpenTelemetry SDKとExporterを追加します。Server Codeを変えずに、SDKが作るSpanをそのままBackendへ送れます。
uv add opentelemetry-sdk opentelemetry-exporter-otlp
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
OTLPSpanExporter,
)
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
resource = Resource.create({
"service.name": "company-mcp",
"service.version": "1.8.2",
"deployment.environment.name": "production",
})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter())
)
trace.set_tracer_provider(provider)
送信先は、OpenTelemetryの一般的な環境変数(OTEL_EXPORTER_OTLP_ENDPOINTなど)で設定できます。この初期化は、通常はアプリケーションの起動時に、MCPServerを作る前に行います。
Trace ContextはClientからServerへつながる
2026-07-28のMCPでは、W3C Trace Contextを_metaで伝播する方法が文書化され、Key名もtraceparent・tracestate・baggageに固定されました。Host側で始まったTraceを、Client SDK、MCP Server、Serverが呼ぶ下流のAPIまで、1つのSpan Treeとして見られます。公式ドキュメントでは、ClientとServerの両方がPython SDKなら、この伝播は自動で行われ、ServerのSpanがClientのSpanの下にぶら下がると説明されています。
下流のPostgreSQL、外部API、Redisなどにも、OpenTelemetryのInstrumentationを入れておくと、「Tool全体が4秒」の内訳が「Embedding API 3.7秒、DB 200ms、MCP処理 100ms」のように分かります。この場合、MCPのコードを最適化しても意味がない、と判断できます。
なおbaggageはRequestをまたいで伝播するため、Access Token、Password、API Key、メール本文などの機密や個人情報全文を入れてはいけません。tenant.idのように、伝播して問題がなくObservabilityに必要な情報だけにします。
Tool単位のメトリクスを作る
Traceだけでも詳細は分かりますが、継続的な監視にはメトリクスが適しています。MCP Python SDKの公式ドキュメントで確認できたのはSpanまでで、「Tool別の呼び出し数・エラー数・実行時間」のメトリクスは、自分で出すか、Spanから生成する必要があります。方法は2つあります。
- アプリケーション側で計測する:OpenTelemetryのMetrics APIでCounterとHistogramを作り、Toolの呼び出しごとに記録します。
- Collectorで生成する:OpenTelemetry Collector Contribには
spanmetricsコネクタがあり、Spanから集計メトリクスを作れます(利用するCollectorのDistributionを確認してください)。アプリのコードを変えずに済みます。
アプリ側で計測する場合の例です(async関数向けです)。Metrics用のMeterProviderとExporterの設定も別途必要です。
import time
from functools import wraps
from opentelemetry import metrics
meter = metrics.get_meter("company-mcp")
tool_calls = meter.create_counter(
"mcp.tool.calls",
description="Number of tool calls",
)
tool_duration = meter.create_histogram(
"mcp.tool.duration",
unit="s",
description="Tool call duration",
)
def observed(tool_name):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start = time.perf_counter()
status = "ok"
try:
return await func(*args, **kwargs)
except Exception:
status = "error"
raise
finally:
attrs = {
"gen_ai.tool.name": tool_name,
"status": status,
}
tool_calls.add(1, attrs)
tool_duration.record(
time.perf_counter() - start,
attrs,
)
return wrapper
return decorator
MCP Toolへ適用するときは、@mcp.tool()の内側に@observed("search_documents")を重ねます。SDKは関数の署名から入力スキーマを作るため、functools.wrapsで元の関数の情報を保っています。筆者の環境(MCP Python SDK 1.27のFastMCP)では、この重ね方で引数のスキーマが保たれ、計測も動くことを確認しました。MCPServerを含め、お使いのSDKのバージョンでも、tools/listで入力スキーマが変わっていないか確認してください。Label名は、SDKのSpan属性に合わせてgen_ai.tool.nameにしています。Span属性と同じ名前にしておくと、TraceとMetricsをつなげて調べやすくなります。
メトリクスはp95・p99とTool別のエラー率を見る
監視する項目は、概念的には次のようなものです。
- Tool別の呼び出し数
- Tool別のエラー数とエラー率
- Tool別の実行時間(p50・p95・p99)
- Tool Resultのサイズ
- HTTP Status別のRequest数(Remote MCP)
平均値だけを見ると、一部のユーザーだけ10秒かかるような問題を見逃します。平均500msでも、p99が10秒なら一部のRequestは非常に遅いからです。AIエージェントは1つのTaskで複数のToolを順に呼ぶため、各Toolが1秒ずつ遅れるとWorkflow全体では数秒単位で遅くなります。
Tool名をLabelにするのは有効ですが、customer_id・request_id・emailのような値の種類が多いものはLabelへ入れません。Metric Backendの負荷が大きくなるためです。個別のRequestはログとTraceで調べます。また、Tool名は後から変えるとDashboardやAlertも壊れるため、Stable Identifierとして扱います(MCPで同じツール名が競合するとどうなる?名前付けとNamespace設計)。
MCP Method別・依存先別にも分けて見る
Toolだけでなく、tools/list・tools/call・resources/read・prompts/getのMethod別に見ると、原因を切り分けられます。tools/listまで遅いならServerやNetworkの問題を疑い、tools/listが20msでtools/call search_documentsだけ8秒なら、Tool内部の処理を疑います。
Streamable HTTPではMcp-MethodとMcp-NameがHTTP Headerへ出るため(MCPのstdioとStreamable HTTPの違い|ローカル・リモート構成の選び方)、Gatewayでも、Bodyを解析せずにMethodやTool名ごとの監視ができます。
さらにMCP Server自身のLatencyだけでなく、PostgreSQL・GitHub・Slack・LLM APIなど依存先ごとのLatencyも別のメトリクスにします。そうすれば「MCP Serverが遅い」のではなく「GitHub APIが遅い」と判断できます。
401・403・421もMonitoringの対象にする
Remote MCPでは、Tool内のエラー以外にも、TransportやSecurityの層で失敗します。MCP Python SDKのStreamable HTTPは、Production Hostを許可していなければ、実HostnameへのRequestに421 Misdirected Requestを返します。デプロイ直後に421が急増したら、Tool実装ではなくHost Allowlistの設定ミスを疑います(設定方法はMCPサーバーをローカルではなくリモート公開する方法|認証・HTTPSの基本で解説しています)。401や403の急増は、OAuthのScope変更や認証Layerの問題かもしれません。HTTP Status別のRequest数も見るようにします。
Health Checkは認証されない前提で作る
Load BalancerやContainer Platformから使うHealth Checkは、@mcp.custom_routeで追加できます。公式ドキュメントでも、このRouteは「サーバーの他の部分が認証されていても、常に認証されない」と明記されています。
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from mcp.server import MCPServer
mcp = MCPServer("Company MCP")
@mcp.custom_route("/health", methods=["GET"])
async def health(request: Request) -> Response:
return JSONResponse({"status": "ok"})
app = mcp.streamable_http_app()
認証されないRouteなので、返すのは{"status": "ok"}程度にします。Database Password、Token、内部IP、環境変数、顧客情報、詳細なError Messageは返しません。詳しい診断情報が必要なら、MCPのcustom_routeではなく、Host Application側で認証付きの別Routeを作ります。
LivenessとReadinessを分ける
Health Checkには、「Processが生きているか」を見るLivenessと、「Trafficを受けられる状態か」を見るReadinessがあります。Process自体は動いていても、Databaseへ接続できなければToolは正常に実行できません。
/health → Processが生きている /ready → DBなど必須のDependencyも使える
ただしReadinessで毎回重い外部APIを呼ぶと、それ自体が障害の原因になります。確認するのは、Requestを処理するために本当に必要な最低限のDependencyだけにします。
MCPServerはアプリケーションサーバーではない
MCP Python SDKの公式ドキュメントでは、MCPServerは「Protocol Implementationであって、Application Serverではない」とされています。Workers、Timeout、TLS、Graceful Shutdown、Connection Limitといった本番の設定は、意図的にMCPServerの範囲外です。mcp.run("streamable-http")はuvicornのProcessを1つ起動するだけで、複数Workerが必要なら、streamable_http_app()をuvicornやgunicornなどのASGI Server、Platformのプロセス管理で動かします。
したがって、Observabilityも複数の層で見ます。
Load Balancer / Gateway / ASGI Server → HTTP Status、Request数、Connection、502・504、TLS、Timeout MCP Application → Method、Tool名、Toolのエラー、Latency、Result Size 外部Dependency → DB・APIのLatency、Timeout、Rate Limit、エラー率
MCP内部のTool Metricsだけでは、Proxyでの502や504は分かりません。層ごとの指標をTrace IDでつなぐと、障害調査が容易になります。
Alertはログの行ではなく症状から作る
「ERRORという文字列が1件出たら通知」というAlertはNoiseが多くなります。一時的なErrorは、正常な運用でも発生するからです。Alertは、ServiceがユーザーにどんなLatencyや失敗を見せているかという症状から作ります。
- Error Rate:
tools/callのError Rateが5分間継続して5%を超えた - Latency:Tool別のp95やp99が、一定時間、基準を超えた
- Availability:Health Checkが失敗した、または5xxが増えた
- Dependency:特定の外部APIのTimeoutやRate Limitが増えた
具体的な閾値はToolの用途によって変わるため、一律の値は決めず、運用しながら調整します。タイムアウト時の再試行や冪等性の設計はMCPツールがタイムアウトする原因|再試行・キャンセル・冪等性の設計も参考にしてください。
Tool Failureをすべて「error」にしない
失敗を1つのerrorにまとめると、原因が分からなくなります。少なくとも次の程度は区別できるようにします。
- Validation Error(入力不正)
- Authorization Error(権限・Scope)
- Timeout
- Dependency Error(外部APIやDBの失敗)
- Internal Error(実装のBug)
- Tool Result Error(
is_errorのResult)
Timeoutだけが増えたなら外部API・Network・Databaseを、Authorization Errorだけなら、OAuthやScopeの変更を疑えます。ログのerror_typeと、メトリクスのLabelの両方に入れておきます。
Trace Samplingはhead samplingとtail samplingを区別する
Trafficが増えると、Traceの保存コストやNetwork費用も増えるため、Samplingを設計します。OpenTelemetryのドキュメントでは、2つの方式が区別されています。
- Head Sampling:Samplingの判断をできるだけ早く、Requestの開始時点で行う
- Tail Sampling:Trace内のすべて、またはほとんどのSpanを見てから判断する
「エラーを含むTraceは必ず残す」「全体のLatencyが長いTraceを残す」といった方針は、Head Samplingでは実現できず、Tail Samplingが必要です。Tail SamplingはOpenTelemetry Collectorのtail sampling processorで実現できますが、設定が複雑になりやすく、大量のデータを保持する状態を持つシステムが必要になる、といった運用上の負担があります。
Trafficが少ないうちは、すべてのTraceを保存して問題ない場合もあります。Sampling率を下げすぎると、まれにしか起きない障害を見失うため、Trafficと障害の頻度に合わせて調整します。
Deploy VersionをLogとTraceに入れる
本番では、いつから壊れたかが非常に重要です。先ほどのOpenTelemetryの初期化のように、service.versionとdeployment.environment.nameをResourceの属性に入れておくと、「v1.8.2へDeployした直後から、delete_customerのError Rateが増えた」と判断できます。Git Commitも入れておくと、さらに調べやすくなります。
複数ReplicaではServer名とInstance IDを分ける
複数のReplicaで動かすと、どのPodやContainerで起きたかをログに入れたくなります。ここで注意したいのが、MCPServer("billing-pod-1")のように、Server名にInstance IDを入れてしまうことです。
ここは、ToolがElicitationなどで追加の入力を求める(Multi Round-Tripを使う)場合の話です。そうしたToolがなければ関係しません。MCP Python SDKの公式ドキュメントによると、Multi Round-TripのrequestStateには、署名されたEnvelopeにServer名がaudienceとして入り、検証されます。複数Instanceで動かすときは、RequestStateSecurity(keys=[...])で全Instanceが同じ鍵を共有し、Server名(または明示的なaudience)を揃える必要があります。揃っていないと、Retryが別のInstanceへ届いたときに、-32602のInvalid or expired requestStateで失敗します。
そのため、Instanceの識別には、Server名ではなくservice.instance.id・pod.name・container.idのようなTelemetryの属性を使います。Multi Round-Tripを使う場合、鍵の共有とServer名の統一は、監視の話というより、複数Instanceで確実に動かすための前提です。
監査ログとApplication Logを分ける
更新・削除Toolを持つMCPでは、通常のApplication Logとは別にAudit Logも必要です。Application Logは、処理時間・例外・Dependency Errorを追うために使います。Audit Logは、誰が・いつ・何を・どのResourceへ・どんな変更をしたかを追跡するために使います。保存期間や閲覧権限も異なるため、可能なら別のStorageやIndexへ分けます。
記録するのはuser_id・client_id・scope・tool_name・resource_id・operation・result・trace_idなどで、Access TokenやAuthorization Headerの値は保存しません。破壊的Toolの設計と監査ログはMCPツールに破壊的操作を持たせるときの設計|削除・更新処理を安全にするで解説しています。
Trace ExporterへPayload全文を送る前にも、保存してよいDataか、保存期間は適切か、誰が閲覧できるかを確認します。Tool名・所要時間・Status・件数・サイズだけを残す構成でも、障害調査には十分です。
障害調査はMetric、Trace、Logの順に絞る
たとえば「検索Toolが遅い」と報告されたとします。巨大なLogを最初から検索するより、次の順に絞り込みます。
Dashboard
→ search_documentsのp95が上昇
Trace
→ tools/call search_documents 6.4秒
└─ vector_search 6.0秒
Log(trace_idで検索)
→ upstream timeout
この順番で調べられるのは、メトリクスのLabel、ログのtrace_id、TraceのService属性がそろっているからです。最初に入れておくと、Toolが数個から数十個に、Serverが1台から複数Replicaに増えても、原因を追いやすくなります。
MCPサーバーの本番運用に関するよくある質問
QMCP ProtocolのLogging機能はもう使えませんか?
A使えます。2026-07-28の非推奨は「注釈のみ」で、メソッド・型・Capability Flagは、このRevisionと1年以内に公開される仕様でも動作するとされています。ただしlogging/setLevelは2026-07-28で削除され、ログレベルはリクエストごとの_metaのio.modelcontextprotocol/logLevelで指定する形に変わっています。新規のServerでは、stdioならstderr、構造化されたObservabilityならOpenTelemetryを中心に設計するのが公式の方向です。
QOpenTelemetryを入れないと、SDKのSpanは何も起きませんか?
ASDKが依存するのはopentelemetry-apiだけで、OpenTelemetry SDKとExporterをInstallするまで、Spanはほぼ何もしないNo-opとして動きます。Traceを収集したくなった時点で、SDKとExporterを追加し、初期化コードを入れます。
QTool別のメトリクスはSDKが自動で出してくれますか?
A公式ドキュメントで確認できたのはSpanまでで、Tool別のメトリクスは自分で計測するか、CollectorでSpanから生成します。Collector Contribのspanmetricsコネクタを使えば、アプリのコードを変えずにSpanから集計メトリクスを作れます。
Q「エラーのTraceだけ残す」ことはできますか?
AHead Samplingでは実現できず、Tail Samplingが必要です。OpenTelemetry Collectorのtail sampling processorで実現できますが、設定が複雑になり、データを保持する運用上の負担も増えます。Trafficが少ないうちは、すべて保存する選択肢もあります。
Q/healthに認証を付けられますか?
AMCP Python SDKのcustom_routeは常に認証されないとされています。認証が必要な診断Endpointは、Host Application側で別Routeとして作ります。/healthは機密情報を含まない最小限のResponseにします。
本番MCPは「どのToolが・どこで・なぜ遅いか」を追える状態にする
MCP ProtocolのLoggingは非推奨になりましたが、すぐに壊れるわけではありません。新しく作るServerでは、stdoutをProtocol専用にしてログはstderrへ出し、構造化ログ、Tool単位のメトリクス、OpenTelemetryのTraceを組み合わせます。
MCP Python SDKは、受信メッセージごとにSpanを作り、Toolの実行にはgen_ai.tool.nameなども付けます。OpenTelemetry SDKを入れればSpanをBackendへ送れます。一方、メトリクス、Health Check、Timeout、Workers、TLSなどはSDKの範囲外なので、自分で用意します。複数ReplicaでMulti Round-Tripを使うなら、requestStateの鍵とServer名を揃え、Instanceの識別はTelemetryの属性で行います。
Alertはログの行ではなく、Error Rate・p95やp99のLatency・Availability・依存先の失敗といった症状から作ります。そして障害時は、Metricで異常に気付き、Traceで遅い箇所を特定し、Logで原因を確認します。
重要なのは、単にError Logを残すことではなく、「どのToolが失敗しているか」「何秒かかったか」「どの依存先で止まったか」「どのDeployから悪化したか」まで追跡できる状態を、最初から作っておくことです。(2026年10月時点の公式ドキュメントに基づきます)

