refactor-mcp
RefactorMCP exposes Roslyn-based refactoring tools for C# over three entry points: a command line interface, a resident daemon that keeps a solution loaded, and a Model Context Protocol server.
RefactorMCP
RefactorMCP exposes Roslyn-based refactoring tools for C# over three entry points: a command line interface, a resident daemon that keeps a solution loaded, and a Model Context Protocol server.
All three dispatch through the same tool dispatcher and session, so a tool behaves the same however it is called.
Install
RefactorMCP is published to NuGet as a .NET tool. It needs a .NET SDK (9.0 or later) on the machine, since solutions are loaded through MSBuild.
dotnet tool install --global RefactorMCP
This puts refactor-mcp on the path. To serve it to an MCP client, register
the command refactor-mcp with the argument mcp, for example:
{
"mcpServers": {
"refactor-mcp": { "command": "refactor-mcp", "args": ["mcp"] }
}
}
The examples below run from source with dotnet run --project RefactorMCP.ConsoleApp --; with the tool installed, refactor-mcp takes its
place.
Usage
# Run a refactoring. Tool commands go to the daemon for that solution, which is
# started on demand, so only the first call pays the MSBuild load.
dotnet run --project RefactorMCP.ConsoleApp -- extract-method \
--solution ./RefactorMCP.sln --file ./src/Foo.cs \
--selection-range 10:5-20:6 --method-name ComputeTotal
# The same call with JSON parameters, for scripting.
dotnet run --project RefactorMCP.ConsoleApp -- --json extract-method \
'{"solutionPath":"./RefactorMCP.sln","filePath":"./src/Foo.cs","selectionRange":"10:5-20:6","methodName":"ComputeTotal"}'
# Discover the tools, and what each one takes.
dotnet run --project RefactorMCP.ConsoleApp -- list-tools --verbose
dotnet run --project RefactorMCP.ConsoleApp -- extract-method --help
# Keep a solution loaded and serve calls from it.
dotnet run --project RefactorMCP.ConsoleApp -- serve --solution ./RefactorMCP.sln
dotnet run --project RefactorMCP.ConsoleApp -- status
dotnet run --project RefactorMCP.ConsoleApp -- stop --all
# Serve the tools over MCP on stdio.
dotnet run --project RefactorMCP.ConsoleApp -- mcp
Options are named after the tool's parameters (--method-names for
methodNames), a trailing Path may be dropped (--file for filePath), and
anything left over is positional in parameter order. --no-daemon runs a call
in the calling process instead. The daemon stops after ten minutes without a
request, or when asked to with stop; serve --idle-timeout changes that
(30s, 1h, or 0 to never expire).
A daemon follows the files it has loaded, so edits made in an editor are picked up rather than refactored on top of stale text: an edited file is replaced in place, and a change to a project file reloads the solution before the next call.
Once built, the executable can be called directly
(RefactorMCP.ConsoleApp/bin/Debug/net9.0/RefactorMCP.ConsoleApp), which skips
the build check dotnet run performs.
For usage examples see EXAMPLES.md.
Tools
list-tools --verbose prints every tool with what it takes; the groups below
are a map of what is there. Each tool refuses, and leaves the files unchanged,
when it cannot keep the code's behaviour.
Solution and session – load-solution, unload-solution,
clear-solution-cache, list-tools, version. Solutions may be .sln or
.slnx files.
Navigation and analysis – find-references, find-implementations,
find-overrides, find-callers, analyze-refactoring-opportunities,
list-class-lengths.
Renaming and signatures – rename-symbol, change-signature,
add-parameter-default-value, remove-unused-parameter, inline-parameter,
introduce-parameter, introduce-parameter-object, preserve-whole-object,
parameterise-method, replace-parameter-with-explicit-methods,
redirect-calls-with-constant-argument, use-named-arguments,
change-accessibility, change-type, change-return-type, use-interface,
introduce-generic-type-parameter.
Extracting and inlining – extract-method, inline-method,
introduce-variable, inline-local-variable, introduce-field,
inline-field, introduce-constant, inline-constant,
replace-expression-with-field, replace-temp-with-query,
split-temporary-variable, split-declaration-and-assignment,
join-declaration-and-assignment, convert-local-to-field,
introduce-type-alias, inline-type-alias.
Moving – move-member, move-multiple-methods, make-static-then-move,
make-method-static, make-method-instance, move-to-separate-file,
move-type-to-namespace, move-member-to-partial-file, make-type-partial,
merge-partial-declarations, rename-file-to-match-type,
sync-namespace-with-folder, convert-to-file-scoped-namespace,
convert-to-block-namespace, cleanup-usings, create-type.
Classes and hierarchies – extract-class, inline-class,
extract-interface, extract-superclass, collapse-hierarchy,
change-base-type, pull-up-field, pull-up-method,
pull-up-constructor-body, push-down-field, push-down-method,
hide-delegate, remove-middle-man, replace-inheritance-with-delegation,
replace-delegation-with-inheritance, add-delegating-member,
remove-delegating-member, replace-base-uses-with-field,
replace-field-uses-with-base, introduce-interface-for-dependency,
inject-constructor-dependency,
initialize-field-from-constructor-parameter,
replace-constructor-with-factory-method,
replace-method-with-method-object.
Fields and properties – encapsulate-field, encapsulate-collection,
make-field-readonly, transform-setter-to-init, convert-to-auto-property,
convert-auto-property-to-backing-field, convert-method-to-property,
convert-property-to-methods.
Conditionals – invert-if, invert-boolean, split-if,
merge-nested-if, merge-sibling-ifs, remove-redundant-else,
decompose-conditional, consolidate-conditional-expression,
consolidate-duplicate-conditional-fragments,
replace-nested-conditional-with-guard-clauses, convert-if-chain-to-switch,
convert-if-to-switch-expression, convert-switch-statement-to-expression,
convert-switch-expression-to-statement, use-pattern-matching,
separate-query-from-modifier, feature-flag-refactor.
Language conversions – convert-to-expression-body,
convert-to-block-body, convert-to-primary-constructor,
convert-primary-constructor-to-constructor, convert-class-to-record,
convert-record-to-class, convert-anonymous-type-to-class,
convert-tuple-to-named-type, convert-to-extension-method,
convert-extension-method-to-static, convert-local-function-to-method,
convert-method-to-local-function, convert-lambda-to-method-group,
convert-method-group-to-lambda, convert-for-to-foreach,
convert-foreach-to-for, convert-foreach-to-linq, convert-linq-to-foreach,
convert-concatenation-to-interpolation, introduce-using-declaration,
make-method-async, convert-to-async.
Generators – add-null-checks, convert-to-nullable-aware,
introduce-null-object, extract-decorator, create-adapter,
add-observer, replace-array-with-object,
replace-conditional-with-polymorphism, replace-error-code-with-exception,
replace-type-code-with-enum, replace-type-code-with-subclasses.
Deleting – safe-delete-member, safe-delete-type, safe-delete-local.
Several of these are composites: they run a sequence of the smaller tools as a recipe, so each step is checked the way the tool on its own would be.
The MCP server also serves two resources: metrics:// for code metrics of a
file, and summary:// for a file with its method bodies omitted.
Refactoring catalog
Catalog/ specifies each refactoring as behaviour, with
before and after fixtures for every case, grouped into primitives, composites
and generators. The test suite runs every case against the tools, and the
catalog site renders them as
diffs.
State and logging
Metrics caches are kept under ~/.refactor-mcp, one folder per solution, so
nothing is written into the repository being refactored. Set
REFACTOR_MCP_HOME to use another folder.
Tool calls are not logged unless REFACTOR_MCP_LOG is set, either to a file
path or to 1 to log into the solution's folder under ~/.refactor-mcp.
Repository layout
| Path | Contents |
|---|---|
RefactorMCP.ConsoleApp/ |
The tools, the CLI, the daemon and the MCP server |
RefactorMCP.Tests/ |
xUnit tests, including the catalog runner |
Catalog/ |
The refactoring specification and its fixtures |
site/ |
The generator for the catalog site |
Examples/, Demos/ |
Worked examples and an end-to-end e-commerce demo |
vscode-extension/ |
A VS Code extension that calls the command line |
Contributing
- Run
dotnet testto ensure all tests pass. The catalog alone runs withdotnet test --filter "FullyQualifiedName~CatalogTests". - A new refactoring starts as a catalog case; see Catalog/README.md.
- Format the code with
dotnet formatbefore opening a pull request.
Releasing
Pushing a tag named v<version> (for example v0.2.0) runs the Publish
workflow: it builds and tests at that version, pushes the package to NuGet
using the NUGET_API_KEY repository secret, and creates a GitHub release with
the package attached.
To try the package locally first:
dotnet pack RefactorMCP.ConsoleApp -c Release -o artifacts
dotnet tool install --global RefactorMCP --add-source ./artifacts
License
Licensed under the Mozilla Public License 2.0.