d365fo-client 0.2.3__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {d365fo_client-0.2.3/src/d365fo_client.egg-info → d365fo_client-0.2.4}/PKG-INFO +1011 -815
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/README.md +1010 -814
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/pyproject.toml +1 -1
- {d365fo_client-0.2.3 → d365fo_client-0.2.4/src/d365fo_client.egg-info}/PKG-INFO +1011 -815
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/LICENSE +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/setup.cfg +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/__init__.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/auth.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/cli.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/client.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/config.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/credential_sources.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/crud.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/exceptions.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/labels.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/main.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/__init__.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/client_manager.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/main.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/models.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/prompts/__init__.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/prompts/action_execution.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/prompts/sequence_analysis.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/resources/__init__.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/resources/database_handler.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/resources/entity_handler.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/resources/environment_handler.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/resources/metadata_handler.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/resources/query_handler.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/server.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/__init__.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/connection_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/crud_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/database_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/label_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/metadata_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/profile_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/mcp/tools/sync_tools.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_api.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/__init__.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/cache_v2.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/database_v2.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/global_version_manager.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/label_utils.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/search_engine_v2.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/sync_manager_v2.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/sync_session_manager.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/metadata_v2/version_detector.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/models.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/output.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/profile_manager.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/profiles.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/query.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/session.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/sync_models.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client/utils.py +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client.egg-info/SOURCES.txt +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client.egg-info/dependency_links.txt +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client.egg-info/entry_points.txt +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client.egg-info/requires.txt +0 -0
- {d365fo_client-0.2.3 → d365fo_client-0.2.4}/src/d365fo_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: d365fo-client
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.4
|
4
4
|
Summary: Microsoft Dynamics 365 Finance & Operations client
|
5
5
|
Author-email: Muhammad Afzaal <mo@thedataguy.pro>
|
6
6
|
License-Expression: MIT
|
@@ -40,1050 +40,1246 @@ Provides-Extra: all
|
|
40
40
|
Requires-Dist: d365fo-client[dev]; extra == "all"
|
41
41
|
Dynamic: license-file
|
42
42
|
|
43
|
-
# Dynamics 365 Finance & Operations
|
43
|
+
# Dynamics 365 Finance & Operations MCP Server
|
44
44
|
|
45
|
-
|
45
|
+
**Production-ready Model Context Protocol (MCP) server** that exposes the full capabilities of Microsoft Dynamics 365 Finance & Operations (D365 F&O) to AI assistants and other MCP-compatible tools. This enables sophisticated Dynamics 365 integration workflows through standardized protocol interactions.
|
46
46
|
|
47
|
-
|
47
|
+
**🚀 One-Click Installation for VS Code:**
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
- 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
|
52
|
-
- 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
|
53
|
-
- ⚡ **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
|
54
|
-
- 🔒 **Authentication**: Azure AD integration with default credentials, service principal, and Azure Key Vault support
|
55
|
-
- 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
|
56
|
-
- 🌐 **Async/Await**: Modern async/await patterns with optimized session management
|
57
|
-
- 📝 **Type Hints**: Full type annotation support with enhanced data models
|
58
|
-
- 🤖 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
|
59
|
-
- 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
|
60
|
-
- 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
|
61
|
-
- 📋 **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery
|
62
|
-
- 🔐 **Enhanced Credential Management**: Support for Azure Key Vault and multiple credential sources
|
63
|
-
- 📊 **Advanced Sync Management**: Session-based synchronization with detailed progress tracking
|
64
|
-
|
65
|
-
## Installation
|
49
|
+
[](https://vscode.dev/redirect/mcp/install?name=d365fo&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22d365fo-client%40latest%22%2C%22d365fo-mcp-server%22%5D%2C%22env%22%3A%7B%22D365FO_CLIENT_ID%22%3A%22%24%7Binput%3Aclient_id%7D%22%2C%22D365FO_CLIENT_SECRET%22%3A%22%24%7Binput%3Aclient_secret%7D%22%2C%22D365FO_TENANT_ID%22%3A%22%24%7Binput%3Atenant_id%7D%22%7D%7D&inputs=%5B%7B%22id%22%3A%22tenant_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22The%20ID%20of%20the%20tenant%20to%20connect%20to%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22The%20ID%20of%20the%20client%20to%20connect%20to%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_secret%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22The%20secret%20of%20the%20client%20to%20connect%20to%22%2C%22password%22%3Atrue%7D%5D)
|
50
|
+
[](https://insiders.vscode.dev/redirect/mcp/install?name=d365fo&quality=insiders&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22d365fo-client%40latest%22%2C%22d365fo-mcp-server%22%5D%2C%22env%22%3A%7B%22D365FO_CLIENT_ID%22%3A%22%24%7Binput%3Aclient_id%7D%22%2C%22D365FO_CLIENT_SECRET%22%3A%22%24%7Binput%3Aclient_secret%7D%22%2C%22D365FO_TENANT_ID%22%3A%22%24%7Binput%3Atenant_id%7D%22%7D%7D&inputs=%5B%7B%22id%22%3A%22tenant_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22The%20ID%20of%20the%20tenant%20to%20connect%20to%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22The%20ID%20of%20the%20client%20to%20connect%20to%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_secret%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22The%20secret%20of%20the%20client%20to%20connect%20to%22%2C%22password%22%3Atrue%7D%5D)
|
66
51
|
|
67
|
-
|
68
|
-
# Install from PyPI
|
69
|
-
pip install d365fo-client
|
70
|
-
|
71
|
-
# Or install from source
|
72
|
-
git clone https://github.com/mafzaal/d365fo-client.git
|
73
|
-
cd d365fo-client
|
74
|
-
uv sync # Installs with exact dependencies from uv.lock
|
75
|
-
```
|
76
|
-
|
77
|
-
**Note**: The package includes MCP (Model Context Protocol) dependencies by default, enabling AI assistant integration. Both `d365fo-client` CLI and `d365fo-mcp-server` commands will be available after installation.
|
52
|
+
**🐳 Docker Installation for VS Code:**
|
78
53
|
|
79
|
-
|
80
|
-
|
81
|
-
- `AZURE_CLIENT_SECRET` → `D365FO_CLIENT_SECRET`
|
82
|
-
- `AZURE_TENANT_ID` → `D365FO_TENANT_ID`
|
54
|
+
[-2496ED?style=flat-square&logo=docker&logoColor=white)](https://vscode.dev/redirect/mcp/install?name=d365fo-docker&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22--rm%22%2C%22-i%22%2C%22-v%22%2C%22d365fo-mcp%3A%2Fhome%2Fmcp_user%2F%22%2C%22-e%22%2C%22D365FO_CLIENT_ID%3D%24%7Binput%3Aclient_id%7D%22%2C%22-e%22%2C%22D365FO_CLIENT_SECRET%3D%24%7Binput%3Aclient_secret%7D%22%2C%22-e%22%2C%22D365FO_TENANT_ID%3D%24%7Binput%3Atenant_id%7D%22%2C%22ghcr.io%2Fmafzaal%2Fd365fo-client%3Alatest%22%5D%2C%22env%22%3A%7B%22D365FO_LOG_LEVEL%22%3A%22DEBUG%22%2C%22D365FO_CLIENT_ID%22%3A%22%24%7Binput%3Aclient_id%7D%22%2C%22D365FO_CLIENT_SECRET%22%3A%22%24%7Binput%3Aclient_secret%7D%22%2C%22D365FO_TENANT_ID%22%3A%22%24%7Binput%3Atenant_id%7D%22%7D%7D&inputs=%5B%7B%22id%22%3A%22tenant_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Azure%20AD%20Tenant%20ID%20for%20D365%20F%26O%20authentication%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Azure%20AD%20Client%20ID%20for%20D365%20F%26O%20authentication%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_secret%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Azure%20AD%20Client%20Secret%20for%20D365%20F%26O%20authentication%22%2C%22password%22%3Atrue%7D%5D)
|
55
|
+
[-2496ED?style=flat-square&logo=docker&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=d365fo-docker&quality=insiders&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22--rm%22%2C%22-i%22%2C%22-v%22%2C%22d365fo-mcp%3A%2Fhome%2Fmcp_user%2F%22%2C%22-e%22%2C%22D365FO_CLIENT_ID%3D%24%7Binput%3Aclient_id%7D%22%2C%22-e%22%2C%22D365FO_CLIENT_SECRET%3D%24%7Binput%3Aclient_secret%7D%22%2C%22-e%22%2C%22D365FO_TENANT_ID%3D%24%7Binput%3Atenant_id%7D%22%2C%22ghcr.io%2Fmafzaal%2Fd365fo-client%3Alatest%22%5D%2C%22env%22%3A%7B%22D365FO_LOG_LEVEL%22%3A%22DEBUG%22%2C%22D365FO_CLIENT_ID%22%3A%22%24%7Binput%3Aclient_id%7D%22%2C%22D365FO_CLIENT_SECRET%22%3A%22%24%7Binput%3Aclient_secret%7D%22%2C%22D365FO_TENANT_ID%22%3A%22%24%7Binput%3Atenant_id%7D%22%7D%7D&inputs=%5B%7B%22id%22%3A%22tenant_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Azure%20AD%20Tenant%20ID%20for%20D365%20F%26O%20authentication%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_id%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Azure%20AD%20Client%20ID%20for%20D365%20F%26O%20authentication%22%2C%22password%22%3Atrue%7D%2C%7B%22id%22%3A%22client_secret%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Azure%20AD%20Client%20Secret%20for%20D365%20F%26O%20authentication%22%2C%22password%22%3Atrue%7D%5D)
|
83
56
|
|
84
|
-
|
57
|
+
[](https://pypi.org/project/d365fo-client/)
|
85
58
|
|
86
|
-
|
59
|
+
**Also includes a comprehensive Python client library** for Microsoft Dynamics 365 Finance & Operations with OData endpoints, metadata operations, label management, and CLI tools.
|
87
60
|
|
88
|
-
##
|
61
|
+
## MCP Server Overview
|
89
62
|
|
90
|
-
d365fo-client
|
63
|
+
The d365fo-client includes a **production-ready Model Context Protocol (MCP) server** that exposes the full capabilities of the D365 Finance & Operations client to AI assistants and other MCP-compatible tools. This enables sophisticated Dynamics 365 integration workflows through standardized protocol interactions.
|
91
64
|
|
92
|
-
###
|
65
|
+
### Key Features
|
93
66
|
|
94
|
-
|
95
|
-
|
96
|
-
|
67
|
+
- **34 comprehensive tools** covering all major D365 F&O operations across 7 functional categories
|
68
|
+
- **6 resource types** with comprehensive metadata exposure and discovery capabilities
|
69
|
+
- **Production-ready** implementation with proper error handling, authentication, and security validation
|
70
|
+
- **Performance optimization** with connection pooling, intelligent caching V2, and session management
|
71
|
+
- **Comprehensive testing** with 14 unit tests (100% pass rate) and multi-tier integration testing
|
72
|
+
- **Advanced profile management** supporting multiple environments with secure credential storage
|
73
|
+
- **Database analysis capabilities** with secure SQL querying and metadata insights
|
74
|
+
- **Session-based synchronization** with detailed progress tracking and multiple sync strategies
|
75
|
+
- **Multi-language support** with label resolution and localization capabilities
|
76
|
+
- **Enterprise security** with Azure AD integration, Key Vault support, and audit logging
|
97
77
|
|
98
|
-
|
99
|
-
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
|
100
|
-
```
|
78
|
+
### Quick Start
|
101
79
|
|
102
|
-
|
80
|
+
#### Installation and Setup
|
103
81
|
|
104
|
-
#### Entity Operations
|
105
82
|
```bash
|
106
|
-
#
|
107
|
-
d365fo-client
|
83
|
+
# Install d365fo-client with MCP dependencies
|
84
|
+
pip install d365fo-client
|
108
85
|
|
109
|
-
#
|
110
|
-
|
86
|
+
# Set up environment variables
|
87
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
88
|
+
export D365FO_CLIENT_ID="your-client-id" # Optional with default credentials
|
89
|
+
export D365FO_CLIENT_SECRET="your-client-secret" # Optional with default credentials
|
90
|
+
export D365FO_TENANT_ID="your-tenant-id" # Optional with default credentials
|
111
91
|
|
112
|
-
#
|
113
|
-
d365fo-
|
114
|
-
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
|
115
|
-
d365fo-client entities delete Customers US-999
|
92
|
+
# Start the MCP server
|
93
|
+
d365fo-mcp-server
|
116
94
|
```
|
117
95
|
|
118
|
-
####
|
119
|
-
```bash
|
120
|
-
# Search and discover entities
|
121
|
-
d365fo-client metadata entities --search "sales" --output json
|
96
|
+
#### Integration with AI Assistants
|
122
97
|
|
123
|
-
|
124
|
-
d365fo-client metadata actions --pattern "calculate" --limit 5
|
98
|
+
##### VS Code Integration (Recommended)
|
125
99
|
|
126
|
-
|
127
|
-
|
100
|
+
**Option 1: Default Credentials**
|
101
|
+
Add to your VS Code `mcp.json` for GitHub Copilot with MCP:
|
128
102
|
|
129
|
-
|
130
|
-
|
103
|
+
```json
|
104
|
+
{
|
105
|
+
"servers": {
|
106
|
+
"d365fo-mcp-server": {
|
107
|
+
"type": "stdio",
|
108
|
+
"command": "uvx",
|
109
|
+
"args": [
|
110
|
+
"--from",
|
111
|
+
"d365fo-client",
|
112
|
+
"d365fo-mcp-server"
|
113
|
+
],
|
114
|
+
"env": {
|
115
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
116
|
+
"D365FO_LOG_LEVEL": "INFO"
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
131
121
|
```
|
132
122
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
123
|
+
**Option 2: Explicit Credentials**
|
124
|
+
For environments requiring service principal authentication:
|
125
|
+
|
126
|
+
```json
|
127
|
+
{
|
128
|
+
"servers": {
|
129
|
+
"d365fo-mcp-server": {
|
130
|
+
"type": "stdio",
|
131
|
+
"command": "uvx",
|
132
|
+
"args": [
|
133
|
+
"--from",
|
134
|
+
"d365fo-client",
|
135
|
+
"d365fo-mcp-server"
|
136
|
+
],
|
137
|
+
"env": {
|
138
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
139
|
+
"D365FO_LOG_LEVEL": "DEBUG",
|
140
|
+
"D365FO_CLIENT_ID": "${input:client_id}",
|
141
|
+
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
142
|
+
"D365FO_TENANT_ID": "${input:tenant_id}"
|
143
|
+
}
|
144
|
+
}
|
145
|
+
},
|
146
|
+
"inputs": [
|
147
|
+
{
|
148
|
+
"id": "tenant_id",
|
149
|
+
"type": "promptString",
|
150
|
+
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
151
|
+
"password": true
|
152
|
+
},
|
153
|
+
{
|
154
|
+
"id": "client_id",
|
155
|
+
"type": "promptString",
|
156
|
+
"description": "Azure AD Client ID for D365 F&O authentication",
|
157
|
+
"password": true
|
158
|
+
},
|
159
|
+
{
|
160
|
+
"id": "client_secret",
|
161
|
+
"type": "promptString",
|
162
|
+
"description": "Azure AD Client Secret for D365 F&O authentication",
|
163
|
+
"password": true
|
164
|
+
}
|
165
|
+
]
|
166
|
+
}
|
139
167
|
```
|
140
168
|
|
141
|
-
|
142
|
-
|
143
|
-
# Resolve single label
|
144
|
-
d365fo-client labels resolve "@SYS13342"
|
169
|
+
**Option 3: Docker Integration**
|
170
|
+
For containerized environments and enhanced isolation:
|
145
171
|
|
146
|
-
|
147
|
-
|
172
|
+
```json
|
173
|
+
{
|
174
|
+
"servers": {
|
175
|
+
"d365fo-mcp-server": {
|
176
|
+
"type": "stdio",
|
177
|
+
"command": "docker",
|
178
|
+
"args": [
|
179
|
+
"run",
|
180
|
+
"--rm",
|
181
|
+
"-i",
|
182
|
+
"-v",
|
183
|
+
"d365fo-mcp:/home/mcp_user/",
|
184
|
+
"-e",
|
185
|
+
"D365FO_CLIENT_ID=${input:client_id}",
|
186
|
+
"-e",
|
187
|
+
"D365FO_CLIENT_SECRET=${input:client_secret}",
|
188
|
+
"-e",
|
189
|
+
"D365FO_TENANT_ID=${input:tenant_id}",
|
190
|
+
"ghcr.io/mafzaal/d365fo-client:latest"
|
191
|
+
],
|
192
|
+
"env": {
|
193
|
+
"D365FO_LOG_LEVEL": "DEBUG",
|
194
|
+
"D365FO_CLIENT_ID": "${input:client_id}",
|
195
|
+
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
196
|
+
"D365FO_TENANT_ID": "${input:tenant_id}"
|
197
|
+
}
|
198
|
+
}
|
199
|
+
},
|
200
|
+
"inputs": [
|
201
|
+
{
|
202
|
+
"id": "tenant_id",
|
203
|
+
"type": "promptString",
|
204
|
+
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
205
|
+
"password": true
|
206
|
+
},
|
207
|
+
{
|
208
|
+
"id": "client_id",
|
209
|
+
"type": "promptString",
|
210
|
+
"description": "Azure AD Client ID for D365 F&O authentication",
|
211
|
+
"password": true
|
212
|
+
},
|
213
|
+
{
|
214
|
+
"id": "client_secret",
|
215
|
+
"type": "promptString",
|
216
|
+
"description": "Azure AD Client Secret for D365 F&O authentication",
|
217
|
+
"password": true
|
218
|
+
}
|
219
|
+
]
|
220
|
+
}
|
148
221
|
```
|
149
222
|
|
150
|
-
|
151
|
-
|
152
|
-
-
|
153
|
-
-
|
154
|
-
-
|
155
|
-
-
|
156
|
-
-
|
223
|
+
**Benefits of Docker approach:**
|
224
|
+
- Complete environment isolation and reproducibility
|
225
|
+
- No local Python installation required
|
226
|
+
- Consistent runtime environment across different systems
|
227
|
+
- Automatic dependency management with pre-built image
|
228
|
+
- Enhanced security through containerization
|
229
|
+
- Persistent data storage via Docker volume (`d365fo-mcp`)
|
157
230
|
|
158
|
-
|
231
|
+
**Prerequisites:**
|
232
|
+
- Docker installed and running
|
233
|
+
- Access to Docker Hub or GitHub Container Registry
|
234
|
+
- Network access for pulling the container image
|
159
235
|
|
160
|
-
|
236
|
+
##### Claude Desktop Integration
|
161
237
|
|
162
|
-
|
163
|
-
profiles:
|
164
|
-
production:
|
165
|
-
base_url: "https://prod.dynamics.com"
|
166
|
-
use_default_credentials: true
|
167
|
-
timeout: 60
|
168
|
-
|
169
|
-
development:
|
170
|
-
base_url: "https://dev.dynamics.com"
|
171
|
-
client_id: "${D365FO_CLIENT_ID}"
|
172
|
-
client_secret: "${D365FO_CLIENT_SECRET}"
|
173
|
-
tenant_id: "${D365FO_TENANT_ID}"
|
174
|
-
use_cache_first: true
|
238
|
+
Add to your Claude Desktop configuration:
|
175
239
|
|
176
|
-
|
240
|
+
```json
|
241
|
+
{
|
242
|
+
"mcpServers": {
|
243
|
+
"d365fo": {
|
244
|
+
"command": "uvx",
|
245
|
+
"args": [
|
246
|
+
"--from",
|
247
|
+
"d365fo-client",
|
248
|
+
"d365fo-mcp-server"
|
249
|
+
],
|
250
|
+
"env": {
|
251
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
252
|
+
"D365FO_LOG_LEVEL": "INFO"
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
177
257
|
```
|
178
258
|
|
179
|
-
|
259
|
+
**Benefits of uvx approach:**
|
260
|
+
- Always uses the latest version from the repository
|
261
|
+
- No local installation required
|
262
|
+
- Automatic dependency management
|
263
|
+
- Works across different environments
|
180
264
|
|
181
|
-
|
182
|
-
# Quick entity discovery
|
183
|
-
d365fo-client entities list --pattern "cust.*" --output json
|
265
|
+
#### Alternative: Programmatic Usage
|
184
266
|
|
185
|
-
|
186
|
-
|
267
|
+
```python
|
268
|
+
from d365fo_client.mcp import D365FOMCPServer
|
187
269
|
|
188
|
-
#
|
189
|
-
|
270
|
+
# Create and run server with custom configuration
|
271
|
+
config = {
|
272
|
+
"default_environment": {
|
273
|
+
"base_url": "https://your-environment.dynamics.com",
|
274
|
+
"use_default_credentials": True
|
275
|
+
}
|
276
|
+
}
|
190
277
|
|
191
|
-
|
192
|
-
|
278
|
+
server = D365FOMCPServer(config)
|
279
|
+
await server.run()
|
193
280
|
```
|
194
281
|
|
195
|
-
|
196
|
-
|
197
|
-
```bash
|
198
|
-
d365fo-client --help
|
199
|
-
d365fo-client entities --help
|
200
|
-
d365fo-client metadata --help
|
201
|
-
```
|
202
|
-
### Basic Usage
|
282
|
+
#### Custom MCP Clients
|
283
|
+
Connect using any MCP-compatible client library:
|
203
284
|
|
204
285
|
```python
|
205
|
-
import
|
206
|
-
from d365fo_client import D365FOClient, FOClientConfig
|
286
|
+
from mcp import Client
|
207
287
|
|
208
|
-
async
|
209
|
-
#
|
210
|
-
|
211
|
-
base_url="https://your-fo-environment.dynamics.com",
|
212
|
-
use_default_credentials=True # Uses Azure Default Credential
|
213
|
-
)
|
288
|
+
async with Client("d365fo-mcp-server") as client:
|
289
|
+
# Discover available tools
|
290
|
+
tools = await client.list_tools()
|
214
291
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
# Get environment information
|
221
|
-
env_info = await client.get_environment_info()
|
222
|
-
print(f"Environment: {env_info.application_version}")
|
223
|
-
|
224
|
-
# Search for entities (uses metadata cache v2)
|
225
|
-
customer_entities = await client.search_entities("customer")
|
226
|
-
print(f"Found {len(customer_entities)} customer entities")
|
227
|
-
|
228
|
-
# Get customers with query options
|
229
|
-
from d365fo_client import QueryOptions
|
230
|
-
options = QueryOptions(
|
231
|
-
select=["CustomerAccount", "Name", "SalesCurrencyCode"],
|
232
|
-
top=10,
|
233
|
-
orderby=["Name"]
|
234
|
-
)
|
235
|
-
|
236
|
-
customers = await client.get_data("/data/CustomersV3", options)
|
237
|
-
print(f"Retrieved {len(customers['value'])} customers")
|
238
|
-
|
239
|
-
if __name__ == "__main__":
|
240
|
-
asyncio.run(main())
|
292
|
+
# Execute operations
|
293
|
+
result = await client.call_tool(
|
294
|
+
"d365fo_query_entities",
|
295
|
+
{"entityName": "Customers", "top": 5}
|
296
|
+
)
|
241
297
|
```
|
242
298
|
|
243
|
-
|
299
|
+
#### Docker Deployment
|
244
300
|
|
245
|
-
|
246
|
-
from d365fo_client import create_client
|
301
|
+
For containerized environments and production deployments:
|
247
302
|
|
248
|
-
|
249
|
-
|
250
|
-
|
303
|
+
**Pull the Docker Image:**
|
304
|
+
```bash
|
305
|
+
# Pull from GitHub Container Registry
|
306
|
+
docker pull ghcr.io/mafzaal/d365fo-client:latest
|
307
|
+
|
308
|
+
# Or pull a specific version
|
309
|
+
docker pull ghcr.io/mafzaal/d365fo-client:v0.2.3
|
251
310
|
```
|
252
311
|
|
253
|
-
|
312
|
+
**Standalone Docker Usage:**
|
313
|
+
```bash
|
314
|
+
# Run MCP server with environment variables
|
315
|
+
docker run --rm -i \
|
316
|
+
-e D365FO_BASE_URL="https://your-environment.dynamics.com" \
|
317
|
+
-e D365FO_CLIENT_ID="your-client-id" \
|
318
|
+
-e D365FO_CLIENT_SECRET="your-client-secret" \
|
319
|
+
-e D365FO_TENANT_ID="your-tenant-id" \
|
320
|
+
-e D365FO_LOG_LEVEL="INFO" \
|
321
|
+
-v d365fo-mcp:/home/mcp_user/ \
|
322
|
+
ghcr.io/mafzaal/d365fo-client:latest
|
323
|
+
|
324
|
+
# Run CLI commands with Docker
|
325
|
+
docker run --rm -it \
|
326
|
+
-e D365FO_BASE_URL="https://your-environment.dynamics.com" \
|
327
|
+
-e D365FO_CLIENT_ID="your-client-id" \
|
328
|
+
-e D365FO_CLIENT_SECRET="your-client-secret" \
|
329
|
+
-e D365FO_TENANT_ID="your-tenant-id" \
|
330
|
+
ghcr.io/mafzaal/d365fo-client:latest \
|
331
|
+
d365fo-client entities --limit 10
|
332
|
+
```
|
254
333
|
|
255
|
-
|
334
|
+
**Docker Compose Example:**
|
335
|
+
```yaml
|
336
|
+
version: '3.8'
|
337
|
+
services:
|
338
|
+
d365fo-mcp:
|
339
|
+
image: ghcr.io/mafzaal/d365fo-client:latest
|
340
|
+
environment:
|
341
|
+
- D365FO_BASE_URL=https://your-environment.dynamics.com
|
342
|
+
- D365FO_CLIENT_ID=${D365FO_CLIENT_ID}
|
343
|
+
- D365FO_CLIENT_SECRET=${D365FO_CLIENT_SECRET}
|
344
|
+
- D365FO_TENANT_ID=${D365FO_TENANT_ID}
|
345
|
+
- D365FO_LOG_LEVEL=INFO
|
346
|
+
volumes:
|
347
|
+
- d365fo-mcp:/home/mcp_user/
|
348
|
+
stdin_open: true
|
349
|
+
tty: true
|
350
|
+
|
351
|
+
volumes:
|
352
|
+
d365fo-mcp:
|
353
|
+
```
|
256
354
|
|
257
|
-
|
258
|
-
|
355
|
+
**Docker Benefits:**
|
356
|
+
- Complete environment isolation and reproducibility
|
357
|
+
- No local Python installation required
|
358
|
+
- Consistent runtime environment across different systems
|
359
|
+
- Built-in dependency management
|
360
|
+
- Enhanced security through containerization
|
361
|
+
- Persistent data storage via Docker volumes
|
362
|
+
- Easy integration with orchestration platforms (Kubernetes, Docker Swarm)
|
259
363
|
|
260
|
-
|
261
|
-
config = FOClientConfig(
|
262
|
-
base_url="https://your-fo-environment.dynamics.com",
|
263
|
-
use_default_credentials=True
|
264
|
-
)
|
364
|
+
### Architecture Benefits
|
265
365
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
tenant_id="your-tenant-id",
|
272
|
-
use_default_credentials=False
|
273
|
-
)
|
366
|
+
#### For AI Assistants
|
367
|
+
- **Standardized Interface**: Consistent MCP protocol access to D365 F&O
|
368
|
+
- **Rich Metadata**: Self-describing entities and operations
|
369
|
+
- **Type Safety**: Schema validation for all operations
|
370
|
+
- **Error Context**: Detailed error information for troubleshooting
|
274
371
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
)
|
372
|
+
#### For Developers
|
373
|
+
- **Minimal Integration**: Standard MCP client libraries
|
374
|
+
- **Comprehensive Coverage**: Full D365 F&O functionality exposed
|
375
|
+
- **Performance Optimized**: Efficient connection and caching strategies
|
376
|
+
- **Well Documented**: Complete API documentation and examples
|
281
377
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
timeout=60, # Request timeout in seconds
|
288
|
-
metadata_cache_dir="./my_cache", # Custom cache directory
|
289
|
-
use_label_cache=True, # Enable label caching
|
290
|
-
label_cache_expiry_minutes=120 # Cache for 2 hours
|
291
|
-
)
|
292
|
-
```
|
378
|
+
#### For Organizations
|
379
|
+
- **Secure Access**: Enterprise-grade authentication (Azure AD, Managed Identity)
|
380
|
+
- **Audit Logging**: Complete operation tracking and monitoring
|
381
|
+
- **Scalable Design**: Connection pooling and session management
|
382
|
+
- **Maintenance Friendly**: Clear architecture and comprehensive test coverage
|
293
383
|
|
294
|
-
|
384
|
+
### Troubleshooting
|
295
385
|
|
296
|
-
|
386
|
+
#### Common Issues
|
297
387
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
}
|
306
|
-
created = await client.create_data("/data/CustomersV3", new_customer)
|
307
|
-
|
308
|
-
# READ - Get single customer by key
|
309
|
-
customer = await client.get_data("/data/CustomersV3('US-001')")
|
310
|
-
|
311
|
-
# UPDATE - Update customer with optimistic concurrency
|
312
|
-
updates = {"Name": "Updated Customer Name"}
|
313
|
-
updated = await client.update_data("/data/CustomersV3('US-001')", updates)
|
314
|
-
|
315
|
-
# DELETE - Delete customer
|
316
|
-
success = await client.delete_data("/data/CustomersV3('US-999')")
|
317
|
-
print(f"Delete successful: {success}")
|
388
|
+
**Connection Failures**
|
389
|
+
```bash
|
390
|
+
# Test connectivity
|
391
|
+
d365fo-client version app --base-url https://your-environment.dynamics.com
|
392
|
+
|
393
|
+
# Check logs
|
394
|
+
tail -f ~/.d365fo-mcp/logs/mcp-server.log
|
318
395
|
```
|
319
396
|
|
320
|
-
|
397
|
+
**Authentication Issues**
|
398
|
+
```bash
|
399
|
+
# Verify Azure CLI authentication
|
400
|
+
az account show
|
321
401
|
|
322
|
-
|
323
|
-
|
402
|
+
# Test with explicit credentials
|
403
|
+
export D365FO_CLIENT_ID="your-client-id"
|
404
|
+
# ... set other variables
|
405
|
+
d365fo-mcp-server
|
406
|
+
```
|
324
407
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
expand=["CustomerGroup"],
|
330
|
-
orderby=["Name desc", "CustomerAccount"],
|
331
|
-
top=50,
|
332
|
-
skip=10,
|
333
|
-
count=True
|
334
|
-
)
|
408
|
+
**Performance Issues**
|
409
|
+
```bash
|
410
|
+
# Enable debug logging
|
411
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
335
412
|
|
336
|
-
|
337
|
-
|
413
|
+
# Adjust connection settings
|
414
|
+
export D365FO_CONNECTION_TIMEOUT="120"
|
415
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="5"
|
338
416
|
```
|
339
417
|
|
340
|
-
|
418
|
+
#### Getting Help
|
341
419
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
"taxGroup": "STANDARD"
|
347
|
-
})
|
420
|
+
- **Logs**: Check `~/.d365fo-mcp/logs/mcp-server.log` for detailed error information
|
421
|
+
- **Environment**: Use `d365fo_get_environment_info` tool to check system status
|
422
|
+
- **Documentation**: See [MCP Implementation Summary](docs/MCP_IMPLEMENTATION_SUMMARY.md) for technical details
|
423
|
+
- **Issues**: Report problems at [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)
|
348
424
|
|
349
|
-
|
350
|
-
result = await client.post_data("/data/CustomersV3/calculateBalances", {
|
351
|
-
"asOfDate": "2024-12-31"
|
352
|
-
})
|
425
|
+
### MCP Tools
|
353
426
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
427
|
+
The server provides **34 comprehensive tools** organized into functional categories:
|
428
|
+
|
429
|
+
#### Connection & Environment Tools (2 tools)
|
430
|
+
- **`d365fo_test_connection`** - Test connectivity and authentication with performance metrics and error diagnostics
|
431
|
+
- **`d365fo_get_environment_info`** - Get comprehensive environment details including versions, configurations, and capabilities
|
432
|
+
|
433
|
+
#### CRUD Operations Tools (6 tools)
|
434
|
+
- **`d365fo_query_entities`** - Advanced OData querying with filters, selections, pagination, and performance optimization
|
435
|
+
- **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options and ETag support
|
436
|
+
- **`d365fo_create_entity_record`** - Create new entity records with validation and business logic execution
|
437
|
+
- **`d365fo_update_entity_record`** - Update existing records with partial updates and optimistic concurrency control
|
438
|
+
- **`d365fo_delete_entity_record`** - Delete entity records with referential integrity checking and cascading rules
|
439
|
+
- **`d365fo_call_action`** - Execute OData actions and functions for complex business operations
|
440
|
+
|
441
|
+
#### Metadata Discovery Tools (6 tools)
|
442
|
+
- **`d365fo_search_entities`** - Search entities by pattern with category filtering and full-text search capabilities
|
443
|
+
- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties, relationships, and label resolution
|
444
|
+
- **`d365fo_search_actions`** - Search available OData actions with binding type and parameter information
|
445
|
+
- **`d365fo_search_enumerations`** - Search system enumerations with keyword-based filtering
|
446
|
+
- **`d365fo_get_enumeration_fields`** - Get detailed enumeration member information with multi-language support
|
447
|
+
- **`d365fo_get_installed_modules`** - Retrieve information about installed modules and their configurations
|
448
|
+
|
449
|
+
#### Label Management Tools (2 tools)
|
450
|
+
- **`d365fo_get_label`** - Get single label text by ID with multi-language support and fallback options
|
451
|
+
- **`d365fo_get_labels_batch`** - Get multiple labels efficiently with batch processing and performance optimization
|
452
|
+
|
453
|
+
#### Profile Management Tools (10 tools)
|
454
|
+
- **`d365fo_list_profiles`** - List all configured D365FO environment profiles with status information
|
455
|
+
- **`d365fo_get_profile`** - Get detailed configuration information for specific profiles
|
456
|
+
- **`d365fo_create_profile`** - Create new environment profiles with comprehensive authentication options
|
457
|
+
- **`d365fo_update_profile`** - Modify existing profile configurations with partial update support
|
458
|
+
- **`d365fo_delete_profile`** - Remove environment profiles with proper cleanup and validation
|
459
|
+
- **`d365fo_set_default_profile`** - Designate a specific profile as the default for operations
|
460
|
+
- **`d365fo_get_default_profile`** - Retrieve information about the currently configured default profile
|
461
|
+
- **`d365fo_validate_profile`** - Validate profile configurations for completeness and security compliance
|
462
|
+
- **`d365fo_test_profile_connection`** - Test connectivity and authentication for specific profiles
|
463
|
+
- **`d365fo_get_profile_status`** - Get comprehensive status information for profiles
|
464
|
+
|
465
|
+
#### Database Analysis Tools (4 tools)
|
466
|
+
- **`d365fo_execute_sql_query`** - Execute SELECT queries against metadata database with security validation
|
467
|
+
- **`d365fo_get_database_schema`** - Get comprehensive database schema information including relationships
|
468
|
+
- **`d365fo_get_table_info`** - Get detailed information about specific database tables with sample data
|
469
|
+
- **`d365fo_get_database_statistics`** - Generate database statistics and analytics for performance monitoring
|
470
|
+
|
471
|
+
#### Synchronization Tools (4 tools)
|
472
|
+
- **`d365fo_start_sync`** - Initiate metadata synchronization with various strategies and session tracking
|
473
|
+
- **`d365fo_get_sync_progress`** - Monitor detailed progress of sync sessions with time estimates
|
474
|
+
- **`d365fo_cancel_sync`** - Cancel running sync sessions with graceful cleanup
|
475
|
+
- **`d365fo_list_sync_sessions`** - List all active sync sessions with status and progress information
|
476
|
+
|
477
|
+
**📖 For detailed information about all MCP tools including usage examples and best practices, see the [Comprehensive MCP Tools Introduction](docs/MCP_TOOLS_COMPREHENSIVE_INTRODUCTION.md).**
|
359
478
|
|
360
|
-
###
|
479
|
+
### MCP Resources
|
361
480
|
|
362
|
-
|
363
|
-
# Intelligent metadata synchronization (v2 system)
|
364
|
-
sync_manager = await client.get_sync_manager()
|
365
|
-
await sync_manager.smart_sync()
|
481
|
+
The server exposes four types of resources for discovery and access:
|
366
482
|
|
367
|
-
|
368
|
-
|
369
|
-
|
483
|
+
#### Entity Resources
|
484
|
+
Access entity metadata and sample data:
|
485
|
+
```
|
486
|
+
d365fo://entities/CustomersV3 # Customer entity with metadata and sample data
|
487
|
+
d365fo://entities/SalesOrders # Sales order entity information
|
488
|
+
d365fo://entities/Products # Product entity details
|
489
|
+
```
|
370
490
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
491
|
+
#### Metadata Resources
|
492
|
+
Access system-wide metadata:
|
493
|
+
```
|
494
|
+
d365fo://metadata/entities # All data entities metadata (V2 cache)
|
495
|
+
d365fo://metadata/actions # Available OData actions
|
496
|
+
d365fo://metadata/enumerations # System enumerations
|
497
|
+
d365fo://metadata/labels # System labels and translations
|
498
|
+
```
|
377
499
|
|
378
|
-
|
379
|
-
|
380
|
-
|
500
|
+
#### Environment Resources
|
501
|
+
Access environment status and information:
|
502
|
+
```
|
503
|
+
d365fo://environment/status # Environment health and connectivity
|
504
|
+
d365fo://environment/version # Version information (app, platform, build)
|
505
|
+
d365fo://environment/cache # Cache status and statistics V2
|
506
|
+
```
|
381
507
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
print(f" {member.name} = {member.value}")
|
508
|
+
#### Query Resources
|
509
|
+
Access predefined and templated queries:
|
510
|
+
```
|
511
|
+
d365fo://queries/customers_recent # Recent customers query template
|
512
|
+
d365fo://queries/sales_summary # Sales summary query with parameters
|
388
513
|
```
|
389
514
|
|
390
|
-
|
515
|
+
#### Database Resources (New in V2)
|
516
|
+
Access metadata database queries:
|
517
|
+
```
|
518
|
+
d365fo://database/entities # SQL-based entity searches with FTS5
|
519
|
+
d365fo://database/actions # Action discovery with metadata
|
520
|
+
d365fo://database/statistics # Cache and performance statistics
|
521
|
+
```
|
391
522
|
|
392
|
-
|
393
|
-
# Get specific label (v2 caching system)
|
394
|
-
label_text = await client.get_label_text("@SYS13342")
|
395
|
-
print(f"Label text: {label_text}")
|
523
|
+
### Usage Examples
|
396
524
|
|
397
|
-
|
398
|
-
labels = await client.get_labels_batch([
|
399
|
-
"@SYS13342", "@SYS9490", "@GLS63332"
|
400
|
-
])
|
401
|
-
for label_id, text in labels.items():
|
402
|
-
print(f"{label_id}: {text}")
|
403
|
-
|
404
|
-
# Enhanced entity info with resolved labels
|
405
|
-
entity_info = await client.get_public_entity_info_with_labels("CustomersV3")
|
406
|
-
if entity_info.label_text:
|
407
|
-
print(f"Entity display name: {entity_info.label_text}")
|
525
|
+
#### Basic Tool Execution
|
408
526
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
527
|
+
```json
|
528
|
+
{
|
529
|
+
"tool": "d365fo_query_entities",
|
530
|
+
"arguments": {
|
531
|
+
"entityName": "CustomersV3",
|
532
|
+
"select": ["CustomerAccount", "Name", "Email"],
|
533
|
+
"filter": "CustomerGroup eq 'VIP'",
|
534
|
+
"top": 10
|
535
|
+
}
|
536
|
+
}
|
413
537
|
```
|
414
538
|
|
415
|
-
|
416
|
-
|
417
|
-
```python
|
418
|
-
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
539
|
+
#### Entity Schema Discovery
|
419
540
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
print(f"Response: {e.response_text}")
|
541
|
+
```json
|
542
|
+
{
|
543
|
+
"tool": "d365fo_get_entity_schema",
|
544
|
+
"arguments": {
|
545
|
+
"entityName": "CustomersV3",
|
546
|
+
"includeProperties": true,
|
547
|
+
"resolveLabels": true,
|
548
|
+
"language": "en-US"
|
549
|
+
}
|
550
|
+
}
|
431
551
|
```
|
432
552
|
|
433
|
-
|
553
|
+
#### Environment Information
|
434
554
|
|
435
|
-
|
555
|
+
```json
|
556
|
+
{
|
557
|
+
"tool": "d365fo_get_environment_info",
|
558
|
+
"arguments": {}
|
559
|
+
}
|
560
|
+
```
|
436
561
|
|
437
|
-
|
438
|
-
# Clone the repository
|
439
|
-
git clone https://github.com/mafzaal/d365fo-client.git
|
440
|
-
cd d365fo-client
|
562
|
+
### Authentication & Configuration
|
441
563
|
|
442
|
-
|
443
|
-
|
564
|
+
#### Default Credentials (Recommended)
|
565
|
+
Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
|
444
566
|
|
445
|
-
|
446
|
-
|
567
|
+
```bash
|
568
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
569
|
+
# No additional auth environment variables needed
|
570
|
+
d365fo-mcp-server
|
571
|
+
```
|
447
572
|
|
448
|
-
|
449
|
-
|
573
|
+
#### Explicit Credentials
|
574
|
+
For service principal authentication:
|
450
575
|
|
451
|
-
|
452
|
-
|
453
|
-
|
576
|
+
```bash
|
577
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
578
|
+
export D365FO_CLIENT_ID="your-client-id"
|
579
|
+
export D365FO_CLIENT_SECRET="your-client-secret"
|
580
|
+
export D365FO_TENANT_ID="your-tenant-id"
|
581
|
+
d365fo-mcp-server
|
582
|
+
```
|
454
583
|
|
455
|
-
|
456
|
-
|
584
|
+
#### Azure Key Vault Integration (New in v0.2.3)
|
585
|
+
For secure credential storage using Azure Key Vault:
|
457
586
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
587
|
+
```bash
|
588
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
589
|
+
export D365FO_CREDENTIAL_SOURCE="keyvault"
|
590
|
+
export D365FO_KEYVAULT_URL="https://your-keyvault.vault.azure.net/"
|
591
|
+
d365fo-mcp-server
|
462
592
|
```
|
463
593
|
|
464
|
-
|
594
|
+
#### Advanced Configuration
|
465
595
|
|
466
|
-
|
467
|
-
d365fo-client/
|
468
|
-
├── src/
|
469
|
-
│ └── d365fo_client/
|
470
|
-
│ ├── __init__.py # Public API exports
|
471
|
-
│ ├── main.py # CLI entry point
|
472
|
-
│ ├── cli.py # CLI command handlers
|
473
|
-
│ ├── client.py # Enhanced D365FOClient class
|
474
|
-
│ ├── config.py # Configuration management
|
475
|
-
│ ├── auth.py # Authentication management
|
476
|
-
│ ├── session.py # HTTP session management
|
477
|
-
│ ├── crud.py # CRUD operations
|
478
|
-
│ ├── query.py # OData query utilities
|
479
|
-
│ ├── metadata.py # Legacy metadata operations
|
480
|
-
│ ├── metadata_api.py # Metadata API client
|
481
|
-
│ ├── metadata_cache.py # Metadata caching layer V2
|
482
|
-
│ ├── metadata_sync.py # Metadata synchronization V2 with session management
|
483
|
-
│ ├── sync_session.py # Enhanced sync session management (New in v0.2.3)
|
484
|
-
│ ├── credential_manager.py # Credential source management (New in v0.2.3)
|
485
|
-
│ ├── labels.py # Label operations V2
|
486
|
-
│ ├── profiles.py # Profile data models
|
487
|
-
│ ├── profile_manager.py # Profile management
|
488
|
-
│ ├── models.py # Data models and configurations
|
489
|
-
│ ├── output.py # Output formatting
|
490
|
-
│ ├── utils.py # Utility functions
|
491
|
-
│ ├── exceptions.py # Custom exceptions
|
492
|
-
│ └── mcp/ # Model Context Protocol server
|
493
|
-
│ ├── __init__.py # MCP server exports
|
494
|
-
│ ├── main.py # MCP server entry point
|
495
|
-
│ ├── server.py # Core MCP server implementation
|
496
|
-
│ ├── client_manager.py# D365FO client connection pooling
|
497
|
-
│ ├── models.py # MCP-specific data models
|
498
|
-
│ ├── tools/ # MCP tool implementations (12 tools)
|
499
|
-
│ │ ├── connection_tools.py
|
500
|
-
│ │ ├── crud_tools.py
|
501
|
-
│ │ ├── metadata_tools.py
|
502
|
-
│ │ └── label_tools.py
|
503
|
-
│ ├── resources/ # MCP resource handlers (4 types)
|
504
|
-
│ │ ├── entity_handler.py
|
505
|
-
│ │ ├── metadata_handler.py
|
506
|
-
│ │ ├── environment_handler.py
|
507
|
-
│ │ └── query_handler.py
|
508
|
-
│ └── prompts/ # MCP prompt templates
|
509
|
-
├── tests/ # Comprehensive test suite
|
510
|
-
│ ├── unit/ # Unit tests (pytest-based)
|
511
|
-
│ ├── integration/ # Multi-tier integration testing
|
512
|
-
│ │ ├── mock_server/ # Mock D365 F&O API server
|
513
|
-
│ │ ├── test_mock_server.py # Mock server tests
|
514
|
-
│ │ ├── test_sandbox.py # Sandbox environment tests ✅
|
515
|
-
│ │ ├── test_live.py # Live environment tests
|
516
|
-
│ │ ├── conftest.py # Shared pytest fixtures
|
517
|
-
│ │ ├── test_runner.py # Python test execution engine
|
518
|
-
│ │ └── integration-test-simple.ps1 # PowerShell automation
|
519
|
-
│ └── test_mcp_server.py # MCP server unit tests ✅
|
520
|
-
├── scripts/ # Metadata discovery scripts
|
521
|
-
│ ├── search_data_entities.ps1 # PowerShell entity search
|
522
|
-
│ ├── get_data_entity_schema.ps1 # PowerShell schema retrieval
|
523
|
-
│ ├── search_enums.py # Python enumeration search
|
524
|
-
│ ├── get_enumeration_info.py # Python enumeration info
|
525
|
-
│ ├── search_actions.ps1 # PowerShell action search
|
526
|
-
│ └── get_action_info.py # Python action information
|
527
|
-
├── docs/ # Comprehensive documentation
|
528
|
-
├── pyproject.toml # Project configuration
|
529
|
-
└── README.md # This file
|
530
|
-
```
|
596
|
+
Create a configuration file or set additional environment variables:
|
531
597
|
|
532
|
-
|
598
|
+
```bash
|
599
|
+
# Optional: Logging configuration
|
600
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
533
601
|
|
534
|
-
|
535
|
-
|
536
|
-
| `base_url` | str | Required | D365 F&O base URL |
|
537
|
-
| `client_id` | str | None | Azure AD client ID |
|
538
|
-
| `client_secret` | str | None | Azure AD client secret |
|
539
|
-
| `tenant_id` | str | None | Azure AD tenant ID |
|
540
|
-
| `use_default_credentials` | bool | True | Use Azure Default Credential |
|
541
|
-
| `credential_source` | str | "environment" | Credential source: "environment", "keyvault" |
|
542
|
-
| `keyvault_url` | str | None | Azure Key Vault URL for credential storage |
|
543
|
-
| `verify_ssl` | bool | False | Verify SSL certificates |
|
544
|
-
| `timeout` | int | 30 | Request timeout in seconds |
|
545
|
-
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
|
546
|
-
| `use_label_cache` | bool | True | Enable label caching V2 |
|
547
|
-
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
|
548
|
-
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |
|
602
|
+
# Optional: Cache settings
|
603
|
+
export D365FO_CACHE_DIR="/custom/cache/path"
|
549
604
|
|
550
|
-
|
605
|
+
# Optional: Performance tuning
|
606
|
+
export D365FO_CONNECTION_TIMEOUT="60"
|
607
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="10"
|
608
|
+
```
|
551
609
|
|
552
|
-
|
610
|
+
## Python Client Library
|
553
611
|
|
554
|
-
|
555
|
-
- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
|
556
|
-
- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)
|
612
|
+
### Features
|
557
613
|
|
558
|
-
|
614
|
+
- 🔗 **OData Client**: Full CRUD operations on D365 F&O data entities with composite key support
|
615
|
+
- 📊 **Metadata Management V2**: Enhanced caching system with intelligent synchronization and FTS5 search
|
616
|
+
- 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
|
617
|
+
- 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
|
618
|
+
- ⚡ **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
|
619
|
+
- 🔒 **Authentication**: Azure AD integration with default credentials, service principal, and Azure Key Vault support
|
620
|
+
- 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
|
621
|
+
- 🌐 **Async/Await**: Modern async/await patterns with optimized session management
|
622
|
+
- 📝 **Type Hints**: Full type annotation support with enhanced data models
|
623
|
+
- 🤖 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
|
624
|
+
- 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
|
625
|
+
- 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
|
626
|
+
- 📋 **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery
|
627
|
+
- 🔐 **Enhanced Credential Management**: Support for Azure Key Vault and multiple credential sources
|
628
|
+
- 📊 **Advanced Sync Management**: Session-based synchronization with detailed progress tracking
|
559
629
|
|
560
|
-
|
561
|
-
from d365fo_client import FOClientConfig
|
630
|
+
### Installation
|
562
631
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
metadata_cache_dir="/custom/cache/path"
|
567
|
-
)
|
632
|
+
```bash
|
633
|
+
# Install from PyPI
|
634
|
+
pip install d365fo-client
|
568
635
|
|
569
|
-
# Or
|
570
|
-
|
636
|
+
# Or install from source
|
637
|
+
git clone https://github.com/mafzaal/d365fo-client.git
|
638
|
+
cd d365fo-client
|
639
|
+
uv sync # Installs with exact dependencies from uv.lock
|
571
640
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
641
|
+
# Or use Docker (no local installation required)
|
642
|
+
docker pull ghcr.io/mafzaal/d365fo-client:latest
|
643
|
+
|
644
|
+
# Run with Docker
|
645
|
+
docker run --rm -it \
|
646
|
+
-e D365FO_BASE_URL="https://your-environment.dynamics.com" \
|
647
|
+
-e D365FO_CLIENT_ID="your-client-id" \
|
648
|
+
-e D365FO_CLIENT_SECRET="your-client-secret" \
|
649
|
+
-e D365FO_TENANT_ID="your-tenant-id" \
|
650
|
+
-v d365fo-mcp:/home/mcp_user/ \
|
651
|
+
ghcr.io/mafzaal/d365fo-client:latest
|
577
652
|
```
|
578
653
|
|
579
|
-
|
654
|
+
**Note**: The package includes MCP (Model Context Protocol) dependencies by default, enabling AI assistant integration. Both `d365fo-client` CLI and `d365fo-mcp-server` commands will be available after installation.
|
580
655
|
|
581
|
-
|
656
|
+
**Breaking Change in v0.2.3**: Environment variable names have been updated for consistency:
|
657
|
+
- `AZURE_CLIENT_ID` → `D365FO_CLIENT_ID`
|
658
|
+
- `AZURE_CLIENT_SECRET` → `D365FO_CLIENT_SECRET`
|
659
|
+
- `AZURE_TENANT_ID` → `D365FO_TENANT_ID`
|
582
660
|
|
583
|
-
|
661
|
+
Please update your environment variables accordingly when upgrading.
|
584
662
|
|
585
|
-
|
663
|
+
## Python Client Quick Start
|
586
664
|
|
587
|
-
|
588
|
-
# Run all unit tests
|
589
|
-
uv run pytest
|
665
|
+
## Command Line Interface (CLI)
|
590
666
|
|
591
|
-
|
592
|
-
uv run pytest --cov=d365fo_client --cov-report=html
|
667
|
+
d365fo-client provides a comprehensive CLI with hierarchical commands for interacting with Dynamics 365 Finance & Operations APIs and metadata. The CLI supports all major operations including entity management, metadata discovery, and system administration.
|
593
668
|
|
594
|
-
|
595
|
-
uv run pytest tests/test_client.py -v
|
596
|
-
```
|
669
|
+
### Usage
|
597
670
|
|
598
|
-
|
671
|
+
```bash
|
672
|
+
# Use the installed CLI command
|
673
|
+
d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]
|
599
674
|
|
600
|
-
|
675
|
+
# Alternative: Module execution
|
676
|
+
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
|
677
|
+
```
|
601
678
|
|
602
|
-
|
679
|
+
### Command Categories
|
603
680
|
|
681
|
+
#### Entity Operations
|
604
682
|
```bash
|
605
|
-
#
|
606
|
-
|
683
|
+
# List entities with filtering
|
684
|
+
d365fo-client entities list --pattern "customer" --limit 10
|
607
685
|
|
608
|
-
#
|
609
|
-
|
686
|
+
# Get entity details and schema
|
687
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels
|
610
688
|
|
611
|
-
#
|
612
|
-
|
689
|
+
# CRUD operations
|
690
|
+
d365fo-client entities create Customers --data '{"CustomerAccount":"US-999","Name":"Test"}'
|
691
|
+
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
|
692
|
+
d365fo-client entities delete Customers US-999
|
613
693
|
```
|
614
694
|
|
615
|
-
####
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
- Complete API simulation
|
620
|
-
- Ideal for CI/CD pipelines
|
621
|
-
|
622
|
-
2. **Sandbox Tests** ⭐ *(Default)* - Tests against real D365 F&O test environments
|
623
|
-
- Validates authentication
|
624
|
-
- Tests real API behavior
|
625
|
-
- Requires test environment access
|
695
|
+
#### Metadata Operations
|
696
|
+
```bash
|
697
|
+
# Search and discover entities
|
698
|
+
d365fo-client metadata entities --search "sales" --output json
|
626
699
|
|
627
|
-
|
628
|
-
|
629
|
-
- Performance benchmarking
|
630
|
-
- Use with caution
|
700
|
+
# Get available actions
|
701
|
+
d365fo-client metadata actions --pattern "calculate" --limit 5
|
631
702
|
|
632
|
-
|
703
|
+
# Enumerate system enumerations
|
704
|
+
d365fo-client metadata enums --search "status" --output table
|
633
705
|
|
634
|
-
|
706
|
+
# Synchronize metadata cache
|
707
|
+
d365fo-client metadata sync --force-refresh
|
708
|
+
```
|
635
709
|
|
710
|
+
#### Version Information
|
636
711
|
```bash
|
637
|
-
#
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
INTEGRATION_TEST_LEVEL=sandbox
|
642
|
-
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
|
643
|
-
D365FO_CLIENT_ID=your-client-id
|
644
|
-
D365FO_CLIENT_SECRET=your-client-secret
|
645
|
-
D365FO_TENANT_ID=your-tenant-id
|
712
|
+
# Get application versions
|
713
|
+
d365fo-client version app
|
714
|
+
d365fo-client version platform
|
715
|
+
d365fo-client version build
|
646
716
|
```
|
647
717
|
|
648
|
-
####
|
649
|
-
|
718
|
+
#### Label Operations
|
650
719
|
```bash
|
651
|
-
#
|
652
|
-
|
653
|
-
|
654
|
-
# Dependency checking
|
655
|
-
.\tests\integration\integration-test-simple.ps1 deps-check
|
656
|
-
|
657
|
-
# Run specific test levels
|
658
|
-
.\tests\integration\integration-test-simple.ps1 test-mock
|
659
|
-
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
660
|
-
.\tests\integration\integration-test-simple.ps1 test-live
|
661
|
-
|
662
|
-
# Coverage and reporting
|
663
|
-
.\tests\integration\integration-test-simple.ps1 coverage
|
720
|
+
# Resolve single label
|
721
|
+
d365fo-client labels resolve "@SYS13342"
|
664
722
|
|
665
|
-
#
|
666
|
-
|
723
|
+
# Search labels by pattern
|
724
|
+
d365fo-client labels search "customer" --language "en-US"
|
667
725
|
```
|
668
726
|
|
669
|
-
|
727
|
+
### Global Options
|
670
728
|
|
671
|
-
|
729
|
+
- `--base-url URL` — Specify D365 F&O environment URL
|
730
|
+
- `--profile NAME` — Use named configuration profile
|
731
|
+
- `--output FORMAT` — Output format: json, table, csv, yaml (default: table)
|
732
|
+
- `--verbose` — Enable verbose output for debugging
|
733
|
+
- `--timeout SECONDS` — Request timeout (default: 30)
|
672
734
|
|
673
|
-
|
674
|
-
- ✅ **Version Methods** - Application, platform, and build version retrieval
|
675
|
-
- ✅ **Metadata Operations** - Entity discovery, metadata API validation
|
676
|
-
- ✅ **Data Operations** - CRUD operations, OData query validation
|
677
|
-
- ✅ **Error Handling** - Network failures, authentication errors, invalid requests
|
678
|
-
- ✅ **Performance** - Response time validation, concurrent operations
|
735
|
+
### Configuration Profiles
|
679
736
|
|
680
|
-
|
737
|
+
Create reusable configurations in `~/.d365fo-client/config.yaml`:
|
681
738
|
|
682
|
-
|
739
|
+
```yaml
|
740
|
+
profiles:
|
741
|
+
production:
|
742
|
+
base_url: "https://prod.dynamics.com"
|
743
|
+
use_default_credentials: true
|
744
|
+
timeout: 60
|
745
|
+
|
746
|
+
development:
|
747
|
+
base_url: "https://dev.dynamics.com"
|
748
|
+
client_id: "${D365FO_CLIENT_ID}"
|
749
|
+
client_secret: "${D365FO_CLIENT_SECRET}"
|
750
|
+
tenant_id: "${D365FO_TENANT_ID}"
|
751
|
+
use_cache_first: true
|
683
752
|
|
684
|
-
|
685
|
-
```
|
686
|
-
✅ 17 passed, 0 failed, 2 warnings in 37.67s
|
687
|
-
======================================================
|
688
|
-
✅ TestSandboxConnection::test_connection_success
|
689
|
-
✅ TestSandboxConnection::test_metadata_connection_success
|
690
|
-
✅ TestSandboxVersionMethods::test_get_application_version
|
691
|
-
✅ TestSandboxVersionMethods::test_get_platform_build_version
|
692
|
-
✅ TestSandboxVersionMethods::test_get_application_build_version
|
693
|
-
✅ TestSandboxVersionMethods::test_version_consistency
|
694
|
-
✅ TestSandboxMetadataOperations::test_download_metadata
|
695
|
-
✅ TestSandboxMetadataOperations::test_search_entities
|
696
|
-
✅ TestSandboxMetadataOperations::test_get_data_entities
|
697
|
-
✅ TestSandboxMetadataOperations::test_get_public_entities
|
698
|
-
✅ TestSandboxDataOperations::test_get_available_entities
|
699
|
-
✅ TestSandboxDataOperations::test_odata_query_options
|
700
|
-
✅ TestSandboxAuthentication::test_authenticated_requests
|
701
|
-
✅ TestSandboxErrorHandling::test_invalid_entity_error
|
702
|
-
✅ TestSandboxErrorHandling::test_invalid_action_error
|
703
|
-
✅ TestSandboxPerformance::test_response_times
|
704
|
-
✅ TestSandboxPerformance::test_concurrent_operations
|
753
|
+
default_profile: "development"
|
705
754
|
```
|
706
755
|
|
707
|
-
|
756
|
+
### Examples
|
708
757
|
|
709
|
-
|
758
|
+
```bash
|
759
|
+
# Quick entity discovery
|
760
|
+
d365fo-client entities list --pattern "cust.*" --output json
|
710
761
|
|
711
|
-
|
762
|
+
# Get comprehensive entity information
|
763
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels --output yaml
|
712
764
|
|
713
|
-
|
714
|
-
-
|
715
|
-
- **4 resource types** with comprehensive metadata exposure
|
716
|
-
- **Production-ready** implementation with proper error handling and authentication
|
717
|
-
- **Performance optimization** with connection pooling and intelligent caching V2
|
718
|
-
- **Comprehensive testing** with 14 unit tests (100% pass rate)
|
719
|
-
- **Profile support** for multi-environment configurations
|
765
|
+
# Search for calculation actions
|
766
|
+
d365fo-client metadata actions --pattern "calculate|compute" --output table
|
720
767
|
|
721
|
-
|
768
|
+
# Test environment connectivity
|
769
|
+
d365fo-client version app --verbose
|
770
|
+
```
|
722
771
|
|
723
|
-
|
772
|
+
For a complete command reference:
|
724
773
|
|
725
774
|
```bash
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
# Set up environment variables
|
730
|
-
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
731
|
-
export D365FO_CLIENT_ID="your-client-id" # Optional with default credentials
|
732
|
-
export D365FO_CLIENT_SECRET="your-client-secret" # Optional with default credentials
|
733
|
-
export D365FO_TENANT_ID="your-tenant-id" # Optional with default credentials
|
734
|
-
|
735
|
-
# Start the MCP server
|
736
|
-
d365fo-mcp-server
|
775
|
+
d365fo-client --help
|
776
|
+
d365fo-client entities --help
|
777
|
+
d365fo-client metadata --help
|
737
778
|
```
|
738
|
-
|
739
|
-
#### Alternative: Programmatic Usage
|
779
|
+
### Basic Usage
|
740
780
|
|
741
781
|
```python
|
742
|
-
|
782
|
+
import asyncio
|
783
|
+
from d365fo_client import D365FOClient, FOClientConfig
|
743
784
|
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
785
|
+
async def main():
|
786
|
+
# Simple configuration with default credentials
|
787
|
+
config = FOClientConfig(
|
788
|
+
base_url="https://your-fo-environment.dynamics.com",
|
789
|
+
use_default_credentials=True # Uses Azure Default Credential
|
790
|
+
)
|
791
|
+
|
792
|
+
async with D365FOClient(config) as client:
|
793
|
+
# Test connection
|
794
|
+
if await client.test_connection():
|
795
|
+
print("✅ Connected successfully!")
|
796
|
+
|
797
|
+
# Get environment information
|
798
|
+
env_info = await client.get_environment_info()
|
799
|
+
print(f"Environment: {env_info.application_version}")
|
800
|
+
|
801
|
+
# Search for entities (uses metadata cache v2)
|
802
|
+
customer_entities = await client.search_entities("customer")
|
803
|
+
print(f"Found {len(customer_entities)} customer entities")
|
804
|
+
|
805
|
+
# Get customers with query options
|
806
|
+
from d365fo_client import QueryOptions
|
807
|
+
options = QueryOptions(
|
808
|
+
select=["CustomerAccount", "Name", "SalesCurrencyCode"],
|
809
|
+
top=10,
|
810
|
+
orderby=["Name"]
|
811
|
+
)
|
812
|
+
|
813
|
+
customers = await client.get_data("/data/CustomersV3", options)
|
814
|
+
print(f"Retrieved {len(customers['value'])} customers")
|
751
815
|
|
752
|
-
|
753
|
-
|
816
|
+
if __name__ == "__main__":
|
817
|
+
asyncio.run(main())
|
754
818
|
```
|
755
819
|
|
756
|
-
###
|
757
|
-
|
758
|
-
The server provides 12 comprehensive tools organized into functional categories:
|
820
|
+
### Using Convenience Function
|
759
821
|
|
760
|
-
|
761
|
-
|
762
|
-
- **`d365fo_get_environment_info`** - Get comprehensive environment details, versions, and statistics
|
822
|
+
```python
|
823
|
+
from d365fo_client import create_client
|
763
824
|
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
- **`d365fo_update_entity_record`** - Update existing records with optimistic concurrency
|
769
|
-
- **`d365fo_delete_entity_record`** - Delete entity records with conflict detection
|
825
|
+
# Quick client creation with enhanced defaults
|
826
|
+
async with create_client("https://your-fo-environment.dynamics.com") as client:
|
827
|
+
customers = await client.get_data("/data/CustomersV3", top=5)
|
828
|
+
```
|
770
829
|
|
771
|
-
|
772
|
-
- **`d365fo_search_entities`** - Search entities by pattern with advanced filtering and FTS5 search
|
773
|
-
- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties and relationships
|
774
|
-
- **`d365fo_search_actions`** - Search available OData actions and functions
|
775
|
-
- **`d365fo_search_enums`** - Search system enumerations with filtering
|
776
|
-
- **`d365fo_get_enum_info`** - Get detailed enumeration information and values
|
830
|
+
## Configuration
|
777
831
|
|
778
|
-
|
779
|
-
- **`d365fo_get_label`** - Get single label text by ID with language support
|
780
|
-
- **`d365fo_get_labels_batch`** - Get multiple labels efficiently in batch operations
|
832
|
+
### Authentication Options
|
781
833
|
|
782
|
-
|
834
|
+
```python
|
835
|
+
from d365fo_client import FOClientConfig
|
783
836
|
|
784
|
-
|
837
|
+
# Option 1: Default Azure credentials (recommended)
|
838
|
+
config = FOClientConfig(
|
839
|
+
base_url="https://your-fo-environment.dynamics.com",
|
840
|
+
use_default_credentials=True
|
841
|
+
)
|
785
842
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
843
|
+
# Option 2: Client credentials
|
844
|
+
config = FOClientConfig(
|
845
|
+
base_url="https://your-fo-environment.dynamics.com",
|
846
|
+
client_id="your-client-id",
|
847
|
+
client_secret="your-client-secret",
|
848
|
+
tenant_id="your-tenant-id",
|
849
|
+
use_default_credentials=False
|
850
|
+
)
|
793
851
|
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
d365fo://metadata/labels # System labels and translations
|
801
|
-
```
|
852
|
+
# Option 3: Azure Key Vault integration (New in v0.2.3)
|
853
|
+
config = FOClientConfig(
|
854
|
+
base_url="https://your-fo-environment.dynamics.com",
|
855
|
+
credential_source="keyvault", # Use Azure Key Vault for credentials
|
856
|
+
keyvault_url="https://your-keyvault.vault.azure.net/"
|
857
|
+
)
|
802
858
|
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
859
|
+
# Option 4: With custom settings
|
860
|
+
config = FOClientConfig(
|
861
|
+
base_url="https://your-fo-environment.dynamics.com",
|
862
|
+
use_default_credentials=True,
|
863
|
+
verify_ssl=False, # For development environments
|
864
|
+
timeout=60, # Request timeout in seconds
|
865
|
+
metadata_cache_dir="./my_cache", # Custom cache directory
|
866
|
+
use_label_cache=True, # Enable label caching
|
867
|
+
label_cache_expiry_minutes=120 # Cache for 2 hours
|
868
|
+
)
|
809
869
|
```
|
810
870
|
|
811
|
-
|
812
|
-
Access predefined and templated queries:
|
813
|
-
```
|
814
|
-
d365fo://queries/customers_recent # Recent customers query template
|
815
|
-
d365fo://queries/sales_summary # Sales summary query with parameters
|
816
|
-
```
|
871
|
+
## Core Operations
|
817
872
|
|
818
|
-
|
819
|
-
|
873
|
+
### CRUD Operations
|
874
|
+
|
875
|
+
```python
|
876
|
+
async with D365FOClient(config) as client:
|
877
|
+
# CREATE - Create new customer (supports composite keys)
|
878
|
+
new_customer = {
|
879
|
+
"CustomerAccount": "US-999",
|
880
|
+
"Name": "Test Customer",
|
881
|
+
"SalesCurrencyCode": "USD"
|
882
|
+
}
|
883
|
+
created = await client.create_data("/data/CustomersV3", new_customer)
|
884
|
+
|
885
|
+
# READ - Get single customer by key
|
886
|
+
customer = await client.get_data("/data/CustomersV3('US-001')")
|
887
|
+
|
888
|
+
# UPDATE - Update customer with optimistic concurrency
|
889
|
+
updates = {"Name": "Updated Customer Name"}
|
890
|
+
updated = await client.update_data("/data/CustomersV3('US-001')", updates)
|
891
|
+
|
892
|
+
# DELETE - Delete customer
|
893
|
+
success = await client.delete_data("/data/CustomersV3('US-999')")
|
894
|
+
print(f"Delete successful: {success}")
|
820
895
|
```
|
821
|
-
|
822
|
-
|
823
|
-
|
896
|
+
|
897
|
+
### Advanced Querying
|
898
|
+
|
899
|
+
```python
|
900
|
+
from d365fo_client import QueryOptions
|
901
|
+
|
902
|
+
# Complex query with multiple options
|
903
|
+
options = QueryOptions(
|
904
|
+
select=["CustomerAccount", "Name", "SalesCurrencyCode", "CustomerGroupId"],
|
905
|
+
filter="SalesCurrencyCode eq 'USD' and contains(Name, 'Corp')",
|
906
|
+
expand=["CustomerGroup"],
|
907
|
+
orderby=["Name desc", "CustomerAccount"],
|
908
|
+
top=50,
|
909
|
+
skip=10,
|
910
|
+
count=True
|
911
|
+
)
|
912
|
+
|
913
|
+
result = await client.get_data("/data/CustomersV3", options)
|
914
|
+
print(f"Total count: {result.get('@odata.count')}")
|
824
915
|
```
|
825
916
|
|
826
|
-
###
|
917
|
+
### Action Execution
|
827
918
|
|
828
|
-
|
919
|
+
```python
|
920
|
+
# Unbound action
|
921
|
+
result = await client.post_data("/data/calculateTax", {
|
922
|
+
"amount": 1000.00,
|
923
|
+
"taxGroup": "STANDARD"
|
924
|
+
})
|
829
925
|
|
830
|
-
|
831
|
-
{
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
"
|
838
|
-
|
839
|
-
}
|
926
|
+
# Bound action on entity set
|
927
|
+
result = await client.post_data("/data/CustomersV3/calculateBalances", {
|
928
|
+
"asOfDate": "2024-12-31"
|
929
|
+
})
|
930
|
+
|
931
|
+
# Bound action on specific entity instance
|
932
|
+
result = await client.post_data("/data/CustomersV3('US-001')/calculateBalance", {
|
933
|
+
"asOfDate": "2024-12-31"
|
934
|
+
})
|
840
935
|
```
|
841
936
|
|
842
|
-
|
937
|
+
### Metadata Operations
|
843
938
|
|
844
|
-
```
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
939
|
+
```python
|
940
|
+
# Intelligent metadata synchronization (v2 system)
|
941
|
+
sync_manager = await client.get_sync_manager()
|
942
|
+
await sync_manager.smart_sync()
|
943
|
+
|
944
|
+
# Search entities with enhanced filtering
|
945
|
+
sales_entities = await client.search_entities("sales")
|
946
|
+
print("Sales-related entities:", [e.name for e in sales_entities])
|
947
|
+
|
948
|
+
# Get detailed entity information with labels
|
949
|
+
entity_info = await client.get_public_entity_info("CustomersV3")
|
950
|
+
if entity_info:
|
951
|
+
print(f"Entity: {entity_info.name}")
|
952
|
+
print(f"Label: {entity_info.label_text}")
|
953
|
+
print(f"Data Service Enabled: {entity_info.data_service_enabled}")
|
954
|
+
|
955
|
+
# Search actions with caching
|
956
|
+
calc_actions = await client.search_actions("calculate")
|
957
|
+
print("Calculation actions:", [a.name for a in calc_actions])
|
958
|
+
|
959
|
+
# Get enumeration information
|
960
|
+
enum_info = await client.get_public_enumeration_info("NoYes")
|
961
|
+
if enum_info:
|
962
|
+
print(f"Enum: {enum_info.name}")
|
963
|
+
for member in enum_info.members:
|
964
|
+
print(f" {member.name} = {member.value}")
|
854
965
|
```
|
855
966
|
|
856
|
-
|
967
|
+
### Label Operations
|
857
968
|
|
858
|
-
```
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
969
|
+
```python
|
970
|
+
# Get specific label (v2 caching system)
|
971
|
+
label_text = await client.get_label_text("@SYS13342")
|
972
|
+
print(f"Label text: {label_text}")
|
973
|
+
|
974
|
+
# Get multiple labels efficiently
|
975
|
+
labels = await client.get_labels_batch([
|
976
|
+
"@SYS13342", "@SYS9490", "@GLS63332"
|
977
|
+
])
|
978
|
+
for label_id, text in labels.items():
|
979
|
+
print(f"{label_id}: {text}")
|
980
|
+
|
981
|
+
# Enhanced entity info with resolved labels
|
982
|
+
entity_info = await client.get_public_entity_info_with_labels("CustomersV3")
|
983
|
+
if entity_info.label_text:
|
984
|
+
print(f"Entity display name: {entity_info.label_text}")
|
985
|
+
|
986
|
+
# Access enhanced properties with labels
|
987
|
+
for prop in entity_info.enhanced_properties[:5]:
|
988
|
+
if hasattr(prop, 'label_text') and prop.label_text:
|
989
|
+
print(f"{prop.name}: {prop.label_text}")
|
863
990
|
```
|
864
991
|
|
865
|
-
|
992
|
+
## Error Handling
|
866
993
|
|
867
|
-
|
868
|
-
|
994
|
+
```python
|
995
|
+
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
869
996
|
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
997
|
+
try:
|
998
|
+
async with D365FOClient(config) as client:
|
999
|
+
customer = await client.get_data("/data/CustomersV3('NON-EXISTENT')")
|
1000
|
+
except ConnectionError as e:
|
1001
|
+
print(f"Connection failed: {e}")
|
1002
|
+
except AuthenticationError as e:
|
1003
|
+
print(f"Authentication failed: {e}")
|
1004
|
+
except D365FOClientError as e:
|
1005
|
+
print(f"Client operation failed: {e}")
|
1006
|
+
print(f"Status code: {e.status_code}")
|
1007
|
+
print(f"Response: {e.response_text}")
|
874
1008
|
```
|
875
1009
|
|
876
|
-
|
877
|
-
|
1010
|
+
## Development
|
1011
|
+
|
1012
|
+
### Setting up Development Environment
|
878
1013
|
|
879
1014
|
```bash
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
1015
|
+
# Clone the repository
|
1016
|
+
git clone https://github.com/mafzaal/d365fo-client.git
|
1017
|
+
cd d365fo-client
|
1018
|
+
|
1019
|
+
# Install with development dependencies using uv
|
1020
|
+
uv sync --dev
|
1021
|
+
|
1022
|
+
# Run tests
|
1023
|
+
uv run pytest
|
1024
|
+
|
1025
|
+
# Run integration tests
|
1026
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
1027
|
+
|
1028
|
+
# Format code
|
1029
|
+
uv run black .
|
1030
|
+
uv run isort .
|
1031
|
+
|
1032
|
+
# Type checking
|
1033
|
+
uv run mypy src/
|
1034
|
+
|
1035
|
+
# Quality checks
|
1036
|
+
.\make.ps1 quality-check # Windows PowerShell
|
1037
|
+
# or
|
1038
|
+
make quality-check # Unix/Linux/macOS
|
885
1039
|
```
|
886
1040
|
|
887
|
-
|
888
|
-
For secure credential storage using Azure Key Vault:
|
1041
|
+
### Project Structure
|
889
1042
|
|
890
|
-
```bash
|
891
|
-
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
892
|
-
export D365FO_CREDENTIAL_SOURCE="keyvault"
|
893
|
-
export D365FO_KEYVAULT_URL="https://your-keyvault.vault.azure.net/"
|
894
|
-
d365fo-mcp-server
|
895
1043
|
```
|
1044
|
+
d365fo-client/
|
1045
|
+
├── src/
|
1046
|
+
│ └── d365fo_client/
|
1047
|
+
│ ├── __init__.py # Public API exports
|
1048
|
+
│ ├── main.py # CLI entry point
|
1049
|
+
│ ├── cli.py # CLI command handlers
|
1050
|
+
│ ├── client.py # Enhanced D365FOClient class
|
1051
|
+
│ ├── config.py # Configuration management
|
1052
|
+
│ ├── auth.py # Authentication management
|
1053
|
+
│ ├── session.py # HTTP session management
|
1054
|
+
│ ├── crud.py # CRUD operations
|
1055
|
+
│ ├── query.py # OData query utilities
|
1056
|
+
│ ├── metadata.py # Legacy metadata operations
|
1057
|
+
│ ├── metadata_api.py # Metadata API client
|
1058
|
+
│ ├── metadata_cache.py # Metadata caching layer V2
|
1059
|
+
│ ├── metadata_sync.py # Metadata synchronization V2 with session management
|
1060
|
+
│ ├── sync_session.py # Enhanced sync session management (New in v0.2.3)
|
1061
|
+
│ ├── credential_manager.py # Credential source management (New in v0.2.3)
|
1062
|
+
│ ├── labels.py # Label operations V2
|
1063
|
+
│ ├── profiles.py # Profile data models
|
1064
|
+
│ ├── profile_manager.py # Profile management
|
1065
|
+
│ ├── models.py # Data models and configurations
|
1066
|
+
│ ├── output.py # Output formatting
|
1067
|
+
│ ├── utils.py # Utility functions
|
1068
|
+
│ ├── exceptions.py # Custom exceptions
|
1069
|
+
│ └── mcp/ # Model Context Protocol server
|
1070
|
+
│ ├── __init__.py # MCP server exports
|
1071
|
+
│ ├── main.py # MCP server entry point
|
1072
|
+
│ ├── server.py # Core MCP server implementation
|
1073
|
+
│ ├── client_manager.py# D365FO client connection pooling
|
1074
|
+
│ ├── models.py # MCP-specific data models
|
1075
|
+
│ ├── tools/ # MCP tool implementations (12 tools)
|
1076
|
+
│ │ ├── connection_tools.py
|
1077
|
+
│ │ ├── crud_tools.py
|
1078
|
+
│ │ ├── metadata_tools.py
|
1079
|
+
│ │ └── label_tools.py
|
1080
|
+
│ ├── resources/ # MCP resource handlers (4 types)
|
1081
|
+
│ │ ├── entity_handler.py
|
1082
|
+
│ │ ├── metadata_handler.py
|
1083
|
+
│ │ ├── environment_handler.py
|
1084
|
+
│ │ └── query_handler.py
|
1085
|
+
│ └── prompts/ # MCP prompt templates
|
1086
|
+
├── tests/ # Comprehensive test suite
|
1087
|
+
│ ├── unit/ # Unit tests (pytest-based)
|
1088
|
+
│ ├── integration/ # Multi-tier integration testing
|
1089
|
+
│ │ ├── mock_server/ # Mock D365 F&O API server
|
1090
|
+
│ │ ├── test_mock_server.py # Mock server tests
|
1091
|
+
│ │ ├── test_sandbox.py # Sandbox environment tests ✅
|
1092
|
+
│ │ ├── test_live.py # Live environment tests
|
1093
|
+
│ │ ├── conftest.py # Shared pytest fixtures
|
1094
|
+
│ │ ├── test_runner.py # Python test execution engine
|
1095
|
+
│ │ └── integration-test-simple.ps1 # PowerShell automation
|
1096
|
+
│ └── test_mcp_server.py # MCP server unit tests ✅
|
1097
|
+
├── scripts/ # Metadata discovery scripts
|
1098
|
+
│ ├── search_data_entities.ps1 # PowerShell entity search
|
1099
|
+
│ ├── get_data_entity_schema.ps1 # PowerShell schema retrieval
|
1100
|
+
│ ├── search_enums.py # Python enumeration search
|
1101
|
+
│ ├── get_enumeration_info.py # Python enumeration info
|
1102
|
+
│ ├── search_actions.ps1 # PowerShell action search
|
1103
|
+
│ └── get_action_info.py # Python action information
|
1104
|
+
├── docs/ # Comprehensive documentation
|
1105
|
+
├── pyproject.toml # Project configuration
|
1106
|
+
└── README.md # This file
|
1107
|
+
```
|
1108
|
+
|
1109
|
+
## Configuration Options
|
1110
|
+
|
1111
|
+
| Option | Type | Default | Description |
|
1112
|
+
|--------|------|---------|-------------|
|
1113
|
+
| `base_url` | str | Required | D365 F&O base URL |
|
1114
|
+
| `client_id` | str | None | Azure AD client ID |
|
1115
|
+
| `client_secret` | str | None | Azure AD client secret |
|
1116
|
+
| `tenant_id` | str | None | Azure AD tenant ID |
|
1117
|
+
| `use_default_credentials` | bool | True | Use Azure Default Credential |
|
1118
|
+
| `credential_source` | str | "environment" | Credential source: "environment", "keyvault" |
|
1119
|
+
| `keyvault_url` | str | None | Azure Key Vault URL for credential storage |
|
1120
|
+
| `verify_ssl` | bool | False | Verify SSL certificates |
|
1121
|
+
| `timeout` | int | 30 | Request timeout in seconds |
|
1122
|
+
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
|
1123
|
+
| `use_label_cache` | bool | True | Enable label caching V2 |
|
1124
|
+
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
|
1125
|
+
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |
|
1126
|
+
|
1127
|
+
### Cache Directory Behavior
|
1128
|
+
|
1129
|
+
By default, the client uses platform-appropriate user cache directories:
|
1130
|
+
|
1131
|
+
- **Windows**: `%LOCALAPPDATA%\d365fo-client` (e.g., `C:\Users\username\AppData\Local\d365fo-client`)
|
1132
|
+
- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
|
1133
|
+
- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)
|
896
1134
|
|
897
|
-
|
1135
|
+
You can override this by explicitly setting `metadata_cache_dir`:
|
898
1136
|
|
899
|
-
|
1137
|
+
```python
|
1138
|
+
from d365fo_client import FOClientConfig
|
900
1139
|
|
901
|
-
|
902
|
-
|
903
|
-
|
1140
|
+
# Use custom cache directory
|
1141
|
+
config = FOClientConfig(
|
1142
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1143
|
+
metadata_cache_dir="/custom/cache/path"
|
1144
|
+
)
|
904
1145
|
|
905
|
-
#
|
906
|
-
|
1146
|
+
# Or get the default cache directory programmatically
|
1147
|
+
from d365fo_client import get_user_cache_dir
|
907
1148
|
|
908
|
-
#
|
909
|
-
|
910
|
-
|
1149
|
+
cache_dir = get_user_cache_dir("my-app") # Platform-appropriate cache dir
|
1150
|
+
config = FOClientConfig(
|
1151
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1152
|
+
metadata_cache_dir=str(cache_dir)
|
1153
|
+
)
|
911
1154
|
```
|
912
1155
|
|
913
|
-
|
1156
|
+
## Testing
|
914
1157
|
|
915
|
-
|
1158
|
+
This project includes comprehensive testing at multiple levels to ensure reliability and quality.
|
916
1159
|
|
917
|
-
|
918
|
-
Add to your Claude Desktop configuration:
|
1160
|
+
### Unit Tests
|
919
1161
|
|
920
|
-
|
921
|
-
{
|
922
|
-
"mcpServers": {
|
923
|
-
"d365fo": {
|
924
|
-
"command": "d365fo-mcp-server",
|
925
|
-
"env": {
|
926
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com" //Optional
|
927
|
-
}
|
928
|
-
}
|
929
|
-
}
|
930
|
-
}
|
931
|
-
```
|
1162
|
+
Run standard unit tests for core functionality:
|
932
1163
|
|
933
|
-
|
1164
|
+
```bash
|
1165
|
+
# Run all unit tests
|
1166
|
+
uv run pytest
|
934
1167
|
|
935
|
-
|
936
|
-
|
1168
|
+
# Run with coverage
|
1169
|
+
uv run pytest --cov=d365fo_client --cov-report=html
|
937
1170
|
|
938
|
-
|
939
|
-
|
940
|
-
"servers": {
|
941
|
-
"d365fo-mcp-server": {
|
942
|
-
"type": "stdio",
|
943
|
-
"command": "uvx",
|
944
|
-
"args": [
|
945
|
-
"--from",
|
946
|
-
"d365fo-client",
|
947
|
-
"d365fo-mcp-server"
|
948
|
-
],
|
949
|
-
"env": {
|
950
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
951
|
-
"D365FO_LOG_LEVEL": "INFO"
|
952
|
-
}
|
953
|
-
}
|
954
|
-
}
|
955
|
-
}
|
1171
|
+
# Run specific test file
|
1172
|
+
uv run pytest tests/test_client.py -v
|
956
1173
|
```
|
957
1174
|
|
958
|
-
|
959
|
-
For environments requiring service principal authentication:
|
1175
|
+
### Integration Tests
|
960
1176
|
|
961
|
-
|
962
|
-
{
|
963
|
-
"servers": {
|
964
|
-
"d365fo-mcp-server": {
|
965
|
-
"type": "stdio",
|
966
|
-
"command": "uvx",
|
967
|
-
"args": [
|
968
|
-
"--from",
|
969
|
-
"d365fo-client",
|
970
|
-
"d365fo-mcp-server"
|
971
|
-
],
|
972
|
-
"env": {
|
973
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
974
|
-
"D365FO_LOG_LEVEL": "DEBUG",
|
975
|
-
"D365FO_CLIENT_ID": "${input:client_id}",
|
976
|
-
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
977
|
-
"D365FO_TENANT_ID": "${input:tenant_id}"
|
978
|
-
}
|
979
|
-
}
|
980
|
-
},
|
981
|
-
"inputs": [
|
982
|
-
{
|
983
|
-
"id": "tenant_id",
|
984
|
-
"type": "promptString",
|
985
|
-
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
986
|
-
"password": true
|
987
|
-
},
|
988
|
-
{
|
989
|
-
"id": "client_id",
|
990
|
-
"type": "promptString",
|
991
|
-
"description": "Azure AD Client ID for D365 F&O authentication",
|
992
|
-
"password": true
|
993
|
-
},
|
994
|
-
{
|
995
|
-
"id": "client_secret",
|
996
|
-
"type": "promptString",
|
997
|
-
"description": "Azure AD Client Secret for D365 F&O authentication",
|
998
|
-
"password": true
|
999
|
-
}
|
1000
|
-
]
|
1001
|
-
}
|
1002
|
-
```
|
1177
|
+
The project includes a sophisticated multi-tier integration testing framework:
|
1003
1178
|
|
1004
|
-
|
1005
|
-
- Always uses the latest version from the repository
|
1006
|
-
- No local installation required
|
1007
|
-
- Automatic dependency management
|
1008
|
-
- Works across different environments
|
1179
|
+
#### Quick Start
|
1009
1180
|
|
1010
|
-
|
1011
|
-
|
1181
|
+
```bash
|
1182
|
+
# Run sandbox integration tests (recommended)
|
1183
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
1012
1184
|
|
1013
|
-
|
1014
|
-
|
1185
|
+
# Run mock server tests (no external dependencies)
|
1186
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
1015
1187
|
|
1016
|
-
|
1017
|
-
|
1018
|
-
tools = await client.list_tools()
|
1019
|
-
|
1020
|
-
# Execute operations
|
1021
|
-
result = await client.call_tool(
|
1022
|
-
"d365fo_query_entities",
|
1023
|
-
{"entityName": "Customers", "top": 5}
|
1024
|
-
)
|
1188
|
+
# Run with verbose output
|
1189
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox -VerboseOutput
|
1025
1190
|
```
|
1026
1191
|
|
1027
|
-
|
1192
|
+
#### Test Levels
|
1028
1193
|
|
1029
|
-
|
1030
|
-
-
|
1031
|
-
-
|
1032
|
-
-
|
1033
|
-
- **Error Context**: Detailed error information for troubleshooting
|
1194
|
+
1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API
|
1195
|
+
- No external dependencies
|
1196
|
+
- Complete API simulation
|
1197
|
+
- Ideal for CI/CD pipelines
|
1034
1198
|
|
1035
|
-
|
1036
|
-
-
|
1037
|
-
-
|
1038
|
-
-
|
1039
|
-
- **Well Documented**: Complete API documentation and examples
|
1199
|
+
2. **Sandbox Tests** ⭐ *(Default)* - Tests against real D365 F&O test environments
|
1200
|
+
- Validates authentication
|
1201
|
+
- Tests real API behavior
|
1202
|
+
- Requires test environment access
|
1040
1203
|
|
1041
|
-
|
1042
|
-
-
|
1043
|
-
-
|
1044
|
-
-
|
1045
|
-
- **Maintenance Friendly**: Clear architecture and comprehensive test coverage
|
1204
|
+
3. **Live Tests** - Optional tests against production environments
|
1205
|
+
- Final validation
|
1206
|
+
- Performance benchmarking
|
1207
|
+
- Use with caution
|
1046
1208
|
|
1047
|
-
|
1209
|
+
#### Configuration
|
1048
1210
|
|
1049
|
-
|
1211
|
+
Set up integration testing with environment variables:
|
1050
1212
|
|
1051
|
-
**Connection Failures**
|
1052
1213
|
```bash
|
1053
|
-
#
|
1054
|
-
|
1214
|
+
# Copy the template and configure
|
1215
|
+
cp tests/integration/.env.template tests/integration/.env
|
1055
1216
|
|
1056
|
-
#
|
1057
|
-
|
1217
|
+
# Edit .env file with your settings:
|
1218
|
+
INTEGRATION_TEST_LEVEL=sandbox
|
1219
|
+
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
|
1220
|
+
D365FO_CLIENT_ID=your-client-id
|
1221
|
+
D365FO_CLIENT_SECRET=your-client-secret
|
1222
|
+
D365FO_TENANT_ID=your-tenant-id
|
1058
1223
|
```
|
1059
1224
|
|
1060
|
-
|
1225
|
+
#### Available Commands
|
1226
|
+
|
1061
1227
|
```bash
|
1062
|
-
#
|
1063
|
-
|
1228
|
+
# Test environment setup
|
1229
|
+
.\tests\integration\integration-test-simple.ps1 setup
|
1064
1230
|
|
1065
|
-
#
|
1066
|
-
|
1067
|
-
# ... set other variables
|
1068
|
-
d365fo-mcp-server
|
1069
|
-
```
|
1231
|
+
# Dependency checking
|
1232
|
+
.\tests\integration\integration-test-simple.ps1 deps-check
|
1070
1233
|
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1234
|
+
# Run specific test levels
|
1235
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
1236
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
1237
|
+
.\tests\integration\integration-test-simple.ps1 test-live
|
1075
1238
|
|
1076
|
-
#
|
1077
|
-
|
1078
|
-
|
1239
|
+
# Coverage and reporting
|
1240
|
+
.\tests\integration\integration-test-simple.ps1 coverage
|
1241
|
+
|
1242
|
+
# Clean up test artifacts
|
1243
|
+
.\tests\integration\integration-test-simple.ps1 clean
|
1079
1244
|
```
|
1080
1245
|
|
1081
|
-
####
|
1246
|
+
#### Test Coverage
|
1082
1247
|
|
1083
|
-
|
1084
|
-
|
1085
|
-
- **
|
1086
|
-
- **
|
1248
|
+
Integration tests cover:
|
1249
|
+
|
1250
|
+
- ✅ **Connection & Authentication** - Azure AD integration, SSL/TLS validation
|
1251
|
+
- ✅ **Version Methods** - Application, platform, and build version retrieval
|
1252
|
+
- ✅ **Metadata Operations** - Entity discovery, metadata API validation
|
1253
|
+
- ✅ **Data Operations** - CRUD operations, OData query validation
|
1254
|
+
- ✅ **Error Handling** - Network failures, authentication errors, invalid requests
|
1255
|
+
- ✅ **Performance** - Response time validation, concurrent operations
|
1256
|
+
|
1257
|
+
For detailed information, see [Integration Testing Documentation](tests/integration/README.md).
|
1258
|
+
|
1259
|
+
### Test Results
|
1260
|
+
|
1261
|
+
Recent sandbox integration test results:
|
1262
|
+
```
|
1263
|
+
✅ 17 passed, 0 failed, 2 warnings in 37.67s
|
1264
|
+
======================================================
|
1265
|
+
✅ TestSandboxConnection::test_connection_success
|
1266
|
+
✅ TestSandboxConnection::test_metadata_connection_success
|
1267
|
+
✅ TestSandboxVersionMethods::test_get_application_version
|
1268
|
+
✅ TestSandboxVersionMethods::test_get_platform_build_version
|
1269
|
+
✅ TestSandboxVersionMethods::test_get_application_build_version
|
1270
|
+
✅ TestSandboxVersionMethods::test_version_consistency
|
1271
|
+
✅ TestSandboxMetadataOperations::test_download_metadata
|
1272
|
+
✅ TestSandboxMetadataOperations::test_search_entities
|
1273
|
+
✅ TestSandboxMetadataOperations::test_get_data_entities
|
1274
|
+
✅ TestSandboxMetadataOperations::test_get_public_entities
|
1275
|
+
✅ TestSandboxDataOperations::test_get_available_entities
|
1276
|
+
✅ TestSandboxDataOperations::test_odata_query_options
|
1277
|
+
✅ TestSandboxAuthentication::test_authenticated_requests
|
1278
|
+
✅ TestSandboxErrorHandling::test_invalid_entity_error
|
1279
|
+
✅ TestSandboxErrorHandling::test_invalid_action_error
|
1280
|
+
✅ TestSandboxPerformance::test_response_times
|
1281
|
+
✅ TestSandboxPerformance::test_concurrent_operations
|
1282
|
+
```
|
1087
1283
|
|
1088
1284
|
## Contributing
|
1089
1285
|
|