Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions deployment/infra/azure-deployment.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ param resourceLabel string = resourceGroup().name
@description('The Azure region for resource deployment. Defaults to the resource group location.')
param location string = resourceGroup().location

@description('Enable private endpoints for Azure resources (ACR, Cosmos DB). When enabled, resources will only be accessible within the VNet.')
param enablePrivateEndpoints bool = false

var resourceLabelLower = toLower(resourceLabel)

var aksNameBase = 'mg-aks-${resourceLabelLower}'
Expand All @@ -36,6 +39,9 @@ var aksSubnetName = substring(aksSubnetNameBase, 0, min(length(aksSubnetNameBase
var appGwSubnetNameBase = 'mg-aag-subnet-${resourceLabelLower}'
var appGwSubnetName = substring(appGwSubnetNameBase, 0, min(length(appGwSubnetNameBase), 80))

var peSubnetNameBase = 'mg-pe-subnet-${resourceLabelLower}'
var peSubnetName = substring(peSubnetNameBase, 0, min(length(peSubnetNameBase), 80))

var appGwNameBase = 'mg-aag-${resourceLabelLower}'
var appGwName = substring(appGwNameBase, 0, min(length(appGwNameBase), 80))

Expand Down Expand Up @@ -73,6 +79,13 @@ resource vnet 'Microsoft.Network/virtualNetworks@2022-07-01' = {
addressPrefix: '10.0.2.0/24'
}
}
{
name: peSubnetName
properties: {
addressPrefix: '10.0.3.0/24'
privateEndpointNetworkPolicies: 'Disabled'
}
}
]
}
}
Expand Down Expand Up @@ -155,6 +168,7 @@ resource aks 'Microsoft.ContainerService/managedClusters@2023-04-01' = {
dependsOn: [vnet]
}


// Attach ACR to AKS
resource acrRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(aks.id, acr.id, 'acrpull')
Expand Down Expand Up @@ -384,6 +398,7 @@ resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
defaultConsistencyLevel: 'Session'
}
enableFreeTier: false
publicNetworkAccess: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
}
}

Expand Down Expand Up @@ -452,6 +467,63 @@ resource cosmosDbRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAs
}
}

// Private DNS Zone for Cosmos DB
resource cosmosPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (enablePrivateEndpoints) {
name: 'privatelink.documents.azure.com'
location: 'global'
}

// Link Private DNS Zone to VNet
resource cosmosDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = if (enablePrivateEndpoints) {
parent: cosmosPrivateDnsZone
name: '${vnetName}-cosmos-link'
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnet.id
}
}
}

// Private Endpoint for Cosmos DB
resource cosmosPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = if (enablePrivateEndpoints) {
name: 'pe-${cosmosDbAccountName}'
location: location
properties: {
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, peSubnetName)
}
privateLinkServiceConnections: [
{
name: 'pe-${cosmosDbAccountName}-connection'
properties: {
privateLinkServiceId: cosmosDb.id
groupIds: [
'Sql'
]
}
}
]
}
dependsOn: [vnet]
}

resource cosmosPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-04-01' = if (enablePrivateEndpoints) {
parent: cosmosPrivateEndpoint
name: 'cosmos-dns-zone-group'
properties: {
privateDnsZoneConfigs: [
{
name: 'privatelink-documents-azure-com'
properties: {
privateDnsZoneId: cosmosPrivateDnsZone.id
}
}
]
}
}

