Skip to content

feat!: proxy Microsoft's almcp, narrow ALCops MCP to code fixes, require BC DevTools 18.0 - #20

Merged
Arthurvdv merged 16 commits into
mainfrom
feat/almcp-proxy
Sep 12, 2026
Merged

Arthurvdv merged 16 commits into
mainfrom
feat/almcp-proxy

Conversation

@Arthurvdv

@Arthurvdv Arthurvdv commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

Reuse Microsoft's almcp for everything it already does, and narrow ALCops MCP to the two things it doesn't do at all.

almcp has no code-fix capability — its LSP mode explicitly advertises CodeActionProvider = false, and it exposes no MCP tool for it — and no way to enumerate rules. Everything else (compiling, diagnostics, symbols, publishing, tests, translations, object IDs) it already does well. So our compile/diagnostics path, our bundled analyzers, and our four-strategy DevTools resolution chain were duplicated infrastructure maintained against a proprietary SDK that changes shape between majors.

This PR started as the proxy (dynamic tool registration, still the core), then finished the job — delete the duplication, bridge the project's own config into the child so both sides agree — and finally hardened the proxy after a review pass: one long-lived MCP session, non-blocking startup, and a clean stdio channel.

AI Client (Claude Code / Copilot / Codex / …)
└── ALCops MCP Server (single stdio entry point)
    ├── Native:  list_rules, get_fixes, apply_fix, apply_fix_all
    └── Proxied: al_compile, al_build, al_getdiagnostics, al_symbolsearch, al_publishapp, … (16 on 18.0.41)
        └── one McpClient ── streamable HTTP ──► almcp child (127.0.0.1:<free port>)

Breaking changes

  • BC Development Tools 18.0 or later are required. The compile floor is 18.0.41.39415, the first stable release with a net10.0 payload (matching our own TFM); 17.x is dropped from the compatibility matrix.
  • analyze is removed. Use the proxied al_compile. Note it defaults to onlyErrors: true while nearly every ALCops rule is a warning — callers must pass onlyErrors: false. Documented rather than patched, because ForwardAsync stays a generic passthrough with no per-tool argument rewriting.
  • Analyzers are no longer bundled. They come solely from the project's al.codeAnalyzers (plus AL-Go rulesetFile and the custom.ruleset.json / app.ruleset.json conventions).
  • New prerequisite. dotnet tool install -g Microsoft.Dynamics.BusinessCentral.Development.Tools, or the AL VS Code extension. Nothing is downloaded at runtime any more; the server fails fast with the install command instead of starting degraded.
  • --almcp-path is gone, superseded by --devtools-path. almcp ships in the same directory as the DevTools DLLs, so a separate override would reintroduce exactly the version skew this change eliminates.

Closes #10 structurally

Bundling pinned ALCops.*Cop.dll beside whatever Nav.CodeAnalysis the user happened to install is what produced the AD0001 / MissingMethodException failures — it was the cause, not a mitigation. Cops and compiler now both come from the user's own toolchain, so we cannot mismatch them. The same co-location means our in-process CodeFixRunner loads the identical Nav.CodeAnalysis the child almcp uses.

What changed

Build — a plain PackageReference to the DevTools package is impossible: as of 17.0 it dropped the Dependency package type and the lib/ folder, and NuGet rejects referencing a DotnetTool package. PackageDownload restores it without referencing it, giving the three <Reference> items a stable HintPath into tools/<tfm>/any/. src uses Private=false so the proprietary DLLs never reach the package (the redistribution guard ExcludeBcDevToolsFromPublish used to provide); tests uses Private=true because the CI matrix hot-swaps them. CI stops hand-rolling curl + unzip and just passes -p:BcDevToolsVersion. The 18.x payload ships its own ModelContextProtocol.Core 1.0.0 beside the referenced DLLs, which MSBuild's dependency walk flags (MSB3277) against our 0.9.0-preview.2; suppressed with a why-comment, since we never reference the only consumer (Nav.Deployment) and our copy wins at runtime.

