chore: archive legacy Identity, Workday, and Intune folders
- Move Identity/, Workday/, Intune/ to archive/ (superseded by nexus-mcp shards) - Move 'Local Setup.md' to archive/ (superseded by nexus-mcp/Local-Setup.md) - Add archive/README.md explaining migration and preserved content - Clean repository structure: only nexus-mcp, documentation, and .github remain active All legacy functionality migrated to nexus-mcp sharded architecture. Archived folders preserved for reference and historical context. Refs: SESSION_SNAPSHOT_2026-04-13.md
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
from typing import Any
|
||||
|
||||
# Mock dataset with reporting-line relationships for manager checks (WIS-017 prep)
|
||||
MOCK_WORKERS: dict[str, dict[str, Any]] = {
|
||||
"EMP001": {
|
||||
"name": "Nathan",
|
||||
"legal_name": "Nathaniel Cole",
|
||||
"preferred_name": "Nathan",
|
||||
"ad_display_name": "Nathan Cole",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "IT",
|
||||
"workday_cost_center": "CC100-IT",
|
||||
"workday_title": "Systems Engineer",
|
||||
"ad_title": "Systems Engineer",
|
||||
"ad_department": "IT",
|
||||
"email": "nathan@example.com",
|
||||
"manager_id": "EMP010",
|
||||
},
|
||||
"EMP002": {
|
||||
"name": "Terminated User",
|
||||
"legal_name": "Taylor Brooks",
|
||||
"preferred_name": "Taylor",
|
||||
"ad_display_name": "Taylor Brooks",
|
||||
"status": "Terminated",
|
||||
"ad_enabled": True,
|
||||
"dept": "Sales",
|
||||
"workday_cost_center": "CC200-SALES",
|
||||
"workday_title": "Account Executive",
|
||||
"ad_title": "Account Executive",
|
||||
"ad_department": "Sales",
|
||||
"email": "user2@example.com",
|
||||
"manager_id": "EMP020",
|
||||
},
|
||||
"EMP003": {
|
||||
"name": "Alicia",
|
||||
"legal_name": "Alicia Gomez",
|
||||
"preferred_name": "Alicia",
|
||||
"ad_display_name": "Alicia Gomez",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "IT",
|
||||
"workday_cost_center": "CC100-IT",
|
||||
"workday_title": "Senior Systems Analyst",
|
||||
"ad_title": "Systems Analyst",
|
||||
"ad_department": "IT",
|
||||
"email": "alicia@example.com",
|
||||
"manager_id": "EMP010",
|
||||
},
|
||||
"EMP004": {
|
||||
"name": "Jordan",
|
||||
"legal_name": "Jordan Lee",
|
||||
"preferred_name": "Jordan",
|
||||
"ad_display_name": "Jordan Lee",
|
||||
"status": "Leave",
|
||||
"ad_enabled": True,
|
||||
"dept": "Finance",
|
||||
"workday_cost_center": "CC300-FIN",
|
||||
"workday_title": "Finance Analyst",
|
||||
"ad_title": "Finance Analyst",
|
||||
"ad_department": "Accounting",
|
||||
"email": "jordan@example.com",
|
||||
"manager_id": "EMP030",
|
||||
},
|
||||
"EMP010": {
|
||||
"name": "Priya Manager",
|
||||
"legal_name": "Priya Narayanan",
|
||||
"preferred_name": "Priya",
|
||||
"ad_display_name": "Priya Manager",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "IT",
|
||||
"workday_cost_center": "CC110-IT-MGMT",
|
||||
"workday_title": "IT Manager",
|
||||
"ad_title": "IT Manager",
|
||||
"ad_department": "IT",
|
||||
"email": "priya@example.com",
|
||||
"manager_id": "EMP100",
|
||||
},
|
||||
"EMP020": {
|
||||
"name": "Ramon Director",
|
||||
"legal_name": "Ramon Alvarez",
|
||||
"preferred_name": "Ramon",
|
||||
"ad_display_name": "Ramon Director",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "Sales",
|
||||
"workday_cost_center": "CC210-SALES-MGMT",
|
||||
"workday_title": "Sales Director",
|
||||
"ad_title": "Sales Director",
|
||||
"ad_department": "Sales",
|
||||
"email": "ramon@example.com",
|
||||
"manager_id": "EMP100",
|
||||
},
|
||||
"EMP030": {
|
||||
"name": "Morgan Lead",
|
||||
"legal_name": "Morgan Patel",
|
||||
"preferred_name": "Morgan",
|
||||
"ad_display_name": "Morgan Patel",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "Finance",
|
||||
"workday_cost_center": "CC310-FIN-MGMT",
|
||||
"workday_title": "Finance Lead",
|
||||
"ad_title": "Finance Lead",
|
||||
"ad_department": "Finance",
|
||||
"email": "morgan@example.com",
|
||||
"manager_id": "EMP100",
|
||||
},
|
||||
"EMP100": {
|
||||
"name": "Chief Exec",
|
||||
"legal_name": "Evelyn Carter",
|
||||
"preferred_name": "Evelyn",
|
||||
"ad_display_name": "Evelyn Carter",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "Executive",
|
||||
"workday_cost_center": "CC999-EXEC",
|
||||
"workday_title": "Chief Executive Officer",
|
||||
"ad_title": "Chief Executive Officer",
|
||||
"ad_department": "Executive",
|
||||
"email": "ceo@example.com",
|
||||
"manager_id": "",
|
||||
},
|
||||
# Intentional unresolved manager reference for mismatch test scenarios
|
||||
"EMP777": {
|
||||
"name": "Mismatch Case",
|
||||
"legal_name": "Alexandra Rivers",
|
||||
"preferred_name": "Alex",
|
||||
"ad_display_name": "Jordan Rivers",
|
||||
"status": "Active",
|
||||
"ad_enabled": True,
|
||||
"dept": "Operations",
|
||||
"workday_cost_center": "CC400-OPS",
|
||||
"workday_title": "Operations Specialist",
|
||||
"ad_title": "Operations Specialist",
|
||||
"ad_department": "Operations",
|
||||
"email": "mismatch@example.com",
|
||||
"manager_id": "EMP999",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def scan_status_reconciliation_mismatches() -> dict[str, Any]:
|
||||
"""Detect workers terminated in Workday but still enabled in AD."""
|
||||
mismatches: list[dict[str, Any]] = []
|
||||
total_scanned = 0
|
||||
|
||||
for employee_id, details in MOCK_WORKERS.items():
|
||||
total_scanned += 1
|
||||
workday_status = details.get("status")
|
||||
ad_enabled = bool(details.get("ad_enabled", False))
|
||||
|
||||
if workday_status == "Terminated" and ad_enabled:
|
||||
mismatches.append(
|
||||
{
|
||||
"employee_id": employee_id,
|
||||
"employee_name": details["name"],
|
||||
"workday_status": workday_status,
|
||||
"ad_enabled": ad_enabled,
|
||||
"mismatch_type": "terminated_but_enabled",
|
||||
"severity": "high",
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"scan_summary": {
|
||||
"total_records_checked": total_scanned,
|
||||
"mismatches_found": len(mismatches),
|
||||
"status": "action_required" if mismatches else "clean",
|
||||
},
|
||||
"mismatches": mismatches,
|
||||
}
|
||||
|
||||
|
||||
def scan_job_title_mismatches() -> dict[str, Any]:
|
||||
"""Detect workers whose Workday title differs from their AD title."""
|
||||
mismatches: list[dict[str, Any]] = []
|
||||
total_scanned = 0
|
||||
|
||||
for employee_id, details in MOCK_WORKERS.items():
|
||||
total_scanned += 1
|
||||
workday_title = details.get("workday_title", "")
|
||||
ad_title = details.get("ad_title", "")
|
||||
|
||||
if workday_title and ad_title and workday_title != ad_title:
|
||||
mismatches.append(
|
||||
{
|
||||
"employee_id": employee_id,
|
||||
"employee_name": details["name"],
|
||||
"workday_title": workday_title,
|
||||
"ad_title": ad_title,
|
||||
"mismatch_type": "job_title_mismatch",
|
||||
"severity": "medium",
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"scan_summary": {
|
||||
"total_records_checked": total_scanned,
|
||||
"mismatches_found": len(mismatches),
|
||||
"status": "action_required" if mismatches else "clean",
|
||||
},
|
||||
"mismatches": mismatches,
|
||||
}
|
||||
|
||||
|
||||
def scan_department_drift() -> dict[str, Any]:
|
||||
"""Detect workers whose Workday department context differs from AD department."""
|
||||
mismatches: list[dict[str, Any]] = []
|
||||
total_scanned = 0
|
||||
|
||||
for employee_id, details in MOCK_WORKERS.items():
|
||||
total_scanned += 1
|
||||
workday_department = details.get("dept", "")
|
||||
workday_cost_center = details.get("workday_cost_center", "")
|
||||
ad_department = details.get("ad_department", "")
|
||||
|
||||
if workday_department and ad_department and workday_department != ad_department:
|
||||
mismatches.append(
|
||||
{
|
||||
"employee_id": employee_id,
|
||||
"employee_name": details["name"],
|
||||
"workday_department": workday_department,
|
||||
"workday_cost_center": workday_cost_center,
|
||||
"ad_department": ad_department,
|
||||
"mismatch_type": "department_drift",
|
||||
"severity": "medium",
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"scan_summary": {
|
||||
"total_records_checked": total_scanned,
|
||||
"mismatches_found": len(mismatches),
|
||||
"status": "action_required" if mismatches else "clean",
|
||||
},
|
||||
"mismatches": mismatches,
|
||||
}
|
||||
|
||||
|
||||
def _normalize_name_tokens(value: str) -> list[str]:
|
||||
return [token for token in value.lower().replace(".", " ").split() if token]
|
||||
|
||||
|
||||
def scan_name_variance() -> dict[str, Any]:
|
||||
"""Detect AD display names that do not align to legal or preferred Workday names."""
|
||||
mismatches: list[dict[str, Any]] = []
|
||||
total_scanned = 0
|
||||
|
||||
for employee_id, details in MOCK_WORKERS.items():
|
||||
total_scanned += 1
|
||||
legal_name = details.get("legal_name", "")
|
||||
preferred_name = details.get("preferred_name", "")
|
||||
ad_display_name = details.get("ad_display_name", "")
|
||||
|
||||
if not legal_name or not ad_display_name:
|
||||
continue
|
||||
|
||||
legal_tokens = _normalize_name_tokens(legal_name)
|
||||
preferred_tokens = _normalize_name_tokens(preferred_name)
|
||||
display_tokens = _normalize_name_tokens(ad_display_name)
|
||||
|
||||
if not legal_tokens or not display_tokens:
|
||||
continue
|
||||
|
||||
legal_first = legal_tokens[0]
|
||||
legal_last = legal_tokens[-1]
|
||||
preferred_first = preferred_tokens[0] if preferred_tokens else ""
|
||||
display_first = display_tokens[0]
|
||||
display_last = display_tokens[-1]
|
||||
|
||||
first_name_aligned = display_first in {legal_first, preferred_first}
|
||||
last_name_aligned = display_last == legal_last
|
||||
|
||||
if first_name_aligned and last_name_aligned:
|
||||
continue
|
||||
|
||||
mismatches.append(
|
||||
{
|
||||
"employee_id": employee_id,
|
||||
"employee_name": details["name"],
|
||||
"workday_legal_name": legal_name,
|
||||
"workday_preferred_name": preferred_name,
|
||||
"ad_display_name": ad_display_name,
|
||||
"mismatch_type": "name_variance_requires_review",
|
||||
"severity": "low",
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"scan_summary": {
|
||||
"total_records_checked": total_scanned,
|
||||
"mismatches_found": len(mismatches),
|
||||
"status": "action_required" if mismatches else "clean",
|
||||
},
|
||||
"mismatches": mismatches,
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from typing import Any
|
||||
from lib.data import (
|
||||
MOCK_WORKERS,
|
||||
scan_department_drift,
|
||||
scan_job_title_mismatches,
|
||||
scan_name_variance,
|
||||
scan_status_reconciliation_mismatches,
|
||||
)
|
||||
|
||||
mcp = FastMCP("Workday-Sync") #Server name for MCP registration, e.g. "Workday-Sync"
|
||||
|
||||
@mcp.tool()
|
||||
def get_worker_status(employee_id: str) -> dict[str, Any]:
|
||||
"""
|
||||
WIS-009: Fetch structured worker status.
|
||||
This replaces the previous string-based version.
|
||||
"""
|
||||
worker_id = employee_id.upper()
|
||||
worker = MOCK_WORKERS.get(worker_id)
|
||||
|
||||
# Structured Return (WIS-004 Allowlist)
|
||||
if worker:
|
||||
return {
|
||||
"employee_id": worker_id,
|
||||
"full_name": worker["name"],
|
||||
"status": worker["status"],
|
||||
"department": worker["dept"],
|
||||
"work_email": worker["email"],
|
||||
"manager_id": worker["manager_id"],
|
||||
}
|
||||
|
||||
return {"error": f"Worker {employee_id} not found."}
|
||||
|
||||
@mcp.tool()
|
||||
def get_worker_manager(employee_id: str) -> dict[str, Any]:
|
||||
"""WIS-017 prep: resolve a worker's manager relationship from mock data."""
|
||||
worker_id = employee_id.upper()
|
||||
worker = MOCK_WORKERS.get(worker_id)
|
||||
if not worker:
|
||||
return {"error": f"Worker {employee_id} not found."}
|
||||
|
||||
manager_id = worker.get("manager_id", "")
|
||||
if not manager_id:
|
||||
return {
|
||||
"employee_id": worker_id,
|
||||
"employee_name": worker["name"],
|
||||
"manager_id": "",
|
||||
"manager_name": None,
|
||||
"relationship_status": "no_manager_assigned",
|
||||
}
|
||||
|
||||
manager = MOCK_WORKERS.get(manager_id)
|
||||
if not manager:
|
||||
return {
|
||||
"employee_id": worker_id,
|
||||
"employee_name": worker["name"],
|
||||
"manager_id": manager_id,
|
||||
"manager_name": None,
|
||||
"relationship_status": "manager_not_found",
|
||||
}
|
||||
|
||||
return {
|
||||
"employee_id": worker_id,
|
||||
"employee_name": worker["name"],
|
||||
"manager_id": manager_id,
|
||||
"manager_name": manager["name"],
|
||||
"relationship_status": "ok",
|
||||
}
|
||||
|
||||
@mcp.tool()
|
||||
def scan_manager_mismatches() -> dict[str, Any]:
|
||||
"""
|
||||
WIS-017: Scan all workers and return a report of manager relationship errors.
|
||||
This provides the data for WIS-020 weekly drift reporting.
|
||||
"""
|
||||
mismatches: list[dict[str, Any]] = []
|
||||
total_scanned = 0
|
||||
|
||||
for emp_id, details in MOCK_WORKERS.items():
|
||||
total_scanned += 1
|
||||
manager_id = details.get("manager_id")
|
||||
|
||||
# We only care about cases where a manager is assigned but cannot be found
|
||||
if manager_id and manager_id not in MOCK_WORKERS:
|
||||
mismatches.append({
|
||||
"employee_id": emp_id,
|
||||
"employee_name": details["name"],
|
||||
"invalid_manager_id": manager_id,
|
||||
"error_type": "manager_not_found"
|
||||
})
|
||||
|
||||
return {
|
||||
"scan_summary": {
|
||||
"total_records_checked": total_scanned,
|
||||
"mismatches_found": len(mismatches),
|
||||
"status": "action_required" if mismatches else "clean"
|
||||
},
|
||||
"mismatches": mismatches
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def scan_status_reconciliation() -> dict[str, Any]:
|
||||
"""
|
||||
WIS-014: Flag workers terminated in Workday but still enabled in AD.
|
||||
"""
|
||||
return scan_status_reconciliation_mismatches()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def scan_job_title_drift() -> dict[str, Any]:
|
||||
"""
|
||||
Detect workers whose Workday title differs from their AD title.
|
||||
"""
|
||||
return scan_job_title_mismatches()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def scan_department_mismatches() -> dict[str, Any]:
|
||||
"""
|
||||
Detect workers whose Workday department differs from their AD department.
|
||||
"""
|
||||
return scan_department_drift()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def scan_name_variance_mismatches() -> dict[str, Any]:
|
||||
"""
|
||||
Detect AD display names that do not align with Workday legal or preferred names.
|
||||
"""
|
||||
return scan_name_variance()
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
@@ -0,0 +1,92 @@
|
||||
from lib.data import (
|
||||
scan_department_drift,
|
||||
scan_job_title_mismatches,
|
||||
scan_name_variance,
|
||||
scan_status_reconciliation_mismatches,
|
||||
)
|
||||
from server import (
|
||||
scan_department_mismatches,
|
||||
scan_job_title_drift,
|
||||
scan_name_variance_mismatches,
|
||||
scan_status_reconciliation,
|
||||
)
|
||||
|
||||
|
||||
def test_scan_status_reconciliation_mismatches_returns_expected_record() -> None:
|
||||
result = scan_status_reconciliation_mismatches()
|
||||
|
||||
assert result["scan_summary"]["total_records_checked"] == 9
|
||||
assert result["scan_summary"]["mismatches_found"] == 1
|
||||
assert result["mismatches"] == [
|
||||
{
|
||||
"employee_id": "EMP002",
|
||||
"employee_name": "Terminated User",
|
||||
"workday_status": "Terminated",
|
||||
"ad_enabled": True,
|
||||
"mismatch_type": "terminated_but_enabled",
|
||||
"severity": "high",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_scan_job_title_mismatches_returns_expected_record() -> None:
|
||||
result = scan_job_title_mismatches()
|
||||
|
||||
assert result["scan_summary"]["total_records_checked"] == 9
|
||||
assert result["scan_summary"]["mismatches_found"] == 1
|
||||
assert result["mismatches"] == [
|
||||
{
|
||||
"employee_id": "EMP003",
|
||||
"employee_name": "Alicia",
|
||||
"workday_title": "Senior Systems Analyst",
|
||||
"ad_title": "Systems Analyst",
|
||||
"mismatch_type": "job_title_mismatch",
|
||||
"severity": "medium",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_scan_department_drift_returns_expected_record() -> None:
|
||||
result = scan_department_drift()
|
||||
|
||||
assert result["scan_summary"]["total_records_checked"] == 9
|
||||
assert result["scan_summary"]["mismatches_found"] == 1
|
||||
assert result["mismatches"] == [
|
||||
{
|
||||
"employee_id": "EMP004",
|
||||
"employee_name": "Jordan",
|
||||
"workday_department": "Finance",
|
||||
"workday_cost_center": "CC300-FIN",
|
||||
"ad_department": "Accounting",
|
||||
"mismatch_type": "department_drift",
|
||||
"severity": "medium",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_scan_name_variance_returns_expected_records() -> None:
|
||||
result = scan_name_variance()
|
||||
|
||||
assert result["scan_summary"]["total_records_checked"] == 9
|
||||
assert result["scan_summary"]["mismatches_found"] == 3
|
||||
assert [item["employee_id"] for item in result["mismatches"]] == [
|
||||
"EMP010",
|
||||
"EMP020",
|
||||
"EMP777",
|
||||
]
|
||||
|
||||
|
||||
def test_scan_status_reconciliation_tool_matches_detector() -> None:
|
||||
assert scan_status_reconciliation() == scan_status_reconciliation_mismatches()
|
||||
|
||||
|
||||
def test_scan_job_title_drift_tool_matches_detector() -> None:
|
||||
assert scan_job_title_drift() == scan_job_title_mismatches()
|
||||
|
||||
|
||||
def test_scan_department_mismatches_tool_matches_detector() -> None:
|
||||
assert scan_department_mismatches() == scan_department_drift()
|
||||
|
||||
|
||||
def test_scan_name_variance_mismatches_tool_matches_detector() -> None:
|
||||
assert scan_name_variance_mismatches() == scan_name_variance()
|
||||
Reference in New Issue
Block a user