Skip to main content

AzSnapshot Explorer

수집된 Azure 환경을 오프라인으로 탐색하는 Python stdio MCP Server다. 각 AzSnapshot의 NDJSON을 DuckDB로 인덱싱하고, 조회 결과를 원본 파일·물리 행·바이트 위치·SHA-256에 연결한다. Azure 로그인, 외부 데이터베이스, 웹 서버 또는 서버 내부 LLM은 필요하지 않다.

한 줄 실행 (PyPI 게시 후)

PyPI에 패키지를 게시하면 로컬 소스나 가상환경 경로 없이 다음처럼 실행할 수 있다.

uvx azsnapshot-explorer@0.2.0 serve --root "D:\References\AzSnapshots"

위 명령은 해당 버전의 PyPI 게시를 확인한 뒤 사용한다. PUBLISHING.md에 배포 절차와 .github/workflows/publish.yml의 빌드·검증·Trusted Publishing 설정을 정리했다. LICENSE의 MIT 라이선스를 적용하며 저작권자는 Networkdog다.

uv 설치와 Python 3.12 이상이 필요하다. uvx는 지정한 버전과 의존성을 격리된 캐시 환경에 설치하며 최초 설치에는 네트워크 접근이 필요하다. 서버 실행 중에는 스냅샷 조회를 위해 네트워크를 사용하지 않는다. 재현 가능한 MCP 설정에는 @latest보다 명시적인 버전을 권장한다.

로컬 실행 (게시 전)

uv 설치 안내에 따라 uv를 설치하고 uvx를 PATH에 등록한다. Python 3.12 이상을 사용한다. 이 프로젝트 폴더에서 다음 한 줄로 stdio MCP 서버를 시작할 수 있다. 별도 가상환경 생성이나 pip install은 필요하지 않다.

uvx --from . azsnapshot-explorer serve --root "D:\References\AzSnapshots"

다른 디렉터리나 MCP Host에서 실행할 때는 --from에 프로젝트의 절대 경로를 지정한다. 데이터 경로인 --root와 패키지 경로인 --from은 서로 다르다.

uvx --from "D:\Workspaces\mcp-azsnapshot-explorer" azsnapshot-explorer serve --root "D:\References\AzSnapshots"

serve는 터미널용 대화 화면이 아니라 Host가 stdin/stdout으로 통신하는 서버다. 터미널에서 실행하면 요청을 기다리며, 수동 실행은 Ctrl+C로 종료한다. 설치와 버전 확인에는 serve --root ... 대신 --version을 사용한다.

uvx는 패키지와 의존성을 격리된 캐시 환경에 설치한다. 최초 실행에는 승인된 패키지 피드에 접근할 수 있어야 하며, Python이 없다면 런타임 다운로드에도 네트워크가 필요할 수 있다. 캐시가 모두 준비된 환경에서는 uvx --offline --from ...으로 네트워크 없이 시작할 수 있다. 서버 자체의 스냅샷 조회에는 네트워크를 사용하지 않는다.

패키지 레지스트리 게시를 전제로 하지 않으므로 --from을 명시한다. uvx azsnapshot-explorer ...처럼 원본을 생략하는 실행은 해당 패키지를 승인된 레지스트리에 게시한 뒤 사용한다.

설치

Python 3.12 이상을 지원한다. 현재 작업 환경의 실제 검증 런타임은 Python 3.14.6이다.

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements-lock.txt
.\.venv\Scripts\python.exe -m pip install -e .

requirements-lock.txt는 실제 검증한 런타임 의존성 버전을 고정한다. 개발용 pytest, ruff, build는 별도로 설치한다.

uv를 사용하는 경우의 선택 경로:

uv sync
uv run azsnapshot-explorer --version

사내 패키지 피드가 설정된 환경에서는 해당 피드의 설치 정책을 따른다. pip의 피드 설정이 uv에 자동으로 적용된다고 가정하지 말고 uv 패키지 피드 설정을 확인한다. 설치를 위해 TLS 인증서 검증을 비활성화할 필요는 없다. uv 전용 lock 파일 대신 위 pip용 고정 버전 파일을 제공한다.

