diff --git a/infra/main.bicep b/infra/main.bicep index 5d6a878ea..f45f61351 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1082,6 +1082,103 @@ 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: [ + 'https://${webSiteName}.azurewebsites.net' + 'http://${webSiteName}.azurewebsites.net' + ] + } + // 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: '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' + 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..6952a4f51 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", "--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