Security engineering · Homelab case study

Deploying an Azure MCP Server
for an Agentic SOC

How I introduced the Model Context Protocol (MCP) between a local n8n AI investigation workflow and Microsoft Sentinel, enabling a locally hosted language model to execute real KQL queries through an authenticated Azure tool interface.

October 2026Azure MCP Server 3.0.0n8n 2.43.4Docker ComposeMicrosoft Sentinel

01 / Project overview

This project was developed in a personal SOC homelab. The objective was to let an AI agent investigate security telemetry in Microsoft Sentinel without hard-coding every investigative query into the workflow. n8n orchestrates the investigation, a locally hosted Qwen model reasons about the incident, and Azure MCP Server exposes Azure operations as callable tools.

Problem

A conventional automation can run known KQL queries but cannot easily select new lines of enquiry based on returned evidence.

Approach

Expose Azure Log Analytics through an MCP service that n8n can call as an AI Agent tool.

Result

The agent successfully executed Sentinel queries, inspected authentication and command events, and retried a failed query after correcting a field name.

02 / Architecture

Microsoft SentinelLog Analytics workspace; Cowrie telemetry
↔
Azure MCP ServerDocker service; Azure API access
↔
n8n AI AgentMCP Client Tool
↔
Ollama / QwenLocally hosted reasoning model

The Azure MCP service and n8n run as containers on a shared Docker network named soc-mcp-net. n8n addresses the MCP service using Docker's internal DNS name, azure-mcp, on port 5000. The language model does not receive Azure credentials; the MCP service authenticates to Azure on its behalf.

Separation of responsibilities: Ollama generates tool requests and KQL; n8n controls orchestration; Azure MCP executes supported Azure operations; Microsoft Sentinel remains the source of truth.

03 / Components and prerequisites

ComponentRoleObserved configuration
n8nWorkflow and AI Agent orchestrationVersion 2.43.4; Docker Compose
Azure MCP ServerAuthenticated Azure tool interfaceVersion 3.0.0; service azure-mcp
Docker networkContainer-to-container communicationsoc-mcp-net
Microsoft Entra IDNon-interactive service authenticationOAuth2 client credentials
Microsoft SentinelSecurity investigation dataLog Analytics workspace sentinel
OllamaLocal model servingQwen3:14b used in later tests

The setup required an Azure subscription, a Log Analytics workspace, an Entra application/service principal with suitable workspace query permissions, Docker and Docker Compose, and an n8n installation with an AI Agent and MCP Client Tool.

04 / Configure Azure authentication

Step 1 — Register an application

Configure a Microsoft Entra application for service-to-service authentication. Record its tenant ID and application (client) ID, then create a client secret. The Azure MCP container uses these values to acquire tokens using the client credentials flow.

Step 2 — Assign least-privilege access

Grant the service principal the permissions required to query the target Log Analytics workspace. Prefer workspace-scoped, read-only access for this investigation use case. Avoid broad subscription-level roles unless a specific MCP operation requires them.

Step 3 — Supply secrets to the container

Keep credentials out of the Compose file and version control. The environment file below is a redacted configuration pattern, not a verbatim export of the deployed environment:

# Illustrative .env pattern — match variable names to the image documentation
AZURE_TENANT_ID=<tenant-id>
AZURE_CLIENT_ID=<application-client-id>
AZURE_CLIENT_SECRET=<client-secret>
AZURE_SUBSCRIPTION_ID=<subscription-id>
Credential hygiene: A client secret was visible during lab troubleshooting. Any secret exposed in a screenshot, terminal recording, or repository should be rotated in Entra and replaced in the container configuration. Never include a live secret in portfolio documentation.

05 / Deploy the MCP container

The MCP service was deployed as a Docker container alongside an existing n8n Docker Compose installation. Both services were connected to the shared soc-mcp-net network. The service was reachable inside Docker at:

http://azure-mcp:5000/

The following snippet documents the networking arrangement without inventing the exact image reference, startup command, or environment-variable mapping used during installation:

# Illustrative Docker Compose networking fragment
services:
  azure-mcp:
    # image: <verified Azure MCP Server 3.0.0 image reference>
    # environment: <image-specific Azure credential variables>
    networks:
      - soc-mcp-net

  n8n:
    # Existing working n8n service configuration retained
    networks:
      - soc-mcp-net

networks:
  soc-mcp-net:
    external: true  # Only if the network was created separately

Because the original Compose file and exact image startup arguments were not preserved in the available project notes, this is intentionally a topology example rather than a copy-and-paste deployment manifest. The important verified details are the running Azure MCP Server 3.0.0 service, the shared network, and the internal HTTP endpoint.

To inspect a deployment without changing it:

docker ps
docker network inspect soc-mcp-net
docker logs --tail 100 azure-mcp

Check the actual Compose service and container names before using the commands.

