scim2-models
Pydantic v2 models for SCIM2 resources and messages, implementing RFC7643 and RFC7644 with context-aware validation and serialization.
Repository Health
Technical Analysis
scim2-models gives Python developers native, type-checked classes for the resources and protocol messages defined by the SCIM2 specification (RFC7643 for schema, RFC7644 for protocol). Instead of hand-rolling dict-based payload handling for User, Group, EnterpriseUser, ServiceProviderConfig, ResourceType, and Schema objects, consumers get full Pydantic v2 models with IDE autocompletion, validation, and serialization built in.
What sets it apart from generic schema libraries is its context-aware validation system: the same model can be serialized or validated differently depending on whether it is being used for a resource creation request, a query response, a replacement, a patch, or a search — mirroring the mutability (readOnly, writeOnly, readWrite, immutable) and returned (always, never, default, request) semantics baked into the SCIM spec itself. It also ships SCIM protocol messages (ListResponse, SearchRequest, PatchOp, Error), SCIM-compliant exception types that convert automatically into spec-shaped error responses, and support for dynamic schema extensions so custom resource types and attributes can be modeled and converted at runtime.
It is maintained by Yaal Coop as the foundational model layer for a small family of SCIM tools (scim2-client, scim2-tester, scim2-cli), making it the natural starting point for anyone building a SCIM2 client or server in Python.
What You Get
- Native Pydantic classes for core SCIM resources:
User,Group,EnterpriseUser,ServiceProviderConfig,ResourceType, andSchema - Context-aware validation and serialization that automatically applies the correct mutability/returned rules for creation, query, replacement, patch, and search HTTP contexts
- SCIM protocol message types:
ListResponse,SearchRequest,PatchOp/PatchOperation,BulkRequest/BulkResponse, andError - Field-level metadata annotations (
Mutability,Returned,Uniqueness,Required,CaseExact) that mirror RFC7643 attribute characteristics - Dynamic schema extension support, including runtime conversion between schema definitions and Python model classes
- SCIM-compliant exception hierarchy (
InvalidFilterException,MutabilityException,UniquenessException, etc.) that converts directly into spec-shapedErrorresponses
Common Use Cases
- Building a SCIM2 server - validate inbound provisioning requests from an Identity Provider and shape outbound responses to match the exact fields the spec allows for each context
- Building a SCIM2 client - construct and serialize creation, query, and patch requests against a third-party SCIM-compliant API with confidence the payload is spec-correct
- Implementing enterprise user provisioning - model
EnterpriseUserextensions (department, manager, cost center) alongside coreUserattributes without custom parsing code - Handling PATCH operations - parse and apply
PatchOp/PatchOperationpayloads for add/remove/replace semantics defined in RFC7644 §3.5.2 - Supporting custom resource schemas - use dynamic schema extension to model organization-specific SCIM resource types beyond the core spec
Under The Hood
Architecture
The package is organized around a BaseModel (scim2_models/base.py) that extends Pydantic’s BaseModel with SCIM-specific behavior: custom model_serializer/model_validator hooks read Context enum values to decide which fields to include or reject, based on per-field Mutability and Returned annotations attached via typing.Annotated. Resource and message classes (scim2_models/resources/, scim2_models/messages/) subclass this base and declare their fields with those annotations rather than encoding context logic per-model. A separate path.py module implements SCIM path expression parsing (used for PATCH operations and attribute selection) as its own resolver over the model tree, and lookup.py provides schema-URN-to-class resolution so payloads can be deserialized into the correct dynamic or extension type at runtime. This clean separation — annotation-driven validation, a dedicated path resolver, and a schema lookup registry — means adding a new resource type is mostly a matter of declaring fields with the right annotations rather than writing new validation logic.
Tech Stack
The library targets Python 3.10+ and has a single runtime dependency: pydantic[email]>=2.12.0. It uses uv_build as its build backend and uv.lock for dependency locking, ruff for linting/formatting (with pydocstyle, pyupgrade, and isort rule sets enabled), and mypy in strict mode with the pydantic.mypy plugin for full static type checking. Documentation is built with Sphinx plus autodoc-pydantic and the Shibuya theme, and testing/tooling orchestration runs through tox with uv-based environments covering multiple Python versions (3.10-3.14) plus a dedicated minimum-versions check.
Code Quality
The project has an extensive test suite (25+ test files covering path parsing, dynamic schemas/resources, patch operations, exceptions, validation, and serialization individually) run via pytest with --doctest-modules so every module and reStructuredText doctest is also exercised as a test. Coverage is configured to fail under 100% in the dedicated coverage tox environment, and mypy --strict is enforced project-wide (excluding tests). Docstrings follow a consistent RFC-referencing style throughout the codebase, and pre-commit hooks (prek) plus a GitHub Actions test workflow gate changes before merge.
What Makes It Unique
Most general-purpose Python validation libraries (Pydantic itself, Marshmallow, attrs) have no built-in concept of “this field behaves differently depending on which HTTP operation is happening” — that’s a SCIM-specific requirement. scim2-models bakes RFC7643’s mutability/returned semantics directly into its Context enum and field annotation system, so the same model definition correctly enforces different validation and serialization rules for resource creation, query, replacement, patch, and search without consumers writing per-endpoint logic themselves. Combined with dynamic schema extension support for custom resource types, it functions less like a generic data-modeling library and more like a purpose-built protocol implementation layer for SCIM2.