The Model Context Protocol (MCP) is an open standard that lets you give Claude direct access to tools and data sources — your own APIs, databases, file systems, or any service you can call from Python. Instead of copying and pasting output from your tools into a chat window, Claude connects to them directly and can query, reason over, and act on real live data. This article walks through building a minimal MCP server that exposes IT infrastructure data to Claude.

What MCP Actually Is

An MCP server is a lightweight process that exposes tools (functions Claude can call), resources (data Claude can read), and prompts (reusable templates). Claude discovers what tools are available and decides when to call them based on the conversation. You define the tools; Claude decides when to use them.

Prerequisites

Install the MCP Python SDK and the Microsoft Graph SDK. Claude Desktop (or any MCP-compatible client) handles the connection automatically when you register your server.

pip install mcp msgraph-sdk azure-identity

Building a Simple IT Infrastructure MCP Server

This server exposes three tools: look up a device in Intune, check a user’s last sign-in in Entra ID, and list open high-severity alerts in Defender. Once connected, you can ask Claude questions like “has jsmith’s laptop been compliant in the last 7 days?” and it will call the appropriate tools to find out.

import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
from msgraph import GraphServiceClient
from azure.identity import ClientSecretCredential

credential = ClientSecretCredential(
    tenant_id=TENANT_ID, client_id=CLIENT_ID, client_secret=CLIENT_SECRET
)
graph = GraphServiceClient(credential)
app = Server("it-infra")

@app.list_tools()
async def list_tools():
    return [
        types.Tool(
            name="get_device_compliance",
            description="Get Intune compliance status for a device by name or serial number",
            inputSchema={"type":"object","properties":{"device_name":{"type":"string"}},"required":["device_name"]}
        ),
        types.Tool(
            name="get_user_last_signin",
            description="Get the last sign-in time and risk level for an Entra ID user",
            inputSchema={"type":"object","properties":{"upn":{"type":"string"}},"required":["upn"]}
        ),
        types.Tool(
            name="list_defender_alerts",
            description="List open Defender for Endpoint alerts filtered by severity",
            inputSchema={"type":"object","properties":{"severity":{"type":"string","enum":["high","medium","low"]}},"required":["severity"]}
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_device_compliance":
        devices = await graph.device_management.managed_devices.get(
            request_configuration=lambda c: setattr(
                c, "query_parameters",
                {"filter": f"deviceName eq '{arguments['device_name']}'", "select": ["deviceName","complianceState","lastSyncDateTime","userPrincipalName"]}
            )
        )
        if devices.value:
            d = devices.value[0]
            return [types.TextContent(type="text", text=f"Device: {d.device_name}\nCompliance: {d.compliance_state}\nLast sync: {d.last_sync_date_time}\nUser: {d.user_principal_name}")]
        return [types.TextContent(type="text", text="Device not found")]

async def main():
    async with stdio_server() as (read, write):
        await app.run(read, write, app.create_initialization_options())

asyncio.run(main())

Registering the Server with Claude Desktop

Add your server to ~/.config/claude/claude_desktop_config.json (macOS/Linux) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "it-infra": {
      "command": "python",
      "args": ["C:/tools/mcp/it_infra_server.py"],
      "env": {
        "TENANT_ID": "your-tenant-id",
        "CLIENT_ID": "your-client-id",
        "CLIENT_SECRET": "your-client-secret"
      }
    }
  }
}

What You Can Ask Claude Once It Is Connected

With this server running, Claude can answer questions that previously required you to open three different portals:

  • “Is LAPTOP-0042 compliant? When did it last sync?”
  • “Has anyone with a high-risk sign-in logged in today?”
  • “Show me all open high-severity Defender alerts and summarise what they have in common”
  • “Which devices owned by the Finance team have not synced in the last 72 hours?”

Summary

MCP turns Claude from a chat assistant into an agent that can directly query your infrastructure. The protocol is open, the Python SDK is easy to use, and the security model is straightforward — your server runs locally, your credentials never leave your machine, and Claude only gets access to the tools you explicitly define.