nexus-mcp/archive/Intune/CoPilot Generated Deployment Plan v2.md
nathan 479df6bd8a chore: archive legacy Identity, Workday, and Intune folders
- Move Identity/, Workday/, Intune/ to archive/ (superseded by nexus-mcp shards)
- Move 'Local Setup.md' to archive/ (superseded by nexus-mcp/Local-Setup.md)
- Add archive/README.md explaining migration and preserved content
- Clean repository structure: only nexus-mcp, documentation, and .github remain active

All legacy functionality migrated to nexus-mcp sharded architecture.
Archived folders preserved for reference and historical context.

Refs: SESSION_SNAPSHOT_2026-04-13.md
2026-04-13 09:38:42 -04:00

11 KiB
Raw Blame History

StepbyStep Guide: Building an Intune MCP Server (Phase 1 ReadOnly)


0. What you are building (anchor this first)

From both documents, the Intune MCP is defined as:

A readonly MCP server that exposes live Microsoft Intune device state (inventory, compliance, ownership, last checkin) to AI clients, using the same delivery pattern as Identity MCP. [wheelsinc-...epoint.com], [wheelsinc-...epoint.com]

Key constraints (do not skip these):

  • Readonly only in Phase 1
  • Microsoft Graph is the backend
  • Stable tool contracts (no raw Graph payloads)
  • STDIO MCP transport
  • Pertool audit logging
  • No device actions yet

1. Complete prerequisites (must be done first)

Everything in this section is taken directly from intune-mcp-prerequisites-and-checklists.md. [wheelsinc-...epoint.com]

1.1 Governance & ownership

Confirm and document:

  • Product owner: Endpoint / Intune
  • Security owner
  • Operational owner (Service Desk / Endpoint Ops)
  • Approved operation list (read operations only)
  • Signed read vs write boundary

Output: Approved Phase 1 scope document


1.2 Microsoft tenant preparation

Verify:

  • Intune tenant is healthy
  • Microsoft Graph access is approved
  • A nonproduction tenant or pilot scope exists

Output: Tenant readiness confirmation


1.3 App registration (authentication model)

Create an Azure App Registration for the Intune MCP:

  • Authentication:
    • Certificatebased auth (preferred)
    • Client secret (temporary only)
  • Create a service principal
  • Define token lifetime & rotation policy

Output: App ID, Tenant ID, auth method documented


1.4 Microsoft Graph permissions (least privilege)

Grant only the following for Phase 1:

  • DeviceManagementManagedDevices.Read.All
  • DeviceManagementConfiguration.Read.All (only if policy context is needed)
  • Directory.Read.All (only if joining user/device info)

Admin consent must be explicitly granted and justified. [wheelsinc-...epoint.com]

Output: Permission justification record


1.5 Runtime & platform

Prepare:

  • Python 3.10+
  • Dependency manager (uv recommended)
  • Secure secret storage (no tokens in code)
  • Logging destination (file or central sink)

Output: Runtime ready


2. Create the MCP project scaffold

This follows the Identity MCP replication pattern explicitly required in the prerequisites file. [wheelsinc-...epoint.com]

2.1 Create project

mkdir intune-mcp
cd intune-mcp
uv init
uv venv
uv add "mcp[cli]" httpx

2.2 Create file structure

From the Build checklist (implementation) section: [wheelsinc-...epoint.com]

intune_mcp_server.py
intune_backend.py
intune_graph_adapter.py
tests/
  test_intune_adapter.py
  test_integration.py
pyproject.toml

3. Implement the MCP server entrypoint

3.1 MCP server (STDIO only)

intune_mcp_server.py

from mcp.server.fastmcp import FastMCP
from intune_backend import IntuneBackend

mcp = FastMCP("intune-mcp")
backend = IntuneBackend()

@mcp.tool()
async def intune_get_device(device_id: str):
    """Return core device metadata."""
    return await backend.get_device(device_id)

@mcp.tool()
async def intune_get_device_last_check_in(device_id: str):
    """Return last Intune check-in timestamp."""
    return await backend.get_last_check_in(device_id)

@mcp.tool()
async def intune_list_stale_devices(days: int):
    """List devices that have not checked in for N days."""
    return await backend.list_stale_devices(days)

