Safe, auditable MSP workflow automation CLI for Microsoft 365 operations.
Project description
msp-copilot
Audit-grade workflow engine with approvals + traceability | Pilot-ready on a sandbox tenant | Architecture won't collapse when we swap mocks → real Graph
CLI quickstart
Install
pip install msp-copilot
From source checkout:
pip install .
Configure
Create a .env file (or set env vars in your shell):
# Required for real Graph runs (optional for mock/dry-run)
MSP_GRAPH_CLIENT_ID="..."
MSP_GRAPH_TENANT_ID="..."
# Safety: execution is OFF by default
MSP_EXECUTION_ENABLED="false"
# Optional: tenant aliases (tickets can use alias instead of GUID)
MSP_TENANT_ALIAS_cryingman-dev="67aca8ef-69a4-4853-9045-d4c345601d6e"
Introspection commands (no auth / safe)
Explain a workflow:
msp-copilot explain offboarding
Validate a ticket (schema + guardrails only):
msp-copilot validate --ticket data/tickets/offboard_001.json
Preview a plan:
msp-copilot plan --ticket data/tickets/offboard_001.json
msp-copilot plan --ticket data/tickets/offboard_001.json --approve remove_licenses,convert_mailbox
Doctor checks (operator readiness):
msp-copilot doctor --tenant cryingman-dev --test-user john.doe@example.com
msp-copilot doctor --json
Live gauntlet (10-scenario operator check):
make test-live
Notes:
- Loads
.envautomatically when present - Stores artifacts under
data/live_runs/<utc-timestamp>/ - Scenario output logs:
data/live_runs/<utc-timestamp>/scenarios/ - New run-log snapshot:
data/live_runs/<utc-timestamp>/run_logs/ - Run metadata:
data/live_runs/<utc-timestamp>/meta.json(commit/version/python/os/redacted env flags) - Set
LIVE_GAUNTLET_USE_REAL=1to keep real Graph credentials instead of forced mock mode
Running workflows
Dry-run (default-safe):
msp-copilot run --ticket data/tickets/offboard_001.json --operator you
Execute (requires kill switch enabled):
MSP_EXECUTION_ENABLED=true \
msp-copilot run --ticket data/tickets/offboard_001.json --execute --operator you
Execute with approvals:
MSP_EXECUTION_ENABLED=true \
msp-copilot run --ticket data/tickets/offboard_001.json --execute \
--approve remove_licenses,convert_mailbox \
--operator you
Last run:
msp-copilot last-run
Operator configuration
License removal configuration:
{
"ticket_id": "offboard_001",
"type": "offboarding",
"tenant_id": "cryingman-dev",
"user_email": "john.doe@contoso.com",
"remove_sku_ids": [
"6fd2c87f-b296-42f0-b197-1e91e994b900"
]
}
or fallback defaults via env:
MSP_DEFAULT_REMOVE_SKU_IDS="6fd2c87f-b296-42f0-b197-1e91e994b900,another-sku-guid"
Rules:
remove_sku_idsmust be explicit (ticket or env fallback)- Empty remove list fails the
remove_licensesstep - Only targeted SKUs are removed
- Compensation payload is logged in run evidence
Exchange Online mode:
MSP_EXCHANGE_REAL=true
MSP_EXECUTION_ENABLED=true
Behavior:
- Uses
pwshsubprocess commands for mailbox conversion - Skips conversion when mailbox is already
SharedMailbox - Captures sanitized command output and timing in evidence
convert_mailboxstill requires explicit approval
Group diff workflow
Config:
# Groups you allow the engine to manage (required for managed_only)
MSP_MANAGED_GROUP_IDS="guid1,guid2,guid3"
# Groups that must NEVER be removed
MSP_PROTECTED_GROUP_IDS="guidX,guidY"
# Safety cap on removals
MSP_GROUP_REMOVE_MAX="10"
Run (safe defaults):
msp-copilot plan --ticket data/tickets/group_diff_001.json
msp-copilot run --ticket data/tickets/group_diff_001.json --operator you
Approve removals (if needed):
MSP_EXECUTION_ENABLED=true \
msp-copilot run --ticket data/tickets/group_diff_001.json --execute \
--approve group_diff_apply \
--operator you
Behavior rules:
desired_group_idsmust be non-empty- In
managed_only, only groups inMSP_MANAGED_GROUP_IDSare enforced - Removals are blocked unless
--approve group_diff_applyis provided - Protected groups are never removed
- Removal cap prevents large accidental group removals
Note:
plandoes not computeto_add/to_removecounts yet; those are computed at execution precheck time
Notes / behavior guarantees
--executeis blocked unlessMSP_EXECUTION_ENABLED=true- Tenant and user guardrails run before any write steps
- Approval-required actions block unless explicitly approved
- Run logs are schema-validated, redacted, and versioned (
run_schema_version: 1)
Deployment
One-page deploy guide: DEPLOYMENT.md
Current State
✅ What you can claim right now:
- Per-step audit trail with unique run_id (UUID v4)
- Approval gating for risky operations
- Real-time status tracking (planned → completed/blocked/failed/skipped)
- Enterprise audit logs:
data/runs/run_<timestamp>.json - Timestamps with millisecond precision (ISO 8601 UTC)
- Operator tracking via
MSP_OPERATOR_EMAILenvironment variable - Architecture validated: swapping MockGraphOperations → real Graph API won't break the core engine
Why "pilot-ready":
- All core workflows tested and working (dry-run, execute, approval gates)
- Mock Graph API provides realistic 50ms latency per operation
- Audit logs capture everything needed for compliance review
- Run logs include: run_id, started_at, completed_at, duration_seconds, all step timings, approval tracking
Why NOT "production-ready":
- Still using mock Graph API (no real Microsoft 365 connectivity)
- No real OAuth authentication (we fake the login)
- No idempotency protection (reruns could conflict)
- Secrets still in .env (should be in keyring/vault)
- No cross-tenant isolation checks
Enterprise Gates
See ENTERPRISE_ROADMAP.md for detailed implementation paths. The critical blockers for production:
- Real Microsoft 365 Auth + Tenant Isolation ← Next priority (MSAL-based OAuth)
- Idempotency + Safe Retries (SQLite store, retry policy)
- Secrets Management + Token Redaction (keyring, log scrubbing)
Documentation
- ENTERPRISE_ROADMAP.md — 6 production-readiness gates with code examples
- QUICK_WINS.md — 4 improvements just implemented (validation, run_id, timestamps, actor tracking)
- NEXT_STEPS.md — 30-day sprint plan + decision framework
Architecture
Domain-driven: Ticket → Planner → Plan → Executor → RunResult + Auditor
Connectors: Microsoft Graph (mocked, real auth coming next)
Storage: JSON run logs + future SQLite (idempotency, audit)
CLI: Argparse with --ticket, --execute, --approve flags
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file msp_copilot-0.1.1.tar.gz.
File metadata
- Download URL: msp_copilot-0.1.1.tar.gz
- Upload date:
- Size: 46.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f19c0a64c13d515f04b3e316f94fc1ba64ba6f68cf439b57e089562c244eff5
|
|
| MD5 |
70e392dc9e21d0dfb96c476ba5d7f872
|
|
| BLAKE2b-256 |
ba6f0c26fd6ddbe7f8ff56bda8c23d2072ae5d36724a45d1b0ff0afcd2156537
|
File details
Details for the file msp_copilot-0.1.1-py3-none-any.whl.
File metadata
- Download URL: msp_copilot-0.1.1-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21d62e801b99954f78d25b2c8ba810b439bf44acd69b4de8ed544480a3b2d953
|
|
| MD5 |
d45f214da56d8d44c0cbc73b9f2a7dcf
|
|
| BLAKE2b-256 |
823f9f51cce0fbb614138646d1dbc190bb997fdc5e58378f2e8aa38fec67785e
|