July 17, 2026
Hunting Local AI Tools on Windows with Microsoft Defender for Endpoint
Executive summary:

By Ahmed Monsri
8 min read
Local AI tools on Windows are not simply another software inventory item. They can run under the signed-in user context, interact with files, spawn processes, use developer tooling, and connect to MCP servers or external services. Microsoft Defender for Endpoint gives security teams a practical starting point through local AI agent discovery, inventory, exposure mapping, and Advanced Hunting. This document adapts the same concept to Windows-specific realities such as PowerShell, WSL, package managers, user profiles, scheduled tasks, IDE extensions, and local admin privileges.
Audience: SOC analysts, endpoint security engineers, AI governance teams
Scope: Windows endpoints onboarded to Microsoft Defender for Endpoint
Important note: Validate all KQL against your tenant schema, licensing, preview status, and telemetry availability
1. Why Windows deserves a dedicated view
On Windows, local AI tools often sit close to developer environments, shells, browsers, package managers, user profile directories, Windows Subsystem for Linux, and automation mechanisms. That creates a broader telemetry surface and a broader governance question: do we only know that an AI tool exists, or do we understand what it can reach and how it behaves?
Microsoft documents local AI agent discovery in Defender for Endpoint as a preview capability that can surface supported local AI agents, MCP server configurations, inventory relationships, and Advanced Hunting data on supported Windows and macOS endpoints. The practical opportunity is to combine that native discovery with Windows hunting patterns that security teams already use every day.
2. Start with Defender-confirmed local AI agents
Microsoft Defender for Endpoint can automatically discover supported local AI agents on onboarded Windows devices. For a low-noise inventory, use the exposure graph, where Defender labels confirmed local agents as endpointAiAgent, instead of relying on process-name keywords.
Query 1 Local AI agents on Windows devices
let WindowsDevices =
DeviceInfo
| where OSPlatform startswith "Windows"
| summarize arg_max(Timestamp, DeviceName, OSPlatform) by DeviceId
| project DeviceId, DeviceName, OSPlatform;
ExposureGraphEdges
| where SourceNodeLabel == "endpointAiAgent"
| where EdgeLabel =~ "runs on"
| project
AgentId = SourceNodeId,
AgentName = SourceNodeName,
DeviceId = TargetNodeId
| join kind=inner WindowsDevices on DeviceId
| project AgentName, DeviceName, OSPlatform
| distinct AgentName, DeviceName, OSPlatform
| order by AgentName asclet WindowsDevices =
DeviceInfo
| where OSPlatform startswith "Windows"
| summarize arg_max(Timestamp, DeviceName, OSPlatform) by DeviceId
| project DeviceId, DeviceName, OSPlatform;
ExposureGraphEdges
| where SourceNodeLabel == "endpointAiAgent"
| where EdgeLabel =~ "runs on"
| project
AgentId = SourceNodeId,
AgentName = SourceNodeName,
DeviceId = TargetNodeId
| join kind=inner WindowsDevices on DeviceId
| project AgentName, DeviceName, OSPlatform
| distinct AgentName, DeviceName, OSPlatform
| order by AgentName ascThis query returns only agents that Defender has classified as local endpoint AI agents and then uses DeviceInfo to retain those associated with Windows devices.
Query 2 — Windows devices hosting multiple agents
let WindowsDevices =
DeviceInfo
| where OSPlatform startswith "Windows"
| summarize arg_max(Timestamp, DeviceName) by DeviceId
| project DeviceId, DeviceName;
ExposureGraphEdges
| where SourceNodeLabel == "endpointAiAgent"
| where EdgeLabel =~ "runs on"
| project
AgentName = SourceNodeName,
DeviceId = TargetNodeId
| join kind=inner WindowsDevices on DeviceId
| summarize
AgentCount = dcount(AgentName),
Agents = make_set(AgentName, 20)
by DeviceName
| where AgentCount >= 2
| order by AgentCount desclet WindowsDevices =
DeviceInfo
| where OSPlatform startswith "Windows"
| summarize arg_max(Timestamp, DeviceName) by DeviceId
| project DeviceId, DeviceName;
ExposureGraphEdges
| where SourceNodeLabel == "endpointAiAgent"
| where EdgeLabel =~ "runs on"
| project
AgentName = SourceNodeName,
DeviceId = TargetNodeId
| join kind=inner WindowsDevices on DeviceId
| summarize
AgentCount = dcount(AgentName),
Agents = make_set(AgentName, 20)
by DeviceName
| where AgentCount >= 2
| order by AgentCount descDevices hosting several agents are not automatically risky, but they provide a practical starting point for reviewing agent concentration and governance coverage.
Note: This approach provides high-confidence results but only covers local AI agents supported and discovered by Defender. The capability is currently documented as preview.
Recommended baseline questions
- Which supported local AI agents are present on Windows devices?
- Which users are associated with those agents?
- Which devices host more than one AI client or coding agent?
- Which agents have MCP server configurations attached?
- Which agents appear on devices with elevated risk, active alerts, or known exposure?
- Which agent-related processes launch shells, scripts, package managers, or network connections?
3. Hunt around the agent, not only for the agent
An AI agent by itself is only one part of the story. The higher-value question is what happens around it: process spawning, script execution, package installation, network access, and interaction with sensitive local paths. Microsoft documents DeviceProcessEvents as the Advanced Hunting table for process creation and related events, including fields such as FileName, FolderPath, process command line, hashes, account, and integrity data.
Query 3 — AI-related process execution on Windows
// Add or remove high-confidence AI tool/agent indicators based on your environment.
// Examples: "claude-code", "cursor", "continue", "ollama", "lm studio",
// "windsurf", "codex", "aider", "openwebui", "mcp-server",
// "github.copilot", and "github.copilot-chat".
// Avoid generic terms such as "copilot", "code", "agent", "node", "python",
// or "npx" unless combined with a product-specific path or identifier.
let aiTerms = dynamic([
"claude-code",
"cursor",
"continue",
"ollama",
"lm studio",
"mcp-server",
"github.copilot",
"github.copilot-chat"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceProcessEvents
| where Timestamp > ago(1h)
| join kind=inner WindowsDevices on DeviceId
| where FileName has_any (aiTerms)
or ProcessCommandLine has_any (aiTerms)
or FolderPath has_any (aiTerms)
| project
Timestamp,
DeviceName,
AccountName,
FileName,
FolderPath,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
ProcessIntegrityLevel,
SHA1
| order by Timestamp desc// Add or remove high-confidence AI tool/agent indicators based on your environment.
// Examples: "claude-code", "cursor", "continue", "ollama", "lm studio",
// "windsurf", "codex", "aider", "openwebui", "mcp-server",
// "github.copilot", and "github.copilot-chat".
// Avoid generic terms such as "copilot", "code", "agent", "node", "python",
// or "npx" unless combined with a product-specific path or identifier.
let aiTerms = dynamic([
"claude-code",
"cursor",
"continue",
"ollama",
"lm studio",
"mcp-server",
"github.copilot",
"github.copilot-chat"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceProcessEvents
| where Timestamp > ago(1h)
| join kind=inner WindowsDevices on DeviceId
| where FileName has_any (aiTerms)
or ProcessCommandLine has_any (aiTerms)
or FolderPath has_any (aiTerms)
| project
Timestamp,
DeviceName,
AccountName,
FileName,
FolderPath,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
ProcessIntegrityLevel,
SHA1
| order by Timestamp descThis is a pragmatic discovery query, not a final detection. It catches naming patterns, but it also creates noise. Use it to build a local allow list, identify legitimate user adoption, and discover tools that are not yet covered by native inventory.
Query 4 — AI tools launching command interpreters
// Add approved or high-confidence AI executables and product-specific markers
// relevant to your environment. Avoid generic terms such as "code", "node",
// "python", "agent", or "mcp" because they can create significant noise.
let aiExecutables = dynamic([
"claude.exe",
"cursor.exe",
"windsurf.exe",
"ollama.exe",
"copilot.exe",
"codex.exe"
]);
let aiMarkers = dynamic([
"claude-code",
"github.copilot",
"github.copilot-chat",
"continue.continue",
"mcp-server",
"lm studio"
]);
let shells = dynamic([
"powershell.exe",
"pwsh.exe",
"cmd.exe",
"wsl.exe",
"bash.exe"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceProcessEvents
| where Timestamp > ago(1h)
| join kind=inner WindowsDevices on DeviceId
| where FileName in~ (shells)
| where InitiatingProcessFileName in~ (aiExecutables)
or InitiatingProcessCommandLine has_any (aiMarkers)
or InitiatingProcessFolderPath has_any (aiMarkers)
| project
Timestamp,
DeviceName,
AccountName,
AIParentProcess = InitiatingProcessFileName,
AIParentPath = InitiatingProcessFolderPath,
AIParentCommandLine = InitiatingProcessCommandLine,
SpawnedShell = FileName,
ShellCommandLine = ProcessCommandLine,
ProcessIntegrityLevel,
SHA1
| order by Timestamp desc// Add approved or high-confidence AI executables and product-specific markers
// relevant to your environment. Avoid generic terms such as "code", "node",
// "python", "agent", or "mcp" because they can create significant noise.
let aiExecutables = dynamic([
"claude.exe",
"cursor.exe",
"windsurf.exe",
"ollama.exe",
"copilot.exe",
"codex.exe"
]);
let aiMarkers = dynamic([
"claude-code",
"github.copilot",
"github.copilot-chat",
"continue.continue",
"mcp-server",
"lm studio"
]);
let shells = dynamic([
"powershell.exe",
"pwsh.exe",
"cmd.exe",
"wsl.exe",
"bash.exe"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceProcessEvents
| where Timestamp > ago(1h)
| join kind=inner WindowsDevices on DeviceId
| where FileName in~ (shells)
| where InitiatingProcessFileName in~ (aiExecutables)
or InitiatingProcessCommandLine has_any (aiMarkers)
or InitiatingProcessFolderPath has_any (aiMarkers)
| project
Timestamp,
DeviceName,
AccountName,
AIParentProcess = InitiatingProcessFileName,
AIParentPath = InitiatingProcessFolderPath,
AIParentCommandLine = InitiatingProcessCommandLine,
SpawnedShell = FileName,
ShellCommandLine = ProcessCommandLine,
ProcessIntegrityLevel,
SHA1
| order by Timestamp descThis query focuses on behavior that matters on Windows: AI-assisted workflows that move from conversation to execution. In a mature environment, this should feed investigation playbooks rather than immediate blocking.
Query 5 — PowerShell activity near AI tooling
// Add or remove high-confidence AI product indicators based on your environment.
// Examples: "claude-code", "github.copilot", "github.copilot-chat",
// "continue.continue", "cursor", "windsurf", "ollama", "codex", "mcp-server".
// Avoid generic terms such as "agent", "mcp", "code", "node", or "python"
// because they can match normal administrative and development activity.
let aiTerms = dynamic([
"claude-code",
"github.copilot",
"github.copilot-chat",
"continue.continue",
"cursor",
"windsurf",
"ollama",
"codex",
"mcp-server"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceProcessEvents
| where Timestamp > ago(1h)
| where DeviceId in (WindowsDevices)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (aiTerms)
or InitiatingProcessCommandLine has_any (aiTerms)
or InitiatingProcessFolderPath has_any (aiTerms)
| project
Timestamp,
DeviceName,
AccountName,
PowerShellProcess = FileName,
PowerShellCommandLine = ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessFolderPath,
InitiatingProcessCommandLine,
ProcessIntegrityLevel,
SHA1
| order by Timestamp desc// Add or remove high-confidence AI product indicators based on your environment.
// Examples: "claude-code", "github.copilot", "github.copilot-chat",
// "continue.continue", "cursor", "windsurf", "ollama", "codex", "mcp-server".
// Avoid generic terms such as "agent", "mcp", "code", "node", or "python"
// because they can match normal administrative and development activity.
let aiTerms = dynamic([
"claude-code",
"github.copilot",
"github.copilot-chat",
"continue.continue",
"cursor",
"windsurf",
"ollama",
"codex",
"mcp-server"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceProcessEvents
| where Timestamp > ago(1h)
| where DeviceId in (WindowsDevices)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (aiTerms)
or InitiatingProcessCommandLine has_any (aiTerms)
or InitiatingProcessFolderPath has_any (aiTerms)
| project
Timestamp,
DeviceName,
AccountName,
PowerShellProcess = FileName,
PowerShellCommandLine = ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessFolderPath,
InitiatingProcessCommandLine,
ProcessIntegrityLevel,
SHA1
| order by Timestamp descPowerShell deserves special attention because it is a normal administration tool and also a powerful execution layer. Treat findings with context: device role, user role, signed scripts, expected developer activity, and whether execution is elevated.
4. MCP servers: the capability extension layer
Model Context Protocol servers are important because they can extend what a local AI tool can do. Microsoft documentation states that Defender can discover supported local AI agents and MCP server configurations, and that discovered agent details can include configured MCP servers when detected. For Windows governance, MCP should be treated as a connector surface, not just a configuration detail.
MCP hunting questions
- Which tools are configured with MCP servers?
- Are MCP servers local, remote, or tied to developer repositories?
- Do MCP configurations appear on privileged devices or administrator workstations?
- Do MCP-related processes initiate outbound connections to unexpected destinations?
- Are sensitive workspace folders exposed to tools that auto-approve actions?
Query 6 — Network activity around AI-related processes
// Add or remove high-confidence AI product indicators based on your environment.
// Examples: "claude-code", "github.copilot", "github.copilot-chat",
// "continue.continue", "cursor", "windsurf", "ollama", "codex", "mcp-server".
// Avoid generic terms such as "agent", "mcp", "code", "node", or "python"
// because they can match normal network activity.
let aiTerms = dynamic([
"claude-code",
"github.copilot",
"github.copilot-chat",
"continue.continue",
"cursor",
"windsurf",
"ollama",
"codex",
"mcp-server"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceNetworkEvents
| where Timestamp > ago(1h)
| where DeviceId in (WindowsDevices)
| where InitiatingProcessFileName has_any (aiTerms)
or InitiatingProcessCommandLine has_any (aiTerms)
or InitiatingProcessFolderPath has_any (aiTerms)
| project
Timestamp,
DeviceName,
AccountName = InitiatingProcessAccountName,
InitiatingProcessFileName,
InitiatingProcessFolderPath,
InitiatingProcessCommandLine,
RemoteUrl,
RemoteIP,
RemotePort,
Protocol
| order by Timestamp desc// Add or remove high-confidence AI product indicators based on your environment.
// Examples: "claude-code", "github.copilot", "github.copilot-chat",
// "continue.continue", "cursor", "windsurf", "ollama", "codex", "mcp-server".
// Avoid generic terms such as "agent", "mcp", "code", "node", or "python"
// because they can match normal network activity.
let aiTerms = dynamic([
"claude-code",
"github.copilot",
"github.copilot-chat",
"continue.continue",
"cursor",
"windsurf",
"ollama",
"codex",
"mcp-server"
]);
let WindowsDevices =
DeviceInfo
| summarize arg_max(Timestamp, OSPlatform) by DeviceId
| where OSPlatform startswith "Windows"
| project DeviceId;
DeviceNetworkEvents
| where Timestamp > ago(1h)
| where DeviceId in (WindowsDevices)
| where InitiatingProcessFileName has_any (aiTerms)
or InitiatingProcessCommandLine has_any (aiTerms)
or InitiatingProcessFolderPath has_any (aiTerms)
| project
Timestamp,
DeviceName,
AccountName = InitiatingProcessAccountName,
InitiatingProcessFileName,
InitiatingProcessFolderPath,
InitiatingProcessCommandLine,
RemoteUrl,
RemoteIP,
RemotePort,
Protocol
| order by Timestamp descUse this query to understand external reach. It is especially useful when paired with proxy, DNS, firewall, or DLP telemetry. The objective is not to block every connection, but to identify where AI tooling creates new egress paths.
5. Windows control strategy: practical and proportional
A good Windows strategy should avoid two extremes: ignoring AI tools because they are productivity tools, or blocking everything and pushing usage into unmanaged paths. The better approach is inventory first, then risk-based control.
A Practical Governance Model for AI Agents on Windows
Discover : See what exists Leverage MDE AI Agent Inventory, software inventory, and process hunting to identify AI tools running across your environment. ➡ Output: Approved, tolerated, unknown, and prohibited AI tools.
Contextualize : Understand risk Add context from device criticality, user privileges, local admin rights, exposure level, and active alerts. ➡ Output: A prioritized investigation and risk queue.
Constrain : Reduce blast radius Apply security controls such as Least Privilege, Controlled Folder Access, WDAC/App Control, ASR rules, and DLP policies. ➡ Output: Governed and controlled AI usage patterns.
Monitor: Detect risky behavior Continuously monitor with Advanced Hunting, custom detections, behavioral analytics, and incident enrichment. ➡ Output: AI-aware investigations with actionable context.
Review: Keep governance current Perform regular AI agent inventory reviews and maintain a structured exception process for developers and business users. ➡ Output: Continuously updated allow/block decisions and governance policies.
Key takeaway: The goal is not just to know which AI tools are installed, but to understand what they can access, execute, and connect to on your Windows endpoints.
6. Suggested operational workflow
Step 1 Build the inventory
Use Defender local AI agent discovery where available. Complement it with process, software, and file telemetry for unsupported or renamed tools.
Step 2 Classify usage
Separate approved business use, developer experimentation, unmanaged personal tools, and prohibited tools.
Step 3 Prioritize risky intersections
Focus first on local admin devices, privileged users, servers, jump boxes, source-code workstations, and devices with active alerts.
Step 4 Hunt for execution paths
Track AI tools launching shells, script engines, package managers, WSL, or remote connections.
Step 5 Govern MCP configurations
Inventory MCP servers and review what each server enables. Treat remote MCP endpoints as third-party integrations.
Step 6 Create clear response rules
Define when to close as expected usage, when to contact the user, when to isolate the device, and when to block the tool.
Step 7 Report adoption and risk
Trend agent count, device coverage, high-risk combinations, and unresolved exceptions over time.
7. Example closure logic for SOC incidents
When a Windows AI tool appears in an incident, the analyst should avoid generic closure notes. The closure should explicitly state whether the tool is approved, which user and device were involved, whether it launched interpreters or performed network activity, and whether any high-risk path was observed.
8. Final takeaways
- Windows AI agent hunting should combine native Defender local AI agent discovery with classic process, network, script, and endpoint governance telemetry.
- The most important hunting question is not only "which AI tool exists?" but "what can it execute, access, and connect to from this Windows device?"
- PowerShell, WSL, IDE extensions, package managers, scheduled tasks, and local admin rights are key Windows-specific considerations.
- MCP servers should be reviewed as capability extensions because they can change what the local AI tool can do.
- The right operating model is inventory first, risk-based control second, and continuous monitoring after that.
Public references
- Microsoft Learn — Local AI agent discovery with Microsoft Defender for Endpoint (Preview)
- Microsoft Learn — Discover local AI agents with Microsoft Defender for Endpoint (Preview)
- Microsoft Learn — DeviceProcessEvents table in Advanced Hunting schema
- Microsoft Learn — DeviceEvents table in Advanced Hunting schema
- Microsoft Support — Microsoft Defender for Endpoint plug-in for WSL2