134 lines
4.7 KiB
Markdown
134 lines
4.7 KiB
Markdown
# Identity MCP implementation kickoff (Phase 1)
|
|
|
|
## Purpose
|
|
|
|
This document starts implementation of an Identity MCP server with a read-only baseline.
|
|
|
|
## What is implemented
|
|
|
|
- Python MCP server scaffold using FastMCP
|
|
- Read-only tool exposed: `get_user`
|
|
- Read-only tool exposed: `get_user_groups`
|
|
- Read-only tool exposed: `get_group_members`
|
|
- Read-only tool exposed: `find_stale_users`
|
|
- Read-only tool exposed: `get_computer`
|
|
- In-memory backend for local contract testing
|
|
- Active Directory backend using PowerShell subprocess wrappers
|
|
- Environment-based backend selection (memory vs AD)
|
|
- STDERR-only logging for STDIO transport safety
|
|
- Basic audit log entries for tool calls
|
|
- Unit tests and integration smoke tests
|
|
|
|
## Files
|
|
|
|
- `identity_mcp_server.py`: FastMCP server, tool definitions, logging, backend selection, and run entrypoint
|
|
- `identity_backend.py`: backend interface and in-memory implementation
|
|
- `ad_adapter.py`: Active Directory backend using PowerShell Get-AD* cmdlets
|
|
- `tests/test_ad_adapter.py`: unit tests for AD adapter output parsing and error handling
|
|
- `tests/test_integration.py`: integration smoke tests against non-production AD
|
|
- `pyproject.toml`: project metadata, dependency pin for MCP SDK, and CLI entrypoint
|
|
- `.gitignore`: excludes Python bytecode, test cache, and local virtual environment artifacts
|
|
|
|
## How to run
|
|
|
|
### Using in-memory backend (default, safe mode)
|
|
|
|
```bash
|
|
uv run identity_mcp_server.py
|
|
```
|
|
|
|
### Using Active Directory backend
|
|
|
|
Set environment variables before running:
|
|
|
|
```bash
|
|
# Test environment with explicit credentials
|
|
export IDENTITY_BACKEND=ad
|
|
export AD_USERNAME=your_test_username
|
|
export AD_PASSWORD=your_test_password
|
|
uv run identity_mcp_server.py
|
|
```
|
|
|
|
```bash
|
|
# Production environment with service account context
|
|
export IDENTITY_BACKEND=ad
|
|
# Service account runs process, no explicit credentials needed
|
|
uv run identity_mcp_server.py
|
|
```
|
|
|
|
### Running tests
|
|
|
|
```bash
|
|
# Install test dependencies
|
|
uv pip install -e ".[test]"
|
|
|
|
# Run unit tests only (no AD connection required)
|
|
pytest tests/test_ad_adapter.py -v
|
|
|
|
# Run integration smoke tests (requires AD credentials)
|
|
export AD_TEST_USERNAME=your_test_username
|
|
export AD_TEST_PASSWORD=your_test_password
|
|
export AD_TEST_USER=known_test_user
|
|
export AD_TEST_GROUP=known_test_group
|
|
export AD_TEST_COMPUTER=known_test_computer
|
|
pytest tests/test_integration.py -v
|
|
|
|
# Run all tests
|
|
pytest tests/ -v
|
|
```
|
|
|
|
## Active Directory adapter details
|
|
|
|
### Query mapping
|
|
|
|
- `get_user`: Uses `Get-ADUser` with samAccountName filter, retrieves Enabled, DistinguishedName, Description, lastLogonTimestamp
|
|
- `get_user_groups`: Uses `Get-ADUser` with MemberOf property, resolves group names
|
|
- `get_group_members`: Uses `Get-ADGroup` + `Get-ADGroupMember`, filters user objects only
|
|
- `find_stale_users`: Uses `Get-ADUser` with lastLogonTimestamp cutoff (FileTime comparison)
|
|
- `get_computer`: Uses `Get-ADComputer`, returns OU and null assigned_username (Phase 1)
|
|
|
|
### Authentication model
|
|
|
|
- Test environments: explicit username/password via environment variables
|
|
- Production: process runs as dedicated service account, no credentials in code
|
|
- Service account requires Read Directory Data permission only (no write permissions)
|
|
|
|
### Error handling
|
|
|
|
All backend failures (timeout, auth denied, permission denied) return:
|
|
|
|
- `None` for get_user and get_computer (tool wrapper converts to "User not found" or "Computer not found")
|
|
- Empty list `[]` for get_user_groups, get_group_members, find_stale_users
|
|
|
|
### Timeouts
|
|
|
|
Default: 30 seconds per query. Override with `AD_TIMEOUT` environment variable.
|
|
|
|
## Known gaps (deferred to later phases)
|
|
|
|
- Ticket-ID enforcement for write actions (Phase 3)
|
|
- PII redaction in audit logs (toggle ready, not enabled by default)
|
|
- Entra ID/Azure AD integration (separate adapter)
|
|
- Production service account provisioning (infrastructure task)
|
|
- Host/client configuration details depend on your selected MCP client
|
|
|
|
## Verification checklist
|
|
|
|
Before deploying to production:
|
|
|
|
- [ ] Unit tests pass: `pytest tests/test_ad_adapter.py -v`
|
|
- [ ] Integration smoke tests pass against non-production AD: `pytest tests/test_integration.py -v`
|
|
- [ ] Service account provisioned with Read Directory Data only
|
|
- [ ] MCP tool responses match expected contract shapes
|
|
- [ ] Error messages are friendly strings (not exceptions or technical errors)
|
|
- [ ] Stale-user logic confirmed using lastLogonTimestamp
|
|
- [ ] Computer assigned_username returns null in Phase 1
|
|
- [ ] Backend selection tested: memory mode and AD mode both run successfully
|
|
|
|
## Next implementation slice
|
|
|
|
1. Add structured PII redaction toggle for audit logs.
|
|
2. Add phase gate config that blocks write tools entirely until explicitly enabled (Phase 3 prep).
|
|
3. Implement Entra ID/Azure AD adapter (separate backend).
|
|
4. Add canary/rollback mechanism for backend switching.
|