Roslyn CodeLens — Semantic .NET Intelligence
Response shape
All list-returning tools wrap their results in an envelope:
{
"items": [...],
"totalCount": 142,
"truncated": false,
"limit": 500,
"summary": { ... }
}
When truncated is true, items are the top N by the tool's natural sort order (severity-first, worst-first, by-project, etc.) — usually that's what you want. Raise limit only if the truncated tail matters for the task.
Tools that include a summary aggregate:
get_diagnostics — { error, warning, info, hidden } counts
find_references — { byProject: { name: count }, byKind: { kind: count } }
find_callers, find_attribute_usages — { byProject: { name: count } }
search_symbols, find_reflection_usage — { byKind: {...} }
find_unused_symbols — { byKind: {...}, filteredOut: { testMethod, testContainer, mcpTool, generated, composition, interop } }
find_naming_violations — { byRule: {...} }
get_complexity_metrics — { max, avg, overThreshold, maxCognitive } — max/avg/overThreshold describe whichever metric metric selected; maxCognitive is always the cognitive one
resolve_stack_trace — { byOrigin: { source, metadata, unresolved }, exceptions, skippedFrameLike } — skippedFrameLike counts frame-like-but-unparseable lines (also present in items as Kind="unknown")
Single-object tools (get_type_overview, get_symbol_context, apply_code_action, etc.) are unchanged — they return their bespoke shape directly.
Error responses
When a tool can't proceed, the response is isError: true with content carrying a JSON body of { code, message, details? }. Switch on code:
| Code |
Meaning |
Common source |
SymbolNotFound |
type/method/property not resolved |
analyze_method, get_symbol_context, get_type_overview, get_type_hierarchy, generate_test_skeleton |
SolutionNotTrusted |
analyzers blocked until trust_solution is called |
get_diagnostics (includeAnalyzers: true), get_code_fixes |
AmbiguousMatch |
name matched multiple solutions; details.matches lists candidates |
set_active_solution, unload_solution |
FileNotFound |
file path / baseline doesn't exist or isn't in solution |
get_file_overview, find_breaking_changes |
ProjectNotFound |
solution name didn't match |
set_active_solution, unload_solution |
InvalidArgument |
malformed / unsupported caller input |
various |
Internal |
unexpected; message carries exception text |
fallback |
If code: SolutionNotTrusted, the right next step is calling trust_solution after asking the user. Don't catch and retry blindly — the user has to authorize analyzer execution.
Detection
If find_implementations is not available as an MCP tool, this skill is inert — do nothing, no errors.
If it IS available, every rule below applies. No exceptions.
The Iron Law
On a .NET codebase where roslyn-codelens MCP tools are available:
- Never use
Grep, Glob, or Bash grep/rg to locate C# symbols, types, methods, interfaces, references, callers, implementations, or usages.
- Never run
dotnet build, dotnet msbuild, msbuild, or any build command to surface compiler errors, warnings, or analyzer diagnostics.
- Never manually read a
.cs file to "grep in my head" for who uses a symbol, or to check if code compiles.
The semantic tools (find_callers, find_references, find_implementations, search_symbols, get_diagnostics, go_to_definition, etc.) are always more accurate than text search and always faster than a build. There is no tradeoff to weigh.
Violating the letter of these rules is violating the spirit. If you're about to run a command or tool that substitutes for one of these semantic tools, stop.
Red Flags — STOP and Use the MCP Tool
If any of these thoughts cross your mind, stop and switch to the MCP tool:
| Thought / Action |
STOP — Use instead |
"Let me Grep for class Foo" |
search_symbols or go_to_definition |
"Let me Grep for Foo\\.Bar\\(" (finding callers) |
find_callers |
| "Who subscribes to this event?" / "Find += sites" / "Are we leaking event subscriptions?" |
find_event_subscribers |
| "What does this method end up calling?" / "Show me the transitive callees" / "What's the blast radius for changing X?" |
get_call_graph |
"Let me Grep for : IFoo" (finding implementations) |
find_implementations |
"Let me Grep for new Foo(" |
find_references |
"Let me Grep for [Authorize]" |
find_attribute_usages |
| "What deprecations do we still use?" / "Plan an Obsolete cleanup" / "Find every [Obsolete] call site" |
find_obsolete_usage |
"I'll run dotnet build to see errors" |
get_diagnostics |
"I'll run dotnet build -warnaserror to find warnings" |
get_diagnostics |
"Let me Read the .cs file to see what's defined" |
get_file_overview or get_type_overview |
"Let me Glob for **/*Service.cs" |
search_symbols with a partial name |
"Let me Grep for Activator.CreateInstance" |
find_reflection_usage |
| "I'll check if this method is used by reading files" |
find_callers / find_unused_symbols |
| "What overloads does this method have?" / "Show me all signatures of Foo" / "Compare overloads side-by-side" |
get_overloads |
| "What can I call on this type?" / "Is there an extension method for X?" / "Does LINQ have something for this?" |
get_extension_methods |
"How do I construct this?" / "What constructors does it have?" / "Why can't I new this up?" / "Can my test project reach this internal constructor?" |
get_instantiation_options |
"What operators does this type define?" / "Does it have a custom == or implicit conversion?" / "List +, -, conversions on this type" |
get_operators |
| "I'll eyeball complexity by reading the method" / "This one looks like the hairiest, let's start there" |
get_complexity_metrics (use metric: "cognitive" to rank by how hard code is to follow) |
| "How is this project doing?" / "Where should I focus?" / "Show me the technical debt picture" |
get_project_health |
| "Which classes are doing too much?" / "Where are my god classes?" / "Worst design smells in this codebase?" |
find_god_objects |
"Let me Grep for tests that call this method" |
find_tests_for_symbol |
| "Which tests will break if I change this?" |
find_tests_for_symbol |
| "What does this test suite cover?" / "List all tests in MyProj.Tests" / "How many xUnit Theory tests do we have?" |
get_test_summary |
| "What should I write tests for?" / "Where's our testing debt?" / "Show me untested public methods" |
find_uncovered_symbols |
| "Generate a test stub for this method" / "Bootstrap tests for this class" / "Give me a skeleton I can fill in" |
generate_test_skeleton |
| "What's the public API of this library?" / "Show me the API surface" / "I need a PublicAPI.txt-style snapshot" |
get_public_api_surface |
| "Will this break consumers?" / "Show me breaking changes vs the previous release" / "Diff this build's API against baseline" |
find_breaking_changes |
"Are there async bugs?" / "Find sync-over-async" / "Are we using .Result anywhere?" |
find_async_violations |
"Are there resource leaks?" / "Find missing using" / "Is this disposable handled?" |
find_disposable_misuse |
| "What exceptions can escape this method?" / "Is this call safe to make?" / "Do I need a try here?" |
get_exception_flow |
"Where is this exception thrown?" / "Who raises TimeoutException?" |
find_throw_sites |
| "Who catches this?" / "Is anything swallowing exceptions?" / "Find empty catch blocks" |
find_catch_blocks |
| "Rename this class/method everywhere" / "Let me edit N files to change this name" |
rename_symbol |
| "Add/remove/reorder a parameter" / "Let me update all the call sites by hand" |
change_signature |
"Where did this exception come from?" / user pastes a stack trace / "What method is <M>d__12.MoveNext?" |
resolve_stack_trace |
"Let me Read the file to see this method's body" / "Show me the source of these 5 methods" |
get_method_source |
| "Where is this field written / who mutates it?" / "Is this ever assigned outside the ctor?" |
find_references with kinds: ["write","readwrite"] |
"Where is this type cast / pattern-matched?" / "Find is/as sites" |
find_references with kinds: ["type_check","cast"] |
All of these mean: the MCP tool is the correct tool. Use it.
Rationalizations — and why they're wrong
| Excuse |
Reality |
| "Just a quick Grep — it's faster." |
It isn't. One find_references beats iterating Grep + reading matches + deduping false positives. |
| "Grep as a sanity check on top of the MCP tool." |
Redundant and misleading. Grep finds comments, strings, partial matches. If Roslyn says there are N references, there are N references. |
| "This is just a string/literal search, so Grep is fine." |
If the target is a C# symbol, it's not "just a string" — it has a definition, scope, and binding. Use find_references. String literal searches in comments/docstrings are the only legitimate Grep case. |
"dotnet build is how everyone checks errors." |
Not here. get_diagnostics returns compiler errors + analyzer results, structured, without rebuilding. A build is minutes; the tool is milliseconds. |
| "The MCP server might be slow / might fail." |
If it fails, report that and ask. Do not silently fall back to Grep — that produces wrong answers that look right. |
| "I need to see the file contents anyway." |
get_file_overview, get_type_overview, and analyze_method give you structure without a Read. Use Read only after you know which lines matter. |
| "The user asked me to Grep." |
If the user asked for a grep specifically, ask if they actually want semantic results. If they asked for information about the code and suggested grep, use the semantic tool and tell them why. |
| "I'm just looking for TODO comments / string literals." |
Fine — Grep is legitimate for comments, string literals, and non-C# files. That's the only free pass. |
Pre-Action Checklist
Before calling Grep or Glob on .cs / .csproj / .sln / .slnx / .cshtml files:
- Is the target a C# symbol (type/member/namespace)? → Use
search_symbols or find_references.
- Is the target an attribute? → Use
find_attribute_usages.
- Is the target a reflection pattern? → Use
find_reflection_usage.
- Is it genuinely a string literal, comment, or non-semantic text? → Grep is OK. State why.
Before running dotnet build / msbuild via Bash:
- Am I looking for errors, warnings, or analyzer diagnostics? → Use
get_diagnostics. Stop.
- Am I actually trying to produce a binary / run tests / package? → Build is OK. State why.
Before Reading a .cs file:
- Do I just need structure (what's in it, what's defined)? →
get_file_overview / get_type_overview.
- Do I need a specific method's shape? →
analyze_method.
- Do I need one or more members' actual source bodies? →
get_method_source (batch-friendly — pass all the names at once).
- Do I need the actual source lines to edit? →
Read is OK.
When to Use Each Tool
Decision Tree for External Assemblies
I want to... Tool / Approach
─────────────────────────────────────────────────────────────────────
Work with types in my source code → existing tools (unchanged)
Look up an external type by name → go_to_definition / get_symbol_context /
get_type_overview / get_type_hierarchy
(pass fully-qualified name; returns origin="metadata")
Browse what a package exposes → inspect_external_assembly
Who in my code uses an ext. type? → find_references / find_callers / find_implementations
See a method's IL bytecode → peek_il (pass fully-qualified method name with param types)
Inspect an arbitrary DLL → add a <ProjectReference> to a throwaway
project, rebuild_solution, then use normally
Understanding a Codebase
get_project_dependencies — solution architecture, how projects relate.
get_symbol_context — full context for a type (namespace, base, interfaces, DI deps, public members).
get_public_api_surface — Enumerate every public/protected type and member declared in production projects; deterministically sorted; suitable for API review or breaking-change baselines. Static analysis; only declared (not inherited) members appear.
find_breaking_changes — Diff the current public API surface against a baseline (JSON snapshot from get_public_api_surface, or a .dll file). Reports Removed/Added/KindChanged/AccessibilityNarrowed/Widened with Breaking/NonBreaking severity. Static analysis; doesn't currently detect return-type, sealed-ness, or nullable changes.
get_type_hierarchy — inheritance chains and extension points.
get_type_overview — one-shot: context + hierarchy + diagnostics (replaces 3 calls).
get_file_overview — types defined in a file + diagnostics, without reading it.
analyze_method — signature + callers + outgoing calls, all in one.
get_method_source — full declaration source (XML docs, attributes, body, original formatting) for one or MANY members by name in a single call: methods (all overloads), constructors (request as Type.TypeName — simple or fully qualified; nested types need full qualification), properties, indexers (Type.this or Type.this[]), fields, events, explicit interface implementations. Per-item statuses (ok/notFound/ambiguous/metadata/unsupportedKind) — a batch never fails wholesale; metadata items carry Origin (assembly name/version for peek_il) and a Note explains non-ok outcomes. StartLine always matches the first line of Source. Whole types are out of scope (get_type_overview / Read).
get_overloads — Every overload of a method or constructor (source + metadata) with full parameter detail, modifiers, generic type params, XML doc summary, and location. One call instead of N analyze_method calls.
get_operators — every +, -, ==, <, conversion, etc. on a type, with kind, signature, source location. Includes synthesized record equality. Covers what get_overloads excludes.
get_extension_methods — every extension member applicable to a type, from the solution AND referenced assemblies, so LINQ shows up for an IEnumerable. Applicability is Roslyn's own (ReduceExtensionMethod), so generic inference is exact: this IEnumerable<T> applies to string, this IEnumerable<string> does not. signature is the reduced, call-site form — receiver dropped, inference applied, return type first for both kinds (IEnumerable<int> Where<int>(Func<int, bool>), int Tripled). It is a signature, not paste-ready source: a partially inferred generic keeps the type parameters still inferred from the arguments (Select<int, TResult> — int came from the receiver, TResult from your lambda). C# 14 extension blocks are covered including properties and static methods, neither of which an IsExtensionMethod scan can see. Check isStatic before writing the call: false (the normal case, including every classic this extension, which is declared static but called on an instance) means value.Doubled(); true means a C# 14 static extension member, called on the type — int.Zero. Receivers may be arrays, nullables or tuples (string[], int?, (int, string)) as well as plain and constructed generic types. Results are not filtered by using scope — the tool has no call site — so namespace is always reported and you may need to add the import. Source extensions sort before metadata ones; nameFilter narrows by substring.
get_instantiation_options — how to construct a type, in one call: constructors (every parameter's type and name, declared accessibility, isImplicit, isObsolete), factories, diRegistrations, and requiredMembers you must set in an object initializer. Read isImplicit before concluding a type has no constructor — a struct, and a class with none declared, both have a usable parameterless one that no source file mentions. factories are static members returning the type from ANYWHERE in the solution, which is the case that matters: a type with a private constructor and a separate WidgetFactory.Create() looks uninstantiable until you see them. Task<T>/ValueTask<T> factories are unwrapped and flagged isAsync; instance builder methods are excluded because the builder itself needs constructing. Pass fromProject to get accessible computed from that project's viewpoint, honouring InternalsVisibleTo — accessible: null means not computed (no fromProject given), never "inaccessible". For interfaces, abstract and static classes, instantiable is false and note says why; follow up with find_implementations.
get_call_graph — Transitive caller/callee graph for a method, depth-bounded with cycle detection. Adjacency-list output. Use when you need depth > 1 (analyze_method is depth=1).
Navigating Code (instead of Grep/Glob)
go_to_definition — jump to the definition.
search_symbols — fuzzy symbol lookup.
find_references — every reference across the solution, each tagged with a referenceKind and reported per occurrence (multiple references on one line each get their own item, distinguished by column). Pass kinds to filter server-side.
Reference kinds — read, write (assignment target, out argument), readwrite (compound assignment, ++/--, ref argument), invocation, method_group (method used as a delegate, not called), object_creation, cast ((T)x, x as T), type_check (x is T, is T v, case T v:), typeof, base_type, type_constraint, type_argument, declaration (variable/parameter/return/field type positions), attribute, nameof, xml_doc (<see cref=...>); usage is a rare fallback. Note a receiver reads: in _map[k] = v the field _map is read (its contents change, the field isn't reassigned) — same as _map.Add(k, v).
Finding Dependencies and Usage
find_callers — every call site for a method.
find_event_subscribers — every += / -= site for an event symbol, with resolved handler name and subscribe/unsubscribe tag. Use for UI-event audits or memory-leak hunts (compare subscribe/unsubscribe pairs).
find_implementations — all implementors of an interface / extenders of a class.
find_tests_for_symbol — xUnit/NUnit/MSTest methods that exercise a production symbol; opt-in transitive walk through helpers.
get_test_summary — Per-project inventory of test methods with framework, attribute kind, [InlineData]/[TestCase]/[DataRow] row count, location, and production symbols referenced. Project → tests direction; complements find_tests_for_symbol (test → production).
find_uncovered_symbols — Public methods and properties no test transitively reaches (≤ 3 helper hops); sorted by cyclomatic complexity for prioritization. Strict reference-based: an override is not marked covered just because its base or interface declaration is reached — a test calling IFoo.Bar does not cover Foo.Bar.
generate_test_skeleton — Emit a test-class skeleton (parseable C#) for a method or type. Returns framework, suggested file path, class name, full file contents, and TodoNotes (e.g. constructor dependencies). Stubs cover happy-path Fact, Theory + InlineData for primitive-param methods, Assert.Throws<T> per direct-throw exception type, async detection. Tool returns text; agent decides whether to Write it. Pairs with find_uncovered_symbols (find gap → generate stub).
get_di_registrations — DI wiring and lifetimes.
find_reflection_usage — hidden/dynamic coupling (Activator.CreateInstance, MethodInfo.Invoke, assembly scanning).
get_nuget_dependencies — NuGet packages and versions.
find_attribute_usages — members decorated with a given attribute.
find_obsolete_usage — Every [Obsolete] call site grouped by deprecation message and severity. Sharper than find_attribute_usages for migration planning. Source AND metadata obsoletes (NuGet) included.
Diagnostics (instead of dotnet build)
get_diagnostics — compiler errors, warnings, analyzer diagnostics. Replaces dotnet build output.
get_code_fixes — structured edits for a diagnostic.
Trust model for analyzer diagnostics
get_diagnostics defaults to includeAnalyzers=false (compiler diagnostics only). If the user asks for analyzer warnings (StyleCop, Microsoft.CodeAnalysis.Analyzers, CA-prefixed rules, etc.) — OR if you need to call get_code_fixes for an analyzer-rule diagnostic:
- Call
get_diagnostics(includeAnalyzers=true).
- If the server returns an "untrusted solution" error: ask the user before calling
trust_solution. Phrase the question to make it clear that analyzer DLLs run as in-process code with the user's privileges. They should only trust solutions they wrote or fully vetted.
- Prefer
scope="session" for one-off reviews. Use scope="persistent" only when the user confirms they regularly use this solution. Use scope="addRoot" to trust an entire directory tree (e.g., c:\projects\).
- Use
list_trusted_paths to inspect current state when the user asks "is X trusted?". Use revoke_trust to drop entries.
Solutions passed on the CLI at server startup are auto-trusted in session scope — get_diagnostics(includeAnalyzers=true) against them works without an extra prompt.
get_code_actions — all refactorings/fixes at a position (with optional range).
apply_code_action — execute a refactoring by title. Preview mode by default. Applying refuses to overwrite files that changed on disk since the snapshot (run rebuild_solution and retry), and on success the in-memory snapshot updates immediately, so follow-up queries see the new text without waiting for the file watcher. Actions that create a new file (extract interface, move type to file) write it, but the new file only enters the snapshot once the watcher picks it up — the result's warning says so when it applies.
rename_symbol — solution-wide safe rename of a type or member (Roslyn Renamer; NOT available via apply_code_action). Preview by default; new-compiler-error conflicts reported; apply refuses unless force=true on: conflicts, a degraded solution load, or files changed on disk since the snapshot (freshness refusal — run rebuild_solution and retry; force does not bypass freshness). After apply the in-memory snapshot updates immediately — follow-up queries see the new name without waiting for a rebuild. Generic types accept the arity-free qualified form (Data.Repository finds Repository<T>).
change_signature — add, remove, and reorder a method's parameters, rewriting every call site (Roslyn's change-signature engine; also NOT reachable via apply_code_action). operations apply in order: remove by parameter name, reorder with a full permutation of the surviving names, and add with a required callSiteValue naming exactly what each existing call site passes — the tool never guesses that. Give an added parameter a defaultValue instead and existing call sites are left untouched. Named arguments, optional parameters, params arrays and the extension-method this are all handled. cascadedTo lists the overrides and interface implementations rewritten alongside it, so check it in preview before applying. Same gates as rename_symbol: preview by default, conflict / degraded-load / freshness refusals, force to override, immediate snapshot update on apply.
resolve_stack_trace — map a pasted .NET stack trace to file/line/symbol; demangles async/iterator state machines, lambdas, and local functions; handles log-prefixed lines, inner-exception chains, and Demystifier-style traces. Source frames get the declaration site (exact location when the trace carries in file:line); external frames resolve with origin="metadata" when the assembly is referenced by the solution — frames outside that closure come back origin="unresolved". Frame-like lines that fail all grammars aren't dropped: they surface as Kind="unknown" items at their original position, and summary.skippedFrameLike counts them. Items stay in trace order.
analyze_data_flow — variable lifecycle over a statement range (declared/read/written/captured/flows-in/out).
analyze_control_flow — reachability, returns, unreachable paths.
Code generation is in apply_code_action — do NOT look for dedicated generation tools. Use get_code_actions to find the title, then apply_code_action:
- Implement missing interface/abstract members → "Implement abstract members" / "Implement interface"
- Generate constructor from fields → "Generate constructor"
- Add null checks → "Add null checks for all parameters"
- Generate
Equals/GetHashCode → "Generate Equals and GetHashCode"
- Encapsulate field → "Encapsulate field"
- Extract method → "Extract method"
- Inline variable → "Inline variable"
Code Quality Analysis
find_unused_symbols — dead code (reference-based). Auto-filters test methods, MCP tool entry points, source-generator output, MEF-composed services, and interop-laid-out fields; counts surface in summary.filteredOut.
get_complexity_metrics — complexity per member (methods, constructors, properties, indexers, operators). Reports three numbers per row:
complexity — cyclomatic: how many independent paths run through the member. Starts at 1, so a straight-line method scores 1. Good for "how many tests do I need".
cognitive — how hard the member is to follow: nested structures cost more than flat ones, a whole switch costs 1 rather than one per case, and else/else if cost 1 without a nesting penalty. Starts at 0 — a cognitive of 0 is not a bug, it means nothing branches.
maxNesting — the deepest control structure, counting lambda and local-function bodies.
Which to use: cognitive is the better refactoring-priority signal — it is what separates a flat 20-case dispatch (easy) from four levels of nested if (hard), which cyclomatic scores the same. Use cyclomatic for test-coverage budgeting. The metric parameter ("cyclomatic" default, or "cognitive") selects which one threshold filters on and the sort orders by; both numbers are always in the response. summary reports max/avg/overThreshold over the selected metric plus maxCognitive alongside.
find_naming_violations — .NET naming conventions.
find_async_violations — Detects sync-over-async (.Result/.Wait()/GetAwaiter().GetResult()), async void outside event handlers, missing awaits in async methods, and fire-and-forget tasks. Severity error/warning per violation. Static analysis; no runtime data.
find_disposable_misuse — Detects IDisposable/IAsyncDisposable instances not wrapped in using/await using/returned/assigned-to-field-or-out-parameter (warning), and bare-expression-statement discards of a disposable creator/factory (error). Static analysis; no runtime data.
Exception analysis
get_exception_flow — What can escape a method. Walks callees to maxDepth (default 3, cycle-safe) collecting explicit throws, propagates each one up the call chain through every enclosing try/catch, and reports whether it still escapes the method you asked about — with the propagation path, depth, and where it was caught. origin is thrown (a real throw site) or documented (an exception XML tag on a metadata callee, e.g. the BCL — set includeDocumented: false to drop those). Filtered catch clauses are skipped rather than accepted, because a when filter may decline at runtime — the search continues to the next clause and outward. hasFilter describes the reported outcome: it is true only when the exception escaped past a filtered clause that might have caught it, so escapes: false always comes with hasFilter: false.
find_throw_sites — Every place an exception type is thrown, solution-wide; includeDerived also matches subclasses. Rethrows (throw;) are flagged and resolved to the enclosing catch's type. Unlike get_exception_flow, this counts throws inside lambdas and local functions — they are throw sites, they just don't escape at their enclosing method's boundary.
find_catch_blocks — Every catch for a type; includeBaseClauses also surfaces catch (Exception) and bare catch. Each item carries hasFilter, rethrows, and isEmpty, so "what is silently swallowing this?" is one call (isEmpty: true, rethrows: false).
Limits (all three): static analysis of explicit throw only — no runtime-implicit exceptions (null deref, division by zero, OOM), no reflection-invoked throws. Virtual/interface calls follow the declared symbol, not runtime overrides. get_exception_flow explores a given callee via the shallowest call path it finds, so a method reached from two call sites at the same depth is analysed via one of them. Async is modelled as synchronous: a throw inside an async method is reported as propagating at the call site, so an enclosing try counts as catching it. That is right for awaited calls and wrong for fire-and-forget ones (_ = M();), where the exception actually surfaces later on another stack and no enclosing try sees it.
find_large_classes — oversized types.
find_god_objects — Types crossing all 3 size thresholds (lines/members/fields) AND a coupling threshold (incoming or outgoing namespace count). Sharper than raw size: a large but isolated class isn't flagged; a 200-line class with 15 callers across many namespaces is.
find_circular_dependencies — project/namespace cycles.
check_architecture — enforce layering rules you supply inline. forbid (Domain.* must not depend on Infrastructure.*) catches the violation you know about; allowOnly (Api.* may depend only on Application.*, Domain.*) catches the ones you haven't thought of. Edges come from resolved symbols, not using directives, so a fully qualified reference with no using is still caught and an unused using is not reported. Two semantics you need to read an empty result correctly: allowOnly evaluates only solution-internal, non-generated targets (framework namespaces are ignored — otherwise every file would violate every rule — and so is generator output, which you cannot remove; use forbid to restrict either), and self-references are never violations. Results group per violated edge with a full referenceCount and the first maxSitesPerViolation sites, so one stray cross-boundary reference is one item, not hundreds.
get_project_health — Composite audit: complexity, large classes, naming, unused, reflection, async violations, disposable misuse — counts + top-N hotspots per dimension, per project. Use when you'd otherwise call 7 separate audit tools.
Source Generators
get_source_generators — list generators and their outputs.
get_generated_code — inspect generated source (filter by generator or file path).
Working with External Assemblies (Closed-Source / NuGet)
External symbols have origin.kind = "metadata" in tool results. Supply fully-qualified names (e.g. Microsoft.Extensions.DependencyInjection.IServiceCollection) to the Tier-1 tools below — they fall back to metadata lookup automatically when no source match is found.
inspect_external_assembly — browse a referenced assembly's public API:
mode="summary" → namespace tree + type counts (start here to orient yourself)
mode="namespace" → full type + member listing for one namespace
peek_il — read ilasm-style IL for a single method in a referenced assembly:
- Input: fully-qualified method name with parameter types (e.g.
Namespace.Type.Method(ParamType1, ParamType2))
- For constructors: use
..ctor notation (e.g. Namespace.Type..ctor(ParamType))
- Output: raw IL instructions (
IL_0000: ldarg.0, etc.) — not decompiled C#
- When to use: after
find_callers / get_symbol_context identifies an interesting external method and you want to understand its implementation
Worked example — drill into a NuGet package:
inspect_external_assembly(assemblyName: "Microsoft.Extensions.DependencyInjection.Abstractions", mode: "summary")
→ shows NamespaceTree with Microsoft.Extensions.DependencyInjection (15 types)
inspect_external_assembly(assemblyName: "Microsoft.Extensions.DependencyInjection.Abstractions",
mode: "namespace", namespaceFilter: "Microsoft.Extensions.DependencyInjection")
→ returns IServiceCollection, ServiceDescriptor, ServiceLifetime, etc. with members and XML doc summaries
To find where your code uses an external symbol:
Use find_references / find_callers / find_implementations with the fully-qualified external symbol name. Results will be source locations in your codebase.
To find all source files using IServiceCollection:
find_references("Microsoft.Extensions.DependencyInjection.IServiceCollection")
→ returns source locations (all origin.kind="source") where your code references IServiceCollection
find_callers("Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton")
→ returns source call sites for AddSingleton (pass the extension class + method name)
To read the raw IL of an external method:
Use peek_il with the fully-qualified method name including parameter types:
peek_il("Microsoft.Extensions.DependencyInjection.ServiceDescriptor..ctor(System.Type, System.Type, Microsoft.Extensions.DependencyInjection.ServiceLifetime)")
→ returns ilasm-style IL text, assembly name, and version
Limitations: abstract methods, interface instance members, and properties-as-whole (target the getter/setter accessor instead) are not supported.
Solution Management
list_solutions — loaded solutions, which is active. Includes a SkippedProjects array per solution: projects MSBuildWorkspace could not load (legacy non-SDK-style csproj, missing files), with Kind and Reason.
set_active_solution — switch active solution by partial name.
load_solution — load a .sln/.slnx at runtime. Returns a confirmation; when projects were skipped, the message summarises them and points to list_solutions for per-project reasons. For very large solutions (hundreds of projects) that take minutes to open, pass background: true to get a taskId back immediately and poll get_task_status instead of blocking — the new solution becomes active only once that task succeeds, so other tools keep working against the current solution meanwhile.
unload_solution — free memory.
rebuild_solution — full reload (after Directory.Build.props changes, new analyzers/packages, or stale diagnostics).
Legacy project handling: when a solution contains non-SDK-style projects (<Project ToolsVersion="..." xmlns=".../msbuild/2003">, typically older .NET Framework projects), the server pre-filters them out before MSBuild sees them. Those projects appear in SkippedProjects with Kind: "Legacy". The SDK-style projects in the same solution load normally and remain fully queryable — partial load, not all-or-nothing. If a tool returns "no results" for what should be a legacy project, check list_solutions first.
Planning a Change — standard order
get_type_overview — context + hierarchy + diagnostics.
analyze_change_impact — blast radius (files, projects, call sites).
find_references / find_callers / find_implementations — detailed dependency breakdown.
get_project_dependencies — architectural position.
get_di_registrations — wiring.
find_reflection_usage — dynamic coupling.
find_attribute_usages — attribute-driven behavior.
get_diagnostics — existing issues.
get_code_fixes / get_code_actions → apply_code_action — auto-fixes and refactorings.
find_unused_symbols — dead code to delete instead of refactor.
get_complexity_metrics — complexity hotspots.
Reference concrete types, interfaces, and call sites in your analysis. Not "the services that implement this" but "These 3 classes implement IUserService: UserService, CachedUserService, AdminUserService."
Tool Quick Reference
| Tool |
When to Use |
find_implementations |
"What implements this interface?" / "What extends this class?" |
find_callers |
"Who calls this method?" / "What depends on this?" |
find_event_subscribers |
"Who subscribes to this event?" |
find_references |
"Where is this symbol used?" / "Show all references" / "Who writes to it?" (kinds filter) |
find_tests_for_symbol |
"What tests cover this method?" / "Which tests will break if I change X?" |
get_test_summary |
"What does this test suite cover?" |
find_uncovered_symbols |
"What should I write tests for?" / "Where's our testing debt?" |
generate_test_skeleton |
"Generate a test stub for this method" / "Bootstrap tests for this class" |
go_to_definition |
"Where is this defined?" / "Jump to source" |
search_symbols |
"Find types/methods matching this name" |
get_type_hierarchy |
"What's the inheritance chain?" |
get_symbol_context |
"Give me everything about this type" |
get_public_api_surface |
"What's the public API of this library?" |
find_breaking_changes |
"Will this break consumers?" |
get_di_registrations |
"How is this wired up?" / "What's the DI lifetime?" |
get_project_dependencies |
"How do projects relate?" |
get_nuget_dependencies |
"What packages does this project use?" |
find_reflection_usage |
"Is this used dynamically?" |
find_attribute_usages |
"What's marked [Obsolete]?" / "Find all [Authorize] controllers" |
find_obsolete_usage |
"What deprecations do we still use?" |
get_diagnostics |
"Any compiler errors?" / "Show warnings" |
get_code_fixes |
"How do I fix this warning?" |
get_code_actions |
"What refactorings are available here?" |
apply_code_action |
"Apply this refactoring" / "Extract method" |
rename_symbol |
"Rename this symbol everywhere" / "Change this name across the solution" |
change_signature |
"Add/remove/reorder a parameter and fix all the callers" |
resolve_stack_trace |
"Where did this exception come from?" / "Resolve this stack trace" |
find_unused_symbols |
"Any dead code?" |
get_complexity_metrics |
"Which methods are too complex?" / "What should I refactor first?" (metric: "cognitive") / "How deeply nested is this?" |
find_naming_violations |
"Check naming conventions" |
find_async_violations |
"Are there async bugs?" / "Find sync-over-async" |
find_disposable_misuse |
"Are there resource leaks?" / "Find missing using" |
get_exception_flow |
"What can escape this method?" / "Where does this exception get caught?" |
find_throw_sites |
"Where is this exception type thrown?" |
find_catch_blocks |
"Who catches this?" / "What's swallowing exceptions?" |
find_large_classes |
"Find classes that need splitting" |
find_god_objects |
"Which classes are doing too much?" |
find_circular_dependencies |
"Any circular dependencies?" |
check_architecture |
"Is anything violating our layering?" / "Does Domain reference Infrastructure?" |
get_project_health |
"How is this project doing?" / "Top hotspots across all dimensions" |
get_source_generators |
"What source generators are active?" |
get_generated_code |
"Show generated code" |
inspect_external_assembly |
"What does this NuGet package expose?" / "Show me the API of X assembly" |
peek_il |
"Show IL for |
…(truncated)
1---2name: roslyn-codelens3description: Use when working with any .NET / C# code (.cs/.csproj/.sln/.slnx files), finding callers/references/implementations, checking compiler errors or warnings, running dotnet build for diagnostics, searching for a type/method/interface by name, inspecting DI registrations, detecting dead code or circular dependencies, inspecting source-generator output, or about to Grep/Glob across a C# codebase — activates when roslyn-codelens MCP tools are available.4---56# Roslyn CodeLens — Semantic .NET Intelligence78## Response shape910All list-returning tools wrap their results in an envelope:1112```json13{14 "items": [...],15 "totalCount": 142,16 "truncated": false,17 "limit": 500,18 "summary": { ... }19}20```2122When `truncated` is `true`, `items` are the **top N by the tool's natural sort order** (severity-first, worst-first, by-project, etc.) — usually that's what you want. Raise `limit` only if the truncated tail matters for the task.2324Tools that include a `summary` aggregate:2526- `get_diagnostics` — `{ error, warning, info, hidden }` counts27- `find_references` — `{ byProject: { name: count }, byKind: { kind: count } }`28- `find_callers`, `find_attribute_usages` — `{ byProject: { name: count } }`29- `search_symbols`, `find_reflection_usage` — `{ byKind: {...} }`30- `find_unused_symbols` — `{ byKind: {...}, filteredOut: { testMethod, testContainer, mcpTool, generated, composition, interop } }`31- `find_naming_violations` — `{ byRule: {...} }`32- `get_complexity_metrics` — `{ max, avg, overThreshold, maxCognitive }` — `max`/`avg`/`overThreshold` describe whichever metric `metric` selected; `maxCognitive` is always the cognitive one33- `resolve_stack_trace` — `{ byOrigin: { source, metadata, unresolved }, exceptions, skippedFrameLike }` — `skippedFrameLike` counts frame-like-but-unparseable lines (also present in items as `Kind="unknown"`)3435Single-object tools (`get_type_overview`, `get_symbol_context`, `apply_code_action`, etc.) are unchanged — they return their bespoke shape directly.3637## Error responses3839When a tool can't proceed, the response is `isError: true` with content carrying a JSON body of `{ code, message, details? }`. Switch on `code`:4041| Code | Meaning | Common source |42|---|---|---|43| `SymbolNotFound` | type/method/property not resolved | `analyze_method`, `get_symbol_context`, `get_type_overview`, `get_type_hierarchy`, `generate_test_skeleton` |44| `SolutionNotTrusted` | analyzers blocked until `trust_solution` is called | `get_diagnostics` (`includeAnalyzers: true`), `get_code_fixes` |45| `AmbiguousMatch` | name matched multiple solutions; `details.matches` lists candidates | `set_active_solution`, `unload_solution` |46| `FileNotFound` | file path / baseline doesn't exist or isn't in solution | `get_file_overview`, `find_breaking_changes` |47| `ProjectNotFound` | solution name didn't match | `set_active_solution`, `unload_solution` |48| `InvalidArgument` | malformed / unsupported caller input | various |49| `Internal` | unexpected; `message` carries exception text | fallback |5051If `code: SolutionNotTrusted`, the right next step is calling `trust_solution` after asking the user. Don't catch and retry blindly — the user has to authorize analyzer execution.5253## Detection5455If `find_implementations` is not available as an MCP tool, this skill is inert — do nothing, no errors.5657If it IS available, every rule below applies. No exceptions.5859## The Iron Law6061**On a .NET codebase where roslyn-codelens MCP tools are available:**62631. **Never** use `Grep`, `Glob`, or Bash `grep`/`rg` to locate C# symbols, types, methods, interfaces, references, callers, implementations, or usages.642. **Never** run `dotnet build`, `dotnet msbuild`, `msbuild`, or any build command to surface compiler errors, warnings, or analyzer diagnostics.653. **Never** manually read a `.cs` file to "grep in my head" for who uses a symbol, or to check if code compiles.6667The semantic tools (`find_callers`, `find_references`, `find_implementations`, `search_symbols`, `get_diagnostics`, `go_to_definition`, etc.) are **always** more accurate than text search and **always** faster than a build. There is no tradeoff to weigh.6869**Violating the letter of these rules is violating the spirit.** If you're about to run a command or tool that *substitutes* for one of these semantic tools, stop.7071## Red Flags — STOP and Use the MCP Tool7273If any of these thoughts cross your mind, stop and switch to the MCP tool:7475| Thought / Action | STOP — Use instead |76|---|---|77| "Let me `Grep` for `class Foo`" | `search_symbols` or `go_to_definition` |78| "Let me `Grep` for `Foo\\.Bar\\(`" (finding callers) | `find_callers` |79| "Who subscribes to this event?" / "Find += sites" / "Are we leaking event subscriptions?" | `find_event_subscribers` |80| "What does this method end up calling?" / "Show me the transitive callees" / "What's the blast radius for changing X?" | `get_call_graph` |81| "Let me `Grep` for `: IFoo`" (finding implementations) | `find_implementations` |82| "Let me `Grep` for `new Foo(`" | `find_references` |83| "Let me `Grep` for `[Authorize]`" | `find_attribute_usages` |84| "What deprecations do we still use?" / "Plan an Obsolete cleanup" / "Find every [Obsolete] call site" | `find_obsolete_usage` |85| "I'll run `dotnet build` to see errors" | `get_diagnostics` |86| "I'll run `dotnet build -warnaserror` to find warnings" | `get_diagnostics` |87| "Let me `Read` the .cs file to see what's defined" | `get_file_overview` or `get_type_overview` |88| "Let me `Glob` for `**/*Service.cs`" | `search_symbols` with a partial name |89| "Let me `Grep` for `Activator.CreateInstance`" | `find_reflection_usage` |90| "I'll check if this method is used by reading files" | `find_callers` / `find_unused_symbols` |91| "What overloads does this method have?" / "Show me all signatures of Foo" / "Compare overloads side-by-side" | `get_overloads` |92| "What can I call on this type?" / "Is there an extension method for X?" / "Does LINQ have something for this?" | `get_extension_methods` |93| "How do I construct this?" / "What constructors does it have?" / "Why can't I `new` this up?" / "Can my test project reach this internal constructor?" | `get_instantiation_options` |94| "What operators does this type define?" / "Does it have a custom `==` or implicit conversion?" / "List `+`, `-`, conversions on this type" | `get_operators` |95| "I'll eyeball complexity by reading the method" / "This one looks like the hairiest, let's start there" | `get_complexity_metrics` (use `metric: "cognitive"` to rank by how hard code is to follow) |96| "How is this project doing?" / "Where should I focus?" / "Show me the technical debt picture" | `get_project_health` |97| "Which classes are doing too much?" / "Where are my god classes?" / "Worst design smells in this codebase?" | `find_god_objects` |98| "Let me `Grep` for tests that call this method" | `find_tests_for_symbol` |99| "Which tests will break if I change this?" | `find_tests_for_symbol` |100| "What does this test suite cover?" / "List all tests in MyProj.Tests" / "How many xUnit Theory tests do we have?" | `get_test_summary` |101| "What should I write tests for?" / "Where's our testing debt?" / "Show me untested public methods" | `find_uncovered_symbols` |102| "Generate a test stub for this method" / "Bootstrap tests for this class" / "Give me a skeleton I can fill in" | `generate_test_skeleton` |103| "What's the public API of this library?" / "Show me the API surface" / "I need a PublicAPI.txt-style snapshot" | `get_public_api_surface` |104| "Will this break consumers?" / "Show me breaking changes vs the previous release" / "Diff this build's API against baseline" | `find_breaking_changes` |105| "Are there async bugs?" / "Find sync-over-async" / "Are we using `.Result` anywhere?" | `find_async_violations` |106| "Are there resource leaks?" / "Find missing `using`" / "Is this disposable handled?" | `find_disposable_misuse` |107| "What exceptions can escape this method?" / "Is this call safe to make?" / "Do I need a try here?" | `get_exception_flow` |108| "Where is this exception thrown?" / "Who raises `TimeoutException`?" | `find_throw_sites` |109| "Who catches this?" / "Is anything swallowing exceptions?" / "Find empty catch blocks" | `find_catch_blocks` |110| "Rename this class/method everywhere" / "Let me edit N files to change this name" | `rename_symbol` |111| "Add/remove/reorder a parameter" / "Let me update all the call sites by hand" | `change_signature` |112| "Where did this exception come from?" / user pastes a stack trace / "What method is `<M>d__12.MoveNext`?" | `resolve_stack_trace` |113| "Let me `Read` the file to see this method's body" / "Show me the source of these 5 methods" | `get_method_source` |114| "Where is this field written / who mutates it?" / "Is this ever assigned outside the ctor?" | `find_references` with `kinds: ["write","readwrite"]` |115| "Where is this type cast / pattern-matched?" / "Find `is`/`as` sites" | `find_references` with `kinds: ["type_check","cast"]` |116117**All of these mean: the MCP tool is the correct tool. Use it.**118119## Rationalizations — and why they're wrong120121| Excuse | Reality |122|---|---|123| "Just a quick Grep — it's faster." | It isn't. One `find_references` beats iterating Grep + reading matches + deduping false positives. |124| "Grep as a sanity check on top of the MCP tool." | Redundant and misleading. Grep finds comments, strings, partial matches. If Roslyn says there are N references, there are N references. |125| "This is just a string/literal search, so Grep is fine." | If the target is a C# symbol, it's not "just a string" — it has a definition, scope, and binding. Use `find_references`. String literal searches in comments/docstrings are the *only* legitimate Grep case. |126| "`dotnet build` is how everyone checks errors." | Not here. `get_diagnostics` returns compiler errors + analyzer results, structured, without rebuilding. A build is minutes; the tool is milliseconds. |127| "The MCP server might be slow / might fail." | If it fails, report that and ask. Do not silently fall back to Grep — that produces wrong answers that look right. |128| "I need to see the file contents anyway." | `get_file_overview`, `get_type_overview`, and `analyze_method` give you structure without a `Read`. Use `Read` only after you know which lines matter. |129| "The user asked me to Grep." | If the user asked for *a grep* specifically, ask if they actually want semantic results. If they asked for *information about the code* and suggested grep, use the semantic tool and tell them why. |130| "I'm just looking for TODO comments / string literals." | Fine — Grep is legitimate for comments, string literals, and non-C# files. That's the only free pass. |131132## Pre-Action Checklist133134**Before calling `Grep` or `Glob` on `.cs` / `.csproj` / `.sln` / `.slnx` / `.cshtml` files:**1351. Is the target a C# symbol (type/member/namespace)? → Use `search_symbols` or `find_references`.1362. Is the target an attribute? → Use `find_attribute_usages`.1373. Is the target a reflection pattern? → Use `find_reflection_usage`.1384. Is it *genuinely* a string literal, comment, or non-semantic text? → Grep is OK. State why.139140**Before running `dotnet build` / `msbuild` via Bash:**1411. Am I looking for errors, warnings, or analyzer diagnostics? → Use `get_diagnostics`. Stop.1422. Am I actually trying to produce a binary / run tests / package? → Build is OK. State why.143144**Before `Read`ing a `.cs` file:**1451. Do I just need structure (what's in it, what's defined)? → `get_file_overview` / `get_type_overview`.1462. Do I need a specific method's shape? → `analyze_method`.1473. Do I need one or more members' actual source bodies? → `get_method_source` (batch-friendly — pass all the names at once).1484. Do I need the actual source lines to edit? → `Read` is OK.149150## When to Use Each Tool151152### Decision Tree for External Assemblies153154```155I want to... Tool / Approach156─────────────────────────────────────────────────────────────────────157Work with types in my source code → existing tools (unchanged)158Look up an external type by name → go_to_definition / get_symbol_context /159 get_type_overview / get_type_hierarchy160 (pass fully-qualified name; returns origin="metadata")161Browse what a package exposes → inspect_external_assembly162Who in my code uses an ext. type? → find_references / find_callers / find_implementations163See a method's IL bytecode → peek_il (pass fully-qualified method name with param types)164Inspect an arbitrary DLL → add a <ProjectReference> to a throwaway165 project, rebuild_solution, then use normally166```167168### Understanding a Codebase169- `get_project_dependencies` — solution architecture, how projects relate.170- `get_symbol_context` — full context for a type (namespace, base, interfaces, DI deps, public members).171- `get_public_api_surface` — Enumerate every public/protected type and member declared in production projects; deterministically sorted; suitable for API review or breaking-change baselines. Static analysis; only declared (not inherited) members appear.172- `find_breaking_changes` — Diff the current public API surface against a baseline (JSON snapshot from `get_public_api_surface`, or a `.dll` file). Reports Removed/Added/KindChanged/AccessibilityNarrowed/Widened with Breaking/NonBreaking severity. Static analysis; doesn't currently detect return-type, sealed-ness, or nullable changes.173- `get_type_hierarchy` — inheritance chains and extension points.174- `get_type_overview` — one-shot: context + hierarchy + diagnostics (replaces 3 calls).175- `get_file_overview` — types defined in a file + diagnostics, without reading it.176- `analyze_method` — signature + callers + outgoing calls, all in one.177- `get_method_source` — full declaration source (XML docs, attributes, body, original formatting) for one or MANY members by name in a single call: methods (all overloads), constructors (request as `Type.TypeName` — simple or fully qualified; nested types need full qualification), properties, indexers (`Type.this` or `Type.this[]`), fields, events, explicit interface implementations. Per-item statuses (`ok`/`notFound`/`ambiguous`/`metadata`/`unsupportedKind`) — a batch never fails wholesale; `metadata` items carry `Origin` (assembly name/version for `peek_il`) and a `Note` explains non-`ok` outcomes. `StartLine` always matches the first line of `Source`. Whole types are out of scope (`get_type_overview` / `Read`).178- `get_overloads` — Every overload of a method or constructor (source + metadata) with full parameter detail, modifiers, generic type params, XML doc summary, and location. One call instead of N analyze_method calls.179- `get_operators` — every `+`, `-`, `==`, `<`, conversion, etc. on a type, with kind, signature, source location. Includes synthesized record equality. Covers what `get_overloads` excludes.180- `get_extension_methods` — every extension member applicable to a type, from the solution AND referenced assemblies, so LINQ shows up for an `IEnumerable`. Applicability is Roslyn's own (`ReduceExtensionMethod`), so generic inference is exact: `this IEnumerable<T>` applies to `string`, `this IEnumerable<string>` does not. `signature` is the reduced, call-site form — receiver dropped, inference applied, return type first for both kinds (`IEnumerable<int> Where<int>(Func<int, bool>)`, `int Tripled`). It is a signature, not paste-ready source: a partially inferred generic keeps the type parameters still inferred from the arguments (`Select<int, TResult>` — `int` came from the receiver, `TResult` from your lambda). C# 14 `extension` blocks are covered including **properties** and **static methods**, neither of which an `IsExtensionMethod` scan can see. Check `isStatic` before writing the call: `false` (the normal case, including every classic `this` extension, which is declared static but called on an instance) means `value.Doubled()`; `true` means a C# 14 static extension member, called on the type — `int.Zero`. Receivers may be arrays, nullables or tuples (`string[]`, `int?`, `(int, string)`) as well as plain and constructed generic types. Results are **not** filtered by `using` scope — the tool has no call site — so `namespace` is always reported and you may need to add the import. Source extensions sort before metadata ones; `nameFilter` narrows by substring.181- `get_instantiation_options` — how to construct a type, in one call: `constructors` (every parameter's type and name, declared accessibility, `isImplicit`, `isObsolete`), `factories`, `diRegistrations`, and `requiredMembers` you must set in an object initializer. **Read `isImplicit` before concluding a type has no constructor** — a struct, and a class with none declared, both have a usable parameterless one that no source file mentions. `factories` are static members returning the type from ANYWHERE in the solution, which is the case that matters: a type with a private constructor and a separate `WidgetFactory.Create()` looks uninstantiable until you see them. `Task<T>`/`ValueTask<T>` factories are unwrapped and flagged `isAsync`; instance builder methods are excluded because the builder itself needs constructing. Pass `fromProject` to get `accessible` computed from that project's viewpoint, honouring `InternalsVisibleTo` — `accessible: null` means *not computed* (no `fromProject` given), never "inaccessible". For interfaces, abstract and static classes, `instantiable` is false and `note` says why; follow up with `find_implementations`.182- `get_call_graph` — Transitive caller/callee graph for a method, depth-bounded with cycle detection. Adjacency-list output. Use when you need depth > 1 (`analyze_method` is depth=1).183184### Navigating Code (**instead of Grep/Glob**)185- `go_to_definition` — jump to the definition.186- `search_symbols` — fuzzy symbol lookup.187- `find_references` — every reference across the solution, each tagged with a `referenceKind` and reported per occurrence (multiple references on one line each get their own item, distinguished by `column`). Pass `kinds` to filter server-side.188189**Reference kinds** — `read`, `write` (assignment target, `out` argument), `readwrite` (compound assignment, `++`/`--`, `ref` argument), `invocation`, `method_group` (method used as a delegate, not called), `object_creation`, `cast` (`(T)x`, `x as T`), `type_check` (`x is T`, `is T v`, `case T v:`), `typeof`, `base_type`, `type_constraint`, `type_argument`, `declaration` (variable/parameter/return/field type positions), `attribute`, `nameof`, `xml_doc` (`<see cref=...>`); `usage` is a rare fallback. Note a receiver reads: in `_map[k] = v` the field `_map` is `read` (its contents change, the field isn't reassigned) — same as `_map.Add(k, v)`.190191### Finding Dependencies and Usage192- `find_callers` — every call site for a method.193- `find_event_subscribers` — every += / -= site for an event symbol, with resolved handler name and subscribe/unsubscribe tag. Use for UI-event audits or memory-leak hunts (compare subscribe/unsubscribe pairs).194- `find_implementations` — all implementors of an interface / extenders of a class.195- `find_tests_for_symbol` — xUnit/NUnit/MSTest methods that exercise a production symbol; opt-in transitive walk through helpers.196- `get_test_summary` — Per-project inventory of test methods with framework, attribute kind, [InlineData]/[TestCase]/[DataRow] row count, location, and production symbols referenced. Project → tests direction; complements `find_tests_for_symbol` (test → production).197- `find_uncovered_symbols` — Public methods and properties no test transitively reaches (≤ 3 helper hops); sorted by cyclomatic complexity for prioritization. Strict reference-based: an override is not marked covered just because its base or interface declaration is reached — a test calling `IFoo.Bar` does not cover `Foo.Bar`.198- `generate_test_skeleton` — Emit a test-class skeleton (parseable C#) for a method or type. Returns framework, suggested file path, class name, full file contents, and TodoNotes (e.g. constructor dependencies). Stubs cover happy-path Fact, Theory + InlineData for primitive-param methods, `Assert.Throws<T>` per direct-throw exception type, async detection. Tool returns text; agent decides whether to `Write` it. Pairs with `find_uncovered_symbols` (find gap → generate stub).199- `get_di_registrations` — DI wiring and lifetimes.200- `find_reflection_usage` — hidden/dynamic coupling (`Activator.CreateInstance`, `MethodInfo.Invoke`, assembly scanning).201- `get_nuget_dependencies` — NuGet packages and versions.202- `find_attribute_usages` — members decorated with a given attribute.203- `find_obsolete_usage` — Every [Obsolete] call site grouped by deprecation message and severity. Sharper than find_attribute_usages for migration planning. Source AND metadata obsoletes (NuGet) included.204205### Diagnostics (**instead of `dotnet build`**)206- `get_diagnostics` — compiler errors, warnings, analyzer diagnostics. Replaces `dotnet build` output.207- `get_code_fixes` — structured edits for a diagnostic.208209#### Trust model for analyzer diagnostics210211`get_diagnostics` defaults to `includeAnalyzers=false` (compiler diagnostics only). If the user asks for analyzer warnings (StyleCop, Microsoft.CodeAnalysis.Analyzers, CA-prefixed rules, etc.) — OR if you need to call `get_code_fixes` for an analyzer-rule diagnostic:2122131. Call `get_diagnostics(includeAnalyzers=true)`.2142. If the server returns an *"untrusted solution"* error: **ask the user before calling `trust_solution`**. Phrase the question to make it clear that analyzer DLLs run as in-process code with the user's privileges. They should only trust solutions they wrote or fully vetted.2153. Prefer `scope="session"` for one-off reviews. Use `scope="persistent"` only when the user confirms they regularly use this solution. Use `scope="addRoot"` to trust an entire directory tree (e.g., `c:\projects\`).2164. Use `list_trusted_paths` to inspect current state when the user asks "is X trusted?". Use `revoke_trust` to drop entries.217218Solutions passed on the CLI at server startup are auto-trusted in session scope — `get_diagnostics(includeAnalyzers=true)` against them works without an extra prompt.219- `get_code_actions` — all refactorings/fixes at a position (with optional range).220- `apply_code_action` — execute a refactoring by title. Preview mode by default. Applying refuses to overwrite files that changed on disk since the snapshot (run `rebuild_solution` and retry), and on success the in-memory snapshot updates immediately, so follow-up queries see the new text without waiting for the file watcher. Actions that create a *new* file (extract interface, move type to file) write it, but the new file only enters the snapshot once the watcher picks it up — the result's `warning` says so when it applies.221- `rename_symbol` — solution-wide safe rename of a type or member (Roslyn Renamer; NOT available via `apply_code_action`). Preview by default; new-compiler-error conflicts reported; apply refuses unless `force=true` on: conflicts, a degraded solution load, or files changed on disk since the snapshot (freshness refusal — run `rebuild_solution` and retry; `force` does not bypass freshness). After apply the in-memory snapshot updates immediately — follow-up queries see the new name without waiting for a rebuild. Generic types accept the arity-free qualified form (`Data.Repository` finds `Repository<T>`).222- `change_signature` — add, remove, and reorder a method's parameters, rewriting every call site (Roslyn's change-signature engine; also NOT reachable via `apply_code_action`). `operations` apply in order: `remove` by parameter name, `reorder` with a full permutation of the surviving names, and `add` with a **required** `callSiteValue` naming exactly what each existing call site passes — the tool never guesses that. Give an added parameter a `defaultValue` instead and existing call sites are left untouched. Named arguments, optional parameters, `params` arrays and the extension-method `this` are all handled. `cascadedTo` lists the overrides and interface implementations rewritten alongside it, so check it in preview before applying. Same gates as `rename_symbol`: preview by default, conflict / degraded-load / freshness refusals, `force` to override, immediate snapshot update on apply.223- `resolve_stack_trace` — map a pasted .NET stack trace to file/line/symbol; demangles async/iterator state machines, lambdas, and local functions; handles log-prefixed lines, inner-exception chains, and Demystifier-style traces. Source frames get the declaration site (exact location when the trace carries `in file:line`); external frames resolve with `origin="metadata"` when the assembly is referenced by the solution — frames outside that closure come back `origin="unresolved"`. Frame-like lines that fail all grammars aren't dropped: they surface as `Kind="unknown"` items at their original position, and `summary.skippedFrameLike` counts them. Items stay in trace order.224- `analyze_data_flow` — variable lifecycle over a statement range (declared/read/written/captured/flows-in/out).225- `analyze_control_flow` — reachability, returns, unreachable paths.226227**Code generation is in `apply_code_action`** — do NOT look for dedicated generation tools. Use `get_code_actions` to find the title, then `apply_code_action`:228- Implement missing interface/abstract members → *"Implement abstract members"* / *"Implement interface"*229- Generate constructor from fields → *"Generate constructor"*230- Add null checks → *"Add null checks for all parameters"*231- Generate `Equals`/`GetHashCode` → *"Generate Equals and GetHashCode"*232- Encapsulate field → *"Encapsulate field"*233- Extract method → *"Extract method"*234- Inline variable → *"Inline variable"*235236### Code Quality Analysis237- `find_unused_symbols` — dead code (reference-based). Auto-filters test methods, MCP tool entry points, source-generator output, MEF-composed services, and interop-laid-out fields; counts surface in `summary.filteredOut`.238- `get_complexity_metrics` — complexity per member (methods, constructors, properties, indexers, operators). Reports **three** numbers per row:239 - `complexity` — cyclomatic: how many independent paths run through the member. **Starts at 1**, so a straight-line method scores 1. Good for "how many tests do I need".240 - `cognitive` — how hard the member is to *follow*: nested structures cost more than flat ones, a whole `switch` costs 1 rather than one per case, and `else`/`else if` cost 1 without a nesting penalty. **Starts at 0** — a `cognitive` of 0 is not a bug, it means nothing branches.241 - `maxNesting` — the deepest control structure, counting lambda and local-function bodies.242243 **Which to use:** `cognitive` is the better *refactoring-priority* signal — it is what separates a flat 20-case dispatch (easy) from four levels of nested `if` (hard), which cyclomatic scores the same. Use `cyclomatic` for test-coverage budgeting. The `metric` parameter (`"cyclomatic"` default, or `"cognitive"`) selects which one `threshold` filters on and the sort orders by; both numbers are always in the response. `summary` reports `max`/`avg`/`overThreshold` over the selected metric plus `maxCognitive` alongside.244- `find_naming_violations` — .NET naming conventions.245- `find_async_violations` — Detects sync-over-async (`.Result`/`.Wait()`/`GetAwaiter().GetResult()`), `async void` outside event handlers, missing awaits in async methods, and fire-and-forget tasks. Severity error/warning per violation. Static analysis; no runtime data.246- `find_disposable_misuse` — Detects `IDisposable`/`IAsyncDisposable` instances not wrapped in `using`/`await using`/returned/assigned-to-field-or-out-parameter (warning), and bare-expression-statement discards of a disposable creator/factory (error). Static analysis; no runtime data.247248### Exception analysis249250- `get_exception_flow` — What can escape a method. Walks callees to `maxDepth` (default 3, cycle-safe) collecting explicit throws, propagates each one up the call chain through every enclosing `try`/`catch`, and reports whether it still escapes the method you asked about — with the propagation `path`, `depth`, and where it was caught. `origin` is `thrown` (a real throw site) or `documented` (an `exception` XML tag on a metadata callee, e.g. the BCL — set `includeDocumented: false` to drop those). Filtered `catch` clauses are skipped rather than accepted, because a `when` filter may decline at runtime — the search continues to the next clause and outward. `hasFilter` describes the reported outcome: it is true only when the exception escaped past a filtered clause that might have caught it, so `escapes: false` always comes with `hasFilter: false`.251- `find_throw_sites` — Every place an exception type is thrown, solution-wide; `includeDerived` also matches subclasses. Rethrows (`throw;`) are flagged and resolved to the enclosing catch's type. Unlike `get_exception_flow`, this counts throws inside lambdas and local functions — they are throw sites, they just don't escape at their enclosing method's boundary.252- `find_catch_blocks` — Every `catch` for a type; `includeBaseClauses` also surfaces `catch (Exception)` and bare `catch`. Each item carries `hasFilter`, `rethrows`, and `isEmpty`, so "what is silently swallowing this?" is one call (`isEmpty: true, rethrows: false`).253254**Limits (all three):** static analysis of explicit `throw` only — no runtime-implicit exceptions (null deref, division by zero, OOM), no reflection-invoked throws. Virtual/interface calls follow the declared symbol, not runtime overrides. `get_exception_flow` explores a given callee via the shallowest call path it finds, so a method reached from two call sites at the same depth is analysed via one of them. **Async is modelled as synchronous:** a `throw` inside an `async` method is reported as propagating at the call site, so an enclosing `try` counts as catching it. That is right for `await`ed calls and wrong for fire-and-forget ones (`_ = M();`), where the exception actually surfaces later on another stack and no enclosing `try` sees it.255- `find_large_classes` — oversized types.256- `find_god_objects` — Types crossing all 3 size thresholds (lines/members/fields) AND a coupling threshold (incoming or outgoing namespace count). Sharper than raw size: a large but isolated class isn't flagged; a 200-line class with 15 callers across many namespaces is.257- `find_circular_dependencies` — project/namespace cycles.258- `check_architecture` — enforce layering rules you supply inline. `forbid` (`Domain.*` must not depend on `Infrastructure.*`) catches the violation you know about; `allowOnly` (`Api.*` may depend only on `Application.*`, `Domain.*`) catches the ones you haven't thought of. Edges come from resolved symbols, not `using` directives, so a fully qualified reference with no `using` is still caught and an unused `using` is not reported. **Two semantics you need to read an empty result correctly:** `allowOnly` evaluates only solution-internal, non-generated targets (framework namespaces are ignored — otherwise every file would violate every rule — and so is generator output, which you cannot remove; use `forbid` to restrict either), and self-references are never violations. Results group per violated edge with a full `referenceCount` and the first `maxSitesPerViolation` sites, so one stray cross-boundary reference is one item, not hundreds.259- `get_project_health` — Composite audit: complexity, large classes, naming, unused, reflection, async violations, disposable misuse — counts + top-N hotspots per dimension, per project. Use when you'd otherwise call 7 separate audit tools.260261### Source Generators262- `get_source_generators` — list generators and their outputs.263- `get_generated_code` — inspect generated source (filter by generator or file path).264265### Working with External Assemblies (Closed-Source / NuGet)266267External symbols have `origin.kind = "metadata"` in tool results. Supply fully-qualified names (e.g. `Microsoft.Extensions.DependencyInjection.IServiceCollection`) to the Tier-1 tools below — they fall back to metadata lookup automatically when no source match is found.268269- `inspect_external_assembly` — browse a referenced assembly's public API:270 - `mode="summary"` → namespace tree + type counts (start here to orient yourself)271 - `mode="namespace"` → full type + member listing for one namespace272- `peek_il` — read ilasm-style IL for a single method in a referenced assembly:273 - Input: fully-qualified method name with parameter types (e.g. `Namespace.Type.Method(ParamType1, ParamType2)`)274 - For constructors: use `..ctor` notation (e.g. `Namespace.Type..ctor(ParamType)`)275 - Output: raw IL instructions (`IL_0000: ldarg.0`, etc.) — not decompiled C#276 - When to use: after `find_callers` / `get_symbol_context` identifies an interesting external method and you want to understand its implementation277278**Worked example — drill into a NuGet package:**279```280inspect_external_assembly(assemblyName: "Microsoft.Extensions.DependencyInjection.Abstractions", mode: "summary")281→ shows NamespaceTree with Microsoft.Extensions.DependencyInjection (15 types)282283inspect_external_assembly(assemblyName: "Microsoft.Extensions.DependencyInjection.Abstractions",284 mode: "namespace", namespaceFilter: "Microsoft.Extensions.DependencyInjection")285→ returns IServiceCollection, ServiceDescriptor, ServiceLifetime, etc. with members and XML doc summaries286```287288**To find where your code uses an external symbol:**289Use `find_references` / `find_callers` / `find_implementations` with the fully-qualified external symbol name. Results will be source locations in your codebase.290291**To find all source files using `IServiceCollection`:**292```293find_references("Microsoft.Extensions.DependencyInjection.IServiceCollection")294→ returns source locations (all origin.kind="source") where your code references IServiceCollection295296find_callers("Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton")297→ returns source call sites for AddSingleton (pass the extension class + method name)298```299300**To read the raw IL of an external method:**301Use `peek_il` with the fully-qualified method name including parameter types:302```303peek_il("Microsoft.Extensions.DependencyInjection.ServiceDescriptor..ctor(System.Type, System.Type, Microsoft.Extensions.DependencyInjection.ServiceLifetime)")304→ returns ilasm-style IL text, assembly name, and version305```306Limitations: abstract methods, interface instance members, and properties-as-whole (target the getter/setter accessor instead) are not supported.307308### Solution Management309- `list_solutions` — loaded solutions, which is active. Includes a `SkippedProjects` array per solution: projects MSBuildWorkspace could not load (legacy non-SDK-style csproj, missing files), with `Kind` and `Reason`.310- `set_active_solution` — switch active solution by partial name.311- `load_solution` — load a `.sln`/`.slnx` at runtime. Returns a confirmation; when projects were skipped, the message summarises them and points to `list_solutions` for per-project reasons. For very large solutions (hundreds of projects) that take minutes to open, pass `background: true` to get a `taskId` back immediately and poll `get_task_status` instead of blocking — the new solution becomes active only once that task succeeds, so other tools keep working against the current solution meanwhile.312- `unload_solution` — free memory.313- `rebuild_solution` — full reload (after `Directory.Build.props` changes, new analyzers/packages, or stale diagnostics).314315**Legacy project handling:** when a solution contains non-SDK-style projects (`<Project ToolsVersion="..." xmlns=".../msbuild/2003">`, typically older .NET Framework projects), the server pre-filters them out before MSBuild sees them. Those projects appear in `SkippedProjects` with `Kind: "Legacy"`. The SDK-style projects in the same solution load normally and remain fully queryable — partial load, not all-or-nothing. If a tool returns "no results" for what should be a legacy project, check `list_solutions` first.316317### Planning a Change — standard order3181. `get_type_overview` — context + hierarchy + diagnostics.3192. `analyze_change_impact` — blast radius (files, projects, call sites).3203. `find_references` / `find_callers` / `find_implementations` — detailed dependency breakdown.3214. `get_project_dependencies` — architectural position.3225. `get_di_registrations` — wiring.3236. `find_reflection_usage` — dynamic coupling.3247. `find_attribute_usages` — attribute-driven behavior.3258. `get_diagnostics` — existing issues.3269. `get_code_fixes` / `get_code_actions` → `apply_code_action` — auto-fixes and refactorings.32710. `find_unused_symbols` — dead code to delete instead of refactor.32811. `get_complexity_metrics` — complexity hotspots.329330Reference concrete types, interfaces, and call sites in your analysis. Not *"the services that implement this"* but *"These 3 classes implement `IUserService`: `UserService`, `CachedUserService`, `AdminUserService`."*331332## Tool Quick Reference333334| Tool | When to Use |335|------|-------------|336| `find_implementations` | "What implements this interface?" / "What extends this class?" |337| `find_callers` | "Who calls this method?" / "What depends on this?" |338| `find_event_subscribers` | "Who subscribes to this event?" |339| `find_references` | "Where is this symbol used?" / "Show all references" / "Who writes to it?" (`kinds` filter) |340| `find_tests_for_symbol` | "What tests cover this method?" / "Which tests will break if I change X?" |341| `get_test_summary` | "What does this test suite cover?" |342| `find_uncovered_symbols` | "What should I write tests for?" / "Where's our testing debt?" |343| `generate_test_skeleton` | "Generate a test stub for this method" / "Bootstrap tests for this class" |344| `go_to_definition` | "Where is this defined?" / "Jump to source" |345| `search_symbols` | "Find types/methods matching this name" |346| `get_type_hierarchy` | "What's the inheritance chain?" |347| `get_symbol_context` | "Give me everything about this type" |348| `get_public_api_surface` | "What's the public API of this library?" |349| `find_breaking_changes` | "Will this break consumers?" |350| `get_di_registrations` | "How is this wired up?" / "What's the DI lifetime?" |351| `get_project_dependencies` | "How do projects relate?" |352| `get_nuget_dependencies` | "What packages does this project use?" |353| `find_reflection_usage` | "Is this used dynamically?" |354| `find_attribute_usages` | "What's marked [Obsolete]?" / "Find all [Authorize] controllers" |355| `find_obsolete_usage` | "What deprecations do we still use?" |356| `get_diagnostics` | "Any compiler errors?" / "Show warnings" |357| `get_code_fixes` | "How do I fix this warning?" |358| `get_code_actions` | "What refactorings are available here?" |359| `apply_code_action` | "Apply this refactoring" / "Extract method" |360| `rename_symbol` | "Rename this symbol everywhere" / "Change this name across the solution" |361| `change_signature` | "Add/remove/reorder a parameter and fix all the callers" |362| `resolve_stack_trace` | "Where did this exception come from?" / "Resolve this stack trace" |363| `find_unused_symbols` | "Any dead code?" |364| `get_complexity_metrics` | "Which methods are too complex?" / "What should I refactor first?" (`metric: "cognitive"`) / "How deeply nested is this?" |365| `find_naming_violations` | "Check naming conventions" |366| `find_async_violations` | "Are there async bugs?" / "Find sync-over-async" |367| `find_disposable_misuse` | "Are there resource leaks?" / "Find missing `using`" |368| `get_exception_flow` | "What can escape this method?" / "Where does this exception get caught?" |369| `find_throw_sites` | "Where is this exception type thrown?" |370| `find_catch_blocks` | "Who catches this?" / "What's swallowing exceptions?" |371| `find_large_classes` | "Find classes that need splitting" |372| `find_god_objects` | "Which classes are doing too much?" |373| `find_circular_dependencies` | "Any circular dependencies?" |374| `check_architecture` | "Is anything violating our layering?" / "Does Domain reference Infrastructure?" |375| `get_project_health` | "How is this project doing?" / "Top hotspots across all dimensions" |376| `get_source_generators` | "What source generators are active?" |377| `get_generated_code` | "Show generated code" |378| `inspect_external_assembly` | "What does this NuGet package expose?" / "Show me the API of X assembly" |379| `peek_il` | "Show IL for 380381…(truncated)