You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On startup, the module fails to load with an AMSI ("Antimalware Scan Interface") detection. PowerShell's antimalware provider flags the embedded MCPPollingEngine.ps1 script as malicious at execution time, so the polling engine never starts and the MCP server is unusable. No tools are exposed to the client.
This is distinct from #46 (which was WDAC/Device Guard blocking the unsigned PowerShell.MCP.Proxy.exe on disk). Here nothing is quarantined on disk — the block happens in memory, when the embedded .ps1 resource is run through Invoke-Expression.
Error
WARNING: [PowerShell.MCP] Failed to start: At line:1 char:1
+ # MCPPollingEngine.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
Note the caret points at line 1 of the embedded script (# MCPPollingEngine.ps1), and the message is the standard text PowerShell surfaces when AMSI returns AMSI_RESULT_DETECTED for a script buffer.
Root cause analysis
The module ships three PowerShell scripts as embedded managed resources inside PowerShell.MCP.dll:
PowerShell.MCP.Resources.MCPPollingEngine.ps1
PowerShell.MCP.Resources.MCPCleanup.ps1
PowerShell.MCP.Resources.MCPLocationProvider.ps1
At load time these are read via Assembly.GetManifestResourceStream(...) and executed as a dynamic string through Invoke-Expression / PowerShell.AddScript(...) (the same Invoke-Expression $cleanupScript pattern is visible in PowerShell.MCP.psm1's OnRemove handler, and the matching Failed to start: warning string and LoadScript/AddScript symbols are present in the DLL).
When a script is executed from a dynamically-built string, AMSI scans the in-memory script buffer before execution. A persistent polling loop that reads instructions from an external channel (a named pipe) and executes arbitrary strings is exactly the behavioral shape AV/EDR heuristics associate with fileless PowerShell backdoors/downloaders — so the AMSI provider (Microsoft Defender or a third-party AV's AMSI provider) returns a detection and PowerShell refuses to run the buffer.
Evidence this is an in-memory AMSI block, not a disk detection
There is no MCPPollingEngine.ps1 file on disk in the installed module directory — it only exists as an embedded resource string inside the DLL.
Get-MpThreatDetection shows no entry referencing the module — consistent with an AMSI execution-time block (which is not necessarily written to file-based threat history) rather than a quarantined file.
The parse/caret location (At line:1 char:1) is the first line of the embedded script content, not a path on disk.
Impact
The MCP server does not start at all — there is no degraded/managed-only fallback; the polling engine is the core loop.
This will affect anyone on a machine with an aggressive AV/EDR AMSI provider, which is common on enterprise-managed Windows workstations. It can also appear intermittently as AV definition updates change heuristics.
These are options for maintainers to weigh, not a prescription:
Avoid Invoke-Expression of dynamic strings where possible. AMSI is far more likely to flag iex/AddScript(<string>) than a script file dot-sourced or a function exported from the (signed) assembly. Loading the polling engine as compiled cmdlets/methods in the DLL, or dot-sourcing a real .ps1 shipped on disk, sidesteps the dynamic-string heuristic.
Reduce the "backdoor-shaped" signature of the polling loop if feasible (naming, structure), since behavioral heuristics key off the read-external-input-then-execute pattern.
Document a supported workaround. Today the only mitigations are user-side and fragile: an AV path/process exclusion (doesn't help for in-memory AMSI on some providers), or an IT allowlist. A documented, supported path would help enterprise users.
Happy to provide additional diagnostics (AMSI event logs, Get-MpThreatDetection output, DLL resource dump) if useful.
Summary
On startup, the module fails to load with an AMSI ("Antimalware Scan Interface") detection. PowerShell's antimalware provider flags the embedded
MCPPollingEngine.ps1script as malicious at execution time, so the polling engine never starts and the MCP server is unusable. No tools are exposed to the client.This is distinct from #46 (which was WDAC/Device Guard blocking the unsigned
PowerShell.MCP.Proxy.exeon disk). Here nothing is quarantined on disk — the block happens in memory, when the embedded.ps1resource is run throughInvoke-Expression.Error
Note the caret points at line 1 of the embedded script (
# MCPPollingEngine.ps1), and the message is the standard text PowerShell surfaces when AMSI returnsAMSI_RESULT_DETECTEDfor a script buffer.Root cause analysis
The module ships three PowerShell scripts as embedded managed resources inside
PowerShell.MCP.dll:PowerShell.MCP.Resources.MCPPollingEngine.ps1PowerShell.MCP.Resources.MCPCleanup.ps1PowerShell.MCP.Resources.MCPLocationProvider.ps1At load time these are read via
Assembly.GetManifestResourceStream(...)and executed as a dynamic string throughInvoke-Expression/PowerShell.AddScript(...)(the sameInvoke-Expression $cleanupScriptpattern is visible inPowerShell.MCP.psm1'sOnRemovehandler, and the matchingFailed to start:warning string andLoadScript/AddScriptsymbols are present in the DLL).When a script is executed from a dynamically-built string, AMSI scans the in-memory script buffer before execution. A persistent polling loop that reads instructions from an external channel (a named pipe) and executes arbitrary strings is exactly the behavioral shape AV/EDR heuristics associate with fileless PowerShell backdoors/downloaders — so the AMSI provider (Microsoft Defender or a third-party AV's AMSI provider) returns a detection and PowerShell refuses to run the buffer.
Evidence this is an in-memory AMSI block, not a disk detection
MCPPollingEngine.ps1file on disk in the installed module directory — it only exists as an embedded resource string inside the DLL.Get-MpThreatDetectionshows no entry referencing the module — consistent with an AMSI execution-time block (which is not necessarily written to file-based threat history) rather than a quarantined file.At line:1 char:1) is the first line of the embedded script content, not a path on disk.Impact
Environment
...\Documents\PowerShell\Modules\PowerShell.MCP\1.10.0)Suggested directions
These are options for maintainers to weigh, not a prescription:
Invoke-Expressionof dynamic strings where possible. AMSI is far more likely to flagiex/AddScript(<string>)than a script file dot-sourced or a function exported from the (signed) assembly. Loading the polling engine as compiled cmdlets/methods in the DLL, or dot-sourcing a real.ps1shipped on disk, sidesteps the dynamic-string heuristic.Happy to provide additional diagnostics (AMSI event logs,
Get-MpThreatDetectionoutput, DLL resource dump) if useful.