Claude Code has this nice thing where it plugs into your editor. On VS Code or a JetBrains IDE, its edits open as a diff you accept or reject, it reads your compiler errors, and it knows what you have selected. On Visual Studio you get none of that. You copy and paste into a terminal like it is 2022. There is an open issue asking for Visual Studio support with a few hundred upvotes and no ETA, so I built the Visual Studio side myself.
It is a native VS 2026 extension. The important thing to say up front is what it is not: it is not another agent. The claude CLI does all the thinking. My extension is the IDE half of Claude Code's integration protocol, which I had to reverse-engineer off the wire since it is undocumented. It makes zero model calls of its own. It is a bridge.
The first thing you get is the diff gate. When Claude wants to change a file, the change opens in Visual Studio's own diff viewer, and approving there is the only step. No second yes/no prompt in the terminal. You can reject with a note, and it reconsiders.
That part was the plan. The part I did not expect is what became possible once the IDE half existed. Because I control the Visual Studio side, I could hand the agent things the CLI can never reach on its own. The one I keep coming back to is the debugger.
Watching the code run, instead of reading it
Here is a scoring function that returns the wrong total. Nothing in the source reads as wrong. The final number is just off. A fresh Claude session, with no idea what the bug was, was asked to check it, and it drove the Visual Studio debugger to find it in about ninety seconds.
It set a breakpoint at the top of the loop, started the session, and stepped through round by round, watching a combo counter. Here is the trace it kept, for the input {5, 3, 0, 4, 2, 0, 6}:
The bug is that the combo multiplier never resets on a zero round. if (points > 0) and else if (points < 0) both miss points == 0, so the streak carries across a zero and inflates the score. You cannot see that by reading the code. It only shows up when you watch the counter stay put across the round it should have cleared. That is the whole difference between reading code and debugging it, and the agent got to do the second one.
Reading runtime state is always on. Driving the debugger, actually stepping and setting breakpoints and running your code, is opt-in behind a toggle in the panel that resets every session, because it runs your program under the model's control.
It grew past stepping
Once the drive loop worked, the rest followed. Claude can now:
-
Attach to a process that is already running. A hosted web service or a desktop app, not just an F5 launch. It lists the processes, attaches, arms a first-chance exception, triggers the request itself with
curl, and stops at the throw site inside the handler instead of the genericcatchthat swallowed it into a bland 500. -
Untangle a deadlock. A deadlocked program never hits a breakpoint, so a breakpoint is useless. Claude pauses the hung process, reads each stuck thread's wait chain, and reconstructs the exact
A -> B -> C -> Alock cycle, correctly ignoring the threads that are merely idle or busy.
And the one I am most proud of, because Visual Studio's own UI cannot even set it through an automation API: data breakpoints. Point Claude at a field and it stops the instant that field is written, or records every value the field takes, in order. It can watch conditionally (break only when the value goes below zero) and watch several fields at once.
I built a little fixture where an order's total is mutated through four different pricing methods and comes out negative, with no single line to breakpoint. Claude armed a plain watch, a conditional watch, and a second watch at once:
The conditional watch broke exactly on the write that corrupted the total, and the plain watch recorded the full timeline:
| # | value (cents) | written by | |
|---|---|---|---|
| 1-3 | 0 → 30000 → 42000 → 54000 | AddItems |
three line items |
| 4 | 54000 → 48600 | ApplyBulkDiscount |
10% off |
| 5 | 48600 → 52488 | ApplyTax |
+8% tax |
| 6 | 52488 → -47512 | ApplyLoyaltyCredit |
the corruption |
Getting that to work was the hardest part of the whole project. There is no EnvDTE surface for it, so it rides a small Concord debug-engine component that ships with the extension and arms the breakpoint from inside the engine, then streams the changes back out over file IPC.
Catching a flaky test red-handed
dotnet test runs your tests. What it cannot do is stop inside a failing one in the debugger, or reproduce an intermittent failure on purpose and pause on it. So I wired Visual Studio's Test Explorer engine to the same debug session.
The signature move is catching a heisenbug. Point it at a flaky test and it loops the test under the debugger with break-on-thrown armed, lets the passing runs finish, and stops on the first failing run, paused at the throw with the exception live in the frame:
The honest twist in that run: when it read the frame, it found there were no captured locals at all, and the throw came straight out of an if (Random.Shared.Next(3) == 0) branch. The test was not exposing a product bug. The test itself was nondeterministic because it branched on an unseeded RNG. A re-run loop would have given you a different red line every time and never told you that. Being paused inside the exact failing run did.
Getting real per-test results out of the engine took a trick too. Its result callback is an internal interface you cannot implement in normal C#, so the extension emits a type that implements it at runtime with Reflection.Emit.
Reading code the way the compiler does
The last piece is less flashy but I use it constantly. Instead of grepping text for "where is this used," Claude asks Visual Studio's compiler (Roslyn) for the resolved answer: find-all-references, go-to-definition, find-implementations, and call and type hierarchies, resolved through interfaces, overrides, and overloads. It catches the cases text search gets wrong, like an explicit interface implementation or a virtual dispatch a grep can never tie back to the interface.
| Tool | What it caught that a text search misses |
|---|---|
| find-implementations |
Triangle's explicit IShape.Area, which .Area() or : IShape never ties back to the interface |
| type-hierarchy |
Circle and Square reach IShape transitively through ShapeBase; grep on : IShape finds only the two direct implementers |
| call-hierarchy | the interface-dispatched callers of IShape.Area, back to Main, that a search on the concrete Circle.Area would miss |
| go-to-definition | the right Describe overload at a call site, not all three declarations |
| decompile | the real body of Enumerable.Sum from the library, with Math.Sqrt falling back to SourceLink |
It also does the one thing reading your repo fundamentally cannot: it decompiles the body of a method that lives in a referenced DLL. Point it at a framework or NuGet call and it hands back the real C#, the way Go-To-Definition does, and for core .NET types it fetches the actual runtime source over SourceLink.
How it hangs together
If you like the plumbing: the extension starts a localhost WebSocket server, writes a lockfile, and launches claude already connected. The diff, diagnostics, and selection tools ride Claude Code's IDE protocol directly. The debugger, the test runner, and the semantic tools ride two extra MCP servers the CLI reaches through a tiny stdio shim, because that channel is the open plugin door where the model actually sees the tools. Everything runs in-process against the live Visual Studio services, on the UI thread where it has to be. The CLI does all the agent work; the extension never calls a model.
The honest part
- Visual Studio 2026 only for now. A 2022 backfill is possible if people want it.
- The integration protocol is undocumented and could change on any CLI update, so I pin and smoke-test against a known-good version.
- The debugger, test, and data-breakpoint features are .NET-focused.
- It is free, MIT, and not affiliated with Anthropic. "Claude" and "Claude Code" are Anthropic's.
If you write C# and use Claude Code, I would genuinely like to know how it holds up on a real codebase.
- Marketplace: Claude Code for Visual Studio
- Source and docs: github.com/firish/claude_code_vs
The deep-dives (the full debugger tool list, the test loop, the semantic surface) are in the repo's docs/ folder if you want to see how far each piece goes.






Top comments (0)