Skip to main content
Glama

Remote MCP with Azure Functions

MIT License
main.bicep7.93 kB
targetScope = 'subscription' @minLength(1) @maxLength(64) @description('Name of the the environment which is used to generate a short unique hash used in all resources.') param environmentName string @minLength(1) @description('Primary location for all resources & Flex Consumption Function App') @allowed([ 'australiaeast' 'australiasoutheast' 'brazilsouth' 'canadacentral' 'centralindia' 'centralus' 'eastasia' 'eastus' 'eastus2' 'eastus2euap' 'francecentral' 'germanywestcentral' 'italynorth' 'japaneast' 'koreacentral' 'northcentralus' 'northeurope' 'norwayeast' 'southafricanorth' 'southcentralus' 'southeastasia' 'southindia' 'spaincentral' 'swedencentral' 'uaenorth' 'uksouth' 'ukwest' 'westcentralus' 'westeurope' 'westus' 'westus2' 'westus3' ]) @metadata({ azd: { type: 'location' } }) param location string param vnetEnabled bool param apiServiceName string = '' param apiUserAssignedIdentityName string = '' param applicationInsightsName string = '' param appServicePlanName string = '' param logAnalyticsName string = '' param resourceGroupName string = '' param storageAccountName string = '' param vNetName string = '' @description('Id of the user identity to be used for testing and debugging. This is not required in production. Leave empty if not needed.') param principalId string = deployer().objectId var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } var functionAppName = !empty(apiServiceName) ? apiServiceName : '${abbrs.webSitesFunctions}api-${resourceToken}' var deploymentStorageContainerName = 'app-package-${take(functionAppName, 32)}-${take(toLower(uniqueString(functionAppName, resourceToken)), 7)}' // Organize resources in a resource group resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}' location: location tags: tags } // User assigned managed identity to be used by the function app to reach storage and other dependencies // Assign specific roles to this identity in the RBAC module module apiUserAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = { name: 'apiUserAssignedIdentity' scope: rg params: { location: location tags: tags name: !empty(apiUserAssignedIdentityName) ? apiUserAssignedIdentityName : '${abbrs.managedIdentityUserAssignedIdentities}api-${resourceToken}' } } // Create an App Service Plan to group applications under the same payment plan and SKU module appServicePlan 'br/public:avm/res/web/serverfarm:0.1.1' = { name: 'appserviceplan' scope: rg params: { name: !empty(appServicePlanName) ? appServicePlanName : '${abbrs.webServerFarms}${resourceToken}' sku: { name: 'FC1' tier: 'FlexConsumption' } reserved: true location: location tags: tags } } module api './app/api.bicep' = { name: 'api' scope: rg params: { name: functionAppName location: location tags: tags applicationInsightsName: monitoring.outputs.name appServicePlanId: appServicePlan.outputs.resourceId runtimeName: 'python' runtimeVersion: '3.12' storageAccountName: storage.outputs.name enableBlob: storageEndpointConfig.enableBlob enableQueue: storageEndpointConfig.enableQueue enableTable: storageEndpointConfig.enableTable deploymentStorageContainerName: deploymentStorageContainerName identityId: apiUserAssignedIdentity.outputs.resourceId identityClientId: apiUserAssignedIdentity.outputs.clientId appSettings: { } virtualNetworkSubnetId: vnetEnabled ? serviceVirtualNetwork.outputs.appSubnetID : '' } } // Backing storage for Azure functions backend API module storage 'br/public:avm/res/storage/storage-account:0.8.3' = { name: 'storage' scope: rg params: { name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${resourceToken}' allowBlobPublicAccess: false allowSharedKeyAccess: false // Disable local authentication methods as per policy dnsEndpointType: 'Standard' publicNetworkAccess: vnetEnabled ? 'Disabled' : 'Enabled' networkAcls: vnetEnabled ? { defaultAction: 'Deny' bypass: 'None' } : { defaultAction: 'Allow' bypass: 'AzureServices' } blobServices: { containers: [{name: deploymentStorageContainerName}] } minimumTlsVersion: 'TLS1_2' // Enforcing TLS 1.2 for better security location: location tags: tags } } // Define the configuration object locally to pass to the modules var storageEndpointConfig = { enableBlob: true // Required for AzureWebJobsStorage, .zip deployment, Event Hubs trigger and Timer trigger checkpointing enableQueue: true // Required for Durable Functions and MCP trigger enableTable: false // Required for Durable Functions and OpenAI triggers and bindings enableFiles: false // Not required, used in legacy scenarios allowUserIdentityPrincipal: true // Allow interactive user identity to access for testing and debugging } // Consolidated Role Assignments module rbac 'app/rbac.bicep' = { name: 'rbacAssignments' scope: rg params: { storageAccountName: storage.outputs.name appInsightsName: monitoring.outputs.name managedIdentityPrincipalId: apiUserAssignedIdentity.outputs.principalId userIdentityPrincipalId: principalId enableBlob: storageEndpointConfig.enableBlob enableQueue: storageEndpointConfig.enableQueue enableTable: storageEndpointConfig.enableTable allowUserIdentityPrincipal: storageEndpointConfig.allowUserIdentityPrincipal } } // Virtual Network & private endpoint to blob storage module serviceVirtualNetwork 'app/vnet.bicep' = if (vnetEnabled) { name: 'serviceVirtualNetwork' scope: rg params: { location: location tags: tags vNetName: !empty(vNetName) ? vNetName : '${abbrs.networkVirtualNetworks}${resourceToken}' } } module storagePrivateEndpoint 'app/storage-PrivateEndpoint.bicep' = if (vnetEnabled) { name: 'servicePrivateEndpoint' scope: rg params: { location: location tags: tags virtualNetworkName: !empty(vNetName) ? vNetName : '${abbrs.networkVirtualNetworks}${resourceToken}' subnetName: vnetEnabled ? serviceVirtualNetwork.outputs.peSubnetName : '' // Keep conditional check for safety, though module won't run if !vnetEnabled resourceName: storage.outputs.name enableBlob: storageEndpointConfig.enableBlob enableQueue: storageEndpointConfig.enableQueue enableTable: storageEndpointConfig.enableTable } } // Monitor application with Azure Monitor - Log Analytics and Application Insights module logAnalytics 'br/public:avm/res/operational-insights/workspace:0.11.1' = { name: '${uniqueString(deployment().name, location)}-loganalytics' scope: rg params: { name: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' location: location tags: tags dataRetention: 30 } } module monitoring 'br/public:avm/res/insights/component:0.6.0' = { name: '${uniqueString(deployment().name, location)}-appinsights' scope: rg params: { name: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' location: location tags: tags workspaceResourceId: logAnalytics.outputs.resourceId disableLocalAuth: true } } // App outputs output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.connectionString output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output SERVICE_API_NAME string = api.outputs.SERVICE_API_NAME output AZURE_FUNCTION_NAME string = api.outputs.SERVICE_API_NAME

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/pravin22kumar/MCP'

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