VS Code MCP

.vscode/mcp.json은 게시 전 로컬 개발용이며 현재 Windows 가상환경의 Python을 사용한다. 위 설치 절차 후 MCP 서버 목록에서 azsnapshot-explorer를 시작한다. 데이터 루트를 묻는 입력 창의 기본값은 D:\References\AzSnapshots다. Linux/macOS의 로컬 가상환경은 command를 ${workspaceFolder}/.venv/bin/python으로 바꾼다.

게시 전에 가상환경 경로 없이 실행하려면 command를 uvx로, args를 ["--from", "${workspaceFolder}", "azsnapshot-explorer", "serve", "--root", "${input:azsnapshotRoot}"]로 지정한다. uv를 설치하고 VS Code를 다시 열어 PATH 변경을 반영한다. 최초 패키지 다운로드가 제한된 환경에서는 기존 가상환경이나 아래 오프라인 wheelhouse 경로를 사용한다.

PyPI 게시 후에는 다른 워크스페이스에서도 다음 설정을 사용할 수 있다. 프로젝트를 내려받을 필요는 없으며 데이터 루트만 로컬 경로로 지정한다.

{
  "servers": {
    "azsnapshot-explorer": {
      "type": "stdio",
      "command": "uvx",
      "args": ["azsnapshot-explorer@0.2.0", "serve", "--root", "D:/References/AzSnapshots"],
      "env": {"PYTHONUTF8": "1", "OTEL_SDK_DISABLED": "true"}
    }
  }
}

게시 전에 다른 워크스페이스에서 실행하려면 args를 ["--from", "D:/Workspaces/mcp-azsnapshot-explorer", "azsnapshot-explorer", "serve", "--root", "D:/References/AzSnapshots"]로 바꾼다.

mcpServers 형식을 사용하는 Host에서는 최상위 servers를 mcpServers로 바꾸고 type을 생략한다. command, args, env는 그대로 사용한다. ${workspaceFolder}와 ${input:...}는 VS Code 전용이므로 다른 Host에서는 절대 경로로 대체한다. 서버는 Host가 시작·종료하며 상시 실행할 별도 포트가 없다.

이미 설치된 wheel을 사용하려면 command를 azsnapshot-explorer로, args를 ["serve", "--root", "D:/References/AzSnapshots"]로 지정할 수도 있다. 실행 파일이 PATH에 없으면 그 절대 경로를 사용한다.

고객·수집 테넌트를 제한하려면 실행 인자에 --allowed-customer와 --allowed-tenant를 추가한다. 두 옵션 모두 여러 번 지정할 수 있으며, 도구 인자로 허용 범위를 넓힐 수 없다. 기본 설정은 지정한 root 아래의 자료를 허용하므로 root 자체를 승인된 자료로 제한해야 한다.

서버가 직접 네트워크를 사용하지 않아도 도구 결과는 MCP Host의 모델 제공자에게 전달될 수 있다. 고객의 데이터 처리 정책을 확인한 뒤 연결한다.

데이터 구조

다음 두 구조를 지원한다.

