Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
This project uses Calendar Versioning (YYYY.MM.MICRO).
Unreleased¶
[2026.4.1] - 2026-04-02¶
Added¶
showSkuDetailModal()– New shared JS component insku-detail-modal.jsthat provides a full-flow SKU detail modal for all plugins. Shows loading state, fetches from/api/sku-detail, renders confidence breakdown (with signal tooltips, knockout reasons, disclaimers), VM profile, zone availability, quota, pricing (with currency selector), and supports plugin-specific extra sections (extraSections,prependSections) and recalculate callbacks. Modal DOM is created lazily — noindex.htmlchanges needed.parse_sku_series()– New utility inazure_api.skusthat extracts the VM series prefix from ARM SKU names (e.g.Standard_D2s_v5→D,Standard_NC24ads_A100_v4→NC). Exported inazure_apipublic API.- Expanded SKU capabilities –
get_skus()now extracts 15 capabilities (up from 4): addedAcceleratedNetworkingEnabled,EphemeralOSDiskSupported,HyperVGenerations,GPUs,CachedDiskBytes,MaxResourceVolumeMB,LowPriorityCapable,TrustedLaunchDisabled,EncryptionAtHostSupported,CpuArchitectureType,UltraSSDAvailable. Enables workload eligibility filtering by plugins. - MSDO security scanning – Added Microsoft Security DevOps (MSDO) CI workflow for automated code security analysis on PRs and main.
- Dockerfile healthcheck – Added
HEALTHCHECKinstruction to the container image.
Changed¶
- Planner modal migration – Planner now uses shared
showSkuDetailModal()instead of its own custom modal. Removes ~200 lines of duplicate rendering code. Planner-specific features (recalculate with Spot, instance count) are injected viaonRecalculatecallback. renderConfidenceBreakdown()– Upgraded shared component with signal description tooltips, knockout reasons alert, disclaimers with "Learn more" link, and dynamic title (blocked/basic+spot/basic).- PLUGIN_API_VERSION bumped to
1.3(additive: new exports, expanded capabilities).
Fixed¶
- XSS:
escapeHtml()attribute-context escape – Replaced thediv.textContent/innerHTMLapproach with OWASP Rule #1 character replacement (& < > " '), fixing unescaped"and'in attribute contexts (e.g.title="...",data-*="..."). Removed duplicate escape helpers fromplugins.jsandcatalog.htmlin favour of the shared global. - XSS:
injectVersion– MovedescHtml(ver)intoinjectVersionitself to enforce safety regardless of call site. - Plugin Manager reload – Restored full page reload after plugin install/uninstall/update so new or removed tabs, routes, and JS are picked up.
- Control-char sanitization –
pmUninstallstrips control characters fromdistNamebeforeconfirm()/showGlobalStatus(). - Biome lint cleanup – Converted
functionexpressions to arrow functions,||guards to optional chaining,vartoconst/letacross shared components. - pymdownx fix – Replaced pygments pin with
pymdownx>=10.21.2(proper fix forfilename=Nonecrash).
Removed¶
- Dead code – Removed unused
showSignInScreen()andgetActiveTabFromHash()fromapp.js. - Planner duplicate code – Removed local
renderConfidenceBreakdown(),renderZoneAvailability(),renderPricingDetail(),fetchPricingDetail(),refreshPricingModal()from planner.js (now handled by shared modal).
[2026.4.0] - 2026-04-01¶
Added¶
SkuDictTypedDict – Formal data contract (SkuDict,SkuCapabilities,SkuQuota,SkuPricing,SkuConfidence) inplugin_api.pydocumenting the canonical SKU dict shape expected by enrichment functions.enrich_skus()pipeline – New async helper inazure_apithat runs the full enrichment chain (quotas → prices → spot → confidence) with opt-in steps and correct ordering. Plugins no longer need to reimplement the 4-step enrichment sequence.- Confidence scoring re-exports –
compute_deployment_confidence,signals_from_sku, andenrich_skus_with_confidenceare now re-exported fromazure_apifor plugin convenience. - Core SKU detail route –
GET /api/sku-detailcombines VM profile, pricing, and deployment confidence into a single response. Shared by all plugins for SKU detail modals. - Tab reordering – Main tabs can be reordered via drag-and-drop. The custom order is persisted in
localStorageand restored on page load. A grip icon (⠿) appears on hover to indicate draggability. New plugin tabs are appended at the end; stale tabs from uninstalled plugins are silently dropped.
[2026.3.8] - 2026-03-28¶
Added¶
- Docs catalog page – The plugin catalog documentation page now embeds the shared
catalog.htmlfragment via an iframe wrapping a standalone Bootstrap page generated at build time by theon_post_buildhook. System theme detection, toggle button, and the full card-based catalog UI are available inline in the docs. - File instructions – Split
copilot-instructions.md(390→93 lines) into 5 domain-specific file instructions that load automatically when editing relevant files:azure-api,obo-auth,frontend,plugin-dev,plugin-scaffold. create-pluginskill – Interactive skill (/create-plugin) that guides plugin scaffold generation, conventions, and quality checks.- Auth guard on API routes –
require_authFastAPI dependency enforces OBO authentication on discovery, chat, AI completion, and all plugin API routes. Unauthenticated requests return 401 when OBO is enabled; in non-OBO mode the guard is a no-op. - Native extension detection – Plugin install/update routes detect newly installed compiled extensions (
.so,.pyd,.dylib) and returnrestart_required: truein the API response, prompting users to restart the instance. - Shared catalog UI – New
catalog.htmlfragment renders plugin cards fromcatalog.jsonwith filter, tags, authors, PyPI badges, long description truncation, and a 3-tab install modal (Plugin Manager / uv / pip). Used by both the Plugin Manager and future docs/standalone catalog pages. - Plugin Manager redesign – Switched from offcanvas to a responsive modal. Catalog cards rendered from shared
catalog.htmlare progressively enhanced with install/update/uninstall buttons. Built-in, external, and dependency plugins are detected and displayed with appropriate badges and actions. - Built-in plugin metadata – Internal plugins (topology, planner) now have
display_nameanddescriptionattributes exposed in the plugin API. - Dependency plugin management – Plugins installed as dependencies into the packages directory (e.g. via another plugin) are now detected and manageable (uninstall) from the UI instead of being labeled "external".
- Restart banner – Plugin Manager shows a warning banner when a plugin with native extensions is installed, indicating a container restart is required.
- Global status bar – Plugin Manager shows an inline status bar with spinner during install/uninstall/update operations.
- Auto update check – Plugin Manager silently checks for updates when opened, showing results immediately.
- CSP: shields.io – Added
img.shields.ioto the Content-Security-Policyimg-srcdirective for PyPI version badges.
Fixed¶
- Plugin core version guard – Plugin Manager now validates that a plugin's
az-scoutversion requirement is compatible with the running instance before install. Incompatible plugins are blocked with a clear error. A pip constraint file also prevents pip from installing a different core version into the packages directory.
Changed¶
- Plugin Manager layout – Replaced table-based views with a card grid layout matching the catalog style. All plugins (catalog, installed, built-in, external) shown in a unified card grid with a filter, action bar, and manual install card.
- Modal sizing – Plugin Manager modal uses
modal-xlwithmax-width: min(95vw, 1400px)for responsive width. - Plugin list API –
/api/pluginsresponse now includesdisplay_name,description,in_packages_dirfields for loaded plugins to support the enhanced UI.
Removed¶
- Duplicate prompts – Removed
add-plugin-to-catalog.prompt.mdandreview-plugin.prompt.mdfrom the core repo. These now live canonically in the plugin-catalog repo. - Separate catalog table – Removed the old table-based catalog/installed views in the Plugin Manager in favour of the shared card-based catalog UI.
[2026.3.7] - 2026-03-26¶
Fixed¶
- Plugin uninstall in ACA –
uv pip uninstallnow appends--systemwhen running outside a virtual environment (e.g. in Azure Container Apps), fixingNo virtual environment founderrors (#115). - Plugin compatibility – Plugin protocol check now uses a lenient attribute check instead of
isinstance(obj, AzScoutPlugin). Plugins missing newer optional methods (e.g.get_navbar_actions) load correctly again (#116). - Broken versioning – Removed non-CalVer tag (
obo-single-tenant-v1) that causedhatch-vcsto produce2.dev4instead of2026.3.x.devN(#117). - Chat tables overflow – Tables in chat bubbles now scroll horizontally instead of overflowing outside the panel.
Added¶
- On-Behalf-Of (OBO) authentication – Multi-user mode where each user signs in with their Microsoft account and az-scout accesses Azure ARM APIs with their RBAC permissions instead of the app's managed identity. Enabled via
AZ_SCOUT_CLIENT_ID,AZ_SCOUT_CLIENT_SECRET, andAZ_SCOUT_TENANT_IDenvironment variables. - Server-side auth flow – OAuth 2.0 authorization code flow with signed HTTP-only session cookies. Login page with two options: sign in with your account (organizations authority) or target a specific tenant (domain/ID input).
- Single-tenant-per-session model – Each session is scoped to the tenant the user authenticated against. OBO always uses the login tenant, eliminating cross-tenant failures. To switch tenants, sign out and sign in with a different tenant.
- OBO validation at login – OBO exchange is validated during login before creating the session. Consent, MFA, and other errors are shown on the login page with actionable messages (Grant Admin Consent button, Copy link, etc.) — the main app never loads with invalid auth.
- Login page – Dedicated sign-in page with side-by-side cards for account login and tenant-specific login, error alerts for all auth failure types, admin login button in navbar.
- Role-based access control – Entra ID App Roles (
Admin) enforced server-side. Plugin management restricted to home-tenant admins. Non-admins see a read-only UI viaadmin-onlyCSS class. - Auth context middleware – Raw ASGI middleware propagates user tokens to all routes (including plugins and MCP tools) via module globals and context vars.
- Sentinel-based OBO guard –
_NO_TOKENsentinel prevents web requests from falling through toDefaultAzureCredentialwhen OBO is enabled. - MCP auth via Bearer token – VS Code MCP clients authenticate via
Authorizationheader. - Retail Prices retry – Connection errors on the Azure Retail Prices API are now retried with exponential backoff.
- Biome JS lint – Added to pre-commit hooks and ship-code-change prompt.
- Non-streaming AI completion endpoint –
POST /api/ai/completeruns the full tool-calling loop server-side and returns a single JSON response. Plugins can useplugin_ai_complete()(Python) oraiComplete()(JS) for inline AI recommendations outside the chat panel. - AI completion caching – Results are cached in-memory with a configurable TTL (default 5 min, max 128 entries). Plugins can set
cache_ttl=0to bypass the cache. is_ai_enabled()/aiEnabled– Plugin helpers (Python and JS) to check if AI capabilities are configured.renderMarkdown()global – Sharedmarked.jsv15 renderer available to all plugins for rendering AI output as HTML.- Chat markdown via marked.js – Chat
_renderMarkdown()now uses marked.js with custom extensions for[[…]]clickable chips, compact chip lists, and styled tables/headings. Replaces the old regex-based parser. - Chat pin/open persistence – Chat panel pinned state and open/closed state are saved to localStorage and restored on reload.
- Chat h1 rendering – Markdown
# heading(h1) is now rendered in chat bubbles. - Clickable choices for all chat modes – The
[[option]]formatting instruction is now appended to all chat modes (including plugin-contributed modes), so LLM responses consistently use clickable chips for selectable options. - Chat history for plugin modes –
_restoreChatHistorynow restores all saved modes (including plugin-contributed ones), not justdiscussionandplanner.
Changed¶
- Plugin error handling –
PluginErrorexceptions caused byOboTokenErrorreturn 401 (not 502) and suppress stacktraces. - Remote plugin catalog – Plugin Manager now fetches from
plugin-catalog.az-scout.cominstead of the embedded JSON file (#107). - Persistent plugin packages – plugin packages now install to
~/.local/share/az-scout/packages/(persistent) instead of/tmp/(lost on reboot). Containers useAZ_SCOUT_PACKAGES_DIR=/tmp/az-scout-packagesto preserve SMB compatibility. - Dynamic docs catalog – plugin catalog page now renders dynamically from the remote catalog with live PyPI version badges, author avatars, and tags.
Removed¶
- Embedded
recommended_plugins.json– replaced by the remote catalog at az-scout/plugin-catalog.
[2026.3.6] - 2026-03-12¶
Added¶
- Interactive CLI chat (
az-scout chat) – terminal-based AI chat with Rich-rendered markdown responses, tool call panels with spinners,[[choice]]patterns as numbered options, and conversation history with Up/Down navigation. Supports one-shot queries (az-scout chat "question") and interactive sessions (#103). - Slash commands –
/help,/context,/tenant,/subscription,/region,/mode,/tenants,/subscriptions,/regions,/clear,/new,/exitwith Tab auto-completion for commands and arguments (mode names, region names, tenant/subscription names). - New dependency –
prompt-toolkit>=3.0for interactive terminal input with completion and history. - ODCR Coverage plugin – added
az-scout-plugin-odcr-coverageto the plugin catalog and recommended plugins.
Changed¶
- Domain migration – updated documentation site URL from
azscout.vupti.metodocs.az-scout.com. - ARM token caching – tokens are now cached per-tenant with thread-safe locking, eliminating redundant
credential.get_token()calls and debug log spam during concurrent requests. - ARM request timing – all ARM HTTP calls now log elapsed time at DEBUG level (
ARM GET … → 200 (0.34s)) for performance troubleshooting. - Plugin naming convention – updated docs and scaffold generator to recommend
az-scout-plugin-{name}package naming (matching all existing plugins).
[2026.3.5] - 2026-03-10¶
Changed¶
- GitHub organization migration – updated all repository URLs, GHCR container image paths, Deploy to Azure button URIs, and OCI labels from
lrivallain/az-scouttoaz-scout/az-scoutacross source, docs, deploy templates, CI workflows, and configuration files.
[2026.3.4] - 2026-03-08¶
Added¶
- Public ARM helpers – new
arm_get(),arm_post(), andarm_paginate()functions inazure_apiprovide authenticated ARM calls with built-in 429/5xx retry, exponential backoff,Retry-Afterheader support, and structured error handling (ArmAuthorizationError,ArmNotFoundError,ArmRequestError). These are the recommended way for plugins and core modules to interact with Azure Resource Manager (#97). get_headers()public alias – promoted from internal_get_headers()for plugins that need raw Bearer-token headers for non-ARM endpoints (#95).PLUGIN_API_VERSIONbumped to1.1– additive change, backward compatible.- Plugin error boundary –
PluginError,PluginValidationError, andPluginUpstreamErrorexception classes inplugin_apiwith a global handler that returns consistent{"error", "detail"}JSON responses. Plugins can raise typed exceptions from route handlers instead of manual try/except + JSONResponse (#98). - Copilot prompts – added
triage-issue,review-plugin,add-plugin-to-catalog,tag-release, andship-code-changereusable prompt files for coding agents.
Changed¶
- Internal modules migrated to ARM helpers –
discovery.py,skus.py,quotas.py, andspot.pynow usearm_get/arm_post/arm_paginateinstead of rawrequests.get/requests.post+ manual retry loops. This eliminates duplicated retry/backoff/error handling code across 4 modules.
Fixed¶
apiFetch/apiPosterror messages – now reads bothbody.errorandbody.detailkeys so FastAPI's standardHTTPExceptionpattern works for all plugins without workarounds (#96).- Biome JS lint errors – fixed optional chaining,
Number.isNaN,useConst, arrow functions, and redundantuse strictacross all JS files.
[2026.3.3] - 2026-03-06¶
Added¶
- Plugin creation -
az-scout create-pluginscaffolds a new plugin project with a Rich-powered interactive CLI experience (prompts + generation summary) and supports non-interactive usage for automation. - Plugin system prompt addendum hook – plugins can now contribute extra guidance to the default AI chat
discussionsystem prompt via an optionalget_system_prompt_addendum()capability. This enables domain-specific disambiguation (for example, interpretingAV*asks as AVS context) without requiring a dedicated chat mode (#93). - Frontend context events for plugin authors – core UI now emits event-driven context updates for plugin scripts:
azscout:tenants-loadedazscout:tenant-changedazscout:regions-loadedazscout:region-changedazscout:subscriptions-loaded
Changed¶
- Plugin documentation simplified to event-first context integration – removed legacy
MutationObserver/direct DOM-watch guidance from plugin docs in favor of the newazscout:*event model. - Plugin scaffold JS template updated – scaffold tab script now listens to
azscout:tenant-changedandazscout:region-changed, and includes an emptyazscout:subscriptions-loadedlistener as an extension point for plugin authors.
Removed¶
- Deployment plan generation – the
/api/deployment-planendpoint and related logic for generating deployment plans based on SKU availability and confidence scores has been removed.
[2026.3.2] - 2026-03-05¶
Added¶
- Documentation - Add
mkdocsdocs based websites, hosted in GitHub Pages, with a custom theme and structure. Initial content includes: - Home page with project overview and quick start guide.
- Detailed API reference generated from FastAPI's OpenAPI schema.
- Plugin development guide with architecture overview, API contract, and scaffold reference.
- Scoring methodology documentation explaining the confidence score components and rationale...
- Internal plugin architecture – core features (AZ Topology, Deployment Planner) are now
structured as internal plugins using the same
AzScoutPluginprotocol as external plugins. Internal plugins ship inside the core package and are discovered automatically at startup. Routes mount at/api(backward-compatible URLs), static assets at/internal/{name}/static, and tabs render dynamically via the Jinja2 template (#76). azure_apistable plugin API surface – addedPLUGIN_API_VERSION = "1.0"and__all__toaz_scout.azure_api, formally declaring the 20 public functions/constants that plugins can rely on. Internal helpers are still accessible but excluded from the stability guarantee.- Planner chat mode as
ChatMode– the Planner system prompt (previously hardcoded inai_chat.py) is now aChatModecontributed by the planner internal plugin, discovered dynamically viaget_plugin_chat_modes(). - Plugin catalog (recommendations) – the Plugin Manager now shows a curated list of
recommended plugins loaded from
recommended_plugins.json, with quick-install buttons and installed status badges. NewGET /api/plugins/recommendedendpoint andload_recommended_plugins()helper inplugin_manager. - Built-in badge in Plugin Manager – loaded plugins list now shows a "built-in" badge
for internal plugins (topology, planner). The
GET /api/pluginsresponse includes aninternalfield. - Content-Security-Policy headers – all HTML responses now include a CSP header restricting
scripts, styles, and fonts to
'self'+ CDN origins (cdn.jsdelivr.net,d3js.org), withframe-ancestors 'none'. - Biome JS linting – added Biome as a JavaScript linter (standalone
binary, no npm). Config in
biome.json, integrated into CI viabiomejs/setup-biome@v2. - Strict mypy compliance –
mypy --strictnow passes with 0 errors (was 67). Added explicit type parameters acrossazure_api/, scoring, and planner modules. enrich_skus_with_confidence()helper – DRY convenience wrapper inscoring/deployment_confidence.pyreplacing 3 duplicate signal-compute-assign loops.- Plugin developer documentation –
docs/plugins.mdupdated with internal plugin architecture,PLUGIN_API_VERSIONcontract,azure_api.__all__reference table, and shared module catalogue for plugin authors.
Changed¶
- AZ Topology extracted to internal plugin – the
/api/mappingsroute,get_zone_mappingsMCP tool, tab HTML, andaz-mapping.jsmoved from core tointernal_plugins/topology/. The tab markup is loaded as an HTML fragment at runtime. - Deployment Planner extracted to internal plugin – 5 API routes (
/api/skus,/api/deployment-confidence,/api/spot-scores,/api/sku-pricing), 4 MCP tools, the planner tab HTML + modals (spot, pricing), andplanner.jsmoved tointernal_plugins/planner/. All API URLs are preserved. app.pyslimmed to bootstrap-only – discovery routes extracted toroutes/discovery.py, logging setup tologging_config.py. Coreapp.pynow 220 lines (was 710).plugin_manager.pysplit into package – 1 434-line monolith replaced by 7-moduleplugin_manager/package (_models,_github,_pypi,_installer,_storage,_operations,__init__).ai_chat.pysplit into package – 741-line monolith replaced by 6-moduleservices/ai_chat/package (_config,_prompts,_tools,_dispatch,_stream,__init__).mcp_server.pyslimmed down – removed ~310 lines of tool definitions that now live in internal plugins. Core only retains discovery tools (tenants, subscriptions, regions).- Deployment Confidence Scoring – reworked scoring signals for better accuracy (#36):
- Replaced
quotawith Quota Pressure (quotaPressure) – non-linear bands that penalise heavily above 80% family usage and when remaining vCPUs cannot fit the requested instance count. - Replaced
restrictionswith Restriction Density (restrictionDensity) – per-zone granularity instead of a binary flag, so partial restrictions reduce the score proportionally. - Added knockout layer – hard blockers (zero quota headroom, zero available zones) force
score = 0, label = "Blocked",
scoreType = "blocked"with explicitknockoutReasons. instanceCountparameter now affects quota pressure calculation (demand-adjusted).- Frontend JS split – monolithic
app.js(3 000+ lines) split into four domain-specific files:app.js(core),az-mapping.js(topology),planner.js(deployment planner),chat.js(AI chat panel). - Spot Placement Score cache TTL increased from 10 minutes to 1 hour to reduce pressure on the rate-limited Azure API.
- Chat UI - Tool call badges (e.g. "list subscriptions", "get zone mappings") now remain visible after the tool finishes executing, and clicking a completed badge opens a detail modal showing the full input arguments and output content with JSON syntax highlighting. (#74)
[2026.3.1] - 2026-03-02¶
Added¶
- PyPI as plugin source – the Plugin Manager now supports installing plugins from PyPI in addition to GitHub repos. Version auto-resolves to the latest release when not specified (#70).
- In-process hot-reload for plugins – installing, updating, or uninstalling a plugin no longer requires a server restart. Routes, MCP tools, static assets, and chat modes are reloaded automatically (#69).
- Diagnostic logging for
azure_api– debug-level log statements across auth, pagination, discovery, SKUs, pricing, quotas, and spot modules for easier troubleshooting (#68). - Unified logging format – all log output (core, plugins, uvicorn, MCP, httpx) now uses a
single coloured format with a
[category]tag:[core],[plugin:<name>],[server],[mcp],[http]. Addedget_plugin_logger()helper inplugin_apifor plugin authors (#72). - Playwright E2E test suite – browser-based integration tests for topology, planner, and bootstrap workflows (#51).
- Plugin Manager: update support – installed plugins can be updated to a newer version from the UI (#54).
/api/locationsendpoint – list Azure locations for a subscription (#55).
Removed¶
- Admission Intelligence – removed the experimental composite heuristic scoring system (6 signals, SQLite time-series store, ~2,700 lines). The feature was never production-validated and added significant complexity without delivering reliable results (#66).
Fixed¶
- Plugin persistence in containers – plugins installed via the Plugin Manager now survive container restarts and scale-to-zero events (#65).
- Plugin install 500 error – fixed crash when
uvis absent in container environments (#63). - Plugin manager PermissionError – fixed file permission issues in container mode (#59).
- Verbose logging (
-v) actually works – fixed bug where--reloadmode ignored the verbose flag (env varAZ_SCOUT_LOG_LEVELnow propagates to reload workers). Uvicornlog_levelis nowdebug(wasinfo) when verbose (#72). - Version scheme for container builds – switched from
calver-by-datetoguess-next-devto support theYYYY.M.MICROtag format (#72).
Changed¶
- Latency Stats extracted to plugin – the inter-region latency dataset and MCP tool are no longer
bundled in the core application. They are now available as a standalone plugin:
az-scout-plugin-latency-stats. - Strategy Advisor extracted to plugin – the Capacity Strategy Advisor is no longer bundled in
the core application. It is now available as a standalone plugin:
az-scout-plugin-strategy-advisor. - Use calver in the plugin scaffold structure.
- Plugin Manager UI: validate/install/uninstall from GitHub repos (#50).
[2026.2.8] - 2026-02-28¶
Added¶
- AI Chat Assistant – interactive chat panel powered by Azure OpenAI with streaming responses, tool calling (zones, SKUs, pricing, spot scores), markdown/table rendering, and clickable choice chips.
- Pin-to-side mode docks the chat as a resizable sidebar.
- Tenant and region context auto-injected into tool calls;
switch_tenant/switch_regiontools update the UI. - Conversation persistence, input history (Up/Down arrows), error retry, and suggested prompts on start.
- Planner chat mode – pill-style toggle in the chat panel header switches between Assistant (general Q&A) and Planner (guided deployment advisor). The planner follows three independent planning paths (region selection, SKU selection, zone selection) and relies on the model's built-in knowledge of Azure VM families and best practices alongside live tool data.
- Per-mode conversation state (messages, input history) persisted independently to
localStorage. - Numeric operator filters on SKU table columns (
>,>=,<,<=,=, ranges). - Plugin system – extend az-scout with pip-installable plugins discovered via Python entry points. Plugins can contribute API routes, MCP tools, UI tabs, static assets, and AI chat modes. See docs/plugins.md and the scaffold.
Fixed¶
- Graph text overflow: subscription names exceeding their box due to font-weight mismatch in
measureText(). - MCP→OpenAI schema converter:
itemsnot propagated fromanyOfbranches forlist[str] | Noneparameters. - Unauthenticated tenants now hidden from the dropdown selector with a disabled hint option.
[2026.2.7] - 2026-02-20¶
Changed¶
- Replacement of SSE transport with Streamable HTTP in the MCP server for broader compatibility (e.g., Azure Container Apps, GitHub Codespaces).
- MCP server available at
/mcpfor integration with web-based clients or when running as a hosted deployment (Container App, etc.).
[2026.2.6] - 2026-02-20¶
Added¶
- Adds Container App deployment with optional Entra ID authentication (EasyAuth), a one-click "Deploy to Azure" portal experience, GHCR container CI, and supporting documentation.
- Dockerfile + GHCR publish workflow
- Bicep template with managed identity, Reader + VM Contributor roles, optional EasyAuth
- Custom portal form (createUiDefinition.json) with multi-select subscription picker
- EASYAUTH.md: full setup guide
- Pre-commit hook to keep ARM JSON in sync with Bicep
- EasyAuth user info in navbar
- Single-tenant UI polish
[2026.2.5] - 2026-02-19¶
Changed¶
- Project renamed from
az-mappingtoaz-scout– package, CLI entry point, imports, documentation, and CI/CD all updated. PyPI package is nowaz-scout.
[2026.2.4] - 2026-02-19¶
Added¶
- Deployment Confidence Score – composite 0–100 score per SKU estimating deployment success, synthesised from quota headroom, Spot Placement Score, zone breadth, restrictions, and price pressure. Missing signals are excluded with automatic weight renormalisation.
- Spot Placement Scores – per-SKU Spot VM allocation likelihood (High / Medium / Low), fetched from the Azure Compute RP with batching, retry/back-off, and 10-minute cache.
- SKU pricing – retail prices (PayGo, Spot, RI 1Y/3Y, SP 1Y/3Y) with currency selector, spot discount badge, and pricing detail modal with VM profile section.
- Region summary bar – readiness and consistency scores at the top of results.
- Tenant preload – background thread warms the tenant cache at startup (5-minute TTL) for faster first page load.
- Version display – package version shown in the API (OpenAPI spec) and web page footer.
- Column toggles – show/hide Prices and Spot columns with
localStoragepersistence. - MCP tools:
get_sku_pricing_detail,get_spot_scores, confidence score and VM profile.
Changed¶
- Project renamed from
az-mappingtoaz-scout– package, CLI entry point, imports, documentation, and CI/CD all updated. PyPI package is nowaz-scout. - Bootstrap 5 rewrite – migrated from vanilla CSS to Bootstrap 5.3 with Simple-DataTables, per-column filters, dark/light theme toggle, and responsive modal (fullscreen on mobile).
- Two-page layout – UI split into Topology and Planner tabs with hash routing.
- SKU table headers show zone availability icons instead of plain text.
- CalVer versioning simplified:
calver-by-datescheme, no local version suffix.
Fixed¶
- Pricing modal now scrollable when content overflows.
- Spot score calculation uses per-zone averaging (not best-zone-only).
- Price Pressure signal computed from modal pricing data even without pre-fetched prices.
- Test warnings from preload daemon thread suppressed by mocking in fixtures.
[2026.2.3] - 2026-02-16¶
Added¶
- MCP server – expose zone mappings and SKU availability as MCP tools for AI agents.
list_tenants– discover Azure AD tenants and auth status.list_subscriptions– list enabled subscriptions.list_regions– list AZ-enabled regions.get_zone_mappings– query logical-to-physical zone mappings.get_sku_availability– query VM SKU availability per zone with filtering (by name, family, vCPU range, memory range).- Supports stdio and SSE transports via
az-scout mcpsubcommand. - New
azure_apimodule – shared Azure ARM logic used by both the web app and MCP server. - Colored logging – reuses uvicorn's
DefaultFormatterfor consistent colored output. --reloadCLI flag – auto-reload on code changes for development (uses uvicorn's watcher).- OpenAPI documentation available at
/docs(Swagger UI) and/redoc.
Changed¶
- Migrated from Flask to FastAPI – async routes, built-in request validation, automatic OpenAPI schema generation.
- Unified CLI –
az-scout webandaz-scout mcpsubcommands replace the separate entry points. Runningaz-scoutwithout a subcommand defaults towebfor backward compatibility.--verboseis available on both subcommands;--reloadis specific toweb. - Tenant authentication checks now suppress noisy Azure CLI subprocess stderr output
using an OS-level fd redirect (
_suppress_stderrcontext manager). - Azure SDK logger silenced to
CRITICALduring auth probes to avoid misleadingAADSTS*error messages for tenants the user is not authenticated to.
Fixed¶
- Thread-safety issue where concurrent tenant auth checks could race on stderr redirection – fd redirect is now applied once around the entire thread pool batch.
[2026.2.2] - 2026-02-16¶
Added¶
- SKU availability table – view VM SKU availability per physical zone with filtering and CSV export.
- Subscription selector dropdown when multiple subscriptions are selected (for SKU loading).
- Automatic retry with exponential backoff for slow Azure SKU API calls.
Changed¶
- SKU table headers now show both logical zone and physical zone (e.g., "Zone 1" / "eastus-az1").
- Improved dark mode contrast for success/warning indicators.
- SKU list auto-resets when region changes or mappings are reloaded.
Fixed¶
- Azure API timeout errors now retry automatically (3 attempts, up to 60s per call).
[2026.2.1] - 2026-02-14¶
Added¶
- Dark mode with system preference detection, manual toggle (sun/moon button), and localStorage persistence.
- Searchable region combobox with keyboard navigation, auto-select on single match, and click-outside-to-close.
- Multi-tenant support with
/api/tenantsendpoint; default tenant auto-detected from JWT token (tidclaim). - Favicon (Azure-themed shield).
- Pre-commit configuration (ruff, mypy, trailing-whitespace, end-of-file-fixer, check-yaml/toml).
[2026.2.0] - 2026-02-13¶
Added¶
- Interactive web UI with Flask backend and D3.js frontend.
- Region selector – auto-loads AZ-enabled regions.
- Subscription picker – searchable, multi-select with select/clear all.
- Graph view – bipartite diagram (Logical Zone → Physical Zone), colour-coded per subscription.
- Interactive hover highlighting (by subscription, logical zone, or physical zone).
- Table view – comparison table with consistency indicators.
- Export – download graph as PNG or table as CSV.
- Collapsible sidebar for the filter panel.
- URL parameter sync – filters are reflected in the URL and restored on reload.
- CLI entry point (
az-scout/uvx az-scout) with--host,--port, and--no-openoptions. - Fault-proof automatic browser opening on startup.
- GitHub Actions workflow for publishing to PyPI via trusted publishing.
- GitHub Actions CI workflow (ruff lint + pytest across Python 3.11–3.13).
- Issue templates (bug report, feature request) and PR template.