From d11970abfa8dd2b4279ed547db80dea19f19c6da Mon Sep 17 00:00:00 2001 From: Abdul-Microsoft Date: Wed, 27 Aug 2025 00:18:27 +0530 Subject: [PATCH 1/2] feat: refactor mcp server Dockerfile to compatible with uv sync and added bicep code for container app --- infra/main.bicep | 95 +++++++++++++++++++++++++++++++++++++++ src/mcp_server/Dockerfile | 43 +++++++++++------- 2 files changed, 122 insertions(+), 16 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index f6ea978ee..cab551f1a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1046,6 +1046,101 @@ module containerApp 'br/public:avm/res/app/container-app:0.14.2' = if (container } } +var containerAppMcpResourceName = 'ca-mcp-${solutionPrefix}' +module containerAppMcp 'br/public:avm/res/app/container-app:0.18.1' = if (containerAppEnabled) { + name: take('avm.res.app.container-app.${containerAppMcpResourceName}', 64) + params: { + name: containerAppMcpResourceName + tags: containerAppConfiguration.?tags ?? tags + location: containerAppConfiguration.?location ?? solutionLocation + enableTelemetry: enableTelemetry + environmentResourceId: containerAppConfiguration.?environmentResourceId ?? containerAppEnvironment.outputs.resourceId + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [userAssignedIdentity!.outputs.resourceId] + } + ingressTargetPort: containerAppConfiguration.?ingressTargetPort ?? 8000 + ingressExternal: true + activeRevisionsMode: 'Single' + corsPolicy: { + allowedOrigins: [ + ] + } + // WAF aligned configuration for Scalability + scaleSettings: { + maxReplicas: containerAppConfiguration.?maxReplicas ?? 1 + minReplicas: containerAppConfiguration.?minReplicas ?? 1 + rules: [ + { + name: 'http-scaler' + http: { + metadata: { + concurrentRequests: containerAppConfiguration.?concurrentRequests ?? '100' + } + } + } + ] + } + containers: [ + { + name: 'mcp' + image: 'macaer.azurecr.io/macaebackend:macaev3' //'${containerAppConfiguration.?containerImageRegistryDomain ?? 'biabcontainerreg.azurecr.io'}/${containerAppConfiguration.?containerImageName ?? 'macaebackend'}:${containerAppConfiguration.?containerImageTag ?? 'latest'}' + resources: { + //TODO: Make cpu and memory parameterized + cpu: containerAppConfiguration.?containerCpu ?? '2.0' + memory: containerAppConfiguration.?containerMemory ?? '4.0Gi' + } + env: [ + { + name: 'MCP_HOST' + value: '0.0.0.0' + } + { + name: 'MCP_PORT' + value: '9000' + } + { + name: 'MCP_DEBUG' + value: 'false' + } + { + name: 'MCP_SERVER_NAME' + value: 'MACAE MCP Server' + } + { + name: 'MCP_ENABLE_AUTH' + value: 'true' + } + { + name: 'AZURE_TENANT_ID' + value: tenant().tenantId + } + { + name: 'AZURE_CLIENT_ID' + value: userAssignedIdentity!.outputs.clientId + } + { + name: 'AZURE_JWKS_URI' + value: 'https://login.microsoftonline.com/${tenant().tenantId}/discovery/v2.0/keys' + } + { + name: 'AZURE_ISSUER' + value: 'https://sts.windows.net/${tenant().tenantId}/' + } + { + name: 'AZURE_AUDIENCE' + value: 'api://${userAssignedIdentity!.outputs.clientId}' + } + { + name: 'DATASET_PATH' + value: './datasets' + } + ] + } + ] + } +} + var webServerFarmEnabled = webServerFarmConfiguration.?enabled ?? true var webServerFarmResourceName = webServerFarmConfiguration.?name ?? 'asp-${solutionPrefix}' diff --git a/src/mcp_server/Dockerfile b/src/mcp_server/Dockerfile index d1702f3d4..9a665b1d9 100644 --- a/src/mcp_server/Dockerfile +++ b/src/mcp_server/Dockerfile @@ -1,21 +1,32 @@ -FROM python:3.11-slim - -# Set working directory +FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye AS base WORKDIR /app -# Install system dependencies -RUN apt-get update && apt-get install -y \ - gcc \ - && rm -rf /var/lib/apt/lists/* +FROM base AS builder + +# Copy uv binaries from astral-sh image +COPY --from=ghcr.io/astral-sh/uv:0.6.3 /uv /uvx /bin/ +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy + +# Copy lock and project files first for caching +COPY uv.lock pyproject.toml /app/ + +# Install dependencies (frozen, no dev) using uv +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-install-project --no-dev -# Copy requirements first for better caching -COPY requirements.txt . +# Copy application code and install project dependencies +COPY . /app +RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev + +# Final stage +FROM base + +WORKDIR /app +COPY --from=builder /app /app +COPY --from=builder /bin/uv /bin/uv -# Install Python dependencies -RUN pip install --no-cache-dir -r requirements.txt -#### -# Copy the application -COPY . . +# Set PATH to use venv created by uv +ENV PATH="/app/.venv/bin:$PATH" # Create non-root user RUN useradd --create-home --shell /bin/bash app && chown -R app:app /app @@ -28,5 +39,5 @@ EXPOSE 9000 HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ CMD curl -f http://localhost:9000/health || exit 1 -# Default command -CMD ["python", "mcp_server.py"] +# Run your main script +CMD ["uv", "run", "python", "mcp_server.py"] From 4e39c1735b7fe41dd2ac04f0defd5f0cd40e41da Mon Sep 17 00:00:00 2001 From: Dhruvkumar-Microsoft Date: Thu, 28 Aug 2025 13:34:56 +0530 Subject: [PATCH 2/2] remove the relative import to run it on docker --- infra/main.bicep | 4 +++- src/mcp_server/Dockerfile | 2 +- src/mcp_server/services/data_tool_service.py | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 91ac5d723..f45f61351 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1100,6 +1100,8 @@ module containerAppMcp 'br/public:avm/res/app/container-app:0.18.1' = if (contai activeRevisionsMode: 'Single' corsPolicy: { allowedOrigins: [ + 'https://${webSiteName}.azurewebsites.net' + 'http://${webSiteName}.azurewebsites.net' ] } // WAF aligned configuration for Scalability @@ -1120,7 +1122,7 @@ module containerAppMcp 'br/public:avm/res/app/container-app:0.18.1' = if (contai containers: [ { name: 'mcp' - image: 'macaer.azurecr.io/macaebackend:macaev3' //'${containerAppConfiguration.?containerImageRegistryDomain ?? 'biabcontainerreg.azurecr.io'}/${containerAppConfiguration.?containerImageName ?? 'macaebackend'}:${containerAppConfiguration.?containerImageTag ?? 'latest'}' + image: 'macaemcpacrdk.azurecr.io/macae-mac-app:t7' //'${containerAppConfiguration.?containerImageRegistryDomain ?? 'biabcontainerreg.azurecr.io'}/${containerAppConfiguration.?containerImageName ?? 'macaebackend'}:${containerAppConfiguration.?containerImageTag ?? 'latest'}' resources: { //TODO: Make cpu and memory parameterized cpu: containerAppConfiguration.?containerCpu ?? '2.0' diff --git a/src/mcp_server/Dockerfile b/src/mcp_server/Dockerfile index 9a665b1d9..6952a4f51 100644 --- a/src/mcp_server/Dockerfile +++ b/src/mcp_server/Dockerfile @@ -40,4 +40,4 @@ HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ CMD curl -f http://localhost:9000/health || exit 1 # Run your main script -CMD ["uv", "run", "python", "mcp_server.py"] +CMD ["uv", "run", "python", "mcp_server.py", "--transport", "http", "--host", "0.0.0.0", "--port", "9000"] diff --git a/src/mcp_server/services/data_tool_service.py b/src/mcp_server/services/data_tool_service.py index ccae5ca2f..e22650a1f 100644 --- a/src/mcp_server/services/data_tool_service.py +++ b/src/mcp_server/services/data_tool_service.py @@ -1,7 +1,7 @@ import os import logging from typing import List -from ..core.factory import MCPToolBase, Domain +from core.factory import MCPToolBase, Domain ALLOWED_FILES = [ "competitor_Pricing_Analysis.csv", @@ -88,3 +88,8 @@ def show_tables() -> List[str]: "No allowed CSV tables found in '%s' directory.", self.dataset_path ) return found_tables + + @property + def tool_count(self) -> int: + """Return the number of tools provided by this service.""" + return 2 # data_provider and show_tables