One locatorBcToolsLocator (~200 lines) replaces BcDevToolsBootstrap, DevToolsLocator, NuGetDevToolsDownloader, AlMcpLocator and AlExtensionLocator (~370). Probe order: --devtools-pathBCDEVELOPMENTTOOLSPATH → dotnet tool store → AL extension bin/ → hard error, each step logged.

Startup config bridgealmcp in MCP mode never reads .vscode/settings.json (its WorkspaceSettingsApplier is LSP-only) and has no per-call analyzer, ruleset or package-cache parameter. Without a bridge the child starts with zero analyzers and al_compile silently disagrees with our fix tools about what is suppressed. WorkspaceStartupResolver discovers projects exactly the way almcp's own LSP mode does, then passes --projects / --codeanalyzers / --rulesetpath / --packagecachepath at launch. --projects entries get the same treatment as the working directory (a workspace root expands to the projects beneath it; entries with none are ignored with a warning). User-supplied args always win. ProjectLoader reads the same al.packageCachePath for the in-process compilation, so a multi-app repo with one shared symbol cache works on both sides.

Proxy hardening (from the review pass):

  • One MCP session per child. Every forwarded call used to build a fresh McpClientinitialize, initialized, tools/call, DELETE: four round-trips per call. The client is now created once after the child is ready and reused for discovery and every forward. almcp's HTTP transport evicts idle sessions (default 2 h) with a 404, which the server never dispatched, so that one case is retried once on a fresh session; any other transport failure drops the cached client and surfaces the error unchanged.
  • Startup no longer blocks the stdio server. AlMcpProxyStartup awaited the child launch, the 30 s readiness poll and tool discovery inside IHostedService.StartAsync, ahead of the SDK's stdio hosted service — so a slow or broken almcp blocked even initialize. It now starts in the background; initialize answers in under a second. tools/list waits up to 10 s for the child so hosts that list once still get the full set; past that it answers with the native tools and arms a one-shot notifications/tools/list_changed (we already advertise listChanged). Early al_* calls wait for readiness instead of failing.
  • Clean JSON-RPC channel. In HTTP mode almcp writes its banner, Port: N and project-load progress to stdout; the proxy redirected only stderr, so the child inherited ours and 17 non-JSON lines landed in the middle of the MCP stream. Both streams are now captured and logged at Debug.
  • Port handling. FindFreePort has to release the port before almcp can bind it; if another process grabs it in between, the child exits and startup retries on a fresh port (up to three attempts). Connections go to 127.0.0.1 — the family the port was reserved on — instead of resolving localhost per connect.

DeletedAnalyzeTool, DiagnosticsRunner, DiagnosticResult, AnalyzerRegistry, and the five resolution/locator services above.

Live bugs found while verifying

  • The proxy had never actually connected. It posted to http://localhost:<port>/mcp/, but almcp calls MapMcp() with no pattern — its endpoint is the server root, and /mcp/ 404s. The readiness probe accepted any response including that 404, so startup reported success and the handshake failed afterwards. Endpoint corrected; readiness now rejects 404.
  • Analyzer dependencies must travel with their analyzer. almcp resolves analyzer dependencies only among the paths it is handed and never probes the analyzer's own directory. Passing just the cop DLLs turned every rule into AD0001 FileNotFoundException — including Microsoft's own CodeCop, which needs its sibling Analyzers.Common.dll. Dependencies are now read from assembly metadata and included.
  • almcp's stdout was our stdout (see above) — invisible to lenient clients, fatal to strict ones.

Also fixed: ParseProxyOptions shared one i + 1 < args.Length guard across every passthrough flag and unconditionally consumed the next arg, so --nolog / --noauth either vanished (when last) or swallowed the following flag as their value. Split into almcp's three real arities, in a testable ProxyOptions.Parse. Three tool descriptions still claimed "ALCops analyzers are always included"; removed.

Version resilience

Unchanged — dynamic registration means our code never references an MS tool name or schema.

MS Change Code change needed?
New tool added No — auto-discovered
Tool renamed / params changed No — schema from ListToolsAsync()
Tool removed No — disappears from list
almcp dropped entirely No — native tools still work

