Skip to main content
Glama

Azure MCP Server

Official
MIT License
1,161
  • Linux
  • Apple
GenerateMermaidChart.cs9.93 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Immutable; using System.Text; using Azure.ResourceManager.Network.Models; using AzureMcp.Deploy.Options; using Microsoft.Extensions.ObjectPool; namespace AzureMcp.Deploy.Commands; public static class GenerateMermaidChart { // used to create a subgraph for AKS cluster in the chart private const string aksClusterInternalName = "akscluster"; private const string aksClusterName = "Azure Kubernetes Service (AKS) Cluster"; private const string acaEnvInternalName = "acaenvironment"; private const string acaEnvName = "Azure Container Apps Environment"; public static string GenerateChart(string workspaceFolder, AppTopology appTopology) { var chartComponents = new List<string>(); chartComponents.Add("graph TD"); if (appTopology.Services.Any(s => s.AzureComputeHost == "aks")) { chartComponents.Add("classDef cluster fill:#ffffd0,stroke:#333,stroke-width:2px,color:#000"); } var services = new List<string> { "%% Services" }; var resources = new List<string> { "%% Compute Resources" }; var dependencyResources = new List<string> { "%% Binding Resources" }; var relationships = new List<string> { "%% Relationships" }; foreach (var service in appTopology.Services) { var serviceName = new List<string> { $"Name: {service.Name}" }; if (!string.IsNullOrWhiteSpace(workspaceFolder)) { var projectRelativePath = Path.GetRelativePath(workspaceFolder, string.IsNullOrWhiteSpace(service.Path) ? workspaceFolder : service.Path); serviceName.Add($"Path: {projectRelativePath}"); } serviceName.Add($"Language: {service.Language}"); serviceName.Add($"Port: {service.Port}"); if (service.DockerSettings != null && string.Equals(service.AzureComputeHost, "azurecontainerapp", StringComparison.OrdinalIgnoreCase)) { serviceName.Add($"DockerFile: {service.DockerSettings.DockerFilePath}"); serviceName.Add($"Docker Context: {service.DockerSettings.DockerContext}"); } var serviceInternalName = $"svc-{service.Name}"; services.Add(CreateComponentName(serviceInternalName, string.Join("\n", serviceName), NodeShape.Rectangle)); relationships.Add(CreateRelationshipString(serviceInternalName, $"{FlattenServiceType(service.AzureComputeHost)}_{service.Name}", "hosted on", ArrowType.Solid)); } var aksClusterExists = false; var containerAppEnvExists = false; foreach (var service in appTopology.Services) { string serviceResourceInternalName = $"{FlattenServiceType(service.AzureComputeHost)}_{service.Name}"; if (service.AzureComputeHost == "aks") { if (!aksClusterExists) { // Add AKS cluster as a subgraph resources.Add($"subgraph {aksClusterInternalName} [\"{aksClusterName}\"]"); // containerized services share the same AKS cluster foreach (var aksservice in appTopology.Services.Where(s => s.AzureComputeHost == "aks")) { resources.Add(CreateComponentName($"{aksservice.AzureComputeHost}_{aksservice.Name}", $"{aksservice.Name} (Containerized Service)", NodeShape.RoundedRectangle)); } resources.Add("end"); resources.Add($"{aksClusterInternalName}:::cluster"); aksClusterExists = true; } } else if (service.AzureComputeHost == "containerapp") { if (!containerAppEnvExists) { // Add Container App Environment as a subgraph resources.Add($"subgraph {acaEnvInternalName} [\"{acaEnvName}\"]"); // containerized services share the same Container App Environment foreach (var containerAppService in appTopology.Services.Where(s => s.AzureComputeHost == "containerapp")) { resources.Add(CreateComponentName($"{containerAppService.AzureComputeHost}_{containerAppService.Name}", $"{containerAppService.Name} (Container App)", NodeShape.RoundedRectangle)); } resources.Add("end"); containerAppEnvExists = true; } } // each service should have a compute resource type else if (!resources.Any(r => r.Contains(serviceResourceInternalName))) { resources.Add(CreateComponentName(serviceResourceInternalName, $"{service.Name} ({GetFormalName(service.AzureComputeHost)})", NodeShape.RoundedRectangle)); } foreach (var dependency in service.Dependencies) { var instanceInternalName = $"{FlattenServiceType(dependency.ServiceType)}.{dependency.Name}"; var instanceName = $"{dependency.Name} ({GetFormalName(dependency.ServiceType)})"; if (IsComputeResourceType(dependency.ServiceType)) { if (!resources.Any(r => r.Contains(EnsureUrlFriendlyName(instanceInternalName)))) { resources.Add(CreateComponentName(instanceInternalName, instanceName, NodeShape.RoundedRectangle)); } } else { dependencyResources.Add(CreateComponentName(instanceInternalName, instanceName, NodeShape.Rectangle)); } relationships.Add(CreateRelationshipString(serviceResourceInternalName, instanceInternalName, dependency.ConnectionType, ArrowType.Dotted)); } } chartComponents.AddRange(services); chartComponents.AddRange(resources); chartComponents.Add("subgraph \"Compute Resources\""); chartComponents.AddRange(resources); chartComponents.Add("end"); chartComponents.Add("subgraph \"Dependency Resources\""); chartComponents.AddRange(dependencyResources); chartComponents.Add("end"); chartComponents.AddRange(relationships); return string.Join("\n", chartComponents); } private static string CreateComponentName(string internalName, string name, NodeShape nodeShape) { var nodeShapeBrackets = GetNodeShapeBrackets(nodeShape); return $"{EnsureUrlFriendlyName(internalName)}{nodeShapeBrackets[0]}\"`{name}`\"{nodeShapeBrackets[1]}"; } private static string CreateRelationshipString(string sourceName, string targetName, string connectionDescription, ArrowType arrowType) { var arrowSymbol = GetArrowSymbol(arrowType); return $"{EnsureUrlFriendlyName(sourceName)} {arrowSymbol} |\"{connectionDescription}\"| {EnsureUrlFriendlyName(targetName)}"; } private static string EnsureUrlFriendlyName(string name) { return name.Replace('.', '_') .Replace(" ", "_") .Trim() .ToLowerInvariant(); } private static string[] GetNodeShapeBrackets(NodeShape nodeShape) { return nodeShape switch { NodeShape.Rectangle => ["[", "]"], NodeShape.Circle => ["((", "))"], NodeShape.RoundedRectangle => ["(", ")"], NodeShape.Cylinder => ["[(", ")]"], NodeShape.Hexagon => ["{{", "}}"], _ => ["[", "]"] }; } private static string GetArrowSymbol(ArrowType arrowType) { return arrowType switch { ArrowType.Solid => "-->", ArrowType.Open => "->", ArrowType.Dotted => "-.->", _ => "-->" }; } private static string GetFormalName(string name) { if (ResourceTypeConverter.TryGetValue(name, out var formalName)) { return formalName; } return name; } private static string FlattenServiceType(string serviceType) { return serviceType.ToLowerInvariant().Replace("azure", ""); } private static bool IsComputeResourceType(string serviceType) { return Enum.GetNames<AzureServiceConstants.AzureComputeServiceType>().Contains(serviceType, StringComparer.OrdinalIgnoreCase); } private static IDictionary<string, string> ResourceTypeConverter = new Dictionary<string, string> { { "appservice", "Azure App Service" }, { "containerapp", "Azure Container Apps" }, { "functionapp", "Azure Functions" }, { "staticwebapp", "Azure Static Web Apps" }, { "aks", "Azure Kubernetes Services" }, { "azureaisearch", "Azure AI Search" }, { "azureaiservices", "Azure AI Services" }, { "azureapplicationinsights", "Azure Application Insights" }, { "azurebotservice", "Azure Bot Service" }, { "azurecosmosdb", "Azure Cosmos DB" }, { "azurekeyvault", "Azure Key Vault" }, { "azuredatabaseformysql", "Azure Database for MySQL" }, { "azureopenai", "Azure OpenAI" }, { "azuredatabaseforpostgresql", "Azure Database for PostgreSQL" }, { "azuresqldatabase", "Azure SQL Database" }, { "azurecacheforredis", "Azure Cache For Redis"}, { "azurestorageaccount", "Azure Storage Account" }, { "azureservicebus", "Azure Service Bus" }, { "azurewebpubsub", "Azure Web PubSub"} }; } public enum NodeShape { Rectangle, Circle, RoundedRectangle, Cylinder, Hexagon } public enum ArrowType { Solid, Open, Dotted }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Azure/azure-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server