// Application Insights
resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: appInsightsName
Expand Down
92 changes: 90 additions & 2 deletions deployment/infra/azure-deployment.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"_generator": {
"name": "bicep",
"version": "0.38.33.27573",
"templateHash": "12874722858654947563"
"templateHash": "5648627402420051357"
}
},
"parameters": {
Expand All @@ -30,6 +30,13 @@
"metadata": {
"description": "The Azure region for resource deployment. Defaults to the resource group location."
}
},
"enablePrivateEndpoints": {
"type": "bool",
"defaultValue": false,
"metadata": {
"description": "Enable private endpoints for Azure resources (ACR, Cosmos DB). When enabled, resources will only be accessible within the VNet."
}
}
},
"variables": {
Expand All @@ -50,6 +57,8 @@
"aksSubnetName": "[substring(variables('aksSubnetNameBase'), 0, min(length(variables('aksSubnetNameBase')), 80))]",
"appGwSubnetNameBase": "[format('mg-aag-subnet-{0}', variables('resourceLabelLower'))]",
"appGwSubnetName": "[substring(variables('appGwSubnetNameBase'), 0, min(length(variables('appGwSubnetNameBase')), 80))]",
"peSubnetNameBase": "[format('mg-pe-subnet-{0}', variables('resourceLabelLower'))]",
"peSubnetName": "[substring(variables('peSubnetNameBase'), 0, min(length(variables('peSubnetNameBase')), 80))]",
"appGwNameBase": "[format('mg-aag-{0}', variables('resourceLabelLower'))]",
"appGwName": "[substring(variables('appGwNameBase'), 0, min(length(variables('appGwNameBase')), 80))]",
"publicIpNameBase": "[format('mg-pip-{0}', variables('resourceLabelLower'))]",
Expand Down Expand Up @@ -84,6 +93,13 @@
"properties": {
"addressPrefix": "10.0.2.0/24"
}
},
{
"name": "[variables('peSubnetName')]",
"properties": {
"addressPrefix": "10.0.3.0/24",
"privateEndpointNetworkPolicies": "Disabled"
}
}
]
}
Expand Down Expand Up @@ -424,7 +440,8 @@
"consistencyPolicy": {
"defaultConsistencyLevel": "Session"
},
"enableFreeTier": false
"enableFreeTier": false,
"publicNetworkAccess": "[if(parameters('enablePrivateEndpoints'), 'Disabled', 'Enabled')]"
}
},
{
Expand Down Expand Up @@ -511,6 +528,77 @@
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName'))]"
]
},
{
"condition": "[parameters('enablePrivateEndpoints')]",
"type": "Microsoft.Network/privateDnsZones",
"apiVersion": "2020-06-01",
"name": "privatelink.documents.azure.com",
"location": "global"
},
{
"condition": "[parameters('enablePrivateEndpoints')]",
"type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
"apiVersion": "2020-06-01",
"name": "[format('{0}/{1}', 'privatelink.documents.azure.com', format('{0}-cosmos-link', variables('vnetName')))]",
"location": "global",
"properties": {
"registrationEnabled": false,
"virtualNetwork": {
"id": "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
}
},
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', 'privatelink.documents.azure.com')]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
]
},
{
"condition": "[parameters('enablePrivateEndpoints')]",
"type": "Microsoft.Network/privateEndpoints",
"apiVersion": "2023-04-01",
"name": "[format('pe-{0}', variables('cosmosDbAccountName'))]",
"location": "[parameters('location')]",
"properties": {
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnetName'), variables('peSubnetName'))]"
},
"privateLinkServiceConnections": [
{
"name": "[format('pe-{0}-connection', variables('cosmosDbAccountName'))]",
"properties": {
"privateLinkServiceId": "[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('cosmosDbAccountName'))]",
"groupIds": [
"Sql"
]
}
}
]
},
"dependsOn": [
"[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('cosmosDbAccountName'))]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
]
},
{
"condition": "[parameters('enablePrivateEndpoints')]",
"type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups",
"apiVersion": "2023-04-01",
"name": "[format('{0}/{1}', format('pe-{0}', variables('cosmosDbAccountName')), 'cosmos-dns-zone-group')]",
"properties": {
"privateDnsZoneConfigs": [
{
"name": "privatelink-documents-azure-com",
"properties": {
"privateDnsZoneId": "[resourceId('Microsoft.Network/privateDnsZones', 'privatelink.documents.azure.com')]"
}
}
]
},
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', 'privatelink.documents.azure.com')]",
"[resourceId('Microsoft.Network/privateEndpoints', format('pe-{0}', variables('cosmosDbAccountName')))]"
]
},
{
"type": "Microsoft.Insights/components",
"apiVersion": "2020-02-02",
Expand Down
14 changes: 14 additions & 0 deletions deployment/k8s/cloud-deployment-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ spec:
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
envFrom:
- configMapRef:
name: app-config
Expand Down Expand Up @@ -145,6 +152,13 @@ spec:
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
envFrom:
- configMapRef:
name: app-config
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.McpGateway.Service.Session;

namespace Microsoft.McpGateway.Service.Controllers
{
[ApiController]
[Route("adapters")]
[Authorize]
public class AdapterReverseProxyController(IHttpClientFactory httpClientFactory, IAdapterSessionStore sessionStore, ISessionRoutingHandler sessionRoutingHandler) : ControllerBase
{
private const string ToolGateway = "toolgateway";
private readonly IHttpClientFactory httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
private readonly IAdapterSessionStore sessionStore = sessionStore ?? throw new ArgumentNullException(nameof(sessionStore));
private readonly ISessionRoutingHandler sessionRoutingHandler = sessionRoutingHandler ?? throw new ArgumentNullException(nameof(sessionRoutingHandler));

/// <summary>
/// Support for MCP streamable HTTP connection.
/// </summary>
[HttpPost("{name}/mcp")]
public async Task ForwardStreamableHttpRequest(string name, CancellationToken cancellationToken)
[HttpPost("mcp")]
[HttpPost("adapters/{name}/mcp")]
public async Task ForwardStreamableHttpRequest(string? name, CancellationToken cancellationToken)
{
var sessionId = AdapterSessionRoutingHandler.GetSessionId(HttpContext);
string? targetAddress;
if (string.IsNullOrEmpty(sessionId))
targetAddress = await sessionRoutingHandler.GetNewSessionTargetAsync(name, HttpContext, cancellationToken).ConfigureAwait(false);
targetAddress = await sessionRoutingHandler.GetNewSessionTargetAsync(name ?? ToolGateway, HttpContext, cancellationToken).ConfigureAwait(false);
else
targetAddress = await sessionRoutingHandler.GetExistingSessionTargetAsync(HttpContext, cancellationToken).ConfigureAwait(false);

Expand Down
Loading