.NET Debugger for MCP について
.NET Debugger for MCP は、AI アシスタントによる .NET プログラムのデバッグ向け MCP サーバーです。プロセスの起動・接続や実行状態の確認に対応し、Debug Adapter Protocol 経由で clrdbg を使用します。
主な機能
- 01.NET プロセスの起動と接続
- 02ファイル・行・メソッドへのブレークポイント
- 03コードのステップ実行とコールスタック表示
- 04ローカル変数の確認と C# 式の評価
- 05例外による停止条件の設定
- 06.NET デバッグ資料の検索
活用シーン
- 01起動直後に発生する例外の調査
- 02スレッドのスタックと変数の確認
- 03.NET MAUI モバイルアプリのデバッグ
MCP 対応
MCP クライアントに、プロセス管理、ブレークポイント、実行制御、変数確認などの .NET デバッグツールを提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
DotnetDebugger.Mcp
AI アシスタントが .NET プログラムをデバッグできる MCP サーバーです。プロセスの起動またはアタッチ、ブレークポイントの設定、コードのステップ実行、変数の調査、式の評価ができます。また、.NET デバッガーの内部動作に関する検索可能なリファレンスも備えています。
NuGet では DotnetDebugger.Mcp として公開されています。clrdbg をベースにし、Debug Adapter Protocol 経由で制御します。デバッガーはパッケージ内に含まれて子プロセスとして実行されるため、ネイティブデバッグコードがクラッシュしても、影響を受けるのはサーバーではなくデバッグセッションです。
起源。 decriptor/SharpDbg.MCP をフォークしたもので、独自の名前を付けられるほど元のプロジェクトから離れています。デバッガーレイヤーはデバッガーの内部 API を呼び出す代わりに Debug Adapter Protocol を使用し、内部のデバッガーはその後 SharpDbg から clrdbg に変更されました。デバッガーでプログラムを起動する機能は新たに追加されており、両者はコミットを共有していません。このリポジトリは初回リリースまで
SharpDbg.MCPという名前で、GitHub は旧名からリダイレクトします。SHARPDBG_*設定は 0.6.3 に続くリリースでDOTNET_DEBUGGER_MCP_*に改名され、旧名は読み取られなくなりました。
できること
- 実行中の .NET プロセスにアタッチするか、プロセスを起動して最初の行が実行される前に停止する
- ファイルと行、またはメソッド名を指定して、条件やヒット回数付きのブレークポイントを設定する
- コードをステップオーバー、ステップイン、ステップアウトし、任意のスレッドのコールスタックを読み取る
- ローカル変数を調べ、オブジェクトのメンバーを順に展開し、C# 式を評価する
- 例外で停止する。すべての例外、未処理の例外のみ、または指定した型のみを対象にでき、スローされた内容を読み取る
- デバッグ対象の標準出力と標準エラーを読み取る
- 複数のプロセスを同時にデバッグする。それぞれに独自のブレークポイントと停止状態を設定できる
- Android エミュレーターまたはスマートフォン、iOS シミュレーターまたはデバイス、あるいはこの Mac 上で Mac Catalyst アプリとして実行する .NET MAUI アプリをデバッグする。デスクトッププログラムと同様にビルドして、最初の行が実行される前に停止する
- ICorDebug、Debug Adapter Protocol、式の評価に関する埋め込みドキュメントを検索する
できないこと
このうち一部は、このサーバーではなく内部のデバッガー側に変更を加える必要があります。
- アタッチしたプロセスの終了コードを報告する。 その場合、
exit_codeはnullになります。デバッガーは自分で起動したプロセスからは終了コードを取得できますが、アタッチ先のプロセスについては取得できません。そのため報告できる値がなく、プロトコルにも「不明」という値はなく、0しかありませんが、これはこのサーバーが捏造した数値になってしまいます。launch_programで起動したプログラムについては、実際の終了コードが報告されます。 - 自己完結型の単一ファイル発行をデバッグする。 ランタイムが実行可能ファイル内に格納されるため、デバッガーのシムは一致するコンポーネントを読み込むためのランタイムを見つけられません。この試みは直ちに
CORDBG_E_DEBUG_COMPONENT_MISSING(0x80131C3C) で失敗し、Attempting to register for runtime startup failed: -2146231236として報告されます。自己完結型のみ、または単一ファイルのみであれば動作しますが、両方を組み合わせると動作しません。 - ウォッチ式、ホットリロード、データブレークポイントは実装されていません。
要件
- .NET 10 SDK 以降
- Claude Code や Claude Desktop などの MCP 対応クライアント
- Windows、macOS、または Linux
インストール
1 つのパッケージに、すべてのプラットフォーム向けのデバッガーとネイティブシムが含まれているため、どこでも同じ設定を使えます。クローンやビルドは不要です。
Claude Code、現在のプロジェクトで使用する場合:
claude mcp add dotnet-debugger -- dotnet tool exec DotnetDebugger.Mcp --version 0.8.0 --yes
すべてのプロジェクトで利用できるようにするには --scope user を追加します。または、チームでコミットして共有する .mcp.json を作成するには --scope project を追加します。
VS Code、ワークスペース用の .vscode/mcp.json に記述するか、すべてのワークスペースで使用するには MCP: Open User Configuration を使います。VS Code ではセクション名に mcpServers ではなく servers を使います。
{
"servers": {
"dotnet-debugger": {
"type": "stdio",
"command": "dotnet",
"args": ["tool", "exec", "DotnetDebugger.Mcp", "--version", "0.8.0", "--yes"]
}
}
}
Claude Desktop、macOS と Linux では ~/.config/Claude/claude_desktop_config.json を、Windows では %APPDATA%\Claude\claude_desktop_config.json を編集します。
{
"mcpServers": {
"dotnet-debugger": {
"command": "dotnet",
"args": ["tool", "exec", "DotnetDebugger.Mcp", "--version", "0.8.0", "--yes"]
}
}
}
dnx DotnetDebugger.Mcp --version 0.8.0 --yes は、より短い同等のコマンドで、NuGet.org が推奨する形式です。デスクトップ環境から起動するクライアントでは、シェルから起動する場合よりも dotnet tool exec を使うことをおすすめします。dnx は SDK ディレクトリにあり、そのようなクライアントの PATH に含まれていないことがよくありますが、dotnet は確実に見つかります。
クライアントが MCP サーバーに接続するのはセッション開始時だけなので、設定を変更した後はクライアントを再起動してください。サーバーが接続されていることを確認するには、claude mcp list を実行するか、クライアントに .NET プロセスを一覧表示するよう依頼します。
バージョンを固定する理由
ここに記載されているすべてのコマンドでは、バージョンを指定しています。--version を指定しないと、dotnet tool exec は起動のたびにパッケージを解決するため、新しいリリースが公開されると、誰も選択していないのに次のセッションで実行されます。デバッガーはアタッチしたプロセスのすべてを読み取り、変更できるため、どのビルドを実行するかは副次的な結果ではなく、明示的に決めるべきことです。アップグレードするには、コマンドのバージョンを変更してください。最新バージョンは nuget.org で確認できます。
ツールを一度インストールしておけば、起動のたびに解決する必要はありません。
dotnet tool install -g DotnetDebugger.Mcp --version 0.8.0
以降は、引数なしで dotnet-debugger-mcp を実行します。これには ~/.dotnet/tools が PATH に含まれている必要があります。コマンドが見つからない場合もパスが間違っている場合と同じように失敗するため、これをデフォルトの推奨方法にはしていません。
パッケージの入手元を確認する
0.6.1 以降のリリースには、ビルド来歴のアテステーションが付属しています。これは、このリポジトリのリリースワークフローが特定のコミットからそのパッケージをビルドしたことを示す、GitHub 経由で署名された証明書です。ミラーリングやインストールの前に確認するには、nuget.org から .nupkg をダウンロードして、次を実行します。
gh attestation verify DotnetDebugger.Mcp.<version>.nupkg --repo nevse/dotnet-debugger-mcp
nuget.org は、受け入れたすべてのパッケージに独自のリポジトリ署名を追加するため、ファイルが変更されます。そのため、リリースワークフローでは両方に対するアテステーションを生成します。1 つはビルド時のパッケージを GitHub Release に添付したもので、もう 1 つはビルドしたものとまったく同じ内容であることを確認した後の、nuget.org にあるコピーを対象としたものです。0.6.2 では前者のみ存在します。gh release download v0.6.2 -R nevse/dotnet-debugger-mcp -p '*.nupkg' で取得したコピーを検証してください。
アップグレード
dotnet tool exec は、新しいバージョンが公開されてインデックスに登録された後も、すでにダウンロード済みのバージョンを実行し続けることがあります。その場合も問題があるようには見えません。サーバーは起動し、クライアントは接続済みと報告し、新しいリリースのツールが見当たらないことだけが兆候です。
0.1.1 のリリース時に測定しました。ローカルのパッケージフォルダーに 0.1.0 しかない状態では、バージョンを指定しないコマンドは 0.1.0 を実行し続け、NuGet の HTTP キャッシュを消去しても変化しませんでした。バージョンを指定すると新しいパッケージが取得され、その後はバージョンを指定しないコマンドでもそれが使われました。
dotnet tool exec DotnetDebugger.Mcp --version 0.1.1 --yes
バージョンを固定している場合は、コマンドのバージョンを変更するだけでアップグレードできます。この問題は発生しません。インストール済みのツールも明示的にアップグレードできます。
dotnet tool update -g DotnetDebugger.Mcp --version <new version>
実際に実行されているバージョンを確認するには、クライアントにこのサーバーのツールを一覧表示するよう依頼するか、サーバーが stderr に出力する最初の行を確認します。起動時にバージョンが表示されます。クライアント独自のヘルスチェックでわかるのは、プロセスが起動したことだけであり、何が実行されているかはわかりません。
実例
アタッチでは対応できない、プログラムが 1 行も実行する前に捕捉する方法:
User: "My app throws before it prints anything. Find out why."
Claude: [launch_program("/path/to/bin/Debug/net10.0/MyApp.dll")]
"Prepared, not running yet. Setting a breakpoint on the first line of Main."
Claude: [set_breakpoint("/path/to/Program.cs", 12)]
"Breakpoint set. It is unverified for now — nothing can bind before the program has
loaded its modules — and takes effect when the program starts."
Claude: [start_program()]
Claude: [wait_for_stop()]
"Stopped at Program.cs:12, before a single line has run."
Claude: [step_over(thread_id: 1)]
Claude: [get_variables(frame_id: 0)]
"configPath is null, and the next line passes it to File.ReadAllText."
Claude: [get_program_output()]
"The program printed nothing before the throw, which matches what you saw."
ツール
各ツールは MCP アノテーションを宣言しているため、これを読み取るクライアントは、読み取り専用のツールを確認なしで承認し、それ以外のツールについては引き続き確認を求めることができます。どのアノテーションに該当するかは最後の列に記載されています。
- read-only - セッションまたはマシンを読み取り、何も変更しません。
- session - デバッグセッションのみを変更します。ステップ実行、再開、ブレークポイントの削除などです。破壊的ではありません。
- runs getters - デバッグ対象の独自コードを実行して読み取ります。デバッガーは値を表示するために、
ToString、プロパティのゲッター、DebuggerDisplayを呼び出します。読み取り専用としてマークされていませんが、破壊的でもありません。 - ends a launched program - 破壊的です。セッションが起動したプログラムを終了します。
- runs code - 破壊的で、対象を限定できません。エージェントが選んだコードを開始、ビルド、または評価します。ブレークポイントの条件はヒットするたびに評価される C# 式であるため、条件の設定も該当します。
アノテーションはあくまでヒントであり、クライアントは無視してもかまいません。
デバッグ
| ツール | 内容 | アノテーション |
|---|---|---|
list_dotnet_processes | このマシンで実行中の .NET プロセスを一覧表示します | read-only |
attach_to_process | 実行中の .NET プロセスにデバッガーをアタッチします | runs code |
launch_program | デバッガーで実行するプログラムを準備し、開始前に停止します | runs code |
start_program | launch_program で準備したプログラムを実行します | runs code |
get_process_status | セッションの実行中・停止中の状態と停止位置を報告します | read-only |
wait_for_stop | ポーリングする代わりに、デバッグ対象が停止するまで待機します | read-only |
get_program_output | デバッグ対象が標準出力と標準エラーに書き込んだ内容を読み取ります | read-only |
detach_from_process | プロセスを実行したままデバッガーをデタッチします | ends a launched program |
list_sessions | 開いているセッションと、それぞれのデバッグ対象を一覧表示します | read-only |
close_session | 必要に応じて先にデタッチしてから、セッションを閉じます | ends a launched program |
.NET MAUI
一度限りのセットアップが必要です。.NET MAUI アプリのデバッグを参照してください。
| ツール | 内容 | アノテーション |
|---|---|---|
list_mobile_devices | Android エミュレーターとスマートフォン、iOS シミュレーターとデバイス、およびこの Mac を一覧表示します | read-only |
build_mobile_app | いずれかのデバイス向けに、CoreCLR を使用しデバッグライブラリを内包した MAUI プロジェクトをビルドします | runs code |
launch_mobile_app | ビルドしたアプリをデバッグ用に準備し、開始前に停止します。実行するには start_program を使用します | runs code |
ブレークポイント
| ツール | 内容 | アノテーション |
|---|---|---|
set_breakpoint | ファイルと行を指定してブレークポイントを設定または更新します。条件またはヒット回数も指定できます | runs code |
set_function_breakpoint | ファイルと行が不明な場合に、名前でメソッドにブレークポイントを設定します | runs code |
remove_breakpoint | どちらの種類のブレークポイントも削除します | session |
list_breakpoints | このセッションのブレークポイントと、それぞれが検証済みかどうかを一覧表示します | read-only |
set_exception_break_mode | プログラムを停止する例外を選択します。すべて、未処理、または指定した型から選べます | session |
実行と検査
| ツール | 内容 | アノテーション |
|---|---|---|
continue_execution | 次のブレークポイントまたは終了まで実行を再開します | session |
pause_execution | プログラムの現在位置でデバッガーを停止します | session |
step_over、step_into、step_out | 行単位で進む、呼び出しに入る、または現在のメソッドから抜ける操作をします | session |
get_threads | デバッグ対象のスレッドを一覧表示します | read-only |
get_stack_trace | スレッドのコールスタックを読み取ります | read-only |
get_variables | スタックフレームのローカル変数を読み取ります | runs getters |
expand_variable | オブジェクトをメンバーに展開します。さらに展開できる場合もあります | runs getters |
evaluate_expression | フレームのコンテキストで C# 式を評価します | runs code |
get_exception_info | スローされた例外の型、メッセージ、スタックトレース、内部例外を読み取ります | runs getters |
ドキュメント
| ツール | 内容 | 注釈 |
|---|---|---|
search_debugging_concepts | 埋め込みドキュメントを検索します | 読み取り専用 |
explain_icordebug_interface | 特定の ICorDebug インターフェイスについて説明します | 読み取り専用 |
get_debugging_flow | デバッグ操作の流れを段階的に説明します | 読み取り専用 |
list_debugging_concepts | カテゴリ別に概念カタログを閲覧します | 読み取り専用 |
知っておくと役立つこと
ブレークポイントにはポータブル PDB が必要です
ブレークポイントはターゲットのシンボルを通じてバインドされるため、デバッグ対象はアセンブリと同じ場所にポータブル PDB を置いてビルドする必要があります。PDB が存在しないか一致しないことが、set_breakpoint が verified: false と No symbols have been loaded for this document を返す最も一般的な原因です。
デバッグビルドでは、すでにこの設定になっています。Release ビルドや、既定値を変更するプロジェクトの場合は、次のようにします。
<PropertyGroup>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
最適化されたコードではローカル変数にもアクセスできなくなるため、get_variables が十分に役立つのは <Optimize>false</Optimize> の場合だけです。
macOS:ヘッドレス環境ではエンタイトルメントが必要な場合があります
macOS では、ターゲットに com.apple.security.get-task-allow が設定されていない限り、デバッガが別のプロセスのタスクポートを取得できません。dotnet マルチプレクサー経由で実行するプログラムは問題ありません。このマルチプレクサーにはそのエンタイトルメントが含まれているためです。一方、dotnet publish で生成された apphost は、エンタイトルメントなしでアドホック署名されています。
デバッガー側では、何も設定する必要はありません。デバッグアダプターも dotnet マルチプレクサー経由で起動されるため、デバッグに必要なエンタイトルメントを引き継ぎます。
デスクトップセッションでは、この影響はありません。 エンタイトルメントのない自己完結型の発行物でも、デバッグが正常に動作することが確認されています。
CI ランナーなどのヘッドレス環境では、この問題が発生する可能性があり、発生した場合は深刻です。macOS はエラーを返さずに処理をブロックするため、デバッガーが応答を停止し、呼び出しが戻ってきません。macOS でエラーがないまま起動またはアタッチがハングする場合は、ターゲットに署名してもう一度試してください。
codesign -s - -f --entitlements get-task-allow.entitlements ./MyApp
その際、get-task-allow.entitlements に次の内容を記述します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
Just My Code がステップ実行に与える影響
DOTNET_DEBUGGER_MCP_JUST_MY_CODE=false にしても、シンボルがないコードでステップが停止するわけではありません。どちらの設定でも同じです。シンボルのないモジュールに到達した場合、ステップはそのままモジュールから抜けます。場所を示すソースがないためです。補間文字列をステップ実行すると、どちらの設定でも System.Private.CoreLib を経由し、自分のメソッド内の次のステートメントに戻ります。
この設定で変わるのは、シンボルを検索するモジュールです。Just My Code が有効な場合は、自分でビルドしたアセンブリだけが検索されます。無効な場合はすべてのモジュールが検索されるため、シンボルがある依存関係内でもステップが停止します。無効にするのは、このような場合です。
launch_programで実行されるもの
デバッガーは渡されたものをそのまま起動するため、launch_programが受け付けるのは.NETアプリのみです。.runtimeconfig.jsonが隣にある.dll、または同じ名前のアプリの.dllと.runtimeconfig.jsonが隣にあるアプリホストが対象です。通常のビルドと自己完結型の発行が該当します。シェル、システムツール、その他のネイティブ実行ファイルは拒否されます。また、デバッグできない単一ファイル発行も拒否されます。
プログラムに別のコードを読み込ませる環境変数も拒否されます。これらの変数が設定されると、正規のアプリを起動する操作が別のものを実行する操作に変わるためです。対象はDOTNET_STARTUP_HOOKS、DOTNET_ADDITIONAL_DEPS、DOTNET_SHARED_STORE、DOTNET_ROOT*、CoreCLRプロファイラー変数(CORECLR_ENABLE_PROFILING、CORECLR_PROFILER*、COR_ENABLE_PROFILING、COR_PROFILER*)、ASP.NET Coreのホスティングスタートアップアセンブリ(ASPNETCORE_HOSTINGSTARTUPASSEMBLIES、DOTNET_HOSTINGSTARTUPASSEMBLIES)、LD_PRELOAD、LD_LIBRARY_PATH、LD_AUDIT、およびすべてのDYLD_*です。
launchSettings.jsonのプロファイルはこのサーバーが読み取り、デバッガーには渡しません。project_path(プロジェクトファイルまたはそのディレクトリ)と、必要に応じてlaunch_profileを渡してください。名前が指定されていない場合は、httpsプロファイルが使われます。それがない場合は、commandName: "Project"を持つ最初のプロファイルが使われます。そのプロファイルのcommandLineArgs、workingDirectory、environmentVariablesは、起動時の引数、作業ディレクトリ、環境変数となり、applicationUrlはASPNETCORE_URLSになります。これらは、上記のパラメーターと同じ方法で検査されます。launch_programに渡した引数と作業ディレクトリは、プロファイルの値を置き換えます。渡した環境変数は、プロファイルの環境変数に追加されます。受け付けるのはProjectプロファイルのみです。Executableプロファイルは独自のプログラムを指定するためです。また、executablePathは無視されるため、プログラムには常に渡されたものが使われます。値はそのまま使用され、$(...)も%VAR%も展開されません。プロファイルはリポジトリに含まれるため、エージェントと同程度にしか信頼できません。プロファイルに設定されたスタートアップフックは、ほかのものと同様に拒否され、エラーにはプロファイル名が示されます。launchSettings.jsonがないプロジェクトは、プロファイルが指定されていない限り、プロファイルなしで起動されます。
これはサンドボックスではなく、launch_programがシェルとして使われるのを防ぐものでもありません。.NETアプリはユーザーができることなら何でも実行でき、コードを書いてビルドできるエージェントは自分がビルドしたものを起動できます。また、ほかのプログラムを実行する既成の.NETアプリはすでにマシン上にあります。SDK独自のsdk/<version>/dotnet.dllには.runtimeconfig.jsonが隣にあるため、これを起動するとdotnet CLI全体に到達し、含まれる任意のプロジェクトをビルドして実行できます。evaluate_expressionは、デバッグ対象のプログラム内でProcess.Startを呼び出せます。この検査が防ぐのは、エージェントがネイティブプログラムを直接指定することです。このサーバーのツールを許可するクライアントは、シェルを許可するものとして扱い、launch_program、evaluate_expression、build_mobile_app、attach_to_processの呼び出しを自動承認せず、確認してください。また、ヒットするたびに実行されるコードを条件に指定するset_breakpointとset_function_breakpointも確認してください。これらはすべて、start_programおよびlaunch_mobile_appとともに、破壊的かつオープンワールドとして注釈されています(ツールを参照)。そのため、注釈に対応したクライアントでは、これらの操作について確認を求め、読み取り操作は承認するよう設定できます。
デバッガーが表示する内容に含まれるシークレット
デバッグ対象から読み取った値はモデルに送信されるため、get_variables、expand_variable、
evaluate_expression、get_exception_info、get_program_output は、シークレットらしき内容をまず
[redacted: N chars] としてマスクします。判定には次の 2 つのルールを使います。
- 名前による判定。 変数名またはメンバー名に
password、passwd、pwd、secret、token、apiKey、connectionString、credential、privateKey、accessKey、authorizationのいずれかを含む文字列は、 全体がマスクされます。対象は文字列のみです。int tokenCountやCancellationToken tokenはそのまま表示されます。オブジェクトは マスクされず、展開時にメンバーが判定されます。evaluate_expressionでは、式が読み取る最後のメンバー名が使われるため、config.Db.Passwordも対象です。 - 形式による判定。 名前に関係なく、あらゆるテキストが対象です。
BearerとBasicの認証情報、JWT、接続文字列や URL 内のPassword=、Pwd=、ApiKey=、sig=など、よく知られたプレフィックス(sk-、ghp_、github_pat_、AKIA、xoxb-、AIza)を持つキー、 PEM 秘密鍵がマスクされます。シークレットそのものだけがマスクされるため、接続文字列の残りの部分は読み取り可能なままです。
null と空文字列は、そのまま表示されます。シークレットが設定されているかどうか自体が重要な場合が多いためです。各応答の
redacted には、マスクされた値の数が示されます。
MCP クライアントが通常ディスクに保存するサーバー独自のログには、プログラム出力の各行の長さだけが記録されます。テキスト自体は
DOTNET_DEBUGGER_MCP_LOG_LEVEL=Debug 以下の場合にログへ記録され、1 行ずつマスクされます。そのため、PEM キーのように複数行にわたって出力されたシークレットは、ログでは一部しかマスクされないことがあります。リクエストがタイムアウトした後にデバッガーが報告するエラーや、デバッガー独自の診断出力もマスクされます。
これにより露出は軽減されますが、防止されるわけではありません。s という名前の変数に含まれるシークレット、パターンが認識しない形式のシークレット、または少しずつ読み取られるシークレット(password.Length や password[0] の evaluate_expression)は、そのまま通過します。値のマスクを解除して表示するには、DOTNET_DEBUGGER_MCP_REDACT_SECRETS=false を設定してください。エージェントが自らこれを無効にする方法はありません。
プログラム内でのコード実行
evaluate_expression は、式が呼び出すもの(プロパティのゲッター、ToString、あらゆるメソッド。Process.Start も含みます)を、プログラムの権限で実行します。また、値を表示するために変数を一覧表示すると、プロパティのゲッター、ToString のオーバーライド、DebuggerTypeProxy のコンストラクターが実行されます。これがデバッガーによるプログラムの保持値の表示方法であり、デフォルトで実行に対する唯一の制限となるのは DOTNET_DEBUGGER_MCP_EVAL_TIMEOUT_MS です。
DOTNET_DEBUGGER_MCP_ALLOW_FUNC_EVAL=false を設定すると、値の表示を目的とする場合も、式から要求された場合も、デバッガーによるプログラムコードの実行を停止できます。フィールド、ローカル変数、引数は通常どおり読み取られます。プロパティは [not evaluated: function evaluation is off] として一覧表示され、オブジェクトは波括弧で囲まれた型名として表示され、ゲッター、メソッド、コンストラクターなどを呼び出す式は失敗します。エージェントが再び有効にする方法はありません。ただし、次の 2 つは引き続きコードを実行します。
get_exception_infoは、例外を報告するために例外のMessageとInnerExceptionのゲッターを呼び出します。- まだ初期化されていない型の静的フィールドを読み取ると、
evaluate_expressionでも静的メンバーの展開時でも、その型の静的コンストラクターが実行されます。
ブレークポイント条件も式なので、items.Count > 3 のようにゲッターやメソッドを呼び出す条件は、これを無効にすると評価できません。そのようなブレークポイントは、条件がない場合と同様に、ヒットするたびに停止し、停止時にその理由は示されません。代わりにフィールドを比較するか、条件を省略して停止後に確認してください。
これにより、デバッグで実行されるコードの範囲は狭まりますが、信頼できないエージェントにサーバーを渡しても安全になるわけではありません。launch_program は引き続き、任意の .NET プログラムを起動します。
他のユーザーのプロセスへのアタッチ
デバッガーは、アタッチ先のプロセス内にあるすべての情報を読み取り、変更できます。そのため、このサーバーはデフォルトで、実行ユーザーが所有するプロセスにのみアタッチします。attach_to_process は、プロセスを調べる前に、それ以外のプロセスへのアタッチを拒否します。また、list_dotnet_processes は各エントリーに、current_user、other_user、unknown のいずれかの owner を表示します。
unknown も拒否されます。Windows では、トークンを開けないシステムプロセスや昇格されたプロセスがこれに該当します。これを自分のプロセスとして扱うと、所有者を確認できない場合にチェックが形骸化してしまいます。
DOTNET_DEBUGGER_MCP_ALLOW_OTHER_USER_PROCESSES=true を設定すると、この制限が解除されます。ただし、オペレーティングシステムによる制限は残ります。Linux と macOS では、これを有効にしても通常のユーザーは他のユーザーのプロセスにアタッチできません。そのため、実際に意味があるのは、サーバーを昇格された権限または root として実行する場合です。
複数のプロセスのデバッグ
デフォルトでは、サーバーは一度に 1 つのプロセスをデバッグします。DOTNET_DEBUGGER_MCP_MAX_SESSIONS を設定すると、セッション数を増やせます。各セッションは、それぞれ独自のプロセス、ブレークポイント、停止状態を持ちます。
attach_to_process と launch_program は session_id を返します。セッションが 1 つだけ開いている間は、すべてのツールが唯一のセッションをデフォルトで使用するため、これを無視できます。2 つ目のプロセスを扱うときは、失敗するのではなく 2 つ目のセッションが開き、それ以降は session_id が必須になります。2 つのプロセスが開いている状態で、どちらに対する continue_execution なのかを推測するより、確認を求めるほうが適切だからです。
detach_from_process はセッションを開いたまま空き状態にするため、次のアタッチや起動では別の枠を使わず、そのセッションが再利用されます。
デフォルトのセッション数が 1 なのには理由があります。アタッチするたびに、デバッグ用シム内でネイティブクラッシュが発生するリスクが伴うため、セッション数が増えるほどリスクも高まります。ただし、そのようなクラッシュによる影響は限定されます。各セッションは独自のデバッグアダプターを別プロセスで動かすため、クラッシュした場合はそのセッションだけが終了し、サーバーと他のセッションは動作を続けます。
.NET MAUI アプリのデバッグ
MAUI アプリのデバッグ方法はデスクトッププログラムと同じです。準備し、ブレークポイントを設定して、開始します。ただし、デバッグするには事前に満たす必要がある条件が2つあり、通常の方法でビルドしたアプリはどちらも満たしていません。
CoreCLR 上で実行される必要があります。 このデバッガーがアタッチできるのは CoreCLR のみですが、MAUI アプリはデフォルトで Mono を対象にビルドされます。CoreCLR は net10.0-android から、また -ios と -maccatalyst では net11.0 から利用できます。それより前のバージョンでは、アプリはビルドされて起動するものの、デバッガーには接続されません。そのため、build_mobile_app はそれらのバージョンを拒否し、使用可能なバージョンを案内します。
リモートデバッグライブラリが含まれている必要があります。 スマートフォン上のランタイムをデバッグするには、デバッガーが接続するための接続を開くネイティブライブラリをアプリに含め、こちら側にも対応するライブラリを用意する必要があります。どちらもオープンソースで、clrdbg の一部です。このパッケージには、デバッガーのビルドに使用された clrdbg リリースのライブラリが同梱されているため、インストールや設定は不要です。
remote-coreclr/
remote-host/ osx-arm64/, osx-x64/, win-x64/, win-arm64/, linux-x64/, linux-arm64/
remote-target/ android/, ios/, maccatalyst/
独自にビルドしたコピーを使用する場合(たとえば、トレース機能を組み込んだものを使用する場合)は、DOTNET_DEBUGGER_MCP_REMOTE_CORECLR_HOST に remote-host ディレクトリを、DOTNET_DEBUGGER_MCP_REMOTE_CORECLR_TARGET に remote-target ディレクトリを指定するか、両方を指定します。どちらか一方を設定しなかった場合は、同梱のコピーが使用されます。
Visual Studio 独自のリモートデバッグライブラリ(vsdbg*)は、入手元にかかわらず使用されません。名前に vsdbg を含むファイルがあるディレクトリは、同梱されたものでも設定されたものでも拒否されます。
セッションは次のようになります。
list_mobile_devices() → ids to choose from
build_mobile_app(project_path, device_id) → the .apk or .app, built to be debuggable
launch_mobile_app(project_path, device_id) → prepared, not started
set_breakpoint(file, line) → in place before the app's first line
start_program() → boots, installs, launches: minutes, not seconds
wait_for_stop() → and from here it is an ordinary debug session
build_mobile_app は、ビルド時に独自の MSBuild ファイルを追加します。このファイルはサーバーと同じ場所に Resources/CopyRemoteCoreclrTargetLibrary.targets として同梱されており、内容を確認できます。このファイルによって、ターゲットライブラリがアプリバンドルまたは Android パッケージに組み込まれます。プロジェクトには何も変更を加えません。
各プラットフォームの動作について、いくつか補足します。
- Android。 エミュレーターは AVD 名で選択します。デバッグ時にデバッガーが停止状態のエミュレーターを自動的に起動するため、事前に起動しておく必要はありません。スマートフォンは adb シリアルで選択します。アプリはすべての ABI 向けに一度にビルドされるため、ランタイム識別子を選ぶ必要はありません。アプリの logcat 出力は
get_program_outputに届きます。uninstall_app: trueを指定すると、以前のインストールが先に削除されます。署名や権限が変更されたときに使用します。 - iOS。 シミュレーターは UDID で選択し、起動していない場合は起動します。実機では通常どおり、アプリ用のプロビジョニングが必要です。デバッガーはアプリをインストールして起動しますが、署名は行いません。
- Mac Catalyst。 デバイス ID は
maccatalystです。起動もインストールも不要なため、セットアップが正しく動作するかを確認するのに最も手早い方法です。
アプリはデバイス上で実行されるため、process_id は常に null のままです。アプリの PID は、このマシンから指定できるものではありません。それ以外は、ブレークポイント、ステップ実行、ローカル変数、式、例外など、すべてローカルでのデバッグとまったく同じように動作します。
エラーの報告方法
すべてのツールは、同じ方法でエラーを報告します。
{
"success": false,
"error": "Returned from a call to Continue that was not matched with a stopping event. (0x8013132F)",
"explanation": "The process was already running, so there was nothing to resume. Check get_process_status before continuing."
}
error は、デバッガーが返した内容をそのまま保持します。explanation は、デバッガーが発生させた CORDBG_E_* の結果について、エラーの意味と対処方法を説明します。対象には、すでに終了したプロセス、デバッグ対象を停止する必要がある操作、現在の命令では有効でない変数、以前の停止時のフレーム ID、すでに別のデバッガーがアタッチしている場合などがあります。無効な引数のように、デバッガーに起因しないエラーの場合は null です。
設定
クライアントの設定で、これらを環境変数として設定します。
{
"mcpServers": {
"dotnet-debugger": {
"command": "dotnet",
"args": ["tool", "exec", "DotnetDebugger.Mcp", "--version", "0.8.0", "--yes"],
"env": {
"DOTNET_DEBUGGER_MCP_LOG_LEVEL": "Debug"
}
}
}
}
| 変数 | 説明 | デフォルト |
|---|---|---|
DOTNET_DEBUGGER_MCP_LOG_LEVEL | Trace、Debug、Information、Warning、Error、Critical のいずれか | Information |
DOTNET_DEBUGGER_MCP_MAX_SESSIONS | 同時に開くセッション数。各セッションはそれぞれ独自のプロセスをデバッグします | 1 |
DOTNET_DEBUGGER_MCP_OPERATION_TIMEOUT_SECONDS | 評価を除く、デバッガーへのすべてのリクエストの制限時間です。アタッチ、起動、一時停止、ステップ実行、状態の読み取り、セッションの終了が対象です。デバッガーは一度に1つのリクエストに応答するため、処理中のリクエストがタイムアウトするまで、その後のすべての処理が滞ります | 30 |
DOTNET_DEBUGGER_MCP_EVAL_TIMEOUT_MS | evaluate_expression と get_exception_info の制限時間です。最小値は100です | 5000 |
DOTNET_DEBUGGER_MCP_BREAKPOINT_BIND_TIMEOUT_MS | ブレークポイントが未検証として報告されるまで、バインドを待つ時間です。最小値は100です | 2000 |
DOTNET_DEBUGGER_MCP_JUST_MY_CODE | デバッグ対象を独自のコードに限定します。これを無効にする前に、上記を参照してください | true |
DOTNET_DEBUGGER_MCP_ALLOW_FUNC_EVAL | デバッガーがプログラム内のコードを実行できるようにします。プロパティゲッター、ToString、evaluate_expression 内のメソッド呼び出しが対象です。上記を参照してください | true |
DOTNET_DEBUGGER_MCP_REDACT_SECRETS | モデルに表示される前に、値の中からシークレットと思われるものをマスクします。上記を参照してください | true |
DOTNET_DEBUGGER_MCP_ALLOW_OTHER_USER_PROCESSES | 現在のユーザーが所有していないプロセスへのアタッチを許可します | false |
DOTNET_DEBUGGER_MCP_ENABLE_DIAGNOSTICS | 詳細な診断ログを有効にします | false |
DOTNET_DEBUGGER_MCP_REMOTE_CORECLR_HOST | MAUI アプリをデバッグする際に、バンドル版の代わりに使用する remote-host ディレクトリです。上記を参照してください | バンドル版 |
DOTNET_DEBUGGER_MCP_REMOTE_CORECLR_TARGET | バンドル版の代わりに使用する remote-target ディレクトリです | バンドル版 |
DOTNET_DEBUGGER_MCP_MOBILE_START_TIMEOUT_SECONDS | モバイルアプリ用の start_program の制限時間です。エミュレーターの起動、インストール、起動が対象です | 600 |
DOTNET_DEBUGGER_MCP_BUILD_TIMEOUT_SECONDS | build_mobile_app の制限時間です | 900 |
トラブルシューティング
サーバーがクライアントに表示されません。 設定内のパスが絶対パスであり、正しいことを確認してから、クライアントを完全に終了して再起動してください。Claude Desktop のログは、macOS では ~/Library/Logs/Claude/ に、Windows では %APPDATA%\Claude\logs\ に保存されます。
アタッチに失敗します。 よくある原因は、プロセスが別のユーザーによって所有されている(上記を参照)、すでに終了している、.NET プロセスではない、またはすでにデバッガーがアタッチされていることです。list_dotnet_processes を使うと、サーバーから見えるプロセスとその所有者を確認できます。
ブレークポイントで停止しません。 verified: true が返ってきていることを確認してください。そうでない場合は、まず PDB を確認します。検証済みなのに停止しない場合は、その行に実際に到達していることと、ファイルパスが PDB に記録されたものと一致していることを確認してください。
macOS または Linux で list_dotnet_processes の結果が空になります。 これらの環境ではモジュールの列挙に権限が必要です。そのため、サーバーはプロセス名による検出に切り替えますが、この方法では apphost の名前が変更されたプログラムを検出できません。
macOS で起動またはアタッチがエラーなしでハングします。 上記のエンタイトルメントのセクションを参照してください。
MAUI アプリが起動しても、ブレークポイントで停止しません。 ほぼ間違いなく CoreCLR ではなく Mono で実行されています。これは、build_mobile_app 以外の方法でビルドした場合に発生します。通常の dotnet build では、デバッガーに必要なランタイムもライブラリも生成されないためです。build_mobile_app で再ビルドし、そこで報告されたアプリを起動してください。
list_mobile_devices に Android デバイスが表示されません。 AVD ディレクトリからエミュレーターを、adb から接続中のスマートフォンを読み取るため、Android SDK が必要です。通常とは異なる場所にある場合は、ANDROID_HOME または ANDROID_SDK_ROOT を設定してください。応答内の warnings には、読み取りできなかったソースとその理由が示されます。
サーバーの動作を確認するには、DOTNET_DEBUGGER_MCP_LOG_LEVEL=Trace と
DOTNET_DEBUGGER_MCP_ENABLE_DIAGNOSTICS=true を設定するか、サーバーを直接実行して stderr を確認してください。
dotnet tool exec DotnetDebugger.Mcp --version 0.8.0 --yes
開発
git clone https://github.com/nevse/dotnet-debugger-mcp.git
cd dotnet-debugger-mcp
dotnet build
dotnet test
ビルドでは clrdbg リリースのデバッグアダプターを取得し、ClrdbgVersion の名前を確認してハッシュを検証したうえで、サーバーの隣に配置するため、個別の手順は不要です。代わりに clrdbg のチェックアウトからアダプターをビルドする場合(修正を含むフォークなど)は、ClrdbgSourcePath にそのパスを指定してください。
ClrdbgSourcePath=~/work/clrdbg dotnet build
統合テストでは、実際のデバッグ対象プログラムを実際のブレークポイントで操作するため、ほかのテストより時間がかかります。そのため、TestCategory=Integration で分けられています。
公開済みパッケージではなく、作業コピーをクライアントに指定するには、次のようにします。
claude mcp add dotnet-debugger -- dotnet run --project "$(pwd)/src/DotnetDebugger.Mcp/DotnetDebugger.Mcp.csproj"
CONTRIBUTING.md では、構成と規約について説明しています。 docs/RELEASING.md では、リリースの作成手順を説明しています。これには、ワークフローが依存しているものの表示されない nuget.org の信頼された公開ポリシーも含まれます。
関連プロジェクト
- clrdbg — このサーバーが操作する .NET デバッガー
- SharpDbg — 移行前に使用していたデバッガー
- ClrDebug — ICorDebug API ラッパー
- Model Context Protocol — 仕様と SDK
ライセンス
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。