Refactoring and style
VisualMCP can change your code, not just read it. This guide explains what makes those changes different from a find/replace, which tools preview before writing, and how to run the style engine without surprises.
What “semantic refactoring” means here
- Resolves the symbol through Roslyn — the exact declaration, overload or override you named.
- Rewrites the affected references across the solution — call sites, interface implementations, usings, constructor calls.
- Refuses rather than guesses when the operation would change meaning or cannot be expressed: a method group in
nameof, an object initializer, a base class that uses the member. - Records the write in the solution's edit history, with the previous text, so it can be audited and reverted.
A rename through apply_rename updates the declaration and
every real reference. A find/replace renames the same text in
comments, strings and unrelated types — and usually misses the
implicit interface implementations.
The safety ladder
| Stage | What it gives you |
|---|---|
| Read-only previews | preview_rename, extract_method_candidates, inspect_code — see the shape before touching anything. |
| Dry run | Most write tools accept dryRun: a before/after pair and a line diff per edit, nothing written. |
| Compiler gate | Risky operations compile the project after the change and refuse it if it would add errors. |
| Edit history | edit_history lists every write; revert_edit(id) restores the file exactly. |
| Your own safety net | A branch and a clean git_status are still the cheapest rollback. |
The catalogue by task
Rename and move
apply_rename / preview_rename,
rename_namespace, move_type,
move_to_namespace, move_static_members,
move_instance_method. Renames are semantic: parameter
names, backing fields, nameof, XML docs and interface
members follow.
Extract and inline
extract_method, extract_class,
extract_superclass, extract_interface,
inline_symbol, introduce_variable.
extract_method handles a contiguous statement block; a
block that awaits becomes an async Task method, and
blocks that cannot be proven equivalent — yield,
break, a mid-block return — are refused.
Signatures and types
change_signature is the one to reach for when a parameter
is added, removed or reordered: it returns every call site as a
before/after pair and is gated on the compiler.
change_type, change_nullability,
use_base_type, convert_to_record,
convert_to_primary_constructor,
constructor_to_factory, copy_type.
Inheritance and membership
pull_members_up, push_members_down,
generate_members, implement_inpc,
make_static, type_to_partial. A selection
that would not compile is refused with the name of the offending
member, not applied half-way.
Complexity
reduce_complexity is the bridge between analysis and
refactoring: it plans the moves that remove decisions from a method —
extract a nested block, name a tangled boolean, flatten a tail
if into a guard clause — applies them one at a time,
re-measures, and stops at the target. fix_complexity is
the same reducer without having to name a method: it ranks the whole
solution and lowers the worst methods.
The style engine
simplify_code applies the IDE's mechanical suggestions —
the quick-fix popup equivalents — without an IDE. It is a rewrite
engine over Roslyn syntax trees, not a formatter: collection
expressions, null-coalescing, pattern matching, expression-bodied
members, switch expressions, LINQ chain simplifications, dead code and
unused members, ^ and ranges, nameof,
nullability-directive cleanup.
simplify_code scope=project projectName=MyApp rules=IDE0028,IDE0041
- Scope is one document, one project or the solution; rules is a comma-separated list, or all supported rules when omitted.
dryRun=trueis the default: the before/after pair per edit and a line diff, nothing written.- On write, each changed document is compiled; a document whose change would add errors is reported under
refusedand left untouched. - Team preferences are option-driven: the rules that depend on a style choice act only when your
.editorconfigsets the option, and the result names the option it waited for. - Visual Basic has its own layer,
simplify_vb_code, with the same shape and its own rule set.
Inspections and code fixes
Two tools, two directions. inspect_code runs the built-in
inspection packs — the house rules, Roslynator, .NET analyzers,
AsyncFixer, Meziantou, StyleCop — over every project, with no analyzer
package referenced by your solution. apply_code_fix
re-runs the packs and executes the analyzer's own
code fix provider: preview by default, FixAll supported, and rules
that ship no fix are refused with that message rather than a guessed
edit.
inspect_code packs=all minSeverity=warning ids=RCS*,CA18*
apply_code_fix diagnosticId=RCS1060 filePath=Services/OrderService.cs line=42
What “safe” does not mean
- The compiler gate proves compilation, not behaviour. A rewrite can compile and still change meaning.
- The style gate is per document: one bad rewrite refuses that document's changes; other documents in the same run still apply.
- Refactorings refuse instead of approximating. A refusal names what blocked it; the next step is usually to prepare the code, then retry.
A safe working loop
git_status— know what is already modified.- Understand the target:
find_symbol,read_symbol,find_references. - Preview:
preview_rename,simplify_codewithdryRun=true. - Write one operation at a time.
build_projectandrun_tests.inspect_codeto catch what the compiler does not.- If something is wrong:
edit_history, thenrevert_edit(id).