06 / Connect n8n to Azure MCP

Step 1 — Add an MCP Client Tool

Within the n8n AI Agent workflow, an MCP Client Tool was connected to the Azure MCP endpoint using the HTTP Streamable transport. The endpoint was configured as http://azure-mcp:5000/. The AI Agent could then discover and invoke supported Azure operations.

Step 2 — Select the Log Analytics query operation

The operation used for Sentinel investigations was monitor_workspace_log_query. The tool was exposed to the agent as MCP_Client_monitor. Its parameters included the workspace, underlying table, KQL query, subscription, lookback hours, and result limit.

{
  "command": "monitor_workspace_log_query",
  "parameters": {
    "workspace": "sentinel",
    "subscription": "<subscription-id>",
    "table": "cowrie_JSON_CL",
    "query": "Cowrie_Normalized | take 5",
    "hours": 168,
    "limit": 100
  }
}
Important distinction: The MCP table parameter remains cowrie_JSON_CL, while the KQL itself references the saved function Cowrie_Normalized. Mixing these up caused early agent-generated queries to fail because SourceIp is created by the function, not stored as a physical column in the raw table.

Step 3 — Confirm tool execution

The n8n MCP Client Tool's standalone test interface was not consistently reliable during setup. Successful tool calls from within an AI Agent execution provided the meaningful end-to-end validation. The execution logs showed the exact query submitted and the response returned by Azure MCP.

07 / Validate access with a read-only KQL query

The Cowrie honeypot sends events to the physical cowrie_JSON_CL table. A saved KQL function, Cowrie_Normalized, parses the RawData JSON into typed fields such as SourceIp, EventID, Session, Username and Input.

Cowrie_Normalized
| where TimeGenerated >= datetime(2026-10-17T15:00:00Z)
| where TimeGenerated <= datetime(2026-10-17T18:00:00Z)
| where SourceIp == "45.148.10.240"
| summarize
    TotalEvents = count(),
    DistinctSessions = dcount(Session),
    FailedLogins = countif(EventID == "cowrie.login.failed"),
    SuccessfulLogins = countif(EventID == "cowrie.login.success"),
    Commands = countif(EventID == "cowrie.command.input")

This query was independently checked in Sentinel and used as a baseline for agent testing. The verified result was 526 events, 98 distinct sessions, 84 failed logins, 13 successful logins and 13 command events for the test IP and window.

A separate command investigation retrieved the observed command /bin/./uname -s -v -n -r -m 13 times. This allowed direct comparison between actual Sentinel evidence and the model's generated report.

08 / Troubleshooting and lessons learned

IssueObserved cause / lessonResolution
Unknown SourceIp columnThe model queried cowrie_JSON_CL directly instead of the saved parsing function.Clarified the distinction between the MCP table argument and the KQL source.
Invalid KQL aggregationModel-generated sum(if(...)) failed with an unknown-function error.Used supported countif() aggregations and supplied known-good query patterns.
Unknown User columnThe model guessed a column that was not exposed by Cowrie_Normalized.The agent inspected the error and retried successfully with Username.
Invented commands and countsA plausible SOC report was not necessarily grounded in tool results.Compared MCP execution logs with Sentinel; identified the need for a separate evidence-validation stage.
Missing incident contextThe model substituted an arbitrary IP and historical dates when no real incident values were supplied.Passed IP and UTC timestamps explicitly from n8n into the agent's user prompt.

Tool invocation is not the same as analytical reliability. In one later test, Azure MCP returned 23 events and two command-input records, including SSH configuration/key-related commands, while the agent's final report incorrectly claimed there were only two events and no commands. This directly motivated plans for deterministic evidence validation before publishing SOC reports.

09 / Security design

Credential isolation

Azure credentials reside with the MCP service, not in the local model prompt or the Discord report.

Read-only investigation

Limit the service principal and agent to required query operations wherever practical.

Network boundary

Use the private Docker network for n8n-to-MCP communication; avoid exposing the MCP endpoint publicly without additional controls.

Auditability

Retain executed KQL, tool errors, responses, incident parameters and generated reports for review.

The design should also treat log data as untrusted input: honeypot usernames, command strings and other attacker-controlled fields must not be interpreted as instructions to the agent. Avoid publishing captured credentials or sensitive content in reports.

10 / Outcome and next steps

The deployment successfully established an authenticated Azure MCP tool interface between n8n and Microsoft Sentinel. A local Qwen3:14b model could execute real KQL queries, perform conditional follow-up investigations and recover from certain schema errors. The project also exposed a critical engineering requirement: the final narrative must be validated against the actual tool outputs.

The next planned enhancement is an n8n evidence-validation stage that compares model-generated findings with recorded MCP results before the investigation report is delivered to Discord. This keeps the agent flexible while preventing unsupported claims from being presented as confirmed SOC evidence.