Reading and analysing code
The first rule of working in a .NET solution with VisualMCP: ask the compiler, not the text. This guide covers how to find, read and understand code — and how to pick the right analysis tool for a question.
The lookup table
| You want | Call |
|---|---|
| A type or member by name | find_symbol |
| The source of that declaration | read_symbol |
| Every place a symbol is used | find_references |
| Every method that calls this one | find_callers |
| Every implementation of an interface | find_implementations |
| The subclasses of a class | find_derived_types |
| A type's full member list, with signatures and docs | get_type_members |
| The project's files, references and packages | get_project_info |
| The solution's README and Markdown | read_project_docs |
Why not grep
Text search answers a different question: where does this string
appear? On real C# that is not the same as where is this
symbol used? A method name can be an overload; a call through an
interface has no textual trace of the concrete method; comments,
strings and same-named members of unrelated types all match.
find_references resolves the symbol, so
it returns the real uses and skips the noise. Text search still exists
— search_text for arbitrary files, text_index
for ranked, word-based search across the whole solution.
A typical walk
find_symbol → OrderService.ChargeAsync (3 matches, in two projects)
read_symbol → the method body, with line numbers
find_references → 14 real uses: 9 calls, 3 interface implementations, 2 registrations
find_callers → the 4 methods that call it
get_type_members → the rest of OrderService, with signatures and docs
Each of those is one call. A text-based agent spends several round trips per step and still misses the interface dispatch.
Build and diagnostics
| Question | Tool |
|---|---|
| Does it compile, and what are the errors? | build_project |
| What does the compiler think right now? | get_diagnostics |
| Fix the mechanical compiler diagnostics | fix_diagnostics |
| What do the configured analyzers say? | run_code_analysis |
| What do the built-in inspection packs say? | inspect_code |
build_project returns structured diagnostics — id,
severity, file, line — instead of a log tail. fix_diagnostics
is the write side: it rewrites eight mechanical CS####
shapes (unused locals and fields, duplicated usings, hidden members…),
each accepted only when the project compiles no worse.
Choose the right analysis
solution_audit is the front door: one call returns
inspections grouped by id, compiler diagnostics, the most complex
methods, the largest files, the project graph with cycles and
suggested first moves. When you need to go deeper:
| Question | Tool |
|---|---|
| How many paths must a reader hold? | complexity_report, get_complexity_hotspots |
| How does this scale with n? | analyze_complexity — a static Big-O estimate with its evidence |
| This method is too complex — lower it | reduce_complexity, or fix_complexity for the whole solution |
| Where does a refactoring pay first? | git_churn_hotspots — git history joined with the complexity ranking |
| Did this branch make the code better or worse? | get_quality_trend |
| Is this code reachable? | find_dead_code — control-flow graph, not heuristics over text |
| Is this code duplicated? | find_duplicate_code — structural clones, normalised over names and literals |
| Is there a security problem? | find_security_issues — CWE-tagged: injection, crypto, secrets, SSRF, XXE, TLS… |
| Is the async code wrong? | find_async_anti_patterns — .Result/.Wait(), async void, await in loops |
| Are the namespaces tangled? | analyze_namespace_coupling, real cycles included |
| What will this change break? | plan_feature_change — ordered impact plan with files and tests |
The two meanings of “complexity”
Cyclomatic and cognitive complexity measure how many paths a
reader has to hold in their head. Big-O measures how cost
grows with input size. They are easy to confuse, and using the wrong
one gives a misleading answer: complexity_report for the
first, analyze_complexity for the second, and
profile_* to measure what actually happens at runtime.
Confidence
These tools are static. They are precise about what the compiler knows
— symbols, overloads, types, control flow — and honest about what they
infer. analyze_complexity reports its confidence and its
evidence; a clean find_security_issues is not a security
audit; code invoked by reflection may look dead. A refusal or a
confidence note is information, not an error.
Worked examples
“Where is this used and what breaks if I change it?”
find_symbol OrderService.ChargeAsync
find_references → every call, implementation and registration
plan_feature_change → impact graph with files and tests, in order
“Make this solution understandable before I touch it.”
read_project_docs → what the project says about itself
solution_audit → health report and first moves
find_unused_symbols → dead weight
analyze_dependencies → project graph, cycles
“Is this safe to ship?”
find_security_issues
find_async_anti_patterns
inspect_code with packs=all, minSeverity=warning
run_tests