d365fo-client 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl
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/auth.py +48 -9
- d365fo_client/client.py +40 -20
- d365fo_client/credential_sources.py +431 -0
- d365fo_client/mcp/client_manager.py +8 -0
- d365fo_client/mcp/main.py +39 -17
- d365fo_client/mcp/server.py +69 -22
- d365fo_client/mcp/tools/__init__.py +2 -0
- d365fo_client/mcp/tools/profile_tools.py +261 -2
- d365fo_client/mcp/tools/sync_tools.py +503 -0
- d365fo_client/metadata_api.py +67 -0
- d365fo_client/metadata_v2/cache_v2.py +11 -9
- d365fo_client/metadata_v2/global_version_manager.py +2 -4
- d365fo_client/metadata_v2/sync_manager_v2.py +1 -1
- d365fo_client/metadata_v2/sync_session_manager.py +1043 -0
- d365fo_client/models.py +22 -3
- d365fo_client/profile_manager.py +7 -1
- d365fo_client/profiles.py +28 -1
- d365fo_client/sync_models.py +181 -0
- {d365fo_client-0.2.2.dist-info → d365fo_client-0.2.4.dist-info}/METADATA +1011 -784
- {d365fo_client-0.2.2.dist-info → d365fo_client-0.2.4.dist-info}/RECORD +24 -20
- {d365fo_client-0.2.2.dist-info → d365fo_client-0.2.4.dist-info}/WHEEL +0 -0
- {d365fo_client-0.2.2.dist-info → d365fo_client-0.2.4.dist-info}/entry_points.txt +0 -0
- {d365fo_client-0.2.2.dist-info → d365fo_client-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {d365fo_client-0.2.2.dist-info → d365fo_client-0.2.4.dist-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
|
@@ -22,6 +22,7 @@ License-File: LICENSE
|
|
22
22
|
Requires-Dist: aiohttp>=3.10.0
|
23
23
|
Requires-Dist: aiofiles>=24.1.0
|
24
24
|
Requires-Dist: azure-identity>=1.19.0
|
25
|
+
Requires-Dist: azure-keyvault-secrets>=4.8.0
|
25
26
|
Requires-Dist: requests>=2.32.0
|
26
27
|
Requires-Dist: aiosqlite>=0.19.0
|
27
28
|
Requires-Dist: cachetools>=6.1.0
|
@@ -39,1020 +40,1246 @@ Provides-Extra: all
|
|
39
40
|
Requires-Dist: d365fo-client[dev]; extra == "all"
|
40
41
|
Dynamic: license-file
|
41
42
|
|
42
|
-
# Dynamics 365 Finance & Operations
|
43
|
+
# Dynamics 365 Finance & Operations MCP Server
|
43
44
|
|
44
|
-
|
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.
|
45
46
|
|
46
|
-
|
47
|
+
**🚀 One-Click Installation for VS Code:**
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
- 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
|
51
|
-
- 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
|
52
|
-
- ⚡ **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
|
53
|
-
- 🔒 **Authentication**: Azure AD integration with default credentials and service principal support
|
54
|
-
- 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
|
55
|
-
- 🌐 **Async/Await**: Modern async/await patterns with optimized session management
|
56
|
-
- 📝 **Type Hints**: Full type annotation support with enhanced data models
|
57
|
-
- 🤖 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
|
58
|
-
- 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
|
59
|
-
- 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
|
60
|
-
- 📋 **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery
|
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)
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
```bash
|
65
|
-
# Install from PyPI
|
66
|
-
pip install d365fo-client
|
52
|
+
**🐳 Docker Installation for VS Code:**
|
67
53
|
|
68
|
-
|
69
|
-
|
70
|
-
cd d365fo-client
|
71
|
-
uv sync # Installs with exact dependencies from uv.lock
|
72
|
-
```
|
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)
|
73
56
|
|
74
|
-
|
57
|
+
[](https://pypi.org/project/d365fo-client/)
|
75
58
|
|
76
|
-
|
59
|
+
**Also includes a comprehensive Python client library** for Microsoft Dynamics 365 Finance & Operations with OData endpoints, metadata operations, label management, and CLI tools.
|
77
60
|
|
78
|
-
##
|
61
|
+
## MCP Server Overview
|
79
62
|
|
80
|
-
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.
|
81
64
|
|
82
|
-
###
|
65
|
+
### Key Features
|
83
66
|
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
87
77
|
|
88
|
-
|
89
|
-
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
|
90
|
-
```
|
78
|
+
### Quick Start
|
91
79
|
|
92
|
-
|
80
|
+
#### Installation and Setup
|
93
81
|
|
94
|
-
#### Entity Operations
|
95
82
|
```bash
|
96
|
-
#
|
97
|
-
d365fo-client
|
83
|
+
# Install d365fo-client with MCP dependencies
|
84
|
+
pip install d365fo-client
|
98
85
|
|
99
|
-
#
|
100
|
-
|
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
|
101
91
|
|
102
|
-
#
|
103
|
-
d365fo-
|
104
|
-
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
|
105
|
-
d365fo-client entities delete Customers US-999
|
92
|
+
# Start the MCP server
|
93
|
+
d365fo-mcp-server
|
106
94
|
```
|
107
95
|
|
108
|
-
####
|
109
|
-
```bash
|
110
|
-
# Search and discover entities
|
111
|
-
d365fo-client metadata entities --search "sales" --output json
|
96
|
+
#### Integration with AI Assistants
|
112
97
|
|
113
|
-
|
114
|
-
d365fo-client metadata actions --pattern "calculate" --limit 5
|
98
|
+
##### VS Code Integration (Recommended)
|
115
99
|
|
116
|
-
|
117
|
-
|
100
|
+
**Option 1: Default Credentials**
|
101
|
+
Add to your VS Code `mcp.json` for GitHub Copilot with MCP:
|
118
102
|
|
119
|
-
|
120
|
-
|
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
|
+
}
|
121
121
|
```
|
122
122
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
+
}
|
129
167
|
```
|
130
168
|
|
131
|
-
|
132
|
-
|
133
|
-
# Resolve single label
|
134
|
-
d365fo-client labels resolve "@SYS13342"
|
169
|
+
**Option 3: Docker Integration**
|
170
|
+
For containerized environments and enhanced isolation:
|
135
171
|
|
136
|
-
|
137
|
-
|
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
|
+
}
|
138
221
|
```
|
139
222
|
|
140
|
-
|
141
|
-
|
142
|
-
-
|
143
|
-
-
|
144
|
-
-
|
145
|
-
-
|
146
|
-
-
|
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`)
|
147
230
|
|
148
|
-
|
231
|
+
**Prerequisites:**
|
232
|
+
- Docker installed and running
|
233
|
+
- Access to Docker Hub or GitHub Container Registry
|
234
|
+
- Network access for pulling the container image
|
149
235
|
|
150
|
-
|
236
|
+
##### Claude Desktop Integration
|
151
237
|
|
152
|
-
|
153
|
-
profiles:
|
154
|
-
production:
|
155
|
-
base_url: "https://prod.dynamics.com"
|
156
|
-
use_default_credentials: true
|
157
|
-
timeout: 60
|
158
|
-
|
159
|
-
development:
|
160
|
-
base_url: "https://dev.dynamics.com"
|
161
|
-
client_id: "${AZURE_CLIENT_ID}"
|
162
|
-
client_secret: "${AZURE_CLIENT_SECRET}"
|
163
|
-
tenant_id: "${AZURE_TENANT_ID}"
|
164
|
-
use_cache_first: true
|
238
|
+
Add to your Claude Desktop configuration:
|
165
239
|
|
166
|
-
|
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
|
+
}
|
167
257
|
```
|
168
258
|
|
169
|
-
|
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
|
170
264
|
|
171
|
-
|
172
|
-
# Quick entity discovery
|
173
|
-
d365fo-client entities list --pattern "cust.*" --output json
|
265
|
+
#### Alternative: Programmatic Usage
|
174
266
|
|
175
|
-
|
176
|
-
|
267
|
+
```python
|
268
|
+
from d365fo_client.mcp import D365FOMCPServer
|
177
269
|
|
178
|
-
#
|
179
|
-
|
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
|
+
}
|
180
277
|
|
181
|
-
|
182
|
-
|
278
|
+
server = D365FOMCPServer(config)
|
279
|
+
await server.run()
|
183
280
|
```
|
184
281
|
|
185
|
-
|
186
|
-
|
187
|
-
```bash
|
188
|
-
d365fo-client --help
|
189
|
-
d365fo-client entities --help
|
190
|
-
d365fo-client metadata --help
|
191
|
-
```
|
192
|
-
### Basic Usage
|
282
|
+
#### Custom MCP Clients
|
283
|
+
Connect using any MCP-compatible client library:
|
193
284
|
|
194
285
|
```python
|
195
|
-
import
|
196
|
-
from d365fo_client import D365FOClient, FOClientConfig
|
286
|
+
from mcp import Client
|
197
287
|
|
198
|
-
async
|
199
|
-
#
|
200
|
-
|
201
|
-
base_url="https://your-fo-environment.dynamics.com",
|
202
|
-
use_default_credentials=True # Uses Azure Default Credential
|
203
|
-
)
|
288
|
+
async with Client("d365fo-mcp-server") as client:
|
289
|
+
# Discover available tools
|
290
|
+
tools = await client.list_tools()
|
204
291
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
# Get environment information
|
211
|
-
env_info = await client.get_environment_info()
|
212
|
-
print(f"Environment: {env_info.application_version}")
|
213
|
-
|
214
|
-
# Search for entities (uses metadata cache v2)
|
215
|
-
customer_entities = await client.search_entities("customer")
|
216
|
-
print(f"Found {len(customer_entities)} customer entities")
|
217
|
-
|
218
|
-
# Get customers with query options
|
219
|
-
from d365fo_client import QueryOptions
|
220
|
-
options = QueryOptions(
|
221
|
-
select=["CustomerAccount", "Name", "SalesCurrencyCode"],
|
222
|
-
top=10,
|
223
|
-
orderby=["Name"]
|
224
|
-
)
|
225
|
-
|
226
|
-
customers = await client.get_data("/data/CustomersV3", options)
|
227
|
-
print(f"Retrieved {len(customers['value'])} customers")
|
228
|
-
|
229
|
-
if __name__ == "__main__":
|
230
|
-
asyncio.run(main())
|
292
|
+
# Execute operations
|
293
|
+
result = await client.call_tool(
|
294
|
+
"d365fo_query_entities",
|
295
|
+
{"entityName": "Customers", "top": 5}
|
296
|
+
)
|
231
297
|
```
|
232
298
|
|
233
|
-
|
234
|
-
|
235
|
-
```python
|
236
|
-
from d365fo_client import create_client
|
299
|
+
#### Docker Deployment
|
237
300
|
|
238
|
-
|
239
|
-
async with create_client("https://your-fo-environment.dynamics.com") as client:
|
240
|
-
customers = await client.get_data("/data/CustomersV3", top=5)
|
241
|
-
```
|
301
|
+
For containerized environments and production deployments:
|
242
302
|
|
243
|
-
|
303
|
+
**Pull the Docker Image:**
|
304
|
+
```bash
|
305
|
+
# Pull from GitHub Container Registry
|
306
|
+
docker pull ghcr.io/mafzaal/d365fo-client:latest
|
244
307
|
|
245
|
-
|
308
|
+
# Or pull a specific version
|
309
|
+
docker pull ghcr.io/mafzaal/d365fo-client:v0.2.3
|
310
|
+
```
|
246
311
|
|
247
|
-
|
248
|
-
|
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
|
+
```
|
249
333
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
+
```
|
255
354
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
)
|
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)
|
264
363
|
|
265
|
-
|
266
|
-
config = FOClientConfig(
|
267
|
-
base_url="https://your-fo-environment.dynamics.com",
|
268
|
-
use_default_credentials=True,
|
269
|
-
verify_ssl=False, # For development environments
|
270
|
-
timeout=60, # Request timeout in seconds
|
271
|
-
metadata_cache_dir="./my_cache", # Custom cache directory
|
272
|
-
use_label_cache=True, # Enable label caching
|
273
|
-
label_cache_expiry_minutes=120 # Cache for 2 hours
|
274
|
-
)
|
275
|
-
```
|
364
|
+
### Architecture Benefits
|
276
365
|
|
277
|
-
|
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
|
278
371
|
|
279
|
-
|
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
|
280
377
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
"Name": "Test Customer",
|
287
|
-
"SalesCurrencyCode": "USD"
|
288
|
-
}
|
289
|
-
created = await client.create_data("/data/CustomersV3", new_customer)
|
290
|
-
|
291
|
-
# READ - Get single customer by key
|
292
|
-
customer = await client.get_data("/data/CustomersV3('US-001')")
|
293
|
-
|
294
|
-
# UPDATE - Update customer with optimistic concurrency
|
295
|
-
updates = {"Name": "Updated Customer Name"}
|
296
|
-
updated = await client.update_data("/data/CustomersV3('US-001')", updates)
|
297
|
-
|
298
|
-
# DELETE - Delete customer
|
299
|
-
success = await client.delete_data("/data/CustomersV3('US-999')")
|
300
|
-
print(f"Delete successful: {success}")
|
301
|
-
```
|
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
|
302
383
|
|
303
|
-
###
|
384
|
+
### Troubleshooting
|
304
385
|
|
305
|
-
|
306
|
-
from d365fo_client import QueryOptions
|
386
|
+
#### Common Issues
|
307
387
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
expand=["CustomerGroup"],
|
313
|
-
orderby=["Name desc", "CustomerAccount"],
|
314
|
-
top=50,
|
315
|
-
skip=10,
|
316
|
-
count=True
|
317
|
-
)
|
388
|
+
**Connection Failures**
|
389
|
+
```bash
|
390
|
+
# Test connectivity
|
391
|
+
d365fo-client version app --base-url https://your-environment.dynamics.com
|
318
392
|
|
319
|
-
|
320
|
-
|
393
|
+
# Check logs
|
394
|
+
tail -f ~/.d365fo-mcp/logs/mcp-server.log
|
321
395
|
```
|
322
396
|
|
323
|
-
|
397
|
+
**Authentication Issues**
|
398
|
+
```bash
|
399
|
+
# Verify Azure CLI authentication
|
400
|
+
az account show
|
324
401
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
})
|
402
|
+
# Test with explicit credentials
|
403
|
+
export D365FO_CLIENT_ID="your-client-id"
|
404
|
+
# ... set other variables
|
405
|
+
d365fo-mcp-server
|
406
|
+
```
|
331
407
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
408
|
+
**Performance Issues**
|
409
|
+
```bash
|
410
|
+
# Enable debug logging
|
411
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
336
412
|
|
337
|
-
#
|
338
|
-
|
339
|
-
|
340
|
-
})
|
413
|
+
# Adjust connection settings
|
414
|
+
export D365FO_CONNECTION_TIMEOUT="120"
|
415
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="5"
|
341
416
|
```
|
342
417
|
|
343
|
-
|
418
|
+
#### Getting Help
|
344
419
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
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)
|
349
424
|
|
350
|
-
|
351
|
-
sales_entities = await client.search_entities("sales")
|
352
|
-
print("Sales-related entities:", [e.name for e in sales_entities])
|
425
|
+
### MCP Tools
|
353
426
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
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).**
|
360
478
|
|
361
|
-
|
362
|
-
calc_actions = await client.search_actions("calculate")
|
363
|
-
print("Calculation actions:", [a.name for a in calc_actions])
|
479
|
+
### MCP Resources
|
364
480
|
|
365
|
-
|
366
|
-
enum_info = await client.get_public_enumeration_info("NoYes")
|
367
|
-
if enum_info:
|
368
|
-
print(f"Enum: {enum_info.name}")
|
369
|
-
for member in enum_info.members:
|
370
|
-
print(f" {member.name} = {member.value}")
|
371
|
-
```
|
481
|
+
The server exposes four types of resources for discovery and access:
|
372
482
|
|
373
|
-
|
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
|
+
```
|
374
490
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
+
```
|
379
499
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
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
|
+
```
|
386
507
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
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
|
513
|
+
```
|
391
514
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
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
|
396
521
|
```
|
397
522
|
|
398
|
-
|
523
|
+
### Usage Examples
|
399
524
|
|
400
|
-
|
401
|
-
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
525
|
+
#### Basic Tool Execution
|
402
526
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
print(f"Response: {e.response_text}")
|
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
|
+
}
|
414
537
|
```
|
415
538
|
|
416
|
-
|
539
|
+
#### Entity Schema Discovery
|
417
540
|
|
418
|
-
|
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
|
+
}
|
551
|
+
```
|
419
552
|
|
420
|
-
|
421
|
-
# Clone the repository
|
422
|
-
git clone https://github.com/mafzaal/d365fo-client.git
|
423
|
-
cd d365fo-client
|
553
|
+
#### Environment Information
|
424
554
|
|
425
|
-
|
426
|
-
|
555
|
+
```json
|
556
|
+
{
|
557
|
+
"tool": "d365fo_get_environment_info",
|
558
|
+
"arguments": {}
|
559
|
+
}
|
560
|
+
```
|
427
561
|
|
428
|
-
|
429
|
-
uv run pytest
|
562
|
+
### Authentication & Configuration
|
430
563
|
|
431
|
-
|
432
|
-
|
564
|
+
#### Default Credentials (Recommended)
|
565
|
+
Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
|
433
566
|
|
434
|
-
|
435
|
-
|
436
|
-
|
567
|
+
```bash
|
568
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
569
|
+
# No additional auth environment variables needed
|
570
|
+
d365fo-mcp-server
|
571
|
+
```
|
437
572
|
|
438
|
-
|
439
|
-
|
573
|
+
#### Explicit Credentials
|
574
|
+
For service principal authentication:
|
440
575
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
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
|
445
582
|
```
|
446
583
|
|
447
|
-
|
584
|
+
#### Azure Key Vault Integration (New in v0.2.3)
|
585
|
+
For secure credential storage using Azure Key Vault:
|
448
586
|
|
449
|
-
```
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
│ ├── main.py # CLI entry point
|
455
|
-
│ ├── cli.py # CLI command handlers
|
456
|
-
│ ├── client.py # Enhanced D365FOClient class
|
457
|
-
│ ├── config.py # Configuration management
|
458
|
-
│ ├── auth.py # Authentication management
|
459
|
-
│ ├── session.py # HTTP session management
|
460
|
-
│ ├── crud.py # CRUD operations
|
461
|
-
│ ├── query.py # OData query utilities
|
462
|
-
│ ├── metadata.py # Legacy metadata operations
|
463
|
-
│ ├── metadata_api.py # Metadata API client
|
464
|
-
│ ├── metadata_cache.py # Metadata caching layer V2
|
465
|
-
│ ├── metadata_sync.py # Metadata synchronization V2
|
466
|
-
│ ├── labels.py # Label operations V2
|
467
|
-
│ ├── profiles.py # Profile data models
|
468
|
-
│ ├── profile_manager.py # Profile management
|
469
|
-
│ ├── models.py # Data models and configurations
|
470
|
-
│ ├── output.py # Output formatting
|
471
|
-
│ ├── utils.py # Utility functions
|
472
|
-
│ ├── exceptions.py # Custom exceptions
|
473
|
-
│ └── mcp/ # Model Context Protocol server
|
474
|
-
│ ├── __init__.py # MCP server exports
|
475
|
-
│ ├── main.py # MCP server entry point
|
476
|
-
│ ├── server.py # Core MCP server implementation
|
477
|
-
│ ├── client_manager.py# D365FO client connection pooling
|
478
|
-
│ ├── models.py # MCP-specific data models
|
479
|
-
│ ├── tools/ # MCP tool implementations (12 tools)
|
480
|
-
│ │ ├── connection_tools.py
|
481
|
-
│ │ ├── crud_tools.py
|
482
|
-
│ │ ├── metadata_tools.py
|
483
|
-
│ │ └── label_tools.py
|
484
|
-
│ ├── resources/ # MCP resource handlers (4 types)
|
485
|
-
│ │ ├── entity_handler.py
|
486
|
-
│ │ ├── metadata_handler.py
|
487
|
-
│ │ ├── environment_handler.py
|
488
|
-
│ │ └── query_handler.py
|
489
|
-
│ └── prompts/ # MCP prompt templates
|
490
|
-
├── tests/ # Comprehensive test suite
|
491
|
-
│ ├── unit/ # Unit tests (pytest-based)
|
492
|
-
│ ├── integration/ # Multi-tier integration testing
|
493
|
-
│ │ ├── mock_server/ # Mock D365 F&O API server
|
494
|
-
│ │ ├── test_mock_server.py # Mock server tests
|
495
|
-
│ │ ├── test_sandbox.py # Sandbox environment tests ✅
|
496
|
-
│ │ ├── test_live.py # Live environment tests
|
497
|
-
│ │ ├── conftest.py # Shared pytest fixtures
|
498
|
-
│ │ ├── test_runner.py # Python test execution engine
|
499
|
-
│ │ └── integration-test-simple.ps1 # PowerShell automation
|
500
|
-
│ └── test_mcp_server.py # MCP server unit tests ✅
|
501
|
-
├── scripts/ # Metadata discovery scripts
|
502
|
-
│ ├── search_data_entities.ps1 # PowerShell entity search
|
503
|
-
│ ├── get_data_entity_schema.ps1 # PowerShell schema retrieval
|
504
|
-
│ ├── search_enums.py # Python enumeration search
|
505
|
-
│ ├── get_enumeration_info.py # Python enumeration info
|
506
|
-
│ ├── search_actions.ps1 # PowerShell action search
|
507
|
-
│ └── get_action_info.py # Python action information
|
508
|
-
├── docs/ # Comprehensive documentation
|
509
|
-
├── pyproject.toml # Project configuration
|
510
|
-
└── README.md # This file
|
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
|
511
592
|
```
|
512
593
|
|
513
|
-
|
594
|
+
#### Advanced Configuration
|
514
595
|
|
515
|
-
|
516
|
-
|--------|------|---------|-------------|
|
517
|
-
| `base_url` | str | Required | D365 F&O base URL |
|
518
|
-
| `client_id` | str | None | Azure AD client ID |
|
519
|
-
| `client_secret` | str | None | Azure AD client secret |
|
520
|
-
| `tenant_id` | str | None | Azure AD tenant ID |
|
521
|
-
| `use_default_credentials` | bool | True | Use Azure Default Credential |
|
522
|
-
| `verify_ssl` | bool | False | Verify SSL certificates |
|
523
|
-
| `timeout` | int | 30 | Request timeout in seconds |
|
524
|
-
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
|
525
|
-
| `use_label_cache` | bool | True | Enable label caching V2 |
|
526
|
-
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
|
527
|
-
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |
|
596
|
+
Create a configuration file or set additional environment variables:
|
528
597
|
|
529
|
-
|
598
|
+
```bash
|
599
|
+
# Optional: Logging configuration
|
600
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
530
601
|
|
531
|
-
|
602
|
+
# Optional: Cache settings
|
603
|
+
export D365FO_CACHE_DIR="/custom/cache/path"
|
532
604
|
|
533
|
-
|
534
|
-
|
535
|
-
|
605
|
+
# Optional: Performance tuning
|
606
|
+
export D365FO_CONNECTION_TIMEOUT="60"
|
607
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="10"
|
608
|
+
```
|
536
609
|
|
537
|
-
|
610
|
+
## Python Client Library
|
538
611
|
|
539
|
-
|
540
|
-
from d365fo_client import FOClientConfig
|
612
|
+
### Features
|
541
613
|
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
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
|
547
629
|
|
548
|
-
|
549
|
-
from d365fo_client import get_user_cache_dir
|
630
|
+
### Installation
|
550
631
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
632
|
+
```bash
|
633
|
+
# Install from PyPI
|
634
|
+
pip install d365fo-client
|
635
|
+
|
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
|
640
|
+
|
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
|
556
652
|
```
|
557
653
|
|
558
|
-
|
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.
|
559
655
|
|
560
|
-
|
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`
|
561
660
|
|
562
|
-
|
661
|
+
Please update your environment variables accordingly when upgrading.
|
563
662
|
|
564
|
-
|
663
|
+
## Python Client Quick Start
|
565
664
|
|
566
|
-
|
567
|
-
# Run all unit tests
|
568
|
-
uv run pytest
|
665
|
+
## Command Line Interface (CLI)
|
569
666
|
|
570
|
-
|
571
|
-
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.
|
572
668
|
|
573
|
-
|
574
|
-
uv run pytest tests/test_client.py -v
|
575
|
-
```
|
669
|
+
### Usage
|
576
670
|
|
577
|
-
|
671
|
+
```bash
|
672
|
+
# Use the installed CLI command
|
673
|
+
d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]
|
578
674
|
|
579
|
-
|
675
|
+
# Alternative: Module execution
|
676
|
+
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
|
677
|
+
```
|
580
678
|
|
581
|
-
|
679
|
+
### Command Categories
|
582
680
|
|
681
|
+
#### Entity Operations
|
583
682
|
```bash
|
584
|
-
#
|
585
|
-
|
683
|
+
# List entities with filtering
|
684
|
+
d365fo-client entities list --pattern "customer" --limit 10
|
586
685
|
|
587
|
-
#
|
588
|
-
|
686
|
+
# Get entity details and schema
|
687
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels
|
589
688
|
|
590
|
-
#
|
591
|
-
|
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
|
592
693
|
```
|
593
694
|
|
594
|
-
####
|
695
|
+
#### Metadata Operations
|
696
|
+
```bash
|
697
|
+
# Search and discover entities
|
698
|
+
d365fo-client metadata entities --search "sales" --output json
|
595
699
|
|
596
|
-
|
597
|
-
|
598
|
-
- Complete API simulation
|
599
|
-
- Ideal for CI/CD pipelines
|
700
|
+
# Get available actions
|
701
|
+
d365fo-client metadata actions --pattern "calculate" --limit 5
|
600
702
|
|
601
|
-
|
602
|
-
|
603
|
-
- Tests real API behavior
|
604
|
-
- Requires test environment access
|
605
|
-
|
606
|
-
3. **Live Tests** - Optional tests against production environments
|
607
|
-
- Final validation
|
608
|
-
- Performance benchmarking
|
609
|
-
- Use with caution
|
703
|
+
# Enumerate system enumerations
|
704
|
+
d365fo-client metadata enums --search "status" --output table
|
610
705
|
|
611
|
-
|
706
|
+
# Synchronize metadata cache
|
707
|
+
d365fo-client metadata sync --force-refresh
|
708
|
+
```
|
612
709
|
|
613
|
-
|
710
|
+
#### Version Information
|
711
|
+
```bash
|
712
|
+
# Get application versions
|
713
|
+
d365fo-client version app
|
714
|
+
d365fo-client version platform
|
715
|
+
d365fo-client version build
|
716
|
+
```
|
614
717
|
|
718
|
+
#### Label Operations
|
615
719
|
```bash
|
616
|
-
#
|
617
|
-
|
720
|
+
# Resolve single label
|
721
|
+
d365fo-client labels resolve "@SYS13342"
|
618
722
|
|
619
|
-
#
|
620
|
-
|
621
|
-
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
|
622
|
-
AZURE_CLIENT_ID=your-client-id
|
623
|
-
AZURE_CLIENT_SECRET=your-client-secret
|
624
|
-
AZURE_TENANT_ID=your-tenant-id
|
723
|
+
# Search labels by pattern
|
724
|
+
d365fo-client labels search "customer" --language "en-US"
|
625
725
|
```
|
626
726
|
|
627
|
-
|
727
|
+
### Global Options
|
628
728
|
|
629
|
-
|
630
|
-
|
631
|
-
|
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)
|
632
734
|
|
633
|
-
|
634
|
-
.\tests\integration\integration-test-simple.ps1 deps-check
|
735
|
+
### Configuration Profiles
|
635
736
|
|
636
|
-
|
637
|
-
.\tests\integration\integration-test-simple.ps1 test-mock
|
638
|
-
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
639
|
-
.\tests\integration\integration-test-simple.ps1 test-live
|
737
|
+
Create reusable configurations in `~/.d365fo-client/config.yaml`:
|
640
738
|
|
641
|
-
|
642
|
-
|
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
|
643
752
|
|
644
|
-
|
645
|
-
.\tests\integration\integration-test-simple.ps1 clean
|
753
|
+
default_profile: "development"
|
646
754
|
```
|
647
755
|
|
648
|
-
|
756
|
+
### Examples
|
649
757
|
|
650
|
-
|
758
|
+
```bash
|
759
|
+
# Quick entity discovery
|
760
|
+
d365fo-client entities list --pattern "cust.*" --output json
|
651
761
|
|
652
|
-
|
653
|
-
-
|
654
|
-
- ✅ **Metadata Operations** - Entity discovery, metadata API validation
|
655
|
-
- ✅ **Data Operations** - CRUD operations, OData query validation
|
656
|
-
- ✅ **Error Handling** - Network failures, authentication errors, invalid requests
|
657
|
-
- ✅ **Performance** - Response time validation, concurrent operations
|
762
|
+
# Get comprehensive entity information
|
763
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels --output yaml
|
658
764
|
|
659
|
-
|
765
|
+
# Search for calculation actions
|
766
|
+
d365fo-client metadata actions --pattern "calculate|compute" --output table
|
660
767
|
|
661
|
-
|
768
|
+
# Test environment connectivity
|
769
|
+
d365fo-client version app --verbose
|
770
|
+
```
|
662
771
|
|
663
|
-
|
772
|
+
For a complete command reference:
|
773
|
+
|
774
|
+
```bash
|
775
|
+
d365fo-client --help
|
776
|
+
d365fo-client entities --help
|
777
|
+
d365fo-client metadata --help
|
664
778
|
```
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
✅
|
682
|
-
|
683
|
-
|
779
|
+
### Basic Usage
|
780
|
+
|
781
|
+
```python
|
782
|
+
import asyncio
|
783
|
+
from d365fo_client import D365FOClient, FOClientConfig
|
784
|
+
|
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")
|
815
|
+
|
816
|
+
if __name__ == "__main__":
|
817
|
+
asyncio.run(main())
|
684
818
|
```
|
685
819
|
|
686
|
-
|
820
|
+
### Using Convenience Function
|
687
821
|
|
688
|
-
|
822
|
+
```python
|
823
|
+
from d365fo_client import create_client
|
689
824
|
|
690
|
-
|
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
|
+
```
|
691
829
|
|
692
|
-
|
693
|
-
- **12 functional tools** covering all major D365 F&O operations
|
694
|
-
- **4 resource types** with comprehensive metadata exposure
|
695
|
-
- **Production-ready** implementation with proper error handling and authentication
|
696
|
-
- **Performance optimization** with connection pooling and intelligent caching V2
|
697
|
-
- **Comprehensive testing** with 14 unit tests (100% pass rate)
|
698
|
-
- **Profile support** for multi-environment configurations
|
830
|
+
## Configuration
|
699
831
|
|
700
|
-
###
|
832
|
+
### Authentication Options
|
701
833
|
|
702
|
-
|
834
|
+
```python
|
835
|
+
from d365fo_client import FOClientConfig
|
703
836
|
|
704
|
-
|
705
|
-
|
706
|
-
|
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
|
+
)
|
707
842
|
|
708
|
-
#
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
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
|
+
)
|
713
851
|
|
714
|
-
#
|
715
|
-
|
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
|
+
)
|
858
|
+
|
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
|
+
)
|
716
869
|
```
|
717
870
|
|
718
|
-
|
871
|
+
## Core Operations
|
719
872
|
|
720
|
-
|
721
|
-
from d365fo_client.mcp import D365FOMCPServer
|
873
|
+
### CRUD Operations
|
722
874
|
|
723
|
-
|
724
|
-
config
|
725
|
-
|
726
|
-
|
727
|
-
"
|
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"
|
728
882
|
}
|
729
|
-
|
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}")
|
895
|
+
```
|
730
896
|
|
731
|
-
|
732
|
-
|
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')}")
|
733
915
|
```
|
734
916
|
|
735
|
-
###
|
917
|
+
### Action Execution
|
736
918
|
|
737
|
-
|
919
|
+
```python
|
920
|
+
# Unbound action
|
921
|
+
result = await client.post_data("/data/calculateTax", {
|
922
|
+
"amount": 1000.00,
|
923
|
+
"taxGroup": "STANDARD"
|
924
|
+
})
|
738
925
|
|
739
|
-
|
740
|
-
|
741
|
-
|
926
|
+
# Bound action on entity set
|
927
|
+
result = await client.post_data("/data/CustomersV3/calculateBalances", {
|
928
|
+
"asOfDate": "2024-12-31"
|
929
|
+
})
|
742
930
|
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
- **`d365fo_delete_entity_record`** - Delete entity records with conflict detection
|
931
|
+
# Bound action on specific entity instance
|
932
|
+
result = await client.post_data("/data/CustomersV3('US-001')/calculateBalance", {
|
933
|
+
"asOfDate": "2024-12-31"
|
934
|
+
})
|
935
|
+
```
|
749
936
|
|
750
|
-
|
751
|
-
- **`d365fo_search_entities`** - Search entities by pattern with advanced filtering and FTS5 search
|
752
|
-
- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties and relationships
|
753
|
-
- **`d365fo_search_actions`** - Search available OData actions and functions
|
754
|
-
- **`d365fo_search_enums`** - Search system enumerations with filtering
|
755
|
-
- **`d365fo_get_enum_info`** - Get detailed enumeration information and values
|
937
|
+
### Metadata Operations
|
756
938
|
|
757
|
-
|
758
|
-
|
759
|
-
|
939
|
+
```python
|
940
|
+
# Intelligent metadata synchronization (v2 system)
|
941
|
+
sync_manager = await client.get_sync_manager()
|
942
|
+
await sync_manager.smart_sync()
|
760
943
|
|
761
|
-
|
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])
|
762
947
|
|
763
|
-
|
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}")
|
764
954
|
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
d365fo://entities/CustomersV3 # Customer entity with metadata and sample data
|
769
|
-
d365fo://entities/SalesOrders # Sales order entity information
|
770
|
-
d365fo://entities/Products # Product entity details
|
771
|
-
```
|
955
|
+
# Search actions with caching
|
956
|
+
calc_actions = await client.search_actions("calculate")
|
957
|
+
print("Calculation actions:", [a.name for a in calc_actions])
|
772
958
|
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
d365fo://metadata/labels # System labels and translations
|
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}")
|
780
965
|
```
|
781
966
|
|
782
|
-
|
783
|
-
Access environment status and information:
|
784
|
-
```
|
785
|
-
d365fo://environment/status # Environment health and connectivity
|
786
|
-
d365fo://environment/version # Version information (app, platform, build)
|
787
|
-
d365fo://environment/cache # Cache status and statistics V2
|
788
|
-
```
|
967
|
+
### Label Operations
|
789
968
|
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
d365fo://queries/sales_summary # Sales summary query with parameters
|
795
|
-
```
|
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}")
|
796
973
|
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
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}")
|
803
990
|
```
|
804
991
|
|
805
|
-
|
992
|
+
## Error Handling
|
806
993
|
|
807
|
-
|
994
|
+
```python
|
995
|
+
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
808
996
|
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
"
|
814
|
-
|
815
|
-
"
|
816
|
-
|
817
|
-
|
818
|
-
}
|
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}")
|
819
1008
|
```
|
820
1009
|
|
821
|
-
|
1010
|
+
## Development
|
822
1011
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
1012
|
+
### Setting up Development Environment
|
1013
|
+
|
1014
|
+
```bash
|
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
|
833
1039
|
```
|
834
1040
|
|
835
|
-
|
1041
|
+
### Project Structure
|
836
1042
|
|
837
|
-
```
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
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
|
842
1107
|
```
|
843
1108
|
|
844
|
-
|
1109
|
+
## Configuration Options
|
845
1110
|
|
846
|
-
|
847
|
-
|
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 |
|
848
1126
|
|
849
|
-
|
850
|
-
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
851
|
-
# No additional auth environment variables needed
|
852
|
-
d365fo-mcp-server
|
853
|
-
```
|
1127
|
+
### Cache Directory Behavior
|
854
1128
|
|
855
|
-
|
856
|
-
For service principal authentication:
|
1129
|
+
By default, the client uses platform-appropriate user cache directories:
|
857
1130
|
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
export AZURE_CLIENT_SECRET="your-client-secret"
|
862
|
-
export AZURE_TENANT_ID="your-tenant-id"
|
863
|
-
d365fo-mcp-server
|
864
|
-
```
|
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`)
|
865
1134
|
|
866
|
-
|
1135
|
+
You can override this by explicitly setting `metadata_cache_dir`:
|
867
1136
|
|
868
|
-
|
1137
|
+
```python
|
1138
|
+
from d365fo_client import FOClientConfig
|
869
1139
|
|
870
|
-
|
871
|
-
|
872
|
-
|
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
|
+
)
|
873
1145
|
|
874
|
-
#
|
875
|
-
|
1146
|
+
# Or get the default cache directory programmatically
|
1147
|
+
from d365fo_client import get_user_cache_dir
|
876
1148
|
|
877
|
-
#
|
878
|
-
|
879
|
-
|
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
|
+
)
|
880
1154
|
```
|
881
1155
|
|
882
|
-
|
1156
|
+
## Testing
|
883
1157
|
|
884
|
-
|
1158
|
+
This project includes comprehensive testing at multiple levels to ensure reliability and quality.
|
885
1159
|
|
886
|
-
|
887
|
-
Add to your Claude Desktop configuration:
|
1160
|
+
### Unit Tests
|
888
1161
|
|
889
|
-
|
890
|
-
{
|
891
|
-
"mcpServers": {
|
892
|
-
"d365fo": {
|
893
|
-
"command": "d365fo-mcp-server",
|
894
|
-
"env": {
|
895
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com" //Optional
|
896
|
-
}
|
897
|
-
}
|
898
|
-
}
|
899
|
-
}
|
900
|
-
```
|
1162
|
+
Run standard unit tests for core functionality:
|
901
1163
|
|
902
|
-
|
1164
|
+
```bash
|
1165
|
+
# Run all unit tests
|
1166
|
+
uv run pytest
|
903
1167
|
|
904
|
-
|
905
|
-
|
1168
|
+
# Run with coverage
|
1169
|
+
uv run pytest --cov=d365fo_client --cov-report=html
|
906
1170
|
|
907
|
-
|
908
|
-
|
909
|
-
"servers": {
|
910
|
-
"d365fo-mcp-server": {
|
911
|
-
"type": "stdio",
|
912
|
-
"command": "uvx",
|
913
|
-
"args": [
|
914
|
-
"--from",
|
915
|
-
"d365fo-client",
|
916
|
-
"d365fo-mcp-server"
|
917
|
-
],
|
918
|
-
"env": {
|
919
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
920
|
-
"D365FO_LOG_LEVEL": "INFO"
|
921
|
-
}
|
922
|
-
}
|
923
|
-
}
|
924
|
-
}
|
1171
|
+
# Run specific test file
|
1172
|
+
uv run pytest tests/test_client.py -v
|
925
1173
|
```
|
926
1174
|
|
927
|
-
|
928
|
-
For environments requiring service principal authentication:
|
1175
|
+
### Integration Tests
|
929
1176
|
|
930
|
-
|
931
|
-
{
|
932
|
-
"servers": {
|
933
|
-
"d365fo-mcp-server": {
|
934
|
-
"type": "stdio",
|
935
|
-
"command": "uvx",
|
936
|
-
"args": [
|
937
|
-
"--from",
|
938
|
-
"d365fo-client",
|
939
|
-
"d365fo-mcp-server"
|
940
|
-
],
|
941
|
-
"env": {
|
942
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
943
|
-
"D365FO_LOG_LEVEL": "DEBUG",
|
944
|
-
"D365FO_CLIENT_ID": "${input:client_id}",
|
945
|
-
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
946
|
-
"D365FO_TENANT_ID": "${input:tenant_id}"
|
947
|
-
}
|
948
|
-
}
|
949
|
-
},
|
950
|
-
"inputs": [
|
951
|
-
{
|
952
|
-
"id": "tenant_id",
|
953
|
-
"type": "promptString",
|
954
|
-
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
955
|
-
"password": true
|
956
|
-
},
|
957
|
-
{
|
958
|
-
"id": "client_id",
|
959
|
-
"type": "promptString",
|
960
|
-
"description": "Azure AD Client ID for D365 F&O authentication",
|
961
|
-
"password": true
|
962
|
-
},
|
963
|
-
{
|
964
|
-
"id": "client_secret",
|
965
|
-
"type": "promptString",
|
966
|
-
"description": "Azure AD Client Secret for D365 F&O authentication",
|
967
|
-
"password": true
|
968
|
-
}
|
969
|
-
]
|
970
|
-
}
|
971
|
-
```
|
1177
|
+
The project includes a sophisticated multi-tier integration testing framework:
|
972
1178
|
|
973
|
-
|
974
|
-
- Always uses the latest version from the repository
|
975
|
-
- No local installation required
|
976
|
-
- Automatic dependency management
|
977
|
-
- Works across different environments
|
1179
|
+
#### Quick Start
|
978
1180
|
|
979
|
-
|
980
|
-
|
1181
|
+
```bash
|
1182
|
+
# Run sandbox integration tests (recommended)
|
1183
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
981
1184
|
|
982
|
-
|
983
|
-
|
1185
|
+
# Run mock server tests (no external dependencies)
|
1186
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
984
1187
|
|
985
|
-
|
986
|
-
|
987
|
-
tools = await client.list_tools()
|
988
|
-
|
989
|
-
# Execute operations
|
990
|
-
result = await client.call_tool(
|
991
|
-
"d365fo_query_entities",
|
992
|
-
{"entityName": "Customers", "top": 5}
|
993
|
-
)
|
1188
|
+
# Run with verbose output
|
1189
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox -VerboseOutput
|
994
1190
|
```
|
995
1191
|
|
996
|
-
|
1192
|
+
#### Test Levels
|
997
1193
|
|
998
|
-
|
999
|
-
-
|
1000
|
-
-
|
1001
|
-
-
|
1002
|
-
- **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
|
1003
1198
|
|
1004
|
-
|
1005
|
-
-
|
1006
|
-
-
|
1007
|
-
-
|
1008
|
-
- **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
|
1009
1203
|
|
1010
|
-
|
1011
|
-
-
|
1012
|
-
-
|
1013
|
-
-
|
1014
|
-
- **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
|
1015
1208
|
|
1016
|
-
|
1209
|
+
#### Configuration
|
1017
1210
|
|
1018
|
-
|
1211
|
+
Set up integration testing with environment variables:
|
1019
1212
|
|
1020
|
-
**Connection Failures**
|
1021
1213
|
```bash
|
1022
|
-
#
|
1023
|
-
|
1214
|
+
# Copy the template and configure
|
1215
|
+
cp tests/integration/.env.template tests/integration/.env
|
1024
1216
|
|
1025
|
-
#
|
1026
|
-
|
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
|
1027
1223
|
```
|
1028
1224
|
|
1029
|
-
|
1225
|
+
#### Available Commands
|
1226
|
+
|
1030
1227
|
```bash
|
1031
|
-
#
|
1032
|
-
|
1228
|
+
# Test environment setup
|
1229
|
+
.\tests\integration\integration-test-simple.ps1 setup
|
1033
1230
|
|
1034
|
-
#
|
1035
|
-
|
1036
|
-
# ... set other variables
|
1037
|
-
d365fo-mcp-server
|
1038
|
-
```
|
1231
|
+
# Dependency checking
|
1232
|
+
.\tests\integration\integration-test-simple.ps1 deps-check
|
1039
1233
|
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
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
|
1044
1238
|
|
1045
|
-
#
|
1046
|
-
|
1047
|
-
|
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
|
1048
1244
|
```
|
1049
1245
|
|
1050
|
-
####
|
1246
|
+
#### Test Coverage
|
1051
1247
|
|
1052
|
-
|
1053
|
-
|
1054
|
-
- **
|
1055
|
-
- **
|
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
|
+
```
|
1056
1283
|
|
1057
1284
|
## Contributing
|
1058
1285
|
|