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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d365fo-client
3
- Version: 0.2.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 Client and MCP Server
43
+ # Dynamics 365 Finance & Operations MCP Server
43
44
 
44
- A comprehensive Python client library and MCP server for Microsoft Dynamics 365 Finance & Operations (D365 F&O) that provides easy access to OData endpoints, metadata operations, label management, and AI assistant integration.
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
- ## Features
47
+ **🚀 One-Click Installation for VS Code:**
47
48
 
48
- - 🔗 **OData Client**: Full CRUD operations on D365 F&O data entities with composite key support
49
- - 📊 **Metadata Management V2**: Enhanced caching system with intelligent synchronization and FTS5 search
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
+ [![Install with UVX in VS Code](https://img.shields.io/badge/VS_Code-Install_D365_FO_MCP_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](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
+ [![Install with UVX in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_D365_FO_MCP_Server-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](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
- ## Installation
63
-
64
- ```bash
65
- # Install from PyPI
66
- pip install d365fo-client
52
+ **🐳 Docker Installation for VS Code:**
67
53
 
68
- # Or install from source
69
- git clone https://github.com/mafzaal/d365fo-client.git
70
- cd d365fo-client
71
- uv sync # Installs with exact dependencies from uv.lock
72
- ```
54
+ [![Install with Docker in VS Code](https://img.shields.io/badge/VS_Code-Install_D365_FO_MCP_Server_(Docker)-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
+ [![Install with Docker in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_D365_FO_MCP_Server_(Docker)-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
- **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.
57
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/d365fo-client?label=Downloads)](https://pypi.org/project/d365fo-client/)
75
58
 
76
- ## Quick Start
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
- ## Command Line Interface (CLI)
61
+ ## MCP Server Overview
79
62
 
80
- 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.
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
- ### Usage
65
+ ### Key Features
83
66
 
84
- ```bash
85
- # Use the installed CLI command
86
- d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]
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
- # Alternative: Module execution
89
- python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
90
- ```
78
+ ### Quick Start
91
79
 
92
- ### Command Categories
80
+ #### Installation and Setup
93
81
 
94
- #### Entity Operations
95
82
  ```bash
96
- # List entities with filtering
97
- d365fo-client entities list --pattern "customer" --limit 10
83
+ # Install d365fo-client with MCP dependencies
84
+ pip install d365fo-client
98
85
 
99
- # Get entity details and schema
100
- d365fo-client entities get CustomersV3 --properties --keys --labels
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
- # CRUD operations
103
- d365fo-client entities create Customers --data '{"CustomerAccount":"US-999","Name":"Test"}'
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
- #### Metadata Operations
109
- ```bash
110
- # Search and discover entities
111
- d365fo-client metadata entities --search "sales" --output json
96
+ #### Integration with AI Assistants
112
97
 
113
- # Get available actions
114
- d365fo-client metadata actions --pattern "calculate" --limit 5
98
+ ##### VS Code Integration (Recommended)
115
99
 
116
- # Enumerate system enumerations
117
- d365fo-client metadata enums --search "status" --output table
100
+ **Option 1: Default Credentials**
101
+ Add to your VS Code `mcp.json` for GitHub Copilot with MCP:
118
102
 
119
- # Synchronize metadata cache
120
- d365fo-client metadata sync --force-refresh
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
- #### Version Information
124
- ```bash
125
- # Get application versions
126
- d365fo-client version app
127
- d365fo-client version platform
128
- d365fo-client version build
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
- #### Label Operations
132
- ```bash
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
- # Search labels by pattern
137
- d365fo-client labels search "customer" --language "en-US"
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
- ### Global Options
141
-
142
- - `--base-url URL` Specify D365 F&O environment URL
143
- - `--profile NAME` Use named configuration profile
144
- - `--output FORMAT` Output format: json, table, csv, yaml (default: table)
145
- - `--verbose` Enable verbose output for debugging
146
- - `--timeout SECONDS` Request timeout (default: 30)
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
- ### Configuration Profiles
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
- Create reusable configurations in `~/.d365fo-client/config.yaml`:
236
+ ##### Claude Desktop Integration
151
237
 
152
- ```yaml
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
- default_profile: "development"
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
- ### Examples
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
- ```bash
172
- # Quick entity discovery
173
- d365fo-client entities list --pattern "cust.*" --output json
265
+ #### Alternative: Programmatic Usage
174
266
 
175
- # Get comprehensive entity information
176
- d365fo-client entities get CustomersV3 --properties --keys --labels --output yaml
267
+ ```python
268
+ from d365fo_client.mcp import D365FOMCPServer
177
269
 
178
- # Search for calculation actions
179
- d365fo-client metadata actions --pattern "calculate|compute" --output table
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
- # Test environment connectivity
182
- d365fo-client version app --verbose
278
+ server = D365FOMCPServer(config)
279
+ await server.run()
183
280
  ```
184
281
 
185
- For a complete command reference:
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 asyncio
196
- from d365fo_client import D365FOClient, FOClientConfig
286
+ from mcp import Client
197
287
 
198
- async def main():
199
- # Simple configuration with default credentials
200
- config = FOClientConfig(
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
- async with D365FOClient(config) as client:
206
- # Test connection
207
- if await client.test_connection():
208
- print(" Connected successfully!")
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
- ### Using Convenience Function
234
-
235
- ```python
236
- from d365fo_client import create_client
299
+ #### Docker Deployment
237
300
 
238
- # Quick client creation with enhanced defaults
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
- ## Configuration
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
- ### Authentication Options
308
+ # Or pull a specific version
309
+ docker pull ghcr.io/mafzaal/d365fo-client:v0.2.3
310
+ ```
246
311
 
247
- ```python
248
- from d365fo_client import FOClientConfig
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
- # Option 1: Default Azure credentials (recommended)
251
- config = FOClientConfig(
252
- base_url="https://your-fo-environment.dynamics.com",
253
- use_default_credentials=True
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
- # Option 2: Client credentials
257
- config = FOClientConfig(
258
- base_url="https://your-fo-environment.dynamics.com",
259
- client_id="your-client-id",
260
- client_secret="your-client-secret",
261
- tenant_id="your-tenant-id",
262
- use_default_credentials=False
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
- # Option 3: With custom settings
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
- ## Core Operations
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
- ### CRUD Operations
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
- ```python
282
- async with D365FOClient(config) as client:
283
- # CREATE - Create new customer (supports composite keys)
284
- new_customer = {
285
- "CustomerAccount": "US-999",
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
- ### Advanced Querying
384
+ ### Troubleshooting
304
385
 
305
- ```python
306
- from d365fo_client import QueryOptions
386
+ #### Common Issues
307
387
 
308
- # Complex query with multiple options
309
- options = QueryOptions(
310
- select=["CustomerAccount", "Name", "SalesCurrencyCode", "CustomerGroupId"],
311
- filter="SalesCurrencyCode eq 'USD' and contains(Name, 'Corp')",
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
- result = await client.get_data("/data/CustomersV3", options)
320
- print(f"Total count: {result.get('@odata.count')}")
393
+ # Check logs
394
+ tail -f ~/.d365fo-mcp/logs/mcp-server.log
321
395
  ```
322
396
 
323
- ### Action Execution
397
+ **Authentication Issues**
398
+ ```bash
399
+ # Verify Azure CLI authentication
400
+ az account show
324
401
 
325
- ```python
326
- # Unbound action
327
- result = await client.post_data("/data/calculateTax", {
328
- "amount": 1000.00,
329
- "taxGroup": "STANDARD"
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
- # Bound action on entity set
333
- result = await client.post_data("/data/CustomersV3/calculateBalances", {
334
- "asOfDate": "2024-12-31"
335
- })
408
+ **Performance Issues**
409
+ ```bash
410
+ # Enable debug logging
411
+ export D365FO_LOG_LEVEL="DEBUG"
336
412
 
337
- # Bound action on specific entity instance
338
- result = await client.post_data("/data/CustomersV3('US-001')/calculateBalance", {
339
- "asOfDate": "2024-12-31"
340
- })
413
+ # Adjust connection settings
414
+ export D365FO_CONNECTION_TIMEOUT="120"
415
+ export D365FO_MAX_CONCURRENT_REQUESTS="5"
341
416
  ```
342
417
 
343
- ### Metadata Operations
418
+ #### Getting Help
344
419
 
345
- ```python
346
- # Intelligent metadata synchronization (v2 system)
347
- sync_manager = await client.get_sync_manager()
348
- await sync_manager.smart_sync()
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
- # Search entities with enhanced filtering
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
- # Get detailed entity information with labels
355
- entity_info = await client.get_public_entity_info("CustomersV3")
356
- if entity_info:
357
- print(f"Entity: {entity_info.name}")
358
- print(f"Label: {entity_info.label_text}")
359
- print(f"Data Service Enabled: {entity_info.data_service_enabled}")
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
- # Search actions with caching
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
- # Get enumeration information
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
- ### Label Operations
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
- ```python
376
- # Get specific label (v2 caching system)
377
- label_text = await client.get_label_text("@SYS13342")
378
- print(f"Label text: {label_text}")
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
- # Get multiple labels efficiently
381
- labels = await client.get_labels_batch([
382
- "@SYS13342", "@SYS9490", "@GLS63332"
383
- ])
384
- for label_id, text in labels.items():
385
- print(f"{label_id}: {text}")
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
- # Enhanced entity info with resolved labels
388
- entity_info = await client.get_public_entity_info_with_labels("CustomersV3")
389
- if entity_info.label_text:
390
- print(f"Entity display name: {entity_info.label_text}")
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
- # Access enhanced properties with labels
393
- for prop in entity_info.enhanced_properties[:5]:
394
- if hasattr(prop, 'label_text') and prop.label_text:
395
- print(f"{prop.name}: {prop.label_text}")
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
- ## Error Handling
523
+ ### Usage Examples
399
524
 
400
- ```python
401
- from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
525
+ #### Basic Tool Execution
402
526
 
403
- try:
404
- async with D365FOClient(config) as client:
405
- customer = await client.get_data("/data/CustomersV3('NON-EXISTENT')")
406
- except ConnectionError as e:
407
- print(f"Connection failed: {e}")
408
- except AuthenticationError as e:
409
- print(f"Authentication failed: {e}")
410
- except D365FOClientError as e:
411
- print(f"Client operation failed: {e}")
412
- print(f"Status code: {e.status_code}")
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
- ## Development
539
+ #### Entity Schema Discovery
417
540
 
418
- ### Setting up Development Environment
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
- ```bash
421
- # Clone the repository
422
- git clone https://github.com/mafzaal/d365fo-client.git
423
- cd d365fo-client
553
+ #### Environment Information
424
554
 
425
- # Install with development dependencies using uv
426
- uv sync --dev
555
+ ```json
556
+ {
557
+ "tool": "d365fo_get_environment_info",
558
+ "arguments": {}
559
+ }
560
+ ```
427
561
 
428
- # Run tests
429
- uv run pytest
562
+ ### Authentication & Configuration
430
563
 
431
- # Run integration tests
432
- .\tests\integration\integration-test-simple.ps1 test-sandbox
564
+ #### Default Credentials (Recommended)
565
+ Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
433
566
 
434
- # Format code
435
- uv run black .
436
- uv run isort .
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
- # Type checking
439
- uv run mypy src/
573
+ #### Explicit Credentials
574
+ For service principal authentication:
440
575
 
441
- # Quality checks
442
- .\make.ps1 quality-check # Windows PowerShell
443
- # or
444
- make quality-check # Unix/Linux/macOS
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
- ### Project Structure
584
+ #### Azure Key Vault Integration (New in v0.2.3)
585
+ For secure credential storage using Azure Key Vault:
448
586
 
449
- ```
450
- d365fo-client/
451
- ├── src/
452
- │ └── d365fo_client/
453
- │ ├── __init__.py # Public API exports
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
- ## Configuration Options
594
+ #### Advanced Configuration
514
595
 
515
- | Option | Type | Default | Description |
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
- ### Cache Directory Behavior
598
+ ```bash
599
+ # Optional: Logging configuration
600
+ export D365FO_LOG_LEVEL="DEBUG"
530
601
 
531
- By default, the client uses platform-appropriate user cache directories:
602
+ # Optional: Cache settings
603
+ export D365FO_CACHE_DIR="/custom/cache/path"
532
604
 
533
- - **Windows**: `%LOCALAPPDATA%\d365fo-client` (e.g., `C:\Users\username\AppData\Local\d365fo-client`)
534
- - **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
535
- - **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)
605
+ # Optional: Performance tuning
606
+ export D365FO_CONNECTION_TIMEOUT="60"
607
+ export D365FO_MAX_CONCURRENT_REQUESTS="10"
608
+ ```
536
609
 
537
- You can override this by explicitly setting `metadata_cache_dir`:
610
+ ## Python Client Library
538
611
 
539
- ```python
540
- from d365fo_client import FOClientConfig
612
+ ### Features
541
613
 
542
- # Use custom cache directory
543
- config = FOClientConfig(
544
- base_url="https://your-fo-environment.dynamics.com",
545
- metadata_cache_dir="/custom/cache/path"
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
- # Or get the default cache directory programmatically
549
- from d365fo_client import get_user_cache_dir
630
+ ### Installation
550
631
 
551
- cache_dir = get_user_cache_dir("my-app") # Platform-appropriate cache dir
552
- config = FOClientConfig(
553
- base_url="https://your-fo-environment.dynamics.com",
554
- metadata_cache_dir=str(cache_dir)
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
- ## Testing
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
- This project includes comprehensive testing at multiple levels to ensure reliability and quality.
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
- ### Unit Tests
661
+ Please update your environment variables accordingly when upgrading.
563
662
 
564
- Run standard unit tests for core functionality:
663
+ ## Python Client Quick Start
565
664
 
566
- ```bash
567
- # Run all unit tests
568
- uv run pytest
665
+ ## Command Line Interface (CLI)
569
666
 
570
- # Run with coverage
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
- # Run specific test file
574
- uv run pytest tests/test_client.py -v
575
- ```
669
+ ### Usage
576
670
 
577
- ### Integration Tests
671
+ ```bash
672
+ # Use the installed CLI command
673
+ d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]
578
674
 
579
- The project includes a sophisticated multi-tier integration testing framework:
675
+ # Alternative: Module execution
676
+ python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
677
+ ```
580
678
 
581
- #### Quick Start
679
+ ### Command Categories
582
680
 
681
+ #### Entity Operations
583
682
  ```bash
584
- # Run sandbox integration tests (recommended)
585
- .\tests\integration\integration-test-simple.ps1 test-sandbox
683
+ # List entities with filtering
684
+ d365fo-client entities list --pattern "customer" --limit 10
586
685
 
587
- # Run mock server tests (no external dependencies)
588
- .\tests\integration\integration-test-simple.ps1 test-mock
686
+ # Get entity details and schema
687
+ d365fo-client entities get CustomersV3 --properties --keys --labels
589
688
 
590
- # Run with verbose output
591
- .\tests\integration\integration-test-simple.ps1 test-sandbox -VerboseOutput
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
- #### Test Levels
695
+ #### Metadata Operations
696
+ ```bash
697
+ # Search and discover entities
698
+ d365fo-client metadata entities --search "sales" --output json
595
699
 
596
- 1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API
597
- - No external dependencies
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
- 2. **Sandbox Tests** ⭐ *(Default)* - Tests against real D365 F&O test environments
602
- - Validates authentication
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
- #### Configuration
706
+ # Synchronize metadata cache
707
+ d365fo-client metadata sync --force-refresh
708
+ ```
612
709
 
613
- Set up integration testing with environment variables:
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
- # Copy the template and configure
617
- cp tests/integration/.env.template tests/integration/.env
720
+ # Resolve single label
721
+ d365fo-client labels resolve "@SYS13342"
618
722
 
619
- # Edit .env file with your settings:
620
- INTEGRATION_TEST_LEVEL=sandbox
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
- #### Available Commands
727
+ ### Global Options
628
728
 
629
- ```bash
630
- # Test environment setup
631
- .\tests\integration\integration-test-simple.ps1 setup
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
- # Dependency checking
634
- .\tests\integration\integration-test-simple.ps1 deps-check
735
+ ### Configuration Profiles
635
736
 
636
- # Run specific test levels
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
- # Coverage and reporting
642
- .\tests\integration\integration-test-simple.ps1 coverage
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
- # Clean up test artifacts
645
- .\tests\integration\integration-test-simple.ps1 clean
753
+ default_profile: "development"
646
754
  ```
647
755
 
648
- #### Test Coverage
756
+ ### Examples
649
757
 
650
- Integration tests cover:
758
+ ```bash
759
+ # Quick entity discovery
760
+ d365fo-client entities list --pattern "cust.*" --output json
651
761
 
652
- - **Connection & Authentication** - Azure AD integration, SSL/TLS validation
653
- - **Version Methods** - Application, platform, and build version retrieval
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
- For detailed information, see [Integration Testing Documentation](tests/integration/README.md).
765
+ # Search for calculation actions
766
+ d365fo-client metadata actions --pattern "calculate|compute" --output table
660
767
 
661
- ### Test Results
768
+ # Test environment connectivity
769
+ d365fo-client version app --verbose
770
+ ```
662
771
 
663
- Recent sandbox integration test results:
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
- 17 passed, 0 failed, 2 warnings in 37.67s
666
- ======================================================
667
- ✅ TestSandboxConnection::test_connection_success
668
- TestSandboxConnection::test_metadata_connection_success
669
- TestSandboxVersionMethods::test_get_application_version
670
- ✅ TestSandboxVersionMethods::test_get_platform_build_version
671
- TestSandboxVersionMethods::test_get_application_build_version
672
- TestSandboxVersionMethods::test_version_consistency
673
- TestSandboxMetadataOperations::test_download_metadata
674
- ✅ TestSandboxMetadataOperations::test_search_entities
675
- TestSandboxMetadataOperations::test_get_data_entities
676
- ✅ TestSandboxMetadataOperations::test_get_public_entities
677
- ✅ TestSandboxDataOperations::test_get_available_entities
678
- TestSandboxDataOperations::test_odata_query_options
679
- TestSandboxAuthentication::test_authenticated_requests
680
- TestSandboxErrorHandling::test_invalid_entity_error
681
- TestSandboxErrorHandling::test_invalid_action_error
682
- ✅ TestSandboxPerformance::test_response_times
683
- TestSandboxPerformance::test_concurrent_operations
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
- ## Model Context Protocol (MCP) Server
820
+ ### Using Convenience Function
687
821
 
688
- 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.
822
+ ```python
823
+ from d365fo_client import create_client
689
824
 
690
- ### Overview
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
- The MCP server provides:
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
- ### Quick Start
832
+ ### Authentication Options
701
833
 
702
- #### Installation and Setup
834
+ ```python
835
+ from d365fo_client import FOClientConfig
703
836
 
704
- ```bash
705
- # Install d365fo-client with MCP dependencies
706
- pip install d365fo-client
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
- # Set up environment variables
709
- export D365FO_BASE_URL="https://your-environment.dynamics.com"
710
- export AZURE_CLIENT_ID="your-client-id" # Optional with default credentials
711
- export AZURE_CLIENT_SECRET="your-client-secret" # Optional with default credentials
712
- export AZURE_TENANT_ID="your-tenant-id" # Optional with default credentials
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
- # Start the MCP server
715
- d365fo-mcp-server
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
- #### Alternative: Programmatic Usage
871
+ ## Core Operations
719
872
 
720
- ```python
721
- from d365fo_client.mcp import D365FOMCPServer
873
+ ### CRUD Operations
722
874
 
723
- # Create and run server with custom configuration
724
- config = {
725
- "default_environment": {
726
- "base_url": "https://your-environment.dynamics.com",
727
- "use_default_credentials": True
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
- server = D365FOMCPServer(config)
732
- await server.run()
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
- ### MCP Tools
917
+ ### Action Execution
736
918
 
737
- The server provides 12 comprehensive tools organized into functional categories:
919
+ ```python
920
+ # Unbound action
921
+ result = await client.post_data("/data/calculateTax", {
922
+ "amount": 1000.00,
923
+ "taxGroup": "STANDARD"
924
+ })
738
925
 
739
- #### Connection Tools (2 tools)
740
- - **`d365fo_test_connection`** - Test environment connectivity and health
741
- - **`d365fo_get_environment_info`** - Get comprehensive environment details, versions, and statistics
926
+ # Bound action on entity set
927
+ result = await client.post_data("/data/CustomersV3/calculateBalances", {
928
+ "asOfDate": "2024-12-31"
929
+ })
742
930
 
743
- #### CRUD Operations (5 tools)
744
- - **`d365fo_query_entities`** - Advanced OData querying with filters, selections, and pagination
745
- - **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options
746
- - **`d365fo_create_entity_record`** - Create new entity records with validation
747
- - **`d365fo_update_entity_record`** - Update existing records with optimistic concurrency
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
- #### Metadata Tools (5 tools)
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
- #### Label Tools (2 tools)
758
- - **`d365fo_get_label`** - Get single label text by ID with language support
759
- - **`d365fo_get_labels_batch`** - Get multiple labels efficiently in batch operations
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
- ### MCP Resources
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
- The server exposes four types of resources for discovery and access:
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
- #### Entity Resources
766
- Access entity metadata and sample data:
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
- #### Metadata Resources
774
- Access system-wide metadata:
775
- ```
776
- d365fo://metadata/entities # All data entities metadata (V2 cache)
777
- d365fo://metadata/actions # Available OData actions
778
- d365fo://metadata/enumerations # System enumerations
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
- #### Environment Resources
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
- #### Query Resources
791
- Access predefined and templated queries:
792
- ```
793
- d365fo://queries/customers_recent # Recent customers query template
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
- #### Database Resources (New in V2)
798
- Access metadata database queries:
799
- ```
800
- d365fo://database/entities # SQL-based entity searches with FTS5
801
- d365fo://database/actions # Action discovery with metadata
802
- d365fo://database/statistics # Cache and performance statistics
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
- ### Usage Examples
992
+ ## Error Handling
806
993
 
807
- #### Basic Tool Execution
994
+ ```python
995
+ from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
808
996
 
809
- ```json
810
- {
811
- "tool": "d365fo_query_entities",
812
- "arguments": {
813
- "entityName": "CustomersV3",
814
- "select": ["CustomerAccount", "Name", "Email"],
815
- "filter": "CustomerGroup eq 'VIP'",
816
- "top": 10
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
- #### Entity Schema Discovery
1010
+ ## Development
822
1011
 
823
- ```json
824
- {
825
- "tool": "d365fo_get_entity_schema",
826
- "arguments": {
827
- "entityName": "CustomersV3",
828
- "includeProperties": true,
829
- "resolveLabels": true,
830
- "language": "en-US"
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
- #### Environment Information
1041
+ ### Project Structure
836
1042
 
837
- ```json
838
- {
839
- "tool": "d365fo_get_environment_info",
840
- "arguments": {}
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
- ### Authentication & Configuration
1109
+ ## Configuration Options
845
1110
 
846
- #### Default Credentials (Recommended)
847
- Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
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
- ```bash
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
- #### Explicit Credentials
856
- For service principal authentication:
1129
+ By default, the client uses platform-appropriate user cache directories:
857
1130
 
858
- ```bash
859
- export D365FO_BASE_URL="https://your-environment.dynamics.com"
860
- export AZURE_CLIENT_ID="your-client-id"
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
- #### Advanced Configuration
1135
+ You can override this by explicitly setting `metadata_cache_dir`:
867
1136
 
868
- Create a configuration file or set additional environment variables:
1137
+ ```python
1138
+ from d365fo_client import FOClientConfig
869
1139
 
870
- ```bash
871
- # Optional: Logging configuration
872
- export D365FO_LOG_LEVEL="DEBUG"
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
- # Optional: Cache settings
875
- export D365FO_CACHE_DIR="/custom/cache/path"
1146
+ # Or get the default cache directory programmatically
1147
+ from d365fo_client import get_user_cache_dir
876
1148
 
877
- # Optional: Performance tuning
878
- export D365FO_CONNECTION_TIMEOUT="60"
879
- export D365FO_MAX_CONCURRENT_REQUESTS="10"
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
- ### Integration with AI Assistants
1156
+ ## Testing
883
1157
 
884
- The MCP server seamlessly integrates with AI assistants and development tools:
1158
+ This project includes comprehensive testing at multiple levels to ensure reliability and quality.
885
1159
 
886
- #### Claude Desktop Integration
887
- Add to your Claude Desktop configuration:
1160
+ ### Unit Tests
888
1161
 
889
- ```json
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
- #### VS Code Integration
1164
+ ```bash
1165
+ # Run all unit tests
1166
+ uv run pytest
903
1167
 
904
- ##### Option 1: Default Credentials (Recommended)
905
- Add to your VS Code `mcp.json` for GitHub Copilot with MCP:
1168
+ # Run with coverage
1169
+ uv run pytest --cov=d365fo_client --cov-report=html
906
1170
 
907
- ```json
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
- ##### Option 2: Explicit Credentials
928
- For environments requiring service principal authentication:
1175
+ ### Integration Tests
929
1176
 
930
- ```json
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
- **Benefits of uvx approach:**
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
- #### Custom MCP Clients
980
- Connect using any MCP-compatible client library:
1181
+ ```bash
1182
+ # Run sandbox integration tests (recommended)
1183
+ .\tests\integration\integration-test-simple.ps1 test-sandbox
981
1184
 
982
- ```python
983
- from mcp import Client
1185
+ # Run mock server tests (no external dependencies)
1186
+ .\tests\integration\integration-test-simple.ps1 test-mock
984
1187
 
985
- async with Client("d365fo-mcp-server") as client:
986
- # Discover available tools
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
- ### Architecture Benefits
1192
+ #### Test Levels
997
1193
 
998
- #### For AI Assistants
999
- - **Standardized Interface**: Consistent MCP protocol access to D365 F&O
1000
- - **Rich Metadata**: Self-describing entities and operations
1001
- - **Type Safety**: Schema validation for all operations
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
- #### For Developers
1005
- - **Minimal Integration**: Standard MCP client libraries
1006
- - **Comprehensive Coverage**: Full D365 F&O functionality exposed
1007
- - **Performance Optimized**: Efficient connection and caching strategies
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
- #### For Organizations
1011
- - **Secure Access**: Enterprise-grade authentication (Azure AD, Managed Identity)
1012
- - **Audit Logging**: Complete operation tracking and monitoring
1013
- - **Scalable Design**: Connection pooling and session management
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
- ### Troubleshooting
1209
+ #### Configuration
1017
1210
 
1018
- #### Common Issues
1211
+ Set up integration testing with environment variables:
1019
1212
 
1020
- **Connection Failures**
1021
1213
  ```bash
1022
- # Test connectivity
1023
- d365fo-client get-version --base-url https://your-environment.dynamics.com
1214
+ # Copy the template and configure
1215
+ cp tests/integration/.env.template tests/integration/.env
1024
1216
 
1025
- # Check logs
1026
- tail -f ~/.d365fo-mcp/logs/mcp-server.log
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
- **Authentication Issues**
1225
+ #### Available Commands
1226
+
1030
1227
  ```bash
1031
- # Verify Azure CLI authentication
1032
- az account show
1228
+ # Test environment setup
1229
+ .\tests\integration\integration-test-simple.ps1 setup
1033
1230
 
1034
- # Test with explicit credentials
1035
- export AZURE_CLIENT_ID="your-client-id"
1036
- # ... set other variables
1037
- d365fo-mcp-server
1038
- ```
1231
+ # Dependency checking
1232
+ .\tests\integration\integration-test-simple.ps1 deps-check
1039
1233
 
1040
- **Performance Issues**
1041
- ```bash
1042
- # Enable debug logging
1043
- export D365FO_LOG_LEVEL="DEBUG"
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
- # Adjust connection settings
1046
- export D365FO_CONNECTION_TIMEOUT="120"
1047
- export D365FO_MAX_CONCURRENT_REQUESTS="5"
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
- #### Getting Help
1246
+ #### Test Coverage
1051
1247
 
1052
- - **Logs**: Check `~/.d365fo-mcp/logs/mcp-server.log` for detailed error information
1053
- - **Environment**: Use `d365fo_get_environment_info` tool to check system status
1054
- - **Documentation**: See [MCP Implementation Summary](docs/MCP_IMPLEMENTATION_SUMMARY.md) for technical details
1055
- - **Issues**: Report problems at [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)
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