CLI args

  • --devtools-path <dir> — use this DevTools directory instead of probing
  • --projects <dir>[;<dir>] — override project discovery; a non-project directory is scanned for projects beneath it
  • --no-proxy — native tools only; retained deliberately, for when the agent already registers almcp itself and would otherwise see the al_* tools twice
  • almcp's own args (--codeanalyzers, --rulesetpath, --packagecachepath, --noauth, --nolog, …) are forwarded and override anything discovered from the project

Test plan

  • dotnet build — 0 errors, 0 warnings (confirms PackageDownload + HintPath resolves the tools/net10.0/any/ layout)
  • dotnet test — 68/68 pass. The code-fix guard assertions genuinely fire rather than passing vacuously; AlMcpProxyTests / AlMcpProxyStartupTests run against the real almcp from the restored DevTools package (session reuse across calls, reconnect-and-retry after an out-of-band DELETE of the session, non-blocking start, early call waits, stop during boot, list_changed armed once) — they self-skip where almcp is not runnable
  • grep for every removed symbol — no live references
  • Resolution: works from the dotnet tool store (18.0.41.39415); an unusable path exits naming the install command
  • --no-proxy — tools list is exactly list_rules, get_fixes, apply_fix, apply_fix_all; analyze absent
  • With proxy — stderr shows discovered projects, the settings.json read, and every resolved analyzer/ruleset/package-cache path; the 16 al_* tools appear
  • Raw stdio session (initializetools/listtools/call): initialize answered at ~0.9 s while almcp was still booting; tools/list returned 4 native + 16 al_*; stdout carried JSON only (0 non-JSON lines, previously 17). With the list budget forced to 0: native-only list, then exactly one tools/list_changed, then the full list. With a stub almcp.exe that dies: initialize/tools/list still instant, al_* call returns "not available" in 18 ms
  • End-to-end: al_compile { onlyErrors: false } returns LC0020 alongside CodeCop's AA0215/0218/0225/0247/0248 and zero AD0001get_fixes returns a fix → apply_fix modifies the file on disk → recompile and LC0020 is gone
  • A ruleset setting LC0020 to None hides it from both al_compile and get_fixes, other rules unaffected — the specific guarantee the startup bridge exists to provide
  • dotnet pack — no Microsoft.Dynamics.Nav.*.dll, no ALCops.*Cop.dll in the nupkg

