A Complete Python client package for developing python code and apps for Alfresco. Great for doing AI development with Python based LangChain, LlamaIndex, neo4j-graphrag, etc. Also great for creating MCP servers (see python-alfresco-mcp-server).
Note this uses the remote Alfresco REST APIs. Not for in-process development in Alfresco.
A modern, type-safe Python client library for Alfresco Content Services REST APIs with dual model architecture (attrs + Pydantic) and async support.
- Complete API Coverage: All 7 Alfresco REST APIs (Auth, Core, Discovery, Search, Workflow, Model, Search SQL)
- 328+ Complete Domain Models: attrs-based raw client models with separate Pydantic models available for AI integration
- Model Conversion Utilities: Bridge utilities for attrs β Pydantic transformation when needed
- Async/Sync Support: Both synchronous and asynchronous API calls
- Authentication: Basic, ticket, or OAuth2/OIDC (Bearer) auth to Alfresco β see Authentication
- Modular Architecture: Individual client design for scalability
- AI/LLM Ready: Pydantic models available for AI integration, MCP servers, and tool interfaces
- Event System: ActiveMQ (STOMP) support for Python apps to handle Alfresco repo change events
- Docker Compatible: Works with Alfresco running in separate Docker Compose setups
- Comprehensive Testing: Extensive unit and live Alfresco integration tests
- ποΈ Architecture Overview and Diagram - V1.1 hierarchical architecture with visual diagram
- π Complete Documentation - Comprehensive guides and API documentation
- π― Working Examples - Live code examples and usage patterns
- π§ͺ Test Suite - Complete test coverage and integration examples
This is a MCP Server that uses Python Alfresco API
uv pip install python-alfresco-api- Requres: Python: 3.10+
- All features included - No optional dependencies needed! Includes event system, async support, and all 7 Alfresco APIs.
Best Practice: Always use a virtual environment to avoid dependency conflicts
This project uses uv for environments and installs. The examples below use Python 3.14 β install Python 3.14.5 or 3.14.6 first and use whichever you have (any Python 3.10+ works; adjust the version).
# Clone the repository
git clone https://github.com/stevereiner/python-alfresco-api.git
cd python-alfresco-api
# Create a virtual environment with uv (Python 3.14.x)
uv venv --python 3.14.5 venv-3.14
# Activate virtual environment
venv-3.14\Scripts\activate
# Verify activation (should show venv path)
where python
# Install the package + dependencies (from pyproject.toml)
uv pip install -e .
# Deactivate when done
deactivate# Clone the repository
git clone https://github.com/stevereiner/python-alfresco-api.git
cd python-alfresco-api
# Create a virtual environment with uv (Python 3.14.x)
uv venv --python 3.14.5 venv-3.14
# Activate virtual environment
source venv-3.14/bin/activate
# Verify activation (should show venv path)
which python
# Install the package + dependencies (from pyproject.toml)
uv pip install -e .
# Deactivate when done
deactivateInstall the package form PyPI use:
uv pip install python-alfresco-apiFor development of your project using python-alfresco-api to have debugging with source:
# After setting up virtual environment above
git clone https://github.com/your-org/python-alfresco-api.git
cd python-alfresco-api
# Activate your virtual environment first
# Windows: venv\Scripts\activate
# Linux/macOS: source venv/bin/activate
# Install in development mode
uv pip install -e .If you don't have an Alfresco server installed you can get a docker for the Community version from Github
git clone https://github.com/Alfresco/acs-deployment.gitStart Alfresco with Docker Compose
cd acs-deployment/docker-composeNote: you will likely need to comment out activemq ports other than 8161 in community-compose.yaml
ports:
- "8161:8161" # Web Console
#- "5672:5672" # AMQP
#- "61616:61616" # OpenWire
#- "61613:61613" # STOMP
docker-compose -f community-compose.yaml upFor easy configuration, copy the sample environment file:
# Windows
copy sample-dot-env.txt .env
# Mac and Linux
cp sample-dot-env.txt .env
# Edit .env and your Alfresco settingsThe factory pattern provides shared authentication and centralized configuration:
from python_alfresco_api import ClientFactory
# Automatic configuration (loads from .env file or environment variables)
factory = ClientFactory() # Uses ALFRESCO_URL, ALFRESCO_USERNAME, etc.
# Or explicit configuration
factory = ClientFactory(
base_url="http://localhost:8080",
username="admin",
password="admin"
)
Note 1: the priority order of ClientFactory parameters: 1. in auth_util passed in, 2. in other parameters passed into ClientFactory, 3. in enviroment .env, etc.
Note 2. For timeout, if not in 1-3, no default will be used. The settings for tickets or your system will be used.
# Create individual clients (all share same authentication session)
auth_client = factory.create_auth_client()
core_client = factory.create_core_client()
search_client = factory.create_search_client()
workflow_client = factory.create_workflow_client()
discovery_client = factory.create_discovery_client()
model_client = factory.create_model_client()
search_sql_client = factory.create_search_sql_client() # SOLR admin only
# Can also use a master client like setup with all clients initialized
master_client = factory.create_master_client()ClientFactory(auth_util=...) accepts any of these auth utilities (all live-tested against Alfresco Community 25.2 / 26.1). The sub-clients call the util synchronously at client-build time, so tokens/tickets are acquired lazily without pre-awaiting.
-
Basic β HTTP Basic. Use
AuthUtil/SimpleAuthUtil, or just passusername/passwordstraight toClientFactory:from python_alfresco_api import AuthUtil, ClientFactory auth_util = AuthUtil( base_url="http://localhost:8080", username="admin", password="admin", ) # Use with factory for shared authentication factory = ClientFactory(auth_util=auth_util) clients = factory.create_all_clients()
Note 1: the priority order of
ClientFactoryparameters: 1.auth_utilpassed in, 2. other parameters passed intoClientFactory, 3. environment.env, etc.Note 2: for
timeout, if not in 1β3, no default will be used β the settings for tickets or your system will be used. -
Ticket β
TicketAuthUtillogs in once at/authentication/versions/1/tickets, then sends the ticket asAuthorization: Basic base64(<ticket>)so the password isn't sent on every request:from python_alfresco_api import ClientFactory from python_alfresco_api.auth_util import TicketAuthUtil auth = TicketAuthUtil("admin", "admin", base_url="http://localhost:8080") factory = ClientFactory(base_url="http://localhost:8080", auth_util=auth)
-
OAuth2 / OIDC Bearer β
OAuth2AuthUtilpresents a Bearer token, validated by Alfresco'sidentity-servicesubsystem against any OIDC IdP (e.g. Keycloak). Two modes:from python_alfresco_api import ClientFactory from python_alfresco_api.auth_util import OAuth2AuthUtil # (a) client_credentials β the util fetches and refreshes its own token auth = OAuth2AuthUtil( base_url="http://localhost:8080", client_id="my-client", client_secret="my-secret", token_endpoint="https://<keycloak>/realms/<realm>/protocol/openid-connect/token", grant_type="client_credentials", ) # (b) pre-obtained token β pass access_token; add refresh_token + token_endpoint for auto-refresh auth = OAuth2AuthUtil( base_url="http://localhost:8080", client_id="my-client", access_token="<access-token>", refresh_token="<refresh-token>", token_endpoint="https://<keycloak>/realms/<realm>/protocol/openid-connect/token", ) factory = ClientFactory(base_url="http://localhost:8080", auth_util=auth)
A provided access token that has already expired is detected from its JWT
expclaim and auto-refreshed when arefresh_token+token_endpointare supplied.ALFRESCO_OAUTH2_*environment variables are also read whenload_env=True.Service account vs. user token.
client_credentialsauthenticates as the client's service account (e.g.service-account-<client-id>) β a just-in-time Alfresco user with no display name and only default permissions (not an admin, and not the same as Alfresco'sguest). For content operations prefer a user token (obtain one via a password grant, then passaccess_token/refresh_token) so operations run as a real user with a display name and that user's ACLs. As of 1.2.1 the client also tolerates a missingdisplayName(defaults it to the user id), so the service-account path no longer raisesKeyError.
import asyncio
from python_alfresco_api import ClientFactory
async def main():
factory = ClientFactory(
base_url="http://localhost:8080",
username="admin",
password="admin"
)
# Create core client for node operations
core_client = factory.create_core_client()
# Sync node operation
sync_node = core_client.get_node("-my-")
print(f"Sync: User folder '{sync_node.entry.name}'")
# Async node operation
async_node = await core_client.get_node_async("-my-")
print(f"Async: User folder '{async_node.entry.name}'")
# Run the async example
asyncio.run(main())Quick examples of the most common operations. π For complete coverage, see π Essential Operations Guide
from python_alfresco_api import ClientFactory
from python_alfresco_api.utils import content_utils_highlevel
factory = ClientFactory(base_url="http://localhost:8080", username="admin", password="admin")
core_client = factory.create_core_client()# Create folder (High-Level Utility)
folder_result = content_utils_highlevel.create_folder_highlevel(
core_client=core_client,
name="My Project Folder",
parent_id="-my-"
)
# Upload document with auto-versioning
document_result = content_utils_highlevel.create_and_upload_file_highlevel(
core_client=core_client,
file_path="/path/to/document.pdf",
parent_id=folder_result['id']
)from python_alfresco_api.utils import search_utils
search_client = factory.create_search_client()
# Simple text search (already optimized!)
results = search_utils.simple_search(
search_client=search_client,
query_str="finance AND reports",
max_items=25
)# Download document content
content_response = core_client.nodes.get_content(node_id=document_id)
# Save to file
with open("downloaded_document.pdf", "wb") as file:
file.write(content_response.content)from python_alfresco_api.utils import content_utils_highlevel
# Get node properties and details
node_info = content_utils_highlevel.get_node_info_highlevel(
core_client=core_client,
node_id=document_id
)
print(f"Title: {node_info.get('properties', {}).get('cm:title', 'No title')}")
# Update node properties
update_request = {
"properties": {
"cm:title": "Updated Document Title",
"cm:description": "Updated via Python API"
}
}
updated_node = core_client.nodes.update(node_id=document_id, request=update_request)from python_alfresco_api.utils import version_utils_highlevel
# Checkout document (lock for editing)
checkout_result = version_utils_highlevel.checkout_document_highlevel(
core_client=core_client,
node_id=document_id
)
# Later: Checkin with updated content (create new version)
checkin_result = version_utils_highlevel.checkin_document_highlevel(
core_client=core_client,
node_id=document_id,
content="Updated document content",
comment="Fixed formatting and added new section"
)| Resource | Purpose | What You'll Find |
|---|---|---|
| π Essential Operations Guide | Complete operation coverage | All operations with both high-level utilities and V1.1 APIs |
| π examples/operations/ | Copy-paste examples | Windows-compatible, production-ready code |
| π§ͺ tests/test_mcp_v11_true_high_level_apis_fixed.py | MCP Server patterns | 15 operations with sync/async patterns |
| π§ͺ tests/test_highlevel_utils.py | High-level utilities testing | Real Alfresco integration examples |
| Example File | Key Operations |
|---|---|
| upload_document.py | Document upload, automatic versioning, batch uploads |
| versioning_workflow.py | Checkout β Edit β Checkin workflow, version history |
| basic_operations.py | Folder creation, CRUD operations, browsing, deletion |
| search_operations.py | Content search, metadata queries, advanced search |
V1.1 implements a dual model system with conversion utilities:
| Component | Model Type | Purpose |
|---|---|---|
| Raw Client Models | @_attrs_define |
Complete OpenAPI domain models (RepositoryInfo, NodeEntry, etc.) |
| Pydantic Models | BaseModel |
AI/LLM integration, validation, type safety |
| Conversion Utils | Bridge utilities | Transformation between attrs β Pydantic |
For detailed guidance, see π Pydantic Models Guide and π Conversion Utilities Design.
# β
V1.1: Two model systems with conversion utilities
from python_alfresco_api.models.alfresco_core_models import NodeBodyCreate # Pydantic
from python_alfresco_api.raw_clients.alfresco_core_client.models import NodeBodyCreate as AttrsNodeBodyCreate # attrs
from python_alfresco_api.clients.conversion_utils import pydantic_to_attrs_dict
# 1. Use Pydantic for validation and AI integration
pydantic_model = NodeBodyCreate(name="document.pdf", nodeType="cm:content")
# 2. Convert for raw client usage
factory = ClientFactory()
core_client = factory.create_core_client()
# Option A: Manual conversion via model_dump()
result = core_client.create_node(pydantic_model.model_dump())
# Option B: Conversion utilities (V1.1)
attrs_dict = pydantic_to_attrs_dict(pydantic_model, target_class_name="NodeBodyCreate")
result = core_client.create_node(attrs_dict)
# 3. Raw clients return attrs-based domain models
repository_info = discovery_client.get_repository_information() # Returns attrs RepositoryInfo
# Convert to dict for further processing
repo_dict = repository_info.to_dict()V1.2 will migrate raw client models from attrs to Pydantic v2:
# π― V1.2 Target: Single Pydantic model system
from python_alfresco_api.raw_clients.alfresco_core_client.models import NodeBodyCreate # Will be Pydantic!
# No conversion needed - everything is Pydantic BaseModel
pydantic_model = NodeBodyCreate(name="document.pdf", nodeType="cm:content")
result = core_client.create_node(pydantic_model) # Direct usage!Notes
- V1.1: Dual system with conversion utilities
- Pydantic models: Available for AI/LLM integration and validation
- Raw client models: attrs-based with 328+ complete domain models
- V1.2: Will unify to Pydantic v2 throughout
Alfresco Content Services uses ActiveMQ for messaging; repo events are published to the STOMP topic /topic/alfresco.repo.event2 (STOMP port 61613; 61616 is OpenWire). ActiveMQ 6.x (ACS 26.1+) enforces broker authentication.
AlfrescoEventClient is a lightweight detection + handler-registry helper:
from python_alfresco_api.events import AlfrescoEventClient
event_client = AlfrescoEventClient(
alfresco_host="localhost",
activemq_port=61613, # ActiveMQ STOMP port
username="admin",
password="admin",
)
def node_created_handler(notification):
print(f"Node created: {notification.node_id}")
event_client.register_event_handler("node.created", node_created_handler)
print(event_client.get_system_info()) # {'activemq_available': ..., 'active_system': 'activemq'|None, ...}Consuming events: actual event listening is intentionally not implemented in this client β subscribe to the STOMP topic directly with
stomp.py. Note that stomp.py's credential kwarg ispasscode=(notpassword=), which matters now that ActiveMQ 6.x enforces auth. See flexible-graphrag'sAlfrescoEventBroadcasterfor a complete shared-connection consumer.
For complete development documentation including the 3-step generation process (Pydantic models β HTTP clients β High-level APIs), see π Package Developers Guide.
For development, testing, and contributing (installs the dev extra β pytest, black, mypy, docs, build tooling):
uv pip install -e ".[dev]"To regenerate models/clients, install the codegen extra instead: uv pip install -e ".[codegen]".
For most development work on python-alfresco-api, you can develop directly without regenerating code:
git clone https://github.com/stevereiner/python-alfresco-api.git
cd python-alfresco-api
# Install in development mode
uv pip install -e .Note: For proper pytest execution, work from the source directory with
uv pip install -e .rather than testing from separate directories. This avoids import path conflicts.
cd python-alfresco-api
# Simple - just run all tests pytest
pytest
# Run all tests with coverage
pytest --cov=python_alfresco_api --cov-report=html
# Custom test runner with additional features
python run_tests.py
# Features:
# - Environment validation (venv, dependencies)
# - Colored output with progress tracking
# - Test selection for 44%+ coverage baseline
# - Performance metrics (client creation speed)
# - Live Alfresco server detection
# - HTML coverage reports (htmlcov/index.html)
# - Test summary with next stepsTo run tests against a live Alfresco server (Note: This package was developed and tested with Community Edition)
# Run one test (test live with Alfresco)
pytest tests/test_mcp_v11_true_high_level_apis_fixed.py -v
python-alfresco-api/
βββ python_alfresco_api/
β βββ __init__.py # Main exports
β βββ auth_util.py # Authentication utility
β βββ client_factory.py # Client factory pattern
β βββ clients/ # Individual API clients + utilities
β β βββ auth_client.py
β β βββ core_client.py
β β βββ discovery_client.py
β β βββ search_client.py
β β βββ workflow_client.py
β β βββ model_client.py
β β βββ search_sql_client.py
β β βββ conversion_utils.py # Pydantic β attrs conversion utilities
β βββ models/ # Pydantic v2 models (available for separate use)
β β βββ alfresco_auth_models.py
β β βββ alfresco_core_models.py
β β βββ alfresco_discovery_models.py
β β βββ alfresco_search_models.py
β β βββ alfresco_workflow_models.py
β β βββ alfresco_model_models.py
β β βββ alfresco_search_sql_models.py
β βββ raw_clients/ # Generated HTTP clients
β βββ utils/ # Utility functions
β β βββ content_utils.py
β β βββ node_utils.py
β β βββ search_utils.py
β β βββ version_utils.py
β β βββ mcp_formatters.py
β βββ events/ # Event system (Community + Enterprise)
β βββ __init__.py # Event exports
β βββ event_client.py # Unified event client (AlfrescoEventClient)
β βββ models.py # Event models (EventSubscription, EventNotification)
βββ config/ # Code generation configurations
β βββ auth.yaml # Auth API config β auth_client
β βββ core.yaml # Core API config β core_client
β βββ discovery.yaml # Discovery API config β discovery_client
β βββ search.yaml # Search API config β search_client
β βββ workflow.yaml # Workflow API config β workflow_client
β βββ model.yaml # Model API config β model_client
β βββ search_sql.yaml # Search SQL API config β search_sql_client
β βββ general.yaml # Unified config β alfresco_client
β βββ README.md # Configuration documentation
βββ openapi/ # OpenAPI specifications (checked in)
β βββ openapi2/ # Original OpenAPI 2.0 specs
β βββ openapi2-processed/ # Cleaned OpenAPI 2.0 specs
β βββ openapi3/ # Converted OpenAPI 3.0 specs
βββ tests/ # Comprehensive test suite
βββ scripts/ # Generation scripts
βββ docs/ # Comprehensive documentation
β βββ PYDANTIC_MODELS_GUIDE.md # Complete Pydantic models guide
β βββ CLIENT_TYPES_GUIDE.md # Client architecture guide
β βββ CONVERSION_UTILITIES_DESIGN.md # Model conversion utilities
β βββ REQUEST_TYPES_GUIDE.md # Node & Search request documentation
β βββ API_DOCUMENTATION_INDEX.md # Complete API reference
βββ examples/ # Working usage examples
βββ pyproject.toml # Package metadata, dependencies, and extras (dev, codegen)
βββ run_tests.py # Test runner with nice display
βββ README.md # This file
- Python: 3.10+
- pydantic: >=2.0.0,<3.0.0
- requests: >=2.31.0
- httpx: >=0.24.0 (for async support)
- aiohttp: >=3.8.0 (for async HTTP)
- stomp.py: >=8.1.0 (for ActiveMQ events)
- ujson: >=5.7.0 (faster JSON parsing)
- requests-oauthlib: >=1.3.0 (OAuth support)
For development workflows, code generation, testing, and contribution guidelines, see π Package Developers Guide.
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
pytest - Submit a pull request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- Issues: GitHub Issues
- Documentation: Project Documentation
- Examples: Usage Examples
- Model Context Protocol (MCP): MCP Documentation - Standard for AI-data source and function integration
- Alfresco Community Edition: Community Documentation
- Alfresco Enterprise Edition: Enterprise Documentation
- Pydantic: Type validation library
- Datamodel-code-generator: Pydantic model generator
- Openapi-python-client: HTTP client generator
- MCP Server based on Python Alfresco API: python-alfresco-mcp-server
If this project helps you, please consider giving it a star! β