AzSnapshots/CUSTOMER/TENANT_ID/COLLECTION_TIMESTAMP/*.ndjson
AzSnapshots/CUSTOMER/azsnapshot-TENANT_ID-COLLECTION_TIMESTAMP/*.ndjson

manifest가 없어도 자료를 발견하지만 수집 범위는 미확인으로 표시한다. 폴더와 manifest의 tenantId가 충돌하거나 manifest가 잘못된 JSON이면 등록 오류로 보고한다. 새로운 테이블은 알려진 목록에 없어도 안전한 파일명이라면 generic 데이터셋으로 조회할 수 있다.

인덱스는 각 스냅샷의 .azsnapshot-explorer/에 생성된다. 원본 NDJSON과 manifest는 수정하지 않는다. 인덱스·lock·임시 파일은 데이터셋 탐색, 원본 해시, 수집 시각 판정에서 제외된다.

SNAPSHOT_DIRECTORY/
  manifest.json
  resources.ndjson
  ...
  .azsnapshot-explorer/
    index.json
    generations/GENERATION_ID/DATASET.duckdb
    jobs/
    locks/
    tmp/

세대 해시에 원본·manifest·엔진·마스킹 정책이 포함된다. 원본이 바뀌면 영향받은 데이터셋을 재색인해야 한다. 스냅샷을 복사할 때 상대 경로와 파일 metadata를 유지하면 기존 인덱스를 재사용할 수 있다. 버전·정책·원본이 다른 캐시는 혼용하지 않는다.

읽기 전용 자료나 Windows의 긴 경로 문제는 --cache-dir D:\AzSnapshotCache처럼 짧은 외부 경로로 처리한다. 이 옵션은 CLI와 stdio serve 모두 지원한다. 심볼릭 링크와 Windows junction을 통한 root 탈출은 허용하지 않는다.

CLI

설치된 console script 또는 python -m azsnapshot_explorer를 사용할 수 있다.

.\.venv\Scripts\azsnapshot-explorer.exe snapshots --root "D:\References\AzSnapshots"
.\.venv\Scripts\azsnapshot-explorer.exe index --root "D:\References\AzSnapshots" --snapshot-id "SNAPSHOT_ID"
.\.venv\Scripts\azsnapshot-explorer.exe verify --root "D:\References\AzSnapshots" --snapshot-id "SNAPSHOT_ID"
.\.venv\Scripts\azsnapshot-explorer.exe analyze --root "D:\References\AzSnapshots" --snapshot-id "SNAPSHOT_ID" --analysis inventory

SNAPSHOT_ID에는 snapshots 결과의 ID를 사용한다. index에 --dataset resources 같은 옵션을 반복 지정하면 일부 테이블만 준비할 수 있다. 지정하지 않으면 전체를 준비한다. verify는 원본 전체 파일 해시와 인덱스 행 수를 다시 대조한다.

serve 중 stdout에는 MCP 프로토콜만 기록한다. 일반 CLI는 stdout에 결과 JSON을, stderr에 오류를 기록한다.

MCP 도구

도구 역할
list_snapshots, get_snapshot 고객·테넌트·수집 구간·경고·범위 선택
list_datasets, describe_dataset 실제/누락 테이블, 출처 추정, 유형·필드 및 bounded sample
prepare_snapshot, get_preparation_status 별도 프로세스에서 비동기 인덱싱 및 진행/실패 확인
query_snapshot 구조화 필터·배열·조인·집계·페이지 조회
search_resources, explain_query, get_query_receipt 이름·태그 검색, 실행 전 준비/조인 검사, Tools 전용 집계 감사
get_resource, get_record ARM ID 형상 조회와 서명된 원본 근거 재확인
list_analyses, run_analysis 도메인 의미와 한계가 명시된 분석
list_investigations, plan_investigation 14개 장애·아키텍처·설계 질문군과 고객 컨텍스트 기반 조사 계획
related_resources 명시적 구성 참조의 깊이 1~2 관계 탐색·미수집 대상·원본 경로 근거

일반 흐름은 스냅샷 선택 → 경고/테이블 확인 → 필요한 자료 준비 → 조회 → 근거 재확인이다. 데이터 조회에는 항상 snapshot_id를 전달한다. 준비되지 않은 자료는 INDEX_REQUIRED로 안내한다.

고객 질문별 사용법은 FAQ.md의 익명 FAQ 30개와 실제 호출 예제 6개를 참고한다. 조사 계획은 고객이 제공한 증상·발생 시각·지역·RTO/RPO·scale 요구를 operator_supplied_unverified로 표시하고, 스냅샷에서 관측한 사실·추가 로그 요청·단정 불가 항목·설계 제안을 분리한다.

get_resource는 큰 상세가 생략되어도 기본 ID/name/type을 반환하며 field_paths로 필요한 상세만 선택할 수 있다. related_resources는 read-only 구성 참조이며 실제 packet path·reachability·장애 인과·전체 의존성을 계산하지 않는다. 큰 참조 필드·충돌·누락·시간/노드/엣지/byte 제한은 불완전 상태로 남긴다.

Resources에도 같은 scope·마스킹·응답 제한이 적용된다.

azsnapshot://snapshots/{snapshot_id}/catalog
azsnapshot://snapshots/{snapshot_id}/datasets/{dataset}/schema
azsnapshot://snapshots/{snapshot_id}/evidence/{evidence_id}
azsnapshot://queries/{query_id}/receipt

cursor, evidence handle, job ID 및 query receipt는 현재 서버 세션에서 사용한다. 서버를 재시작하면 조회를 다시 실행해 새 handle을 받아야 한다. 인덱스 파일은 재사용된다.

구조화 질의

필드는 alias:/JSON/Pointer 형식이다. 문자열은 기본적으로 case-sensitive이고 ARM type/ID 비교에는 eq_ci를 사용한다. /가 key 자체에 포함되면 ~1, ~는 ~0으로 표현한다. exists는 필드 존재, is_null은 명시적인 JSON null을 검사하며 둘을 혼동하지 않는다.

다음은 query_snapshot의 계약 예시다. 실제 실행에는 선택한 스냅샷 ID를 지정한다.

{
  "snapshot_id": "SNAPSHOT_ID",
  "query": {
    "from": {"dataset": "resources", "alias": "resource"},
    "where": {
      "all": [
        {"field": "resource:/type", "op": "eq_ci", "value": "microsoft.compute/virtualmachines"},
        {"field": "resource:/tags/Environment", "op": "eq", "value": "prod"}
      ]
    },
    "joins": [
      {
        "kind": "left",
        "from": {"dataset": "resourcecontainers", "alias": "subscription"},
        "where": {"field": "subscription:/type", "op": "eq_ci", "value": "microsoft.resources/subscriptions"},
        "on": {"left": "resource:/subscriptionId", "right": "subscription:/subscriptionId", "comparison": "eq_ci"}
      }
    ],
    "select": ["resource:/id", "resource:/name", "resource:/location", "subscription:/name"],
    "order_by": [{"field": "resource:/id", "direction": "asc"}],
    "include_count": true
  },
  "page_size": 100
}

집계 예시:

{
  "from": {"dataset": "resources"},
  "group_by": ["resource:/location"],
  "aggregates": [
    {"op": "count", "as": "observed_rows"},
    {"op": "count_distinct", "field": "resource:/id", "as": "distinct_ids", "case_insensitive": true}
  ]
}

집계는 페이지 제한 전에 수행한다. count는 조인·배열 확장 후 행 수이고 리소스 수가 필요하면 명시적인 count_distinct를 사용한다. 집계의 원본 모집단은 source generation·데이터셋 hash·query receipt로 재현하며 샘플 몇 행을 집계 전체의 근거처럼 제시하지 않는다.

all/any/not, eq/eq_ci/in, contains/starts_with, 숫자·시간 비교, array_any/array_all, expand, 제한된 equality join 및 count/count_distinct/sum/min/max를 지원한다. 배열 predicate 안에서는 item:/...를 사용한다. 빈 배열의 array_all은 참이고 배열이 없는 필드는 거짓이다.

KQL과 자유 SQL, 파일 경로, 함수 fragment 및 online fallback은 제공하지 않는다. 로컬 질의의 null·배열·문자열 의미를 ARG/KQL과 동일하다고 주장하지 않는다.

분석과 해석 한계

MVP 분석 ID는 inventory, tag_coverage, role_assignments, policy_summary, security_observations, public_configuration_candidates다.

  • 모든 결과는 수집된 범위의 관측이다. 파일 부재, 빈 결과, 경고, 파싱 무결성 및 수집 완전성을 별도로 표시한다.
  • 인덱스가 유효하고 manifest의 건수가 일치해도 테넌트 전체 수집이 완료됐다는 뜻은 아니다.
  • Azure 역할 할당은 그룹 membership, PIM, deny assignment, 조건부 역할 및 data-plane 권한까지 포함한 완전한 유효 권한이 아니다.
  • 정책 설정은 컴플라이언스 평가가 아니다. PolicyStates가 없으면 별도 수집한 summary의 실제 제공 수준까지만 답한다.
  • 보안 결과가 없다고 정상 환경인 것은 아니다. SecurityResources의 payload 경고도 유지한다.
  • Public IP나 publicNetworkAccess는 설정상 노출 후보이며 실제 통신 가능성을 증명하지 않는다.
  • 수집은 시작·종료 구간을 가진다. 이벤트·평가·형상의 관측 시각이 서로 다를 수 있다.

명시적 outbound 구성 관계 탐색은 지원한다. 두 스냅샷 자동 비교, 인덱스 준비 취소, KQL 호환성 및 실제 reachability/완전한 유효 권한 엔진은 아직 제공하지 않는다. 자세한 분석 근거와 목표 설계는 DESIGN.md를 참고한다.

보안과 자원 제한

credentials, token, API key, SAS signature, connection string, secure parameter 및 배포 request/response는 반환하지 않는다. Entra 연락처·이메일 등의 PII도 기본 마스킹한다. --allow-pii는 운영자가 승인한 실행 설정에서만 사용하며 비밀값은 여전히 마스킹된다. 비밀 필드 필터링도 금지해 조회를 통한 추측을 막는다.

질의는 별도 worker에서 처리하고 모델의 SQL은 받지 않는다. 데이터베이스는 read-only로 연결한 뒤 DuckDB external access와 설정 변경을 잠근다. 기본 timeout은 10초이며 --query-timeout으로 조절한다. 기본 DuckDB 메모리는 worker당 1 GiB이며 --memory-limit으로 조절한다. 동시에 한 질의와 한 인덱스 준비를 실행한다.

페이지 기본값은 100행, 최대 500행이다. 큰 필드는 명시적인 omitted 상태로 제한하고 응답 byte budget에 따라 더 작은 페이지를 반환할 수 있다. 한 JSON object 기본 상한은 64 MiB이며 --max-object-mib로 조절한다.

정상 변경은 원본 file size/mtime으로 빠르게 감지한다. get_record는 해당 원본 행의 바이트 hash도 다시 확인한다. size/mtime을 유지한 변경까지 전체 파일에 대해 검사하려면 verify를 실행한다. stat만으로 매 호출 전체 무결성을 보증하지 않는다.

테스트·빌드

.\.venv\Scripts\python.exe -m pip install pytest ruff build
.\.venv\Scripts\python.exe -m pytest
.\.venv\Scripts\python.exe -m ruff check src tests
.\.venv\Scripts\python.exe -m build --wheel

tests/test_stdio.py는 공식 SDK 클라이언트와 실제 subprocess의 legacy handshake, 도구 조회, evidence 및 Resource 마스킹을 검사한다. 실제 자료 검사는 AZSNAPSHOT_TEST_ROOT를 명시할 때만 활성화한다. .vscode/tasks.json에는 합성 데이터 회귀 테스트 작업이 있다.

배포 실행 명령을 검사하려면 AZSNAPSHOT_SERVER_COMMAND에 ["uvx", "--from", "ABSOLUTE_WHEEL_PATH", "azsnapshot-explorer"] 같은 JSON 배열을 지정하고 stdio 테스트를 실행한다. 테스트가 serve --root ...를 덧붙인다. 기존 AZSNAPSHOT_SERVER_PYTHON으로 설치된 wheel의 Python 실행 파일을 선택하는 방식도 유지한다.

.vscode/launch.json은 카탈로그·인덱싱 CLI의 소스 디버깅 설정을 제공한다. stdio 프로토콜 호출은 MCP Host 또는 위 통합 테스트를 사용한다.

배포용 wheel을 설치하면 azsnapshot-explorer console script를 직접 MCP Host에 등록할 수 있다. 폐쇄망에는 wheel 하나뿐 아니라 대상 Python/OS/architecture에 맞는 모든 의존 wheel을 준비하고 --no-index --find-links로 설치한다. 런타임의 인덱싱과 질의에는 인터넷 연결이 필요하지 않다.

현재 생성한 배포 ZIP은 Windows x64 / CPython 3.14용 wheelhouse 33개를 포함한다. Python 런타임과 고객 스냅샷은 포함하지 않는다. 다른 Python 버전·OS에는 해당 플랫폼의 의존 wheel을 별도로 준비해야 한다.

ZIP을 푼 디렉터리에서 다음 방식으로 설치할 수 있다.

uvx를 사용하면 wheelhouse에서 한 줄로 설치·실행할 수 있다. 대상 Python/OS/architecture와 일치하는 wheelhouse와 Python 3.12 이상이 필요하다. 다음 예시는 위 Windows x64 / CPython 3.14 번들에 해당한다.

uvx --offline --no-index --python 3.14 --find-links ./wheelhouse --from ./azsnapshot_explorer-0.2.0-py3-none-any.whl azsnapshot-explorer serve --root "D:\References\AzSnapshots"

MCP 설정에서는 --find-links와 --from의 값을 절대 경로로 지정한다. 폐쇄망에서는 --offline을 유지하고, wheelhouse 없이 캐시만으로 실행하려면 의존성까지 모두 캐시되어 있어야 한다.

pip를 사용하는 설치 경로도 지원한다.

python -m venv .venv
.\.venv\Scripts\python.exe -m pip install --no-index --find-links wheelhouse -r requirements-lock.txt azsnapshot_explorer-0.2.0-py3-none-any.whl
.\.venv\Scripts\azsnapshot-explorer.exe serve --root "D:\References\AzSnapshots"

실제 자료 검증

2026-10-13에 제공된 표본의 NDJSON 38개, 3,319,763,366바이트, 1,074,546행을 전체 인덱싱했다. 최초 처리 시간은 약 17분 45초였으며, 원본 전체를 다시 해싱한 결과 인덱스의 원본 SHA-256 및 manifest 선언 건수가 모두 일치했다. 실제 stdio 클라이언트로 리소스 집계와 원본 근거 재조회도 검증했다.

이는 로컬 파싱·무결성 검증이지 Azure 전체 수집의 완전성 증명은 아니다. 수집 manifest의 보안 payload 경고와 Entra 권한 경고는 그대로 유지된다.

추가로 새 가상환경에 배포 wheel과 고정 의존성 33개를 --no-index로 설치하고, 그 환경의 실행 파일로 실제 인벤토리 분석 및 stdio 조회·근거 재확인을 검증했다. 기존 개발 환경의 editable 소스를 사용하는 검증과 구분한다.

Metadata

Release files for azsnapshot-explorer 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for azsnapshot-explorer 0.2.0
File Size Uploaded
azsnapshot_explorer-0.2.0.tar.gz 111.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for azsnapshot-explorer 0.2.0
File Interpreter ABI Platform
azsnapshot_explorer-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 173.3 kB

Release files / azsnapshot_explorer-0.2.0.tar.gz

Download URL azsnapshot_explorer-0.2.0.tar.gz
Size 111.8 kB
Tags Source
SHA-256 checksum
How to use checksums
7612a3fd823b6af4bccc2a60f7a59cecdeb3d9780fea0facf9edef66ab5ead7c
BLAKE2b-256 checksum
How to use checksums
06b75cfe28d3db6fd876a880949d61f2f90d39738e68bd1bcdc2c919d91b0090
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / azsnapshot_explorer-0.2.0-py3-none-any.whl

Download URL azsnapshot_explorer-0.2.0-py3-none-any.whl
Size 61.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8438244c2ac603786e8c3b460d5b6bbfb3c205552bc8c4042c7284f25871c899
BLAKE2b-256 checksum
How to use checksums
a8c876f6d1e4f43fa88ac963db4b0e95073c7c260f408a0c87d2bade3c595f54
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page