Skip to content

Architecture

This page describes az-scout's internal architecture — useful for contributors and plugin developers.


System Overview

graph TB
    subgraph Browser["Web Browser"]
        UI["Single-page UI<br/>(Bootstrap 5 + D3.js)"]
    end

    subgraph Core["az-scout core"]
        APP["app.py<br/>(FastAPI bootstrap)"]
        DISC["routes/discovery.py<br/>(tenants, subs, regions)"]
        CHAT["POST /api/chat<br/>(SSE streaming)"]
        MCP["MCP Server<br/>(/mcp endpoint)"]
    end

    subgraph Plugins["Internal Plugins"]
        TOPO["topology/<br/>GET /api/mappings<br/>get_zone_mappings tool"]
        PLAN["planner/<br/>GET /api/skus<br/>POST /api/deployment-confidence<br/>+ 3 more routes, 4 MCP tools"]
    end

    subgraph Shared["Shared Modules"]
        API["azure_api/<br/>(ARM calls, caching)"]
        SCORE["scoring/<br/>(Deployment Confidence)"]
        SVC["services/<br/>(deployment_planner, ai_chat)"]
    end

    subgraph Azure["Azure"]
        ARM["Azure Resource Manager"]
        PRICES["Retail Prices API"]
        OPENAI["Azure OpenAI"]
    end

    UI -->|REST| APP
    UI -->|REST| DISC
    UI -->|REST| TOPO
    UI -->|REST| PLAN
    UI -->|SSE| CHAT

    MCP -->|tool calls| TOPO
    MCP -->|tool calls| PLAN

    TOPO --> API
    PLAN --> API
    PLAN --> SCORE
    DISC --> API
    CHAT --> SVC

    API --> ARM
    API --> PRICES
    SVC --> OPENAI

Request Flow: SKU Availability

How a GET /api/skus?region=westeurope&subscriptionId=xxx&includePrices=true request flows through the system:

sequenceDiagram
    participant B as Browser
    participant R as planner/routes.py
    participant A as azure_api/skus.py
    participant Q as azure_api/quotas.py
    participant P as azure_api/pricing.py
    participant S as scoring/
    participant ARM as Azure ARM

    B->>R: GET /api/skus?region=westeurope&...
    R->>A: get_skus(region, sub_id, ...)
    A->>ARM: GET /providers/Microsoft.Compute/skus?$filter=location eq 'westeurope'
    ARM-->>A: SKU list (zones, restrictions, capabilities)
    A-->>R: list[dict]

    R->>Q: enrich_skus_with_quotas(skus, ...)
    Q->>ARM: GET /providers/Microsoft.Compute/locations/westeurope/usages
    ARM-->>Q: quota usage per family
    Q-->>R: skus updated in-place

    R->>P: enrich_skus_with_prices(skus, ...)
    P->>ARM: GET prices.azure.com/retail/prices?$filter=...
    ARM-->>P: PAYGO + Spot prices
    P-->>R: skus updated in-place

    R->>S: enrich_skus_with_confidence(skus)
    S-->>R: confidence scores added

    R-->>B: JSONResponse(skus)

Internal Plugin Architecture

Both built-in features (AZ Topology, Deployment Planner) and external plugins use the same AzScoutPlugin protocol:

graph LR
    subgraph Core
        DISC2["discover_internal_plugins()"]
        EP["importlib entry_points()"]
        REG["register_plugins()"]
    end

    subgraph Internal["Internal Plugins (core package)"]
        T["topology/"]
        P["planner/"]
    end

    subgraph External["External Plugins (pip packages)"]
        E1["az-scout-plugin-batch-sku"]
        E2["az-scout-plugin-latency-stats"]
    end

    DISC2 --> T
    DISC2 --> P
    EP --> E1
    EP --> E2

    T --> REG
    P --> REG
    E1 --> REG
    E2 --> REG

    REG -->|routes| FA["FastAPI app"]
    REG -->|tools| MC["MCP server"]
    REG -->|tabs| UI2["Jinja2 template"]
    REG -->|chat modes| CH["AI chat"]

Internal vs external plugins:

Aspect Internal External
Location src/az_scout/internal_plugins/ Separate pip package
Route prefix /api (backward-compatible) /plugins/{name}
Static prefix /internal/{name}/static /plugins/{name}/static
Discovery discover_internal_plugins() importlib.metadata.entry_points
Plugin Manager Shows "built-in" badge Install / uninstall / update

Module Map

src/az_scout/
├── app.py                    # FastAPI bootstrap (220 lines)
├── logging_config.py         # Unified coloured logging
├── cli.py                    # Click CLI (web + mcp)
├── mcp_server.py             # MCP server (discovery tools only)
├── plugin_api.py             # AzScoutPlugin protocol + dataclasses
├── plugins.py                # Plugin discovery + registration
├── plugin_manager/           # Plugin install/validate/uninstall (7 modules)
├── azure_api/                # Azure ARM helpers (stable API: __all__ + PLUGIN_API_VERSION)
├── scoring/                  # Deployment Confidence Score
├── services/
│   ├── ai_chat/              # AI chat (6 modules)
├── models/                   # Pydantic models
├── routes/
│   ├── __init__.py            # Plugin manager API
│   └── discovery.py           # Tenants, subscriptions, regions
├── internal_plugins/
│   ├── topology/              # AZ Topology tab
│   └── planner/               # Deployment Planner tab
├── static/                    # Core JS/CSS/images
└── templates/                 # Jinja2 template