Notes for review

  • ALCops.Analyzers moves to the test project rather than being deleted outright. The fixtures test LC0020/AC0012; with nothing bundled they would find zero diagnostics and pass vacuously. Test-only keeps the package clean (verified above) and means the tests now exercise the real production path — spec → ExternalAnalyzerLoaderAnalyzerAssemblyLoadContext — which is better coverage than the built-in registry path it replaces.
  • Microsoft.Dynamics.Nav.LanguageModelTools.AnalyzerReferenceResolver is public in the pinned floor version, but was rejected: it resolves through CompilerPathUtilities.GetPathToAnalyzerFolder() (the running process's base directory), so it cannot honour --devtools-path or our locator at all — and it would add a 4th version-drift surface to the three DLLs the CI matrix swaps. AnalyzerSpec.cs stays.
  • The review flagged the removed <devtools>/<tfm> entry in packageCachePaths as a loss of system symbols. It was not: no .app ships in the DevTools package or the AL extension, and the fixtures compile and fix without any .alpackages. The real gap next to it — al.packageCachePath being ignored — is what got fixed.
  • Agent instructions moved from .github/copilot-instructions.md to AGENTS.md (imported by CLAUDE.md), so Copilot and Claude Code read the same file.

Known, out of scope

  • After apply_fix, an al_compile in the same session still reports the fixed diagnostic — the child almcp serves its cached compilation (Recompilation of all app for every Analyze call #4). A fresh session is clean.
  • The DevTools nupkg ships no Linux/macOS almcp launcher (only almcp.exe + almcp.dll, and DotnetToolSettings.xml declares only al), so off Windows the proxy is unavailable from the dotnet tool store until BcToolsLocator learns to fall back to dotnet almcp.dll. This is also why the almcp-backed tests skip on the Ubuntu CI runner.
  • ModelContextProtocol 0.9.0-preview.21.0.0 (the version almcp 18.0 itself ships) would drop the MSB3277 suppression. Separate PR.

🤖 Generated with Claude Code

Arthurvdv and others added 7 commits August 24, 2026 11:22
Locates the Microsoft AL MCP server (almcp.exe) via:
1. ALMCP_PATH environment variable
2. --almcp-path CLI argument
3. Auto-discovery from AL Language VS Code extension

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Manages the almcp child process lifecycle and per-call McpClient
connections over HTTP (matching Microsoft's own proxy pattern).
Includes AlMcpProxyStartup IHostedService for lifecycle management.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses WithListToolsHandler + WithCallToolHandler to dynamically proxy
MS AL MCP tools alongside native ALCops tools. The SDK merges both
tool sets automatically — no static tool wrappers needed.

New CLI args: --almcp-path, --no-proxy, plus passthrough of
analyzer-related args to the child almcp process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…folder

A plain PackageReference to Microsoft.Dynamics.BusinessCentral.Development.Tools
is impossible: as of 17.0 the package dropped the Dependency package type and the
lib/ folder, and NuGet rejects referencing a DotnetTool package. PackageDownload
restores the nupkg into the global packages folder without referencing it, which
gives the three <Reference> items a stable HintPath into tools/<tfm>/any/.

src keeps Private=false so the proprietary DLLs never enter the build output and
therefore never the published package — the redistribution guard that the now
redundant ExcludeBcDevToolsFromPublish target used to provide. tests keep
Private=true on purpose: the CI compatibility matrix hot-swaps those three files
in the prebuilt test binary to run against every supported SDK version.

CI no longer curls and unzips the nupkg by hand; restore provides it, and the
version matrix is expressed as -p:BcDevToolsVersion. The release job now passes
that version to pack as well, so the packed binary matches what was tested.

ALCops.Analyzers moves to the test project: the fixtures need real cops with real
code fixes, but nothing bundled may ship. Dependabot follows it there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Microsoft's almcp has no code-fix capability at all (its LSP mode advertises
CodeActionProvider = false) and no way to enumerate rules. Everything else —
compiling, diagnostics, symbols, publishing, tests, translations — it already
does, and PR #20 already proxies it. So the compile/diagnostics path, the
bundled analyzers and the four-strategy DevTools resolution chain all become
duplicated infrastructure maintained against a proprietary SDK. This deletes
them.

BREAKING: the `analyze` tool is gone; use the proxied `al_compile` with
onlyErrors: false (it defaults to true, and nearly every ALCops rule is a
warning). Analyzers are no longer bundled and must be configured through the
project's own al.codeAnalyzers.

Not bundling analyzers is what closes #10 structurally rather than papering
over it: pinned cop DLLs loaded beside whatever Nav.CodeAnalysis the user has
installed is exactly what produced AD0001 / MissingMethodException. Cops and
compiler now both come from the user's toolchain, so we cannot mismatch them.

One BcToolsLocator (~200 lines) replaces BcDevToolsBootstrap, DevToolsLocator,
NuGetDevToolsDownloader, AlMcpLocator and AlExtensionLocator (~370). It works
because almcp and the DevTools DLLs ship in the same directory in both delivery
channels, which is also what guarantees our in-process fixes load the same
Nav.CodeAnalysis the child almcp uses. Runtime NuGet download is gone: the
server now fails fast with the install command instead of starting degraded.

New WorkspaceStartupResolver bridges the project's own configuration into the
child almcp at launch. almcp in MCP mode never reads .vscode/settings.json (its
WorkspaceSettingsApplier is LSP-only) and exposes no per-call analyzer or
ruleset parameter, so without this the child starts with zero analyzers and
al_compile disagrees with our fix tools about what is suppressed. It discovers
projects the same way almcp's own LSP mode does, then passes --projects,
--codeanalyzers and --rulesetpath. ForwardAsync stays a generic passthrough with
no per-tool rewriting; user-supplied args always win.

Analyzer dependencies travel with their analyzer: almcp resolves them only among
the paths it was handed and never probes the analyzer's own directory, so
omitting ALCops.Common.dll or Microsoft.Dynamics.Nav.Analyzers.Common.dll turns
every rule in that assembly into an AD0001 instead of a diagnostic. They are
read from assembly metadata, not loaded.

Also fixes two live defects found while verifying:

- The proxy pointed at http://localhost:<port>/mcp/, but almcp calls MapMcp()
  with no pattern, so its endpoint is the server root. /mcp/ returns 404, and
  the readiness probe accepted any response — including that 404 — so startup
  "succeeded" and the handshake then failed. It has never actually connected.
- ParseProxyOptions shared one `i + 1 < args.Length` guard across every
  passthrough flag and unconditionally consumed the next arg, so --nolog and
  --noauth either vanished (when last) or swallowed the following flag as their
  value. Split into almcp's three real arities, in a testable ProxyOptions.Parse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With nothing bundled, a fixture that declares no analyzers finds no diagnostics
and every code-fix test would pass vacuously. Each fixture is now copied to temp
and given an al.codeAnalyzers pointing at the cop DLLs beside the test binary —
written at run time because the paths are absolute. That means these tests
exercise exactly the path production uses: spec -> ExternalAnalyzerLoader ->
AnalyzerAssemblyLoadContext, which is better coverage than the old built-in
registry path it replaces. The existing guard assertions stay, and now carry the
loaded-analyzer count so a silent regression to zero diagnostics reports why.

ApplyFix_WritesModifiedContentToDisk drops its DiagnosticsRunner-based location
discovery for the hardcoded line/column already used by the ruleset test, and
CopyDirectory is recursive so fixture subdirectories come along.

New coverage for the parts that had none and now carry the design:
BcToolsLocator probe precedence and layouts, ProxyOptions arity handling, and
WorkspaceStartupResolver's discovery plus the child argument list it composes —
including that a suppressing ruleset reaches al_compile, which is the specific
guarantee the startup bridge exists to provide.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Documents what the server actually is now — a code-fix server plus a proxy — and
the new prerequisite, the DevTools dotnet tool with the AL extension as
fallback. Records that analyzers must come from al.codeAnalyzers and why
bundling them was the cause of #10 rather than a convenience.

Calls out the al_compile onlyErrors: true default explicitly: nearly every
ALCops rule is a warning, so callers who leave it alone see nothing. That stays
a docs fix precisely because ForwardAsync must not rewrite call arguments.

Removes the old five-strategy resolution-order section, which was also factually
wrong — it listed the local cache before the dotnet tools store, the reverse of
what the code did. copilot-instructions goes from ".NET 8" to .NET 10 and picks
up the constraints a future change most needs to know: why the DevTools
dependency is shaped the way it is, why analyzers must never move back to src,
and why analyzer dependencies have to travel with their analyzer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Arthurvdv Arthurvdv changed the title feat: proxy Microsoft AL MCP Server tools feat!: reuse Microsoft's almcp, narrow ALCops MCP to code fixes Aug 24, 2026
Arthurvdv and others added 9 commits September 11, 2026 20:24
…st net10.0

18.0.41 is the current stable release and the first to ship a net10.0 payload,
matching our own TFM. Drops 17.x from the compatibility matrix.

The net10.0 payload ships its own ModelContextProtocol.Core (1.0.0) next to the
DLLs we reference, so MSBuild's dependency walk now reports MSB3277 against our
0.9.0-preview.2. Suppressed in both projects: we never reference the only
consumer (Nav.Deployment), and BcToolsLocator's resolver is a fallback hook, so
our own copy always wins at runtime.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Each forwarded tool call built a fresh McpClient — initialize, initialized,
tools/call, DELETE — four round-trips per call. Create the client once after
the child is ready and reuse it for discovery and every forward. almcp's HTTP
transport evicts idle sessions (default 2h) with a 404, which the server never
dispatched, so that one case is retried once on a fresh session; any other
transport failure drops the cached client and surfaces the error unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The test project already restores the DevTools nupkg, which ships almcp next
to the DLLs from 17.0 onward. Spawn it once per class and assert session reuse
across calls plus reconnect-and-retry after the session is deleted out of band.
Skips where almcp is not runnable (16.2, or the Linux CI runner — the package
has no non-Windows almcp launcher).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Move .github/copilot-instructions.md to AGENTS.md so Copilot and other
agents keep reading it, add CLAUDE.md importing it, pre-allow dotnet
build/test/pack/restore in .claude/settings.json, register alcops-mcp in
.mcp.json, and ignore .claude/settings.local.json.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ately

AlMcpProxyStartup awaited the child launch, the 30s readiness poll and tool
discovery inside IHostedService.StartAsync, and it is registered ahead of the
SDK's stdio hosted service — so a slow or broken almcp blocked even
`initialize`, although the native code-fix tools need no child at all.

Start the proxy on a background task and expose AlMcpProxy.Ready. tools/list
waits up to 10s for it so hosts that list once still get the full set; past
that it answers with the native tools and arms a one-shot
notifications/tools/list_changed for when almcp comes up. Early al_* calls
wait for readiness instead of failing. StartAsync now creates its client
through the same gate as ForwardAsync, closing a two-client race that
concurrent startup would otherwise open.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
In HTTP mode almcp writes its banner, "Port: N" and project-load progress to
stdout (Program.cs passes Console.WriteLine as the output sink). The proxy
only redirected the child's stderr, so the child inherited our stdout and
that text landed in the middle of the JSON-RPC stream — 17 non-JSON lines in
a captured session, with the box-drawing banner arriving as mojibake.
Capture stdout too and route both streams to our stderr logger at Debug.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review finding #3 claimed the removed DevTools packageCachePaths entry cost
us system symbols. It did not: no .app ships in the DevTools package or the
AL extension, and the fixtures compile and fix without any .alpackages. What
was actually missing is the one symbol-location setting the AL extension
honours: al.packageCachePath. ProjectLoader hardcoded <project>/.alpackages
and the startup bridge never passed --packagecachepath, so a multi-app repo
with a shared symbol cache got missing-symbol errors from al_compile and
symbol-less compilations in get_fixes, with no hint why.

Read the setting (string or array) once, resolve it per project in
ProjectLoader, pass it as written to almcp so it resolves per project too,
and warn when no package cache directory exists at all.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… text

--projects paths went to almcp verbatim: a workspace root or a typo failed
there with a generic load error, while our own analyzer/ruleset resolution
silently read from a folder with no .vscode/settings.json. Run each entry
through the same discovery the working directory gets — use it if it is a
project, otherwise scan beneath it — and ignore entries with nothing under
them, with a warning naming which it was.

Three tool descriptions still told clients "ALCops analyzers are always
included"; nothing has been bundled since the un-bundling, and list_rules
already says so.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
FindFreePort reserves an IPv4 loopback port and has to release it before
almcp can bind it, so another process can take it in between; almcp then
exits with "address already in use" and startup failed for good. Relaunch on
a fresh port when the child exits before it is ready, up to three attempts.
A child that is alive but serves no MCP endpoint still fails outright.

Connect to 127.0.0.1 — the family the port was reserved on — instead of
resolving localhost on every connection. almcp binds both loopback families,
so this only removes a possible ::1 attempt per connect.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Arthurvdv Arthurvdv changed the title feat!: reuse Microsoft's almcp, narrow ALCops MCP to code fixes feat!: proxy Microsoft's almcp, narrow ALCops MCP to code fixes, require BC DevTools 18.0 Sep 12, 2026
@Arthurvdv
Arthurvdv merged commit 167ab3c into main Sep 12, 2026
5 checks passed
@Arthurvdv
Arthurvdv deleted the feat/almcp-proxy branch September 12, 2026 08:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LinterCop analyzer crashes (AD0001 MissingMethodException) when AL Language extension DevTools are newer than bundled ALCops.Analyzers

1 participant