if __name__ == "__main__":
    # STDIO transport is mandatory for safety
    mcp.run(transport="stdio")

This aligns with the recommended Phase 1 tool set. [wheelsinc-...epoint.com]


4. Implement backend abstraction (safe by default)

4.1 Backend selector

intune_backend.py

import os
from intune_graph_adapter import GraphIntuneAdapter

class IntuneBackend:
    def __init__(self):
        backend = os.getenv("INTUNE_BACKEND", "graph")
        if backend == "graph":
            self.adapter = GraphIntuneAdapter()
        else:
            raise ValueError("Unsupported backend")

    async def get_device(self, device_id):
        return await self.adapter.get_device(device_id)

    async def get_last_check_in(self, device_id):
        return await self.adapter.get_last_check_in(device_id)

    async def list_stale_devices(self, days):
        return await self.adapter.list_stale_devices(days)

This matches the environmentbased backend selection requirement. [wheelsinc-...epoint.com]


5. Implement the Microsoft Graph adapter

5.1 Graph adapter responsibilities

From the checklist, the adapter must: [wheelsinc-...epoint.com]

  • Handle token acquisition
  • Handle throttling (429)
  • Map Graph fields → stable schemas
  • Never return raw Graph payloads

5.2 Example adapter

intune_graph_adapter.py

import httpx
import datetime

class GraphIntuneAdapter:
    def __init__(self):
        self.base_url = "https://graph.microsoft.com/v1.0"

    async def get_device(self, device_id):
        data = await self._get(f"/deviceManagement/managedDevices/{device_id}")
        return {
            "device_id": data["id"],
            "device_name": data["deviceName"],
            "os": data["operatingSystem"],
            "owner": data.get("userPrincipalName"),
            "compliance_state": data["complianceState"],
        }

    async def get_last_check_in(self, device_id):
        data = await self._get(f"/deviceManagement/managedDevices/{device_id}")
        return {
            "device_id": data["id"],
            "last_check_in": data["lastSyncDateTime"],
        }

    async def list_stale_devices(self, days):
        cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=days)
        devices = await self._get("/deviceManagement/managedDevices")
        return [
            {
                "device_id": d["id"],
                "device_name": d["deviceName"],
                "last_check_in": d["lastSyncDateTime"],
            }
            for d in devices["value"]
            if datetime.datetime.fromisoformat(
                d["lastSyncDateTime"].replace("Z", "")
            ) < cutoff
        ]

    async def _get(self, path):
        # token acquisition omitted here by design (handled securely)
        async with httpx.AsyncClient(timeout=10) as client:
            r = await client.get(self.base_url + path, headers=self._headers())
            if r.status_code == 429:
                raise Exception("Graph throttling encountered")
            r.raise_for_status()
            return r.json()

    def _headers(self):
        return {"Authorization": "Bearer <token>"}

6. Logging and audit controls (mandatory)

From the prerequisites: [wheelsinc-...epoint.com]

  • STDERRonly logging
  • Pertool audit record
  • No secrets logged

Implement:

  • tool name
  • parameters (redacted)
  • result size
  • correlation ID

Failure to do this blocks Phase 1 completion


7. Testing gates

7.1 Unit tests

From the checklist: [wheelsinc-...epoint.com]

  • Parser behavior
  • Field mapping
  • Error handling

7.2 Integration tests

  • Run against nonproduction tenant
  • Compare MCP output vs Intune portal for:
    • compliant device
    • noncompliant device
    • stale device

Output: Test evidence retained


8. Pilot rollout

From Phase 5 checklist: [wheelsinc-...epoint.com]

  • Enable MCP for pilot users only
  • Validate top service desk questions:
    • “Devices not checking in”
    • “Devices assigned to disabled users”
  • Test rollback by disabling Graph backend

9. Definition of Done (Phase 1)

All must be true: [wheelsinc-...epoint.com]

  • No write tools exist
  • Stable response schemas
  • Friendly errors
  • Complete audit logs
  • Tests passing
  • Security signoff recorded
  • Runbook published

What you have when this is finished

  • A real MCP server
  • Backed by Microsoft Graph
  • Answering live Intune questions
  • Safe, auditable, and productiondefensible
  • Ready for Phase 2 correlation with Identity + Inventory MCPs [wheelsinc-...epoint.com]