FSharp.Refactor MCP sunucusu
Commit 13 Kas 2026F# kodunun niyetini, performansını ve idiom kullanımını iyileştirmek için işlevsel refactoring önerileri bulan ve uygulayan çevrimdışı bir analiz aracı.
Ayrıca bilinen adı Thorium/fsharp-refactor, fsharp-refactor
- Yıldızlar
- 16
- Araçlar
- —
- İndirmeler, 30 gün
- —
- En son sürüm
- 0.8.45
- Son commit
- 13 Kas 2026
- Lisans
- Unlicense
FSharp.Refactor MCP nedir?
FSharp.Refactor bir MCP sunucusudur. F# kodunun niyetini, performansını ve idiom kullanımını iyileştirmek için işlevsel refactoring önerileri bulan ve uygulayan çevrimdışı bir analiz aracı.
Özet, projenin README dosyasından oluşturuldu
FSharp.Refactor MCP sunucusu hakkında
F# geliştiricileri, işlevsel refactoring önerileri sunan çevrimdışı bir analiz aracı sayesinde kod niyetini, performansını ve idiom kullanımını iyileştirebilirler. Araç; internet bağlantısına veya yapay zeka işlemine ihtiyaç duymadan iyileştirme fırsatlarını belirlemek için mevcut projeleri, kaynak dosyalarını veya scriptleri okuyarak çalışır. Toplu olarak çalıştırılabilen bir komut satırı aracı olarak işlev görür veya özel eklentiler aracılığıyla VS Code ve Visual Studio gibi editörlere entegre edilebilir.
Yazılımlar, değişiklikleri disk üzerine yazılmadan önce önizleme yapmaya olanak tanıyan dry run özelliğini destekleyen fsharp-refactor dotnet aracını kullanarak uygulayabilirler. Sistem güvenli olacak şekilde tasarlanmıştır; halihazırda derlenemeyen kod üzerinde çalışmayı reddeder ve uygulanan herhangi bir düzeltme yeni bir hataya yol açarsa işlemi durdurur. --api-changes bayrağı ile genel metod güncellemelerini yönetmeye veya --categories bayrağı ile doğruluk veya performans gibi belirli iyileştirme türlerini hedeflemeye olanak tanıyarak hassas kontrol sağlar.
Neler yapabilirsiniz
- 1F# projelerinin toplu refactoring işlemiBir geliştirici, komut satırını kullanarak tüm bir çözüm (solution) genelinde birden fazla işlevsel iyileştirmeyi uygulayabilir.
- 2Kod değişikliklerini güvenli bir şekilde önizlemeKaynak dosyalarınızı gerçekten değiştirmeden, aracın yapmayı planladığı her değişiketi listeleyebilirsiniz.
- 3Baz çizgileri (baselines) ile teknik borç yönetimiGeliştiriciler mevcut sorunları görmezden gelebilir ve yalnızca yeni refactoring fırsatlarını ortaya çıkarmaya odaklanabilirler.
Projenin README dosyasından alınmıştır. Her kullanım durumu, oradaki bir alıntı ile kontrol edilir.
Otomatik kontroller
Çalıştırıldı 13 Kas 2026Tam rapor →Henüz doğrulanmadı. Bir sunucunun 80 puana, ciddi bir bulgu olmamasına ve kontrol edebilmemiz için yeterli veriye ihtiyacı vardır. Kontroller nasıl çalışır · Daha fazla puan için nelerin düzeltileceği
- Kaynak deposu bağlantılı ve erişilebilirGeçti
- Açık kaynak lisansı (OSI)Geçti
- Son bir yıl içinde commit veya sürüm mevcutGeçti
- Paket adı popüler bir pakete yakın değilKontrol edilmedi
- Uzak uç nokta HTTPS kullanıyorKontrol edilmedi
- Yıkıcı araçlar etiketlenmişKontrol edilmedi
- Modele yönelik gizli talimat yokGeçti
- Pakette veya bağımlılıklarında kritik uyarı yokKontrol edilmedi
FSharp.Refactor
Let's remove parentheses from the internet
Functional refactoring suggestions for F#. The tool clarifies the (agent-generated or old) code's intent, reduces non-alphanumeric noise, and uses built-in .NET libraries more efficiently.
- light bulb quick fixes in your editor
- command-line tool that applies them in bulk.
Suggestions are Hint severity: they mark an opportunity, not a defect, and
never gate your build. The priority rules, which point at
likely defects, show as warnings in the editor.
This project is the big brother of CSharp.Refactor
Using it
The tool is fully offline: AI has been used to make it, but it doesn't use AI.
Quick start
Nothing to configure - the tool reads your project, reports what it would change, and only edits when you tell it to. The tool runs on the .NET 10 SDK; the projects it works on can target any framework:
dotnet tool install --global fsharp-refactor
fsharp-refactor Your.fsproj --dry-run --api-changes
That prints every fix it would make, with file and position, and writes nothing. When the list looks right, drop the flag to apply them:
fsharp-refactor Your.fsproj --api-changes
It refuses a compilation that does not already build, and fails loudly if applying ever introduces an error.
If you don't want change public methods / API, drop --api-changes:
fsharp-refactor Your.fsproj
For light bulbs while you type, see VS Code / Ionide and Visual Studio IDE-plugin instructions below.
For agentic scenarios the dotnet tool supports MCP.
Using the tool: Point it at whatever you have - the kind is read off the path:
Your.fsproj |
one project |
Thing.fs |
one source file - its project is found and analysed, but only that file is edited |
build.fsx |
one script - no MSBuild step at all, so it starts instantly. A script with unresolvable references is not refused: the syntactic rules still run over it (an fsi-run script may reference things only the run supplies) |
Your.sln, Your.slnx |
every F# project the solution lists |
src/ |
the solution in that directory, or the projects beneath it |
"src/**/*.fsproj" |
everything the glob matches |
What it changes
Examples:
| Before | After | |
|---|---|---|
| Correctness | let s = new FileStream(p, m) |
use s = new FileStream(p, m) |
| Correctness | raise (Exception "boom") |
failwith "boom" |
| Performance | s.Contains "x" |
s.Contains 'x' |
| Performance | xs |> Seq.toList |> List.map f |
xs |> Seq.map f |> Seq.toList |
| Idiom | match b with | true -> 1 | false -> 0 |
if b then 1 else 0 |
| Redundancy | new StringBuilder() |
StringBuilder() |
| Redundancy | [<SerializableAttribute>] |
[<Serializable>] |
| Diagnostics | failwith "Error" |
failwith $"Error, calling f with x: {x}" |
A spread of what the rules do - the full list is in Refactorings:
Editor and CI setup
The analyzers ship as FSharp.Refactor.Analyzers.
The package is a development dependency: it only produces hints and quick
fixes - nothing from it flows into your compiled output.
NOTE: EVEN WHEN ADDING A NUGET REFERENCE, THIS ANALYSER WILL NOT COME TO OUTPUT PATH (BIN) AND WILL NOT BE PART OF YOUR PROJECT.
VS Code / Ionide
Easiest: install the FSharp.Refactor VS Code extension
- it bundles the analyzers and (with your consent) wires them into Ionide's settings globally, so every F# project gets the hints with no per-project setup.
Or wire a single project by hand: reference the package from the project you want analyzed:
<PackageReference Include="FSharp.Refactor.Analyzers" Version="*" PrivateAssets="all" />
then point Ionide at the restored analyzers in .vscode/settings.json:
{
"FSharp.enableAnalyzers": true,
"FSharp.analyzersPath": [
"~/.nuget/packages/fsharp.refactor.analyzers/<version>/analyzers/dotnet/fs"
]
}
replacing <version> with the version restore actually picked (the NuGet
cache always keys folders by version, so this one path cannot float; check
with ls ~/.nuget/packages/fsharp.refactor.analyzers/). On Windows the
cache lives under %USERPROFILE%\.nuget\packages. Suggestions
appear as Hint-severity diagnostics with a light-bulb one-click fix.
Ionide bundles its own FsAutoComplete; to run another one, dotnet tool install -g fsautocomplete and set FSharp.fsac.netCoreDllPath to its
fsautocomplete.dll (the extension above offers to do both).
Supported hosts
FSharp.Analyzers.SDK loads an analyzer assembly only when it was built
against the SDK's own major.minor version. The package ships one build,
in analyzers/dotnet/fs, for SDK 0.41 on .NET 10:
| SDK | Hosts |
|---|---|
| 0.41 | FsAutoComplete 0.86 and later (Ionide 8), fsharp-analyzers 0.41 |
An older host (Ionide 7, FsAutoComplete 0.85 and earlier, fsharp-analyzers
0.37 and earlier) loads nothing from this package; FSharp.Refactor.Analyzers
0.8.51 is the last release with builds for SDK 0.37 and 0.35.
If no suggestions appear, open Output -> "F# Language Service": it names the analyzer dlls it scanned and how many analyzers loaded, and a version-pairing skip is spelt out there rather than surfacing in the editor.
Visual Studio 2022-2026
Install the
FSharp.Refactor Visual Studio extension
(user-facing overview):
squiggles and Ctrl+. quick fixes in full Visual Studio, analyzed
through an FsAutoComplete sidecar the extension bundles.
CLI / CI
dotnet tool install --global fsharp-analyzers --version "0.41.*"
fsharp-analyzers --project src/YourProject.fsproj --analyzers-path ~/.nuget/packages/fsharp.refactor.analyzers/<version>/analyzers/dotnet/fs --code-root . --report analysis.sarif
fsharp-analyzers is the analyzer HOST, and it is the dotnet tool you
install, at a version from the supported hosts table:
an unpinned install takes the newest, which may be ahead of the package.
This package is not a tool: it is a library of analyzer assemblies
the host loads, so it is passed as a directory rather than installed. That
directory sits in the NuGet cache because an analyzer package deliberately
has no lib/ folder, declares no dependencies and is marked a development
dependency - a PackageReference therefore puts nothing in your bin, and
after a restore, the cache is where the assemblies live. --analyzers-path
takes any folder holding them and searches it recursively.
The CLI only REPORTS; it never edits your files, which is what you want in
CI (SARIF output works in GitHub code scanning). To apply the fixes, use our
own tool below. Individual rules can be turned off per repository with a
fsharprefactor.json - see Configuration below.
Applying fixes from the command line
The fsharp-refactor
dotnet tool applies the quick fixes directly to your files:
dotnet tool install --global fsharp-refactor
fsharp-refactor Your.fsproj [--dry-run] [--codes FR0002,FR0031] [--api-changes] [--jobs 4] [--max-passes 5]
(or from this repository:
dotnet run --project src/FSharp.Refactor.Tool -c Release -- Your.fsproj ...)
For a project it takes the exact compiler arguments from MSBuild; for a script FCS resolves the references itself and MSBuild never runs. Either way it then runs every analyzer, applies non-overlapping fixes bottom-up, and re-analyzes until a pass applies nothing - a fix can enable further fixes. It refuses a compilation that already has errors, and fails loudly if applying ever introduces one.
Warnings as errors: the project's own settings decide, and the tool never
overrides them - its verification is the build your CI runs. The
in-memory typecheck takes the compiler's own arguments, so --warnaserror
and <WarningsAsErrors> turn an F# warning a fix raises into an error the
pass rolls back. A C# project that references the F# one and turns an
analyzer warning into an error (error CA1859, error IDE0005) fails the
verification build as code, not as tooling noise, and the run says which
setting to change if you want such fixes anyway
(<WarningsNotAsErrors>CA1859</WarningsNotAsErrors>, or
dotnet_diagnostic.CA1859.severity = suggestion). <NoWarn> works too,
but silences the analyzer for everyone.
| Flag | |
|---|---|
--dry-run |
Report only: lists every fix it would make, with file and position, and writes nothing. Rewriting is never implicit - drop the flag to let it edit. |
--codes FR0002,FR0031 |
Restrict the run to chosen rules; the other analyzers do not run. |
--categories <list> |
Restrict the run to kinds of rule: correctness, performance, idiom, cosmetic. Combined with --codes it narrows further, in either order. The analyzers of the rules left out do not run at all. A filtered run can stop at a point a full run passes through: FR0157 (idiom) leaves a ` |
--jobs <n> |
Typecheck that many files at once (default 4, clamped to 2-4 by core count). Trades CPU for wall clock; because FCS reuses each file's prefix within one incremental build, the gain peaks around 4 and reverses if pushed higher. --jobs 1 is the sequential sweep. |
--framework <tfm> |
Analyse against this target framework instead of the narrowest one - see below. |
--max-passes <n> |
Fix-then-reanalyze iterations (default 5). A pass after the first sweeps only the files the previous pass changed and those compiled after the first of them. |
--help |
The same list, from the tool itself (-h and /? also work). |
--api-changes |
Also apply the cross-file fixes described below. |
--no-if-defs |
Never emit #if/#else/#endif pairs for capability fixes on multi-targeted projects (see below). The fixes stay plain, and any the legacy frameworks reject are put back by the final build check. |
--report <file> |
Write every finding the run surfaced to a file; the extension picks the format. .sarif (or .json) is SARIF 2.1.0 - what GitHub code scanning renders as inline PR annotations; .html is a self-contained page with per-rule grouping, highlighted source, before/after fixes and category filters; .csv is one row per finding for a spreadsheet. Pairs naturally with --dry-run for a CI lint gate. See CI setup below. A many-target run rewrites the report after every target, so a crash or a Ctrl-C an hour in still leaves what was found. Pointing the tool at a workspace of checkouts - a directory with no solution of its own whose sub-directories carry theirs, C:git say - analyses each checkout on its own (its solutions honoured, its fsharprefactor.json applied) into the one report, with paths relative to the workspace. |
--baseline <sarif> |
The ratchet: findings whose fingerprints appear in this earlier --report output are neither reported nor fixed - only what is NEW surfaces. Fingerprints hash the rule code, file name and normalized surrounding source, so they survive line shifts, other edits in the file, and different checkouts. Triage once, ratchet forever. |
--fail-on-findings |
Exit 3 when any finding survives the filters - the hard CI gate. The full exit contract: 0 clean, 1 analysis or apply failure, 2 usage error, 3 findings (only with this flag). |
| `--notes [on | off |
--notes only |
A review pass: every rule runs, only the findings WITHOUT a fix are listed inline, and nothing is written. That is the advisory rules (an em dash in Rules.md's fix column) plus the cases where a fixing rule can only advise. fsharp-refactor src/Your.fsproj --notes only --report notes.html writes them as a page. |
--format json |
Machine-readable stdout: progress prose moves to stderr and the run's findings leave as one JSON document (code, severity, fixable, position, message, fingerprint, source snippet). The default output stays human-readable. |
--rules |
Print the rule catalog - code, category, enabled-by-default (honors --format json). |
--create-config |
Write a fsharprefactor.json of this build's defaults - every rule, every run-level key, one comment each - into the current directory, or into <what> when that is a directory. It changes nothing until you edit it, and never overwrites an existing config. |
--mcp |
Serve the tool as an MCP server over stdio (newline-delimited JSON-RPC, no extra dependencies): tools analyze (target, codes/categories, parseOnly, apply) and list_rules. One warm typechecker lives across calls, so the first analyze pays the reference parse and the rest answer from a hot cache - the economics agent loops need. The server works inside one directory: fsharp-refactor <directory> --mcp, or the directory it is started in. A call's target must lie inside it (a relative target is relative to it), and an apply writes nothing outside it: a compilation whose fixes reach a file outside, such as a linked source or a #load, is put back whole and reported. --mcp --dry-run is a server that applies nothing; --define and --jobs hold for every call, and any other option is refused, since it would be ignored. Analysing a project still builds it, which runs its MSBuild logic, so the directory should be a tree you trust. |
--parse-only |
For a codebase that cannot COMPILE on this machine - a type provider needing its database, references that cannot restore. No MSBuild, no reference resolution: sources come straight from the fsproj's <Compile> items, and only the analyzers that never consult the typechecker run, about a third of them (the typed rules are excluded outright, not trusted to self-silence). It is not a substitute for a real run, and what survives is skewed the wrong way: measured across the corpus, roughly a quarter of the correctness rules and a quarter of the performance rules still fire, against three quarters of the cosmetic ones - so a clean --parse-only says very little, and says least about the things worth knowing. Findings lost run to 38% on a typed-heavy codebase and under 10% on one the cosmetic rules dominate. Safety shifts accordingly: instead of a build, the gate is that a pass must not RAISE the compilation's error count over its baseline, and the usual parse-level protections (comment guard, overlap holds) still apply. Limitations: #if branches behind conditional or computed DefineConstants are not parsed, wildcard <Compile> globs are refused, and multi-framework passes collapse to one. Review the diff - the all-frameworks build arbiter is exactly what this mode does without. |
CI setup (SARIF)
A dry run plus --report gives CI the full findings list without
touching a file; uploading the SARIF turns each finding into an inline
annotation on the pull request. Paths in the report are relative to the
repository that holds the target (the nearest .git above it), so the
tool may run from anywhere; each result carries the rule's description
and help link, the finding's own text with three lines of context, the
fix as a SARIF fixes entry (code scanning renders it as a suggested
change), a stable fingerprint, and the run's invocation record:
refactor-lint:
runs-on: ubuntu-latest
permissions:
security-events: write # required by upload-sarif
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x # the tool runs on .NET 10
- run: dotnet tool install --global fsharp-refactor
- run: fsharp-refactor src/Your.fsproj --dry-run --report findings.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: findings.sarif
category: fsharp-refactor
Two honest notes. First, a dry run exits 0 whether or not it found anything - findings are hints, not errors, and only a broken build or a crashed run fails the step. Annotations therefore inform without blocking; to make findings HARD-fail the job, add an explicit check:
jq -e '.runs[0].results | length == 0' findings.sarif
Second, scope the gate before turning it on: --categories correctness,performance keeps the signal defensible on a shared
repository (see Someone else's codebase),
and a fsharprefactor.json turns off anything the team has decided
against. The suppression comments described under
Configuration silence individual findings at the
line, for both the gate and editors, in one place.
Running alongside other analyzer packages
The fsharp-analyzers host loads every analyzer assembly it is
pointed at, so one invocation - and one FCS typecheck, the expensive
part - can run this package together with others built on the same
SDK, all findings landing in one report. With
G-Research's analyzers,
at a release on the same FSharp.Analyzers.SDK minor as the folder you
pass from this package (see Supported hosts):
fsharp-analyzers --project src/Your.fsproj \
--analyzers-path ~/.nuget/packages/fsharp.refactor.analyzers/<version>/analyzers/dotnet/fs \
~/.nuget/packages/g-research.fsharp.analyzers/<version>/analyzers/dotnet/fs \
--code-root . --report findings.sarif
Rule codes are disjoint (FR* here, GRA* there), and the
fsharpanalyzer: ignore-line suppression comments work for their codes
too - the machinery lives in the shared SDK, not in any one package.
The SDK is strict about version agreement between the host and every
analyzer assembly it loads; a mismatched assembly is skipped with a log
line, and the CLI exits non-zero for it. The day the two packages pin
different SDK minors, fall back to separate CI jobs - each producing
its own SARIF and uploading under its own category: (code scanning
keeps the streams apart) - at the cost of typechecking the project
once per job. Applying fixes stays this package's own tool either way:
fsharp-refactor applies only its own rules, and report-only
analyzers have nothing to collide with.
Someone else's codebase
Every rule is one of four kinds, shown in the last column of Refactorings:
| Kind | Count | |
|---|---|---|
correctness |
The code does something other than what it looks like it does: a race, a swallowed exception, a disposable that leaks, a comparison that never holds | 69 |
performance |
Correct, but doing work it need not: allocations that need not happen, repeated work, a scan where a lookup would do | 41 |
idiom |
The same behaviour written the way F# writes it. Worth doing, and worth agreeing on first - it is a matter of house style as much as anything | 57 |
cosmetic |
The punctuation and spelling of code. Real cleanups, and nobody's idea of a welcome pull request from a stranger | 17 |
Every version's notes are in CHANGELOG.md; the NuGet packages carry the last six.
Thirteen rules carry a priority flag on top of their category - the
likely defects and security holes too costly to hold back: FR0020,
FR0028, FR0032, FR0046, FR0047, FR0048, FR0061, FR0063, FR0065, FR0066,
FR0122, FR0126 and FR0127. Swallowed exceptions (FR0055) and public
mutables (FR0062) stay plain correctness: bad habits more often than
live defects. Their notes print
without --notes, editors show them as warnings, and SARIF carries them
at warning level. Rules.md marks them in its Priority column.
This matters when the repository is not yours. Running everything over a project you do not maintain and opening a pull request from the result is a good way to waste an afternoon of someone's life: no maintainer wants "removed an empty attribute argument list" across two hundred files, and a diff that size buries anything that mattered. A disposable that is never disposed is a different conversation entirely.
So for a codebase you are a guest in:
fsharp-refactor Their.fsproj --categories correctness,performance --dry-run
That is the set that earns its review time. For your own code, run the lot.
The category claims are measured, not assumed:
dotnet run -c Release --project benchmarks/PerfClaims
re-checks them on your machine, on BOTH axes - wall clock and allocation
(GC pressure is performance too). The contract: a performance rule's
rewrite must win on at least one axis, and an idiom rule's must hold
parity. FR0050 once emitted Seq.sum for a list - ~50%% slower than the
mutable loop it replaced, plus an enumerator allocation - which is how
the benchmark file, the rule's module-resolved output, and its idiom
recategorization all came to exist.
Multi-targeted projects
Nothing extra to do: a multi-targeted project is worked through framework by framework, narrowest first.
Capability fixes get both worlds - using the project's own vocabulary.
When a project also targets frameworks older than an overload (net4x,
netstandard2.0), the tool reads the fsproj's DefineConstants and looks
for a framework-shaped constant - NETSTANDARD21, NET8, digits
required - whose '$(TargetFramework)' == '...' conditions cover only
the modern frameworks. Names denoting a legacy framework (NET48,
NET451, NETSTANDARD2_0) are refused outright whatever their
conditions say: the SDK defines exactly those constants during the
legacy compilations themselves, where no fsproj parse can see them. Flavor names sharing the same condition
(SQLProvider defines MICROSOFTSQL right beside NETSTANDARD21) are
passed over: their meaning is the flavor, and a sibling project
compiling the same shared file may define them on legacy frameworks
too. If a constant qualifies and the file already uses conditional
compilation, FR0038 and FR0106 emit a pair instead of a fix the legacy
half cannot compile:
#if NETSTANDARD21
let orderNumber (s: string) = Int32.Parse(s.AsSpan(6, 5))
#else
let orderNumber (s: string) = Int32.Parse(s.Substring(6, 5))
FSharp.Refactor MCP soruları
Bu araç yapay zeka kullanıyor mu?
Hayır, araç tamamen çevrimdışıdır. Oluşturulurken yapay zekadan yararlanılmış olsa da aracın kendisi yapay zeka kullanmaz.
Araç derleme (build) sürecimi bozar mı?
Hayır, öneriler 'Hint' (İpucu) seviyesinde işaretlenir ve derleme sürecinizi engellemez. Ayrıca araç, halihazırda derlenemeyen projelerde çalışmayı reddeder ve düzeltmeler uygulandığında hata oluşursa işlemi durdurur.
Bunu bir CI/CD hattında kullanabilir miyim?
Evet, bulguları GitHub kod taraması için SARIF formatında raporlamak üzere CLI kullanabilir veya katı bir CI engeli oluşturmak için --fail-on-findings bayrağını kullanabilirsiniz.