Connecting LLMs to Azure through MCPs

We all know how amazing MCP is! Now, one of the things still lacking is an MCP Server for Azure. There are some for competing platforms, but Azure remains difficult. So how can we still get Azure Context into our LLM?

Let's discover that in this article, on how you can get started and achieve the below, where we can use Claude to manage all our resources!

Azure OpenAPI Generation

The easiest way to create an MCP server is to utilize OpenAPI, where we can utilize an abstraction layer that generates an MCP server automatically for us. To do so, we thus first need to get the OpenAPI spec. Now, let this be just what is a bit "interesting" with Azure. Documentation is a bit scattered, and there is no single OpenAPI URL that I could find.

Luckily, there is StackQL that allows us to generate OpenAPI specifications for Azure. So let's get started by cloning that repository

git clone https://github.com/stackql/stackql-azure-openapi
cd stackql-azure-openapi 
chmod +x bin/stackql-azure-openapi
chmod +x ./prereq.sh
bun i

Once that is done, our dependencies are installed, and we are ready to generate OpenAPI specifications. Let's generate the OpenAPI specification for the compute and resource API specifications.

# Generate resources en compute openapi spec
# more info: https://github.com/stackql/stackql-azure-openapi
bin/stackql-azure-openapi generate resources compute
bin/stackql-azure-openapi dereference resources compute
bin/stackql-azure-openapi combine resources compute

When ran, it will create an OpenAPI specification without any external pointer references and output it to ./openapi/3-combined/resources/resources.yaml in our stackql-azure-openapi directory.

As this is a YAML file, we need to convert it to JSON to work with the MCP Server we are using:

brew install yq
cat resources.yaml | yq e -j > resources.json

Finally, a last manual action is needed, and that is to find and remove all "x-api-version": "20.*" references in that JSON file.

Getting Azure Bearer Token

Create a Service Principal & Configure Permissions

Now our MCP server is ready to be ran, let's configure Azure with a Service Principal that has access to the resources that we want to access:

  1. Create an Azure AD Application and Service Principal: by going to the Azure Portal –> Microsoft Entra ID (formerly Azure AD) –> Navigate to "App registrations" and click "+ New registration"–> Fill in the necessary information for your application –> After creation, note the Application (client) ID and Directory (tenant) ID
  2. Generate a Client Secret: In your registered app, go to "Certificates & secrets"
  3. Create a new client secret and save it securely (you won't be able to view it again)
  4. Provide "Global Reader": by navigating to Microsoft Entra ID –> Select "Roles and administrators" –> Search for and select "Global Reader" –> Click "Add assignments" –> In the search box, enter your service principal name or ID –> Check the box next to the matching entry and select "Add"
  5. Provide "Reader": Do this for the individual subscriptions

Generate a Bearer Token

Now the service principal has the correct rights assigned, let's generate a Bearer token:

export CLIENT_ID=""
export CLIENT_SECRET=""
export TENANT_ID=""

curl https://login.microsoftonline.com/$TENANT_ID/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&resource=https%3A%2F%2Fmanagement.azure.com%2F"

Start the MCP Server

To get the MCP Server to start, open your claude_desktop_config.json and add the below to configure the MCP server to start correctly:

{
    "mcpServers": {
        "azure": {
            "command": "/Users/<your_user>/.bun/bin/bun",
            "args": [
                "run",
                "<FULL_PATH>/mcp-openapi-server/src/index.ts"
            ],
            "env": {
                "API_BASE_URL": "https://management.azure.com/",
                "OPENAPI_SPEC_PATH": "<FULL_PATH>/stackql-azure-openapi/openapi/3-combined/resources/resources.json",
                "API_HEADERS": "Authorization: Bearer <TOKEN>"
            }
        }
    }
}

Testing

Now when we launch Claude, we will see all the tools available! Running a prompt such as Generate me an architecture of all my resources and dependencies in Azure for the subscription "<SUB_ID>". Print it as a Mermaid diagram. will beautifully render the below!

Further Research

Now, why is this important? Imagine importing all your Azure APIs here, your other technology landscape APIs here through MCP servers. Your LLM tool will then be your Enterprise Architect! Beautifully creating an always up-to-date AS-IS architecture.