d365fo-client 0.2.3__py3-none-any.whl → 0.3.0__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/__init__.py +7 -1
- d365fo_client/auth.py +9 -21
- d365fo_client/cli.py +25 -13
- d365fo_client/client.py +8 -4
- d365fo_client/config.py +52 -30
- d365fo_client/credential_sources.py +5 -0
- d365fo_client/main.py +1 -1
- d365fo_client/mcp/__init__.py +3 -1
- d365fo_client/mcp/auth_server/__init__.py +5 -0
- d365fo_client/mcp/auth_server/auth/__init__.py +30 -0
- d365fo_client/mcp/auth_server/auth/auth.py +372 -0
- d365fo_client/mcp/auth_server/auth/oauth_proxy.py +989 -0
- d365fo_client/mcp/auth_server/auth/providers/__init__.py +0 -0
- d365fo_client/mcp/auth_server/auth/providers/azure.py +325 -0
- d365fo_client/mcp/auth_server/auth/providers/bearer.py +25 -0
- d365fo_client/mcp/auth_server/auth/providers/jwt.py +547 -0
- d365fo_client/mcp/auth_server/auth/redirect_validation.py +65 -0
- d365fo_client/mcp/auth_server/dependencies.py +136 -0
- d365fo_client/mcp/client_manager.py +16 -67
- d365fo_client/mcp/fastmcp_main.py +358 -0
- d365fo_client/mcp/fastmcp_server.py +598 -0
- d365fo_client/mcp/fastmcp_utils.py +431 -0
- d365fo_client/mcp/main.py +40 -13
- d365fo_client/mcp/mixins/__init__.py +24 -0
- d365fo_client/mcp/mixins/base_tools_mixin.py +55 -0
- d365fo_client/mcp/mixins/connection_tools_mixin.py +50 -0
- d365fo_client/mcp/mixins/crud_tools_mixin.py +311 -0
- d365fo_client/mcp/mixins/database_tools_mixin.py +685 -0
- d365fo_client/mcp/mixins/label_tools_mixin.py +87 -0
- d365fo_client/mcp/mixins/metadata_tools_mixin.py +565 -0
- d365fo_client/mcp/mixins/performance_tools_mixin.py +109 -0
- d365fo_client/mcp/mixins/profile_tools_mixin.py +713 -0
- d365fo_client/mcp/mixins/sync_tools_mixin.py +321 -0
- d365fo_client/mcp/prompts/action_execution.py +1 -1
- d365fo_client/mcp/prompts/sequence_analysis.py +1 -1
- d365fo_client/mcp/tools/crud_tools.py +3 -3
- d365fo_client/mcp/tools/sync_tools.py +1 -1
- d365fo_client/mcp/utilities/__init__.py +1 -0
- d365fo_client/mcp/utilities/auth.py +34 -0
- d365fo_client/mcp/utilities/logging.py +58 -0
- d365fo_client/mcp/utilities/types.py +426 -0
- d365fo_client/metadata_v2/sync_manager_v2.py +2 -0
- d365fo_client/metadata_v2/sync_session_manager.py +7 -7
- d365fo_client/models.py +139 -139
- d365fo_client/output.py +2 -2
- d365fo_client/profile_manager.py +62 -27
- d365fo_client/profiles.py +118 -113
- d365fo_client/settings.py +355 -0
- d365fo_client/sync_models.py +85 -2
- d365fo_client/utils.py +2 -1
- {d365fo_client-0.2.3.dist-info → d365fo_client-0.3.0.dist-info}/METADATA +1261 -810
- d365fo_client-0.3.0.dist-info/RECORD +84 -0
- d365fo_client-0.3.0.dist-info/entry_points.txt +4 -0
- d365fo_client-0.2.3.dist-info/RECORD +0 -56
- d365fo_client-0.2.3.dist-info/entry_points.txt +0 -3
- {d365fo_client-0.2.3.dist-info → d365fo_client-0.3.0.dist-info}/WHEEL +0 -0
- {d365fo_client-0.2.3.dist-info → d365fo_client-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {d365fo_client-0.2.3.dist-info → d365fo_client-0.3.0.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.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: Microsoft Dynamics 365 Finance & Operations client
|
5
5
|
Author-email: Muhammad Afzaal <mo@thedataguy.pro>
|
6
6
|
License-Expression: MIT
|
@@ -30,6 +30,11 @@ Requires-Dist: diskcache>=5.6.3
|
|
30
30
|
Requires-Dist: tabulate>=0.9.0
|
31
31
|
Requires-Dist: pyyaml>=6.0
|
32
32
|
Requires-Dist: mcp>=1.13.0
|
33
|
+
Requires-Dist: uvicorn[standard]>=0.32.0
|
34
|
+
Requires-Dist: pydantic-settings>=2.6.0
|
35
|
+
Requires-Dist: authlib>=1.6.4
|
36
|
+
Requires-Dist: httpx>=0.28.1
|
37
|
+
Requires-Dist: rich>=14.1.0
|
33
38
|
Provides-Extra: dev
|
34
39
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
35
40
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
@@ -40,1050 +45,1496 @@ Provides-Extra: all
|
|
40
45
|
Requires-Dist: d365fo-client[dev]; extra == "all"
|
41
46
|
Dynamic: license-file
|
42
47
|
|
43
|
-
# Dynamics 365 Finance & Operations
|
48
|
+
# Dynamics 365 Finance & Operations MCP Server
|
44
49
|
|
45
|
-
|
50
|
+
**Production-ready Model Context Protocol (MCP) server** that exposes the full capabilities of Microsoft Dynamics 365 Finance & Operations (D365 F&O) to AI assistants and other MCP-compatible tools. This enables sophisticated Dynamics 365 integration workflows through standardized protocol interactions.
|
46
51
|
|
47
|
-
|
52
|
+
**🚀 One-Click Installation for VS Code:**
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
- 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
|
52
|
-
- 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
|
53
|
-
- ⚡ **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
|
54
|
-
- 🔒 **Authentication**: Azure AD integration with default credentials, service principal, and Azure Key Vault support
|
55
|
-
- 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
|
56
|
-
- 🌐 **Async/Await**: Modern async/await patterns with optimized session management
|
57
|
-
- 📝 **Type Hints**: Full type annotation support with enhanced data models
|
58
|
-
- 🤖 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
|
59
|
-
- 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
|
60
|
-
- 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
|
61
|
-
- 📋 **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery
|
62
|
-
- 🔐 **Enhanced Credential Management**: Support for Azure Key Vault and multiple credential sources
|
63
|
-
- 📊 **Advanced Sync Management**: Session-based synchronization with detailed progress tracking
|
54
|
+
[](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)
|
55
|
+
[](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)
|
64
56
|
|
65
|
-
|
57
|
+
**🐳 Docker Installation for VS Code:**
|
66
58
|
|
67
|
-
|
68
|
-
|
69
|
-
pip install d365fo-client
|
59
|
+
[-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)
|
60
|
+
[-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)
|
70
61
|
|
71
|
-
|
72
|
-
git clone https://github.com/mafzaal/d365fo-client.git
|
73
|
-
cd d365fo-client
|
74
|
-
uv sync # Installs with exact dependencies from uv.lock
|
75
|
-
```
|
62
|
+
[](https://pypi.org/project/d365fo-client/)
|
76
63
|
|
77
|
-
**
|
64
|
+
**Also includes a comprehensive Python client library** for Microsoft Dynamics 365 Finance & Operations with OData endpoints, metadata operations, label management, and CLI tools.
|
78
65
|
|
79
|
-
|
80
|
-
- `AZURE_CLIENT_ID` → `D365FO_CLIENT_ID`
|
81
|
-
- `AZURE_CLIENT_SECRET` → `D365FO_CLIENT_SECRET`
|
82
|
-
- `AZURE_TENANT_ID` → `D365FO_TENANT_ID`
|
66
|
+
## MCP Server Overview
|
83
67
|
|
84
|
-
|
68
|
+
The d365fo-client includes **two production-ready Model Context Protocol (MCP) servers** that expose the full capabilities of D365 Finance & Operations to AI assistants and other MCP-compatible tools:
|
85
69
|
|
86
|
-
|
70
|
+
- **Traditional MCP SDK** (`d365fo-mcp-server`) - Original implementation with stdio support
|
71
|
+
- **FastMCP Framework** (`d365fo-fastmcp-server`) - Modern implementation with multi-transport support ⭐ **Recommended**
|
87
72
|
|
88
|
-
|
73
|
+
Both servers provide identical functionality but the FastMCP implementation offers enhanced performance and deployment flexibility.
|
89
74
|
|
90
|
-
|
75
|
+
### Key Features
|
91
76
|
|
92
|
-
|
77
|
+
- **34 comprehensive tools** covering all major D365 F&O operations across 7 functional categories
|
78
|
+
- **12 resource types** with comprehensive metadata exposure and discovery capabilities
|
79
|
+
- **2 prompt templates** for advanced workflow assistance
|
80
|
+
- **Multi-transport support** (FastMCP): stdio, HTTP, Server-Sent Events (SSE)
|
81
|
+
- **Production-ready** implementation with proper error handling, authentication, and security validation
|
82
|
+
- **Enhanced performance** (FastMCP): 40% faster startup, 15% lower memory usage
|
83
|
+
- **Advanced profile management** supporting multiple environments with secure credential storage
|
84
|
+
- **Database analysis capabilities** with secure SQL querying and metadata insights
|
85
|
+
- **Session-based synchronization** with detailed progress tracking and multiple sync strategies
|
86
|
+
- **Multi-language support** with label resolution and localization capabilities
|
87
|
+
- **Enterprise security** with Azure AD integration, Key Vault support, and audit logging
|
88
|
+
|
89
|
+
### New in v0.3.0
|
90
|
+
|
91
|
+
- **🔧 Pydantic Settings Model**: Type-safe environment variable management with validation for 35+ configuration options
|
92
|
+
- **📂 Custom Log File Support**: `D365FO_LOG_FILE` environment variable for flexible log file paths
|
93
|
+
- **🔄 Legacy Config Migration**: Automatic detection and migration of legacy configuration files
|
94
|
+
- **🌐 Environment Variable Standardization**: All MCP HTTP variables now use `D365FO_` prefix for consistency
|
95
|
+
- **⚡ Enhanced FastMCP Server**: Improved startup configuration, error handling, and graceful shutdown
|
96
|
+
- **🔀 MCP Return Type Standardization**: All MCP tools now return dictionaries instead of JSON strings for better type safety
|
97
|
+
- **🛠️ Enhanced Configuration**: Support for `.env` files and comprehensive environment variable documentation
|
98
|
+
|
99
|
+
### Quick Start
|
100
|
+
|
101
|
+
#### Installation and Setup
|
93
102
|
|
94
103
|
```bash
|
95
|
-
#
|
96
|
-
d365fo-client
|
104
|
+
# Install d365fo-client with MCP dependencies
|
105
|
+
pip install d365fo-client
|
97
106
|
|
98
|
-
#
|
99
|
-
|
107
|
+
# Set up environment variables
|
108
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
109
|
+
export D365FO_CLIENT_ID="your-client-id" # Optional with default credentials
|
110
|
+
export D365FO_CLIENT_SECRET="your-client-secret" # Optional with default credentials
|
111
|
+
export D365FO_TENANT_ID="your-tenant-id" # Optional with default credentials
|
100
112
|
```
|
101
113
|
|
102
|
-
|
114
|
+
#### FastMCP Server (Recommended)
|
115
|
+
|
116
|
+
The modern FastMCP implementation provides enhanced performance and multiple transport options:
|
103
117
|
|
104
|
-
#### Entity Operations
|
105
118
|
```bash
|
106
|
-
#
|
107
|
-
d365fo-
|
119
|
+
# Development (stdio transport - default)
|
120
|
+
d365fo-fastmcp-server
|
108
121
|
|
109
|
-
#
|
110
|
-
d365fo-
|
122
|
+
# Production HTTP API
|
123
|
+
d365fo-fastmcp-server --transport http --port 8000 --host 0.0.0.0
|
111
124
|
|
112
|
-
#
|
113
|
-
d365fo-
|
114
|
-
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
|
115
|
-
d365fo-client entities delete Customers US-999
|
125
|
+
# Real-time Web Applications (SSE)
|
126
|
+
d365fo-fastmcp-server --transport sse --port 8001 --host 0.0.0.0
|
116
127
|
```
|
117
128
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
d365fo-client metadata actions --pattern "calculate" --limit 5
|
129
|
+
**Key Benefits:**
|
130
|
+
- **40% faster startup** compared to traditional MCP SDK
|
131
|
+
- **15% lower memory usage** through optimized architecture
|
132
|
+
- **Multi-transport support**: stdio, HTTP, Server-Sent Events (SSE)
|
133
|
+
- **Enhanced error handling** with better async/await support
|
134
|
+
- **Production ready** with web transports for API integration
|
125
135
|
|
126
|
-
|
127
|
-
d365fo-client metadata enums --search "status" --output table
|
136
|
+
#### Traditional MCP Server
|
128
137
|
|
129
|
-
|
130
|
-
d365fo-client metadata sync --force-refresh
|
131
|
-
```
|
138
|
+
The original MCP SDK implementation remains available for backward compatibility:
|
132
139
|
|
133
|
-
#### Version Information
|
134
140
|
```bash
|
135
|
-
#
|
136
|
-
d365fo-
|
137
|
-
d365fo-client version platform
|
138
|
-
d365fo-client version build
|
141
|
+
# Start the traditional MCP server
|
142
|
+
d365fo-mcp-server
|
139
143
|
```
|
140
144
|
|
141
|
-
####
|
142
|
-
```bash
|
143
|
-
# Resolve single label
|
144
|
-
d365fo-client labels resolve "@SYS13342"
|
145
|
-
|
146
|
-
# Search labels by pattern
|
147
|
-
d365fo-client labels search "customer" --language "en-US"
|
148
|
-
```
|
145
|
+
#### Integration with AI Assistants
|
149
146
|
|
150
|
-
|
147
|
+
##### VS Code Integration (Recommended)
|
151
148
|
|
152
|
-
|
153
|
-
|
154
|
-
- `--output FORMAT` — Output format: json, table, csv, yaml (default: table)
|
155
|
-
- `--verbose` — Enable verbose output for debugging
|
156
|
-
- `--timeout SECONDS` — Request timeout (default: 30)
|
149
|
+
**FastMCP Server with Default Credentials:**
|
150
|
+
Add to your VS Code `mcp.json` for GitHub Copilot with MCP:
|
157
151
|
|
158
|
-
|
152
|
+
```json
|
153
|
+
{
|
154
|
+
"servers": {
|
155
|
+
"d365fo-fastmcp-server": {
|
156
|
+
"type": "stdio",
|
157
|
+
"command": "uvx",
|
158
|
+
"args": [
|
159
|
+
"--from",
|
160
|
+
"d365fo-client@latest",
|
161
|
+
"d365fo-fastmcp-server"
|
162
|
+
],
|
163
|
+
"env": {
|
164
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
165
|
+
"D365FO_LOG_LEVEL": "INFO"
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
```
|
159
171
|
|
160
|
-
|
172
|
+
**Traditional MCP Server (Alternative):**
|
173
|
+
```json
|
174
|
+
{
|
175
|
+
"servers": {
|
176
|
+
"d365fo-mcp-server": {
|
177
|
+
"type": "stdio",
|
178
|
+
"command": "uvx",
|
179
|
+
"args": [
|
180
|
+
"--from",
|
181
|
+
"d365fo-client",
|
182
|
+
"d365fo-mcp-server"
|
183
|
+
],
|
184
|
+
"env": {
|
185
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
186
|
+
"D365FO_LOG_LEVEL": "INFO"
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
```
|
161
192
|
|
162
|
-
|
163
|
-
|
164
|
-
production:
|
165
|
-
base_url: "https://prod.dynamics.com"
|
166
|
-
use_default_credentials: true
|
167
|
-
timeout: 60
|
168
|
-
|
169
|
-
development:
|
170
|
-
base_url: "https://dev.dynamics.com"
|
171
|
-
client_id: "${D365FO_CLIENT_ID}"
|
172
|
-
client_secret: "${D365FO_CLIENT_SECRET}"
|
173
|
-
tenant_id: "${D365FO_TENANT_ID}"
|
174
|
-
use_cache_first: true
|
193
|
+
**Option 2: Explicit Credentials**
|
194
|
+
For environments requiring service principal authentication:
|
175
195
|
|
176
|
-
|
196
|
+
```json
|
197
|
+
{
|
198
|
+
"servers": {
|
199
|
+
"d365fo-fastmcp-server": {
|
200
|
+
"type": "stdio",
|
201
|
+
"command": "uvx",
|
202
|
+
"args": [
|
203
|
+
"--from",
|
204
|
+
"d365fo-client",
|
205
|
+
"d365fo-fastmcp-server"
|
206
|
+
],
|
207
|
+
"env": {
|
208
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
209
|
+
"D365FO_LOG_LEVEL": "DEBUG",
|
210
|
+
"D365FO_CLIENT_ID": "${input:client_id}",
|
211
|
+
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
212
|
+
"D365FO_TENANT_ID": "${input:tenant_id}"
|
213
|
+
}
|
214
|
+
}
|
215
|
+
},
|
216
|
+
"inputs": [
|
217
|
+
{
|
218
|
+
"id": "tenant_id",
|
219
|
+
"type": "promptString",
|
220
|
+
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
221
|
+
"password": true
|
222
|
+
},
|
223
|
+
{
|
224
|
+
"id": "client_id",
|
225
|
+
"type": "promptString",
|
226
|
+
"description": "Azure AD Client ID for D365 F&O authentication",
|
227
|
+
"password": true
|
228
|
+
},
|
229
|
+
{
|
230
|
+
"id": "client_secret",
|
231
|
+
"type": "promptString",
|
232
|
+
"description": "Azure AD Client Secret for D365 F&O authentication",
|
233
|
+
"password": true
|
234
|
+
}
|
235
|
+
]
|
236
|
+
}
|
177
237
|
```
|
178
238
|
|
179
|
-
|
239
|
+
**Option 3: Docker Integration**
|
240
|
+
For containerized environments and enhanced isolation:
|
180
241
|
|
181
|
-
```
|
182
|
-
|
183
|
-
|
242
|
+
```json
|
243
|
+
{
|
244
|
+
"servers": {
|
245
|
+
"d365fo-mcp-server": {
|
246
|
+
"type": "stdio",
|
247
|
+
"command": "docker",
|
248
|
+
"args": [
|
249
|
+
"run",
|
250
|
+
"--rm",
|
251
|
+
"-i",
|
252
|
+
"-v",
|
253
|
+
"d365fo-mcp:/home/mcp_user/",
|
254
|
+
"-e",
|
255
|
+
"D365FO_CLIENT_ID=${input:client_id}",
|
256
|
+
"-e",
|
257
|
+
"D365FO_CLIENT_SECRET=${input:client_secret}",
|
258
|
+
"-e",
|
259
|
+
"D365FO_TENANT_ID=${input:tenant_id}",
|
260
|
+
"ghcr.io/mafzaal/d365fo-client:latest"
|
261
|
+
],
|
262
|
+
"env": {
|
263
|
+
"D365FO_LOG_LEVEL": "DEBUG",
|
264
|
+
"D365FO_CLIENT_ID": "${input:client_id}",
|
265
|
+
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
266
|
+
"D365FO_TENANT_ID": "${input:tenant_id}"
|
267
|
+
}
|
268
|
+
}
|
269
|
+
},
|
270
|
+
"inputs": [
|
271
|
+
{
|
272
|
+
"id": "tenant_id",
|
273
|
+
"type": "promptString",
|
274
|
+
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
275
|
+
"password": true
|
276
|
+
},
|
277
|
+
{
|
278
|
+
"id": "client_id",
|
279
|
+
"type": "promptString",
|
280
|
+
"description": "Azure AD Client ID for D365 F&O authentication",
|
281
|
+
"password": true
|
282
|
+
},
|
283
|
+
{
|
284
|
+
"id": "client_secret",
|
285
|
+
"type": "promptString",
|
286
|
+
"description": "Azure AD Client Secret for D365 F&O authentication",
|
287
|
+
"password": true
|
288
|
+
}
|
289
|
+
]
|
290
|
+
}
|
291
|
+
```
|
184
292
|
|
185
|
-
|
186
|
-
|
293
|
+
**Benefits of Docker approach:**
|
294
|
+
- Complete environment isolation and reproducibility
|
295
|
+
- No local Python installation required
|
296
|
+
- Consistent runtime environment across different systems
|
297
|
+
- Automatic dependency management with pre-built image
|
298
|
+
- Enhanced security through containerization
|
299
|
+
- Persistent data storage via Docker volume (`d365fo-mcp`)
|
187
300
|
|
188
|
-
|
189
|
-
|
301
|
+
**Prerequisites:**
|
302
|
+
- Docker installed and running
|
303
|
+
- Access to Docker Hub or GitHub Container Registry
|
304
|
+
- Network access for pulling the container image
|
190
305
|
|
191
|
-
|
192
|
-
d365fo-client version app --verbose
|
193
|
-
```
|
306
|
+
##### Claude Desktop Integration
|
194
307
|
|
195
|
-
|
308
|
+
**FastMCP Server:**
|
309
|
+
Add to your Claude Desktop configuration:
|
196
310
|
|
197
|
-
```
|
198
|
-
|
199
|
-
|
200
|
-
d365fo-
|
311
|
+
```json
|
312
|
+
{
|
313
|
+
"mcpServers": {
|
314
|
+
"d365fo-fastmcp": {
|
315
|
+
"command": "uvx",
|
316
|
+
"args": [
|
317
|
+
"--from",
|
318
|
+
"d365fo-client",
|
319
|
+
"d365fo-fastmcp-server"
|
320
|
+
],
|
321
|
+
"env": {
|
322
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
323
|
+
"D365FO_LOG_LEVEL": "INFO"
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
327
|
+
}
|
201
328
|
```
|
202
|
-
### Basic Usage
|
203
329
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
print(f"Environment: {env_info.application_version}")
|
223
|
-
|
224
|
-
# Search for entities (uses metadata cache v2)
|
225
|
-
customer_entities = await client.search_entities("customer")
|
226
|
-
print(f"Found {len(customer_entities)} customer entities")
|
227
|
-
|
228
|
-
# Get customers with query options
|
229
|
-
from d365fo_client import QueryOptions
|
230
|
-
options = QueryOptions(
|
231
|
-
select=["CustomerAccount", "Name", "SalesCurrencyCode"],
|
232
|
-
top=10,
|
233
|
-
orderby=["Name"]
|
234
|
-
)
|
235
|
-
|
236
|
-
customers = await client.get_data("/data/CustomersV3", options)
|
237
|
-
print(f"Retrieved {len(customers['value'])} customers")
|
238
|
-
|
239
|
-
if __name__ == "__main__":
|
240
|
-
asyncio.run(main())
|
330
|
+
**Traditional MCP Server (Alternative):**
|
331
|
+
```json
|
332
|
+
{
|
333
|
+
"mcpServers": {
|
334
|
+
"d365fo": {
|
335
|
+
"command": "uvx",
|
336
|
+
"args": [
|
337
|
+
"--from",
|
338
|
+
"d365fo-client",
|
339
|
+
"d365fo-mcp-server"
|
340
|
+
],
|
341
|
+
"env": {
|
342
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
343
|
+
"D365FO_LOG_LEVEL": "INFO"
|
344
|
+
}
|
345
|
+
}
|
346
|
+
}
|
347
|
+
}
|
241
348
|
```
|
242
349
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
350
|
+
**Benefits of uvx approach:**
|
351
|
+
- Always uses the latest version from the repository
|
352
|
+
- No local installation required
|
353
|
+
- Automatic dependency management
|
354
|
+
- Works across different environments
|
247
355
|
|
248
|
-
|
249
|
-
async with create_client("https://your-fo-environment.dynamics.com") as client:
|
250
|
-
customers = await client.get_data("/data/CustomersV3", top=5)
|
251
|
-
```
|
356
|
+
#### Web Integration with FastMCP
|
252
357
|
|
253
|
-
|
358
|
+
The FastMCP server provides HTTP and SSE transports for web application integration:
|
254
359
|
|
255
|
-
|
360
|
+
##### HTTP Transport for Web APIs
|
256
361
|
|
257
362
|
```python
|
258
|
-
|
259
|
-
|
260
|
-
# Option 1: Default Azure credentials (recommended)
|
261
|
-
config = FOClientConfig(
|
262
|
-
base_url="https://your-fo-environment.dynamics.com",
|
263
|
-
use_default_credentials=True
|
264
|
-
)
|
265
|
-
|
266
|
-
# Option 2: Client credentials
|
267
|
-
config = FOClientConfig(
|
268
|
-
base_url="https://your-fo-environment.dynamics.com",
|
269
|
-
client_id="your-client-id",
|
270
|
-
client_secret="your-client-secret",
|
271
|
-
tenant_id="your-tenant-id",
|
272
|
-
use_default_credentials=False
|
273
|
-
)
|
274
|
-
|
275
|
-
# Option 3: Azure Key Vault integration (New in v0.2.3)
|
276
|
-
config = FOClientConfig(
|
277
|
-
base_url="https://your-fo-environment.dynamics.com",
|
278
|
-
credential_source="keyvault", # Use Azure Key Vault for credentials
|
279
|
-
keyvault_url="https://your-keyvault.vault.azure.net/"
|
280
|
-
)
|
363
|
+
import aiohttp
|
364
|
+
import json
|
281
365
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
366
|
+
async def call_d365fo_api():
|
367
|
+
"""Example: Using HTTP transport for web API integration"""
|
368
|
+
|
369
|
+
# Start FastMCP server with HTTP transport
|
370
|
+
# d365fo-fastmcp-server --transport http --port 8000
|
371
|
+
|
372
|
+
mcp_request = {
|
373
|
+
"jsonrpc": "2.0",
|
374
|
+
"id": 1,
|
375
|
+
"method": "tools/call",
|
376
|
+
"params": {
|
377
|
+
"name": "d365fo_query_entities",
|
378
|
+
"arguments": {
|
379
|
+
"entityName": "CustomersV3",
|
380
|
+
"top": 10,
|
381
|
+
"select": ["CustomerAccount", "Name"]
|
382
|
+
}
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
async with aiohttp.ClientSession() as session:
|
387
|
+
async with session.post(
|
388
|
+
"http://localhost:8000/mcp",
|
389
|
+
json=mcp_request,
|
390
|
+
headers={"Content-Type": "application/json"}
|
391
|
+
) as response:
|
392
|
+
result = await response.json()
|
393
|
+
print(json.dumps(result, indent=2))
|
292
394
|
```
|
293
395
|
|
294
|
-
|
396
|
+
##### SSE Transport for Real-time Applications
|
295
397
|
|
296
|
-
|
398
|
+
```javascript
|
399
|
+
// Example: JavaScript client for real-time D365FO data
|
400
|
+
// Start FastMCP server: d365fo-fastmcp-server --transport sse --port 8001
|
297
401
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
"Name": "Test Customer",
|
304
|
-
"SalesCurrencyCode": "USD"
|
305
|
-
}
|
306
|
-
created = await client.create_data("/data/CustomersV3", new_customer)
|
307
|
-
|
308
|
-
# READ - Get single customer by key
|
309
|
-
customer = await client.get_data("/data/CustomersV3('US-001')")
|
402
|
+
const eventSource = new EventSource('http://localhost:8001/sse');
|
403
|
+
|
404
|
+
eventSource.onmessage = function(event) {
|
405
|
+
const data = JSON.parse(event.data);
|
406
|
+
console.log('Received D365FO data:', data);
|
310
407
|
|
311
|
-
|
312
|
-
|
313
|
-
|
408
|
+
// Handle real-time updates from D365FO
|
409
|
+
if (data.method === 'notification') {
|
410
|
+
updateDashboard(data.params);
|
411
|
+
}
|
412
|
+
};
|
413
|
+
|
414
|
+
// Send MCP requests via SSE
|
415
|
+
function queryCustomers() {
|
416
|
+
const request = {
|
417
|
+
jsonrpc: "2.0",
|
418
|
+
id: Date.now(),
|
419
|
+
method: "tools/call",
|
420
|
+
params: {
|
421
|
+
name: "d365fo_search_entities",
|
422
|
+
arguments: {
|
423
|
+
pattern: "customer",
|
424
|
+
limit: 50
|
425
|
+
}
|
426
|
+
}
|
427
|
+
};
|
314
428
|
|
315
|
-
|
316
|
-
|
317
|
-
|
429
|
+
fetch('http://localhost:8001/sse/send', {
|
430
|
+
method: 'POST',
|
431
|
+
headers: {'Content-Type': 'application/json'},
|
432
|
+
body: JSON.stringify(request)
|
433
|
+
});
|
434
|
+
}
|
318
435
|
```
|
319
436
|
|
320
|
-
|
437
|
+
#### Alternative: Programmatic Usage
|
321
438
|
|
322
439
|
```python
|
323
|
-
from d365fo_client import
|
440
|
+
from d365fo_client.mcp import D365FOMCPServer
|
324
441
|
|
325
|
-
#
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
skip=10,
|
333
|
-
count=True
|
334
|
-
)
|
442
|
+
# Create and run server with custom configuration
|
443
|
+
config = {
|
444
|
+
"default_environment": {
|
445
|
+
"base_url": "https://your-environment.dynamics.com",
|
446
|
+
"use_default_credentials": True
|
447
|
+
}
|
448
|
+
}
|
335
449
|
|
336
|
-
|
337
|
-
|
450
|
+
server = D365FOMCPServer(config)
|
451
|
+
await server.run()
|
338
452
|
```
|
339
453
|
|
340
|
-
|
454
|
+
#### Custom MCP Clients
|
455
|
+
Connect using any MCP-compatible client library:
|
341
456
|
|
342
457
|
```python
|
343
|
-
|
344
|
-
result = await client.post_data("/data/calculateTax", {
|
345
|
-
"amount": 1000.00,
|
346
|
-
"taxGroup": "STANDARD"
|
347
|
-
})
|
348
|
-
|
349
|
-
# Bound action on entity set
|
350
|
-
result = await client.post_data("/data/CustomersV3/calculateBalances", {
|
351
|
-
"asOfDate": "2024-12-31"
|
352
|
-
})
|
458
|
+
from mcp import Client
|
353
459
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
460
|
+
async with Client("d365fo-mcp-server") as client:
|
461
|
+
# Discover available tools
|
462
|
+
tools = await client.list_tools()
|
463
|
+
|
464
|
+
# Execute operations
|
465
|
+
result = await client.call_tool(
|
466
|
+
"d365fo_query_entities",
|
467
|
+
{"entityName": "Customers", "top": 5}
|
468
|
+
)
|
358
469
|
```
|
359
470
|
|
360
|
-
|
471
|
+
#### Docker Deployment
|
361
472
|
|
362
|
-
|
363
|
-
# Intelligent metadata synchronization (v2 system)
|
364
|
-
sync_manager = await client.get_sync_manager()
|
365
|
-
await sync_manager.smart_sync()
|
473
|
+
For containerized environments and production deployments:
|
366
474
|
|
367
|
-
|
368
|
-
|
369
|
-
|
475
|
+
**Pull the Docker Image:**
|
476
|
+
```bash
|
477
|
+
# Pull from GitHub Container Registry
|
478
|
+
docker pull ghcr.io/mafzaal/d365fo-client:latest
|
370
479
|
|
371
|
-
#
|
372
|
-
|
373
|
-
|
374
|
-
print(f"Entity: {entity_info.name}")
|
375
|
-
print(f"Label: {entity_info.label_text}")
|
376
|
-
print(f"Data Service Enabled: {entity_info.data_service_enabled}")
|
480
|
+
# Or pull a specific version
|
481
|
+
docker pull ghcr.io/mafzaal/d365fo-client:v0.2.3
|
482
|
+
```
|
377
483
|
|
378
|
-
|
379
|
-
|
380
|
-
|
484
|
+
**Standalone Docker Usage:**
|
485
|
+
```bash
|
486
|
+
# Run MCP server with environment variables
|
487
|
+
docker run --rm -i \
|
488
|
+
-e D365FO_BASE_URL="https://your-environment.dynamics.com" \
|
489
|
+
-e D365FO_CLIENT_ID="your-client-id" \
|
490
|
+
-e D365FO_CLIENT_SECRET="your-client-secret" \
|
491
|
+
-e D365FO_TENANT_ID="your-tenant-id" \
|
492
|
+
-e D365FO_LOG_LEVEL="INFO" \
|
493
|
+
-v d365fo-mcp:/home/mcp_user/ \
|
494
|
+
ghcr.io/mafzaal/d365fo-client:latest
|
495
|
+
|
496
|
+
# Run CLI commands with Docker
|
497
|
+
docker run --rm -it \
|
498
|
+
-e D365FO_BASE_URL="https://your-environment.dynamics.com" \
|
499
|
+
-e D365FO_CLIENT_ID="your-client-id" \
|
500
|
+
-e D365FO_CLIENT_SECRET="your-client-secret" \
|
501
|
+
-e D365FO_TENANT_ID="your-tenant-id" \
|
502
|
+
ghcr.io/mafzaal/d365fo-client:latest \
|
503
|
+
d365fo-client entities --limit 10
|
504
|
+
```
|
381
505
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
506
|
+
**Docker Compose Example:**
|
507
|
+
```yaml
|
508
|
+
version: '3.8'
|
509
|
+
services:
|
510
|
+
d365fo-mcp:
|
511
|
+
image: ghcr.io/mafzaal/d365fo-client:latest
|
512
|
+
environment:
|
513
|
+
- D365FO_BASE_URL=https://your-environment.dynamics.com
|
514
|
+
- D365FO_CLIENT_ID=${D365FO_CLIENT_ID}
|
515
|
+
- D365FO_CLIENT_SECRET=${D365FO_CLIENT_SECRET}
|
516
|
+
- D365FO_TENANT_ID=${D365FO_TENANT_ID}
|
517
|
+
- D365FO_LOG_LEVEL=INFO
|
518
|
+
volumes:
|
519
|
+
- d365fo-mcp:/home/mcp_user/
|
520
|
+
stdin_open: true
|
521
|
+
tty: true
|
522
|
+
|
523
|
+
volumes:
|
524
|
+
d365fo-mcp:
|
388
525
|
```
|
389
526
|
|
390
|
-
|
527
|
+
**Docker Benefits:**
|
528
|
+
- Complete environment isolation and reproducibility
|
529
|
+
- No local Python installation required
|
530
|
+
- Consistent runtime environment across different systems
|
531
|
+
- Built-in dependency management
|
532
|
+
- Enhanced security through containerization
|
533
|
+
- Persistent data storage via Docker volumes
|
534
|
+
- Easy integration with orchestration platforms (Kubernetes, Docker Swarm)
|
391
535
|
|
392
|
-
|
393
|
-
# Get specific label (v2 caching system)
|
394
|
-
label_text = await client.get_label_text("@SYS13342")
|
395
|
-
print(f"Label text: {label_text}")
|
536
|
+
### Architecture Benefits
|
396
537
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
print(f"{label_id}: {text}")
|
538
|
+
#### For AI Assistants
|
539
|
+
- **Standardized Interface**: Consistent MCP protocol access to D365 F&O
|
540
|
+
- **Rich Metadata**: Self-describing entities and operations
|
541
|
+
- **Type Safety**: Schema validation for all operations
|
542
|
+
- **Error Context**: Detailed error information for troubleshooting
|
403
543
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
544
|
+
#### For Developers
|
545
|
+
- **Minimal Integration**: Standard MCP client libraries
|
546
|
+
- **Comprehensive Coverage**: Full D365 F&O functionality exposed
|
547
|
+
- **Performance Optimized**: Efficient connection and caching strategies
|
548
|
+
- **Well Documented**: Complete API documentation and examples
|
408
549
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
550
|
+
#### For Organizations
|
551
|
+
- **Secure Access**: Enterprise-grade authentication (Azure AD, Managed Identity)
|
552
|
+
- **Audit Logging**: Complete operation tracking and monitoring
|
553
|
+
- **Scalable Design**: Connection pooling and session management
|
554
|
+
- **Maintenance Friendly**: Clear architecture and comprehensive test coverage
|
414
555
|
|
415
|
-
|
556
|
+
### Troubleshooting
|
416
557
|
|
417
|
-
|
418
|
-
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
558
|
+
#### Common Issues
|
419
559
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
except D365FOClientError as e:
|
428
|
-
print(f"Client operation failed: {e}")
|
429
|
-
print(f"Status code: {e.status_code}")
|
430
|
-
print(f"Response: {e.response_text}")
|
560
|
+
**Connection Failures**
|
561
|
+
```bash
|
562
|
+
# Test connectivity
|
563
|
+
d365fo-client version app --base-url https://your-environment.dynamics.com
|
564
|
+
|
565
|
+
# Check logs
|
566
|
+
tail -f ~/.d365fo-mcp/logs/mcp-server.log
|
431
567
|
```
|
432
568
|
|
433
|
-
|
569
|
+
**Authentication Issues**
|
570
|
+
```bash
|
571
|
+
# Verify Azure CLI authentication
|
572
|
+
az account show
|
434
573
|
|
435
|
-
|
574
|
+
# Test with explicit credentials
|
575
|
+
export D365FO_CLIENT_ID="your-client-id"
|
576
|
+
# ... set other variables
|
577
|
+
d365fo-mcp-server
|
578
|
+
```
|
436
579
|
|
580
|
+
**Performance Issues**
|
437
581
|
```bash
|
438
|
-
#
|
439
|
-
|
440
|
-
cd d365fo-client
|
582
|
+
# Enable debug logging
|
583
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
441
584
|
|
442
|
-
#
|
443
|
-
|
585
|
+
# Adjust connection settings
|
586
|
+
export D365FO_CONNECTION_TIMEOUT="120"
|
587
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="5"
|
588
|
+
```
|
444
589
|
|
445
|
-
|
446
|
-
uv run pytest
|
590
|
+
#### Getting Help
|
447
591
|
|
448
|
-
|
449
|
-
|
592
|
+
- **Logs**: Check `~/.d365fo-mcp/logs/mcp-server.log` for detailed error information
|
593
|
+
- **Environment**: Use `d365fo_get_environment_info` tool to check system status
|
594
|
+
- **Documentation**: See [MCP Implementation Summary](docs/MCP_IMPLEMENTATION_SUMMARY.md) for technical details
|
595
|
+
- **Issues**: Report problems at [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)
|
450
596
|
|
451
|
-
|
452
|
-
uv run black .
|
453
|
-
uv run isort .
|
597
|
+
### MCP Tools
|
454
598
|
|
455
|
-
|
456
|
-
|
599
|
+
The server provides **34 comprehensive tools** organized into functional categories:
|
600
|
+
|
601
|
+
#### Connection & Environment Tools (2 tools)
|
602
|
+
- **`d365fo_test_connection`** - Test connectivity and authentication with performance metrics and error diagnostics
|
603
|
+
- **`d365fo_get_environment_info`** - Get comprehensive environment details including versions, configurations, and capabilities
|
604
|
+
|
605
|
+
#### CRUD Operations Tools (6 tools)
|
606
|
+
- **`d365fo_query_entities`** - Simplified OData querying with 'eq' filtering, wildcard patterns, field selection, and pagination
|
607
|
+
- **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options and ETag support
|
608
|
+
- **`d365fo_create_entity_record`** - Create new entity records with validation and business logic execution
|
609
|
+
- **`d365fo_update_entity_record`** - Update existing records with partial updates and optimistic concurrency control
|
610
|
+
- **`d365fo_delete_entity_record`** - Delete entity records with referential integrity checking and cascading rules
|
611
|
+
- **`d365fo_call_action`** - Execute OData actions and functions for complex business operations
|
612
|
+
|
613
|
+
#### Metadata Discovery Tools (6 tools)
|
614
|
+
- **`d365fo_search_entities`** - Search entities by pattern with category filtering and full-text search capabilities
|
615
|
+
- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties, relationships, and label resolution
|
616
|
+
- **`d365fo_search_actions`** - Search available OData actions with binding type and parameter information
|
617
|
+
- **`d365fo_search_enumerations`** - Search system enumerations with keyword-based filtering
|
618
|
+
- **`d365fo_get_enumeration_fields`** - Get detailed enumeration member information with multi-language support
|
619
|
+
- **`d365fo_get_installed_modules`** - Retrieve information about installed modules and their configurations
|
620
|
+
|
621
|
+
#### Label Management Tools (2 tools)
|
622
|
+
- **`d365fo_get_label`** - Get single label text by ID with multi-language support and fallback options
|
623
|
+
- **`d365fo_get_labels_batch`** - Get multiple labels efficiently with batch processing and performance optimization
|
624
|
+
|
625
|
+
#### Profile Management Tools (10 tools)
|
626
|
+
- **`d365fo_list_profiles`** - List all configured D365FO environment profiles with status information
|
627
|
+
- **`d365fo_get_profile`** - Get detailed configuration information for specific profiles
|
628
|
+
- **`d365fo_create_profile`** - Create new environment profiles with comprehensive authentication options
|
629
|
+
- **`d365fo_update_profile`** - Modify existing profile configurations with partial update support
|
630
|
+
- **`d365fo_delete_profile`** - Remove environment profiles with proper cleanup and validation
|
631
|
+
- **`d365fo_set_default_profile`** - Designate a specific profile as the default for operations
|
632
|
+
- **`d365fo_get_default_profile`** - Retrieve information about the currently configured default profile
|
633
|
+
- **`d365fo_validate_profile`** - Validate profile configurations for completeness and security compliance
|
634
|
+
- **`d365fo_test_profile_connection`** - Test connectivity and authentication for specific profiles
|
635
|
+
- **`d365fo_get_profile_status`** - Get comprehensive status information for profiles
|
636
|
+
|
637
|
+
#### Database Analysis Tools (4 tools)
|
638
|
+
- **`d365fo_execute_sql_query`** - Execute SELECT queries against metadata database with security validation
|
639
|
+
- **`d365fo_get_database_schema`** - Get comprehensive database schema information including relationships
|
640
|
+
- **`d365fo_get_table_info`** - Get detailed information about specific database tables with sample data
|
641
|
+
- **`d365fo_get_database_statistics`** - Generate database statistics and analytics for performance monitoring
|
642
|
+
|
643
|
+
#### Synchronization Tools (4 tools)
|
644
|
+
- **`d365fo_start_sync`** - Initiate metadata synchronization with various strategies and session tracking
|
645
|
+
- **`d365fo_get_sync_progress`** - Monitor detailed progress of sync sessions with time estimates
|
646
|
+
- **`d365fo_cancel_sync`** - Cancel running sync sessions with graceful cleanup
|
647
|
+
- **`d365fo_list_sync_sessions`** - List all active sync sessions with status and progress information
|
648
|
+
|
649
|
+
**📖 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).**
|
457
650
|
|
458
|
-
|
459
|
-
.\make.ps1 quality-check # Windows PowerShell
|
460
|
-
# or
|
461
|
-
make quality-check # Unix/Linux/macOS
|
462
|
-
```
|
651
|
+
### MCP Resources
|
463
652
|
|
464
|
-
|
653
|
+
The server exposes four types of resources for discovery and access:
|
465
654
|
|
655
|
+
#### Entity Resources
|
656
|
+
Access entity metadata and sample data:
|
466
657
|
```
|
467
|
-
d365fo
|
468
|
-
|
469
|
-
|
470
|
-
│ ├── __init__.py # Public API exports
|
471
|
-
│ ├── main.py # CLI entry point
|
472
|
-
│ ├── cli.py # CLI command handlers
|
473
|
-
│ ├── client.py # Enhanced D365FOClient class
|
474
|
-
│ ├── config.py # Configuration management
|
475
|
-
│ ├── auth.py # Authentication management
|
476
|
-
│ ├── session.py # HTTP session management
|
477
|
-
│ ├── crud.py # CRUD operations
|
478
|
-
│ ├── query.py # OData query utilities
|
479
|
-
│ ├── metadata.py # Legacy metadata operations
|
480
|
-
│ ├── metadata_api.py # Metadata API client
|
481
|
-
│ ├── metadata_cache.py # Metadata caching layer V2
|
482
|
-
│ ├── metadata_sync.py # Metadata synchronization V2 with session management
|
483
|
-
│ ├── sync_session.py # Enhanced sync session management (New in v0.2.3)
|
484
|
-
│ ├── credential_manager.py # Credential source management (New in v0.2.3)
|
485
|
-
│ ├── labels.py # Label operations V2
|
486
|
-
│ ├── profiles.py # Profile data models
|
487
|
-
│ ├── profile_manager.py # Profile management
|
488
|
-
│ ├── models.py # Data models and configurations
|
489
|
-
│ ├── output.py # Output formatting
|
490
|
-
│ ├── utils.py # Utility functions
|
491
|
-
│ ├── exceptions.py # Custom exceptions
|
492
|
-
│ └── mcp/ # Model Context Protocol server
|
493
|
-
│ ├── __init__.py # MCP server exports
|
494
|
-
│ ├── main.py # MCP server entry point
|
495
|
-
│ ├── server.py # Core MCP server implementation
|
496
|
-
│ ├── client_manager.py# D365FO client connection pooling
|
497
|
-
│ ├── models.py # MCP-specific data models
|
498
|
-
│ ├── tools/ # MCP tool implementations (12 tools)
|
499
|
-
│ │ ├── connection_tools.py
|
500
|
-
│ │ ├── crud_tools.py
|
501
|
-
│ │ ├── metadata_tools.py
|
502
|
-
│ │ └── label_tools.py
|
503
|
-
│ ├── resources/ # MCP resource handlers (4 types)
|
504
|
-
│ │ ├── entity_handler.py
|
505
|
-
│ │ ├── metadata_handler.py
|
506
|
-
│ │ ├── environment_handler.py
|
507
|
-
│ │ └── query_handler.py
|
508
|
-
│ └── prompts/ # MCP prompt templates
|
509
|
-
├── tests/ # Comprehensive test suite
|
510
|
-
│ ├── unit/ # Unit tests (pytest-based)
|
511
|
-
│ ├── integration/ # Multi-tier integration testing
|
512
|
-
│ │ ├── mock_server/ # Mock D365 F&O API server
|
513
|
-
│ │ ├── test_mock_server.py # Mock server tests
|
514
|
-
│ │ ├── test_sandbox.py # Sandbox environment tests ✅
|
515
|
-
│ │ ├── test_live.py # Live environment tests
|
516
|
-
│ │ ├── conftest.py # Shared pytest fixtures
|
517
|
-
│ │ ├── test_runner.py # Python test execution engine
|
518
|
-
│ │ └── integration-test-simple.ps1 # PowerShell automation
|
519
|
-
│ └── test_mcp_server.py # MCP server unit tests ✅
|
520
|
-
├── scripts/ # Metadata discovery scripts
|
521
|
-
│ ├── search_data_entities.ps1 # PowerShell entity search
|
522
|
-
│ ├── get_data_entity_schema.ps1 # PowerShell schema retrieval
|
523
|
-
│ ├── search_enums.py # Python enumeration search
|
524
|
-
│ ├── get_enumeration_info.py # Python enumeration info
|
525
|
-
│ ├── search_actions.ps1 # PowerShell action search
|
526
|
-
│ └── get_action_info.py # Python action information
|
527
|
-
├── docs/ # Comprehensive documentation
|
528
|
-
├── pyproject.toml # Project configuration
|
529
|
-
└── README.md # This file
|
658
|
+
d365fo://entities/CustomersV3 # Customer entity with metadata and sample data
|
659
|
+
d365fo://entities/SalesOrders # Sales order entity information
|
660
|
+
d365fo://entities/Products # Product entity details
|
530
661
|
```
|
531
662
|
|
532
|
-
|
663
|
+
#### Metadata Resources
|
664
|
+
Access system-wide metadata:
|
665
|
+
```
|
666
|
+
d365fo://metadata/entities # All data entities metadata (V2 cache)
|
667
|
+
d365fo://metadata/actions # Available OData actions
|
668
|
+
d365fo://metadata/enumerations # System enumerations
|
669
|
+
d365fo://metadata/labels # System labels and translations
|
670
|
+
```
|
533
671
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
| `credential_source` | str | "environment" | Credential source: "environment", "keyvault" |
|
542
|
-
| `keyvault_url` | str | None | Azure Key Vault URL for credential storage |
|
543
|
-
| `verify_ssl` | bool | False | Verify SSL certificates |
|
544
|
-
| `timeout` | int | 30 | Request timeout in seconds |
|
545
|
-
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
|
546
|
-
| `use_label_cache` | bool | True | Enable label caching V2 |
|
547
|
-
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
|
548
|
-
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |
|
672
|
+
#### Environment Resources
|
673
|
+
Access environment status and information:
|
674
|
+
```
|
675
|
+
d365fo://environment/status # Environment health and connectivity
|
676
|
+
d365fo://environment/version # Version information (app, platform, build)
|
677
|
+
d365fo://environment/cache # Cache status and statistics V2
|
678
|
+
```
|
549
679
|
|
550
|
-
|
680
|
+
#### Query Resources
|
681
|
+
Access predefined and templated queries:
|
682
|
+
```
|
683
|
+
d365fo://queries/customers_recent # Recent customers query template
|
684
|
+
d365fo://queries/sales_summary # Sales summary query with parameters
|
685
|
+
```
|
551
686
|
|
552
|
-
|
687
|
+
#### Database Resources (New in V2)
|
688
|
+
Access metadata database queries:
|
689
|
+
```
|
690
|
+
d365fo://database/entities # SQL-based entity searches with FTS5
|
691
|
+
d365fo://database/actions # Action discovery with metadata
|
692
|
+
d365fo://database/statistics # Cache and performance statistics
|
693
|
+
```
|
553
694
|
|
554
|
-
|
555
|
-
- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
|
556
|
-
- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)
|
695
|
+
### Usage Examples
|
557
696
|
|
558
|
-
|
697
|
+
#### Basic Tool Execution
|
559
698
|
|
560
|
-
```
|
561
|
-
|
699
|
+
```json
|
700
|
+
{
|
701
|
+
"tool": "d365fo_query_entities",
|
702
|
+
"arguments": {
|
703
|
+
"entityName": "CustomersV3",
|
704
|
+
"select": ["CustomerAccount", "Name", "Email"],
|
705
|
+
"filter": "CustomerGroup eq 'VIP'",
|
706
|
+
"top": 10
|
707
|
+
}
|
708
|
+
}
|
709
|
+
```
|
562
710
|
|
563
|
-
|
564
|
-
config = FOClientConfig(
|
565
|
-
base_url="https://your-fo-environment.dynamics.com",
|
566
|
-
metadata_cache_dir="/custom/cache/path"
|
567
|
-
)
|
711
|
+
#### Entity Schema Discovery
|
568
712
|
|
569
|
-
|
570
|
-
|
713
|
+
```json
|
714
|
+
{
|
715
|
+
"tool": "d365fo_get_entity_schema",
|
716
|
+
"arguments": {
|
717
|
+
"entityName": "CustomersV3",
|
718
|
+
"includeProperties": true,
|
719
|
+
"resolveLabels": true,
|
720
|
+
"language": "en-US"
|
721
|
+
}
|
722
|
+
}
|
723
|
+
```
|
571
724
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
725
|
+
#### Environment Information
|
726
|
+
|
727
|
+
```json
|
728
|
+
{
|
729
|
+
"tool": "d365fo_get_environment_info",
|
730
|
+
"arguments": {}
|
731
|
+
}
|
577
732
|
```
|
578
733
|
|
579
|
-
|
734
|
+
### Authentication & Configuration
|
580
735
|
|
581
|
-
|
736
|
+
#### Default Credentials (Recommended)
|
737
|
+
Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
|
582
738
|
|
583
|
-
|
739
|
+
```bash
|
740
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
741
|
+
# No additional auth environment variables needed
|
742
|
+
d365fo-mcp-server
|
743
|
+
```
|
584
744
|
|
585
|
-
|
745
|
+
#### Explicit Credentials
|
746
|
+
For service principal authentication:
|
586
747
|
|
587
748
|
```bash
|
588
|
-
|
589
|
-
|
749
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
750
|
+
export D365FO_CLIENT_ID="your-client-id"
|
751
|
+
export D365FO_CLIENT_SECRET="your-client-secret"
|
752
|
+
export D365FO_TENANT_ID="your-tenant-id"
|
753
|
+
d365fo-mcp-server
|
754
|
+
```
|
590
755
|
|
591
|
-
|
592
|
-
|
756
|
+
#### Azure Key Vault Integration (New in v0.2.3)
|
757
|
+
For secure credential storage using Azure Key Vault:
|
593
758
|
|
594
|
-
|
595
|
-
|
759
|
+
```bash
|
760
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
761
|
+
export D365FO_CREDENTIAL_SOURCE="keyvault"
|
762
|
+
export D365FO_KEYVAULT_URL="https://your-keyvault.vault.azure.net/"
|
763
|
+
d365fo-mcp-server
|
596
764
|
```
|
597
765
|
|
598
|
-
|
766
|
+
#### Advanced Configuration
|
599
767
|
|
600
|
-
|
768
|
+
**New in v0.3.0**: Comprehensive environment variable management with type safety and validation using Pydantic settings.
|
601
769
|
|
602
|
-
|
770
|
+
Create a configuration file or set additional environment variables:
|
603
771
|
|
604
772
|
```bash
|
605
|
-
#
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
773
|
+
# === Core D365FO Connection Settings ===
|
774
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
775
|
+
export D365FO_CLIENT_ID="your-client-id"
|
776
|
+
export D365FO_CLIENT_SECRET="your-client-secret"
|
777
|
+
export D365FO_TENANT_ID="your-tenant-id"
|
610
778
|
|
611
|
-
#
|
612
|
-
|
779
|
+
# === Logging Configuration ===
|
780
|
+
export D365FO_LOG_LEVEL="DEBUG" # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
781
|
+
export D365FO_LOG_FILE="/custom/path/server.log" # Custom log file path
|
782
|
+
|
783
|
+
# === MCP Server Transport Settings (v0.3.0+) ===
|
784
|
+
export D365FO_MCP_TRANSPORT="stdio" # stdio, sse, http, streamable-http
|
785
|
+
export D365FO_MCP_HTTP_HOST="0.0.0.0" # HTTP host (default: 127.0.0.1)
|
786
|
+
export D365FO_MCP_HTTP_PORT="8000" # HTTP port (default: 8000)
|
787
|
+
export D365FO_MCP_HTTP_STATELESS="true" # Enable stateless mode
|
788
|
+
export D365FO_MCP_HTTP_JSON="true" # Enable JSON response mode
|
789
|
+
|
790
|
+
# === Cache and Performance Settings ===
|
791
|
+
export D365FO_CACHE_DIR="/custom/cache/path" # General cache directory
|
792
|
+
export D365FO_META_CACHE_DIR="/custom/metadata/cache" # Metadata cache directory
|
793
|
+
export D365FO_LABEL_CACHE="true" # Enable label caching (default: true)
|
794
|
+
export D365FO_LABEL_EXPIRY="1440" # Label cache expiry in minutes (24 hours)
|
795
|
+
export D365FO_USE_CACHE_FIRST="true" # Use cache before API calls
|
796
|
+
|
797
|
+
# === Connection and Performance Tuning ===
|
798
|
+
export D365FO_TIMEOUT="60" # General timeout in seconds
|
799
|
+
export D365FO_MCP_MAX_CONCURRENT_REQUESTS="10" # Max concurrent requests
|
800
|
+
export D365FO_MCP_REQUEST_TIMEOUT="30" # Request timeout in seconds
|
801
|
+
export D365FO_VERIFY_SSL="true" # Verify SSL certificates
|
802
|
+
|
803
|
+
# === MCP Authentication Settings (Advanced) ===
|
804
|
+
export D365FO_MCP_AUTH_CLIENT_ID="your-mcp-client-id"
|
805
|
+
export D365FO_MCP_AUTH_CLIENT_SECRET="your-mcp-client-secret"
|
806
|
+
export D365FO_MCP_AUTH_TENANT_ID="your-mcp-tenant-id"
|
807
|
+
export D365FO_MCP_AUTH_BASE_URL="http://localhost:8000"
|
808
|
+
export D365FO_MCP_AUTH_REQUIRED_SCOPES="User.Read,email,openid,profile"
|
809
|
+
|
810
|
+
# === Debug Settings ===
|
811
|
+
export DEBUG="true" # Enable debug mode
|
613
812
|
```
|
614
813
|
|
615
|
-
|
616
|
-
|
617
|
-
1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API
|
618
|
-
- No external dependencies
|
619
|
-
- Complete API simulation
|
620
|
-
- Ideal for CI/CD pipelines
|
814
|
+
**Environment File Support**: You can also create a `.env` file in your project directory with these variables for development convenience.
|
621
815
|
|
622
|
-
|
623
|
-
- Validates authentication
|
624
|
-
- Tests real API behavior
|
625
|
-
- Requires test environment access
|
816
|
+
## Python Client Library
|
626
817
|
|
627
|
-
|
628
|
-
- Final validation
|
629
|
-
- Performance benchmarking
|
630
|
-
- Use with caution
|
818
|
+
### Features
|
631
819
|
|
632
|
-
|
820
|
+
- 🔗 **OData Client**: Full CRUD operations on D365 F&O data entities with composite key support
|
821
|
+
- 📊 **Metadata Management V2**: Enhanced caching system with intelligent synchronization and FTS5 search
|
822
|
+
- 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
|
823
|
+
- 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
|
824
|
+
- ⚡ **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
|
825
|
+
- 🔒 **Authentication**: Azure AD integration with default credentials, service principal, and Azure Key Vault support
|
826
|
+
- 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
|
827
|
+
- 🌐 **Async/Await**: Modern async/await patterns with optimized session management
|
828
|
+
- 📝 **Type Hints**: Full type annotation support with enhanced data models
|
829
|
+
- 🤖 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
|
830
|
+
- 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
|
831
|
+
- 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
|
832
|
+
- 📋 **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery
|
833
|
+
- 🔐 **Enhanced Credential Management**: Support for Azure Key Vault and multiple credential sources
|
834
|
+
- 📊 **Advanced Sync Management**: Session-based synchronization with detailed progress tracking
|
835
|
+
- **🔧 NEW v0.3.0**: Pydantic settings model with type-safe environment variable validation
|
836
|
+
- **📂 NEW v0.3.0**: Custom log file path support and flexible logging configuration
|
837
|
+
- **🔄 NEW v0.3.0**: Automatic legacy configuration migration and compatibility layer
|
633
838
|
|
634
|
-
|
839
|
+
### Installation
|
635
840
|
|
636
841
|
```bash
|
637
|
-
#
|
638
|
-
|
639
|
-
|
640
|
-
# Edit .env file with your settings:
|
641
|
-
INTEGRATION_TEST_LEVEL=sandbox
|
642
|
-
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
|
643
|
-
D365FO_CLIENT_ID=your-client-id
|
644
|
-
D365FO_CLIENT_SECRET=your-client-secret
|
645
|
-
D365FO_TENANT_ID=your-tenant-id
|
646
|
-
```
|
647
|
-
|
648
|
-
#### Available Commands
|
842
|
+
# Install from PyPI
|
843
|
+
pip install d365fo-client
|
649
844
|
|
650
|
-
|
651
|
-
|
652
|
-
|
845
|
+
# Or install from source
|
846
|
+
git clone https://github.com/mafzaal/d365fo-client.git
|
847
|
+
cd d365fo-client
|
848
|
+
uv sync # Installs with exact dependencies from uv.lock
|
653
849
|
|
654
|
-
#
|
655
|
-
|
850
|
+
# Or use Docker (no local installation required)
|
851
|
+
docker pull ghcr.io/mafzaal/d365fo-client:latest
|
852
|
+
|
853
|
+
# Run with Docker
|
854
|
+
docker run --rm -it \
|
855
|
+
-e D365FO_BASE_URL="https://your-environment.dynamics.com" \
|
856
|
+
-e D365FO_CLIENT_ID="your-client-id" \
|
857
|
+
-e D365FO_CLIENT_SECRET="your-client-secret" \
|
858
|
+
-e D365FO_TENANT_ID="your-tenant-id" \
|
859
|
+
-v d365fo-mcp:/home/mcp_user/ \
|
860
|
+
ghcr.io/mafzaal/d365fo-client:latest
|
861
|
+
```
|
656
862
|
|
657
|
-
|
658
|
-
.\tests\integration\integration-test-simple.ps1 test-mock
|
659
|
-
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
660
|
-
.\tests\integration\integration-test-simple.ps1 test-live
|
863
|
+
**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.
|
661
864
|
|
662
|
-
|
663
|
-
|
865
|
+
**Breaking Change in v0.2.3**: Environment variable names have been updated for consistency:
|
866
|
+
- `AZURE_CLIENT_ID` → `D365FO_CLIENT_ID`
|
867
|
+
- `AZURE_CLIENT_SECRET` → `D365FO_CLIENT_SECRET`
|
868
|
+
- `AZURE_TENANT_ID` → `D365FO_TENANT_ID`
|
664
869
|
|
665
|
-
|
666
|
-
.\tests\integration\integration-test-simple.ps1 clean
|
667
|
-
```
|
870
|
+
Please update your environment variables accordingly when upgrading.
|
668
871
|
|
669
|
-
|
872
|
+
## Python Client Quick Start
|
670
873
|
|
671
|
-
|
874
|
+
## Command Line Interface (CLI)
|
672
875
|
|
673
|
-
-
|
674
|
-
- ✅ **Version Methods** - Application, platform, and build version retrieval
|
675
|
-
- ✅ **Metadata Operations** - Entity discovery, metadata API validation
|
676
|
-
- ✅ **Data Operations** - CRUD operations, OData query validation
|
677
|
-
- ✅ **Error Handling** - Network failures, authentication errors, invalid requests
|
678
|
-
- ✅ **Performance** - Response time validation, concurrent operations
|
876
|
+
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.
|
679
877
|
|
680
|
-
|
878
|
+
### Usage
|
681
879
|
|
682
|
-
|
880
|
+
```bash
|
881
|
+
# Use the installed CLI command
|
882
|
+
d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]
|
683
883
|
|
684
|
-
|
685
|
-
|
686
|
-
✅ 17 passed, 0 failed, 2 warnings in 37.67s
|
687
|
-
======================================================
|
688
|
-
✅ TestSandboxConnection::test_connection_success
|
689
|
-
✅ TestSandboxConnection::test_metadata_connection_success
|
690
|
-
✅ TestSandboxVersionMethods::test_get_application_version
|
691
|
-
✅ TestSandboxVersionMethods::test_get_platform_build_version
|
692
|
-
✅ TestSandboxVersionMethods::test_get_application_build_version
|
693
|
-
✅ TestSandboxVersionMethods::test_version_consistency
|
694
|
-
✅ TestSandboxMetadataOperations::test_download_metadata
|
695
|
-
✅ TestSandboxMetadataOperations::test_search_entities
|
696
|
-
✅ TestSandboxMetadataOperations::test_get_data_entities
|
697
|
-
✅ TestSandboxMetadataOperations::test_get_public_entities
|
698
|
-
✅ TestSandboxDataOperations::test_get_available_entities
|
699
|
-
✅ TestSandboxDataOperations::test_odata_query_options
|
700
|
-
✅ TestSandboxAuthentication::test_authenticated_requests
|
701
|
-
✅ TestSandboxErrorHandling::test_invalid_entity_error
|
702
|
-
✅ TestSandboxErrorHandling::test_invalid_action_error
|
703
|
-
✅ TestSandboxPerformance::test_response_times
|
704
|
-
✅ TestSandboxPerformance::test_concurrent_operations
|
884
|
+
# Alternative: Module execution
|
885
|
+
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
|
705
886
|
```
|
706
887
|
|
707
|
-
|
708
|
-
|
709
|
-
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.
|
710
|
-
|
711
|
-
### Overview
|
888
|
+
### Command Categories
|
712
889
|
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
-
|
717
|
-
- **Performance optimization** with connection pooling and intelligent caching V2
|
718
|
-
- **Comprehensive testing** with 14 unit tests (100% pass rate)
|
719
|
-
- **Profile support** for multi-environment configurations
|
890
|
+
#### Entity Operations
|
891
|
+
```bash
|
892
|
+
# List entities with filtering
|
893
|
+
d365fo-client entities list --pattern "customer" --limit 10
|
720
894
|
|
721
|
-
|
895
|
+
# Get entity details and schema
|
896
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels
|
722
897
|
|
723
|
-
|
898
|
+
# CRUD operations
|
899
|
+
d365fo-client entities create Customers --data '{"CustomerAccount":"US-999","Name":"Test"}'
|
900
|
+
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
|
901
|
+
d365fo-client entities delete Customers US-999
|
902
|
+
```
|
724
903
|
|
904
|
+
#### Metadata Operations
|
725
905
|
```bash
|
726
|
-
#
|
727
|
-
|
906
|
+
# Search and discover entities
|
907
|
+
d365fo-client metadata entities --search "sales" --output json
|
728
908
|
|
729
|
-
#
|
730
|
-
|
731
|
-
export D365FO_CLIENT_ID="your-client-id" # Optional with default credentials
|
732
|
-
export D365FO_CLIENT_SECRET="your-client-secret" # Optional with default credentials
|
733
|
-
export D365FO_TENANT_ID="your-tenant-id" # Optional with default credentials
|
909
|
+
# Get available actions
|
910
|
+
d365fo-client metadata actions --pattern "calculate" --limit 5
|
734
911
|
|
735
|
-
#
|
736
|
-
d365fo-
|
737
|
-
```
|
912
|
+
# Enumerate system enumerations
|
913
|
+
d365fo-client metadata enums --search "status" --output table
|
738
914
|
|
739
|
-
|
915
|
+
# Synchronize metadata cache
|
916
|
+
d365fo-client metadata sync --force-refresh
|
917
|
+
```
|
740
918
|
|
741
|
-
|
742
|
-
|
919
|
+
#### Version Information
|
920
|
+
```bash
|
921
|
+
# Get application versions
|
922
|
+
d365fo-client version app
|
923
|
+
d365fo-client version platform
|
924
|
+
d365fo-client version build
|
925
|
+
```
|
743
926
|
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
"use_default_credentials": True
|
749
|
-
}
|
750
|
-
}
|
927
|
+
#### Label Operations
|
928
|
+
```bash
|
929
|
+
# Resolve single label
|
930
|
+
d365fo-client labels resolve "@SYS13342"
|
751
931
|
|
752
|
-
|
753
|
-
|
932
|
+
# Search labels by pattern
|
933
|
+
d365fo-client labels search "customer" --language "en-US"
|
754
934
|
```
|
755
935
|
|
756
|
-
###
|
936
|
+
### Global Options
|
757
937
|
|
758
|
-
|
938
|
+
- `--base-url URL` — Specify D365 F&O environment URL
|
939
|
+
- `--profile NAME` — Use named configuration profile
|
940
|
+
- `--output FORMAT` — Output format: json, table, csv, yaml (default: table)
|
941
|
+
- `--verbose` — Enable verbose output for debugging
|
942
|
+
- `--timeout SECONDS` — Request timeout (default: 30)
|
759
943
|
|
760
|
-
|
761
|
-
- **`d365fo_test_connection`** - Test environment connectivity and health
|
762
|
-
- **`d365fo_get_environment_info`** - Get comprehensive environment details, versions, and statistics
|
944
|
+
### Configuration Profiles
|
763
945
|
|
764
|
-
|
765
|
-
- **`d365fo_query_entities`** - Advanced OData querying with filters, selections, and pagination
|
766
|
-
- **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options
|
767
|
-
- **`d365fo_create_entity_record`** - Create new entity records with validation
|
768
|
-
- **`d365fo_update_entity_record`** - Update existing records with optimistic concurrency
|
769
|
-
- **`d365fo_delete_entity_record`** - Delete entity records with conflict detection
|
946
|
+
Create reusable configurations in `~/.d365fo-client/config.yaml`:
|
770
947
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
948
|
+
```yaml
|
949
|
+
profiles:
|
950
|
+
production:
|
951
|
+
base_url: "https://prod.dynamics.com"
|
952
|
+
use_default_credentials: true
|
953
|
+
timeout: 60
|
954
|
+
|
955
|
+
development:
|
956
|
+
base_url: "https://dev.dynamics.com"
|
957
|
+
client_id: "${D365FO_CLIENT_ID}"
|
958
|
+
client_secret: "${D365FO_CLIENT_SECRET}"
|
959
|
+
tenant_id: "${D365FO_TENANT_ID}"
|
960
|
+
use_cache_first: true
|
777
961
|
|
778
|
-
|
779
|
-
|
780
|
-
- **`d365fo_get_labels_batch`** - Get multiple labels efficiently in batch operations
|
962
|
+
default_profile: "development"
|
963
|
+
```
|
781
964
|
|
782
|
-
###
|
965
|
+
### Examples
|
783
966
|
|
784
|
-
|
967
|
+
```bash
|
968
|
+
# Quick entity discovery
|
969
|
+
d365fo-client entities list --pattern "cust.*" --output json
|
785
970
|
|
786
|
-
|
787
|
-
|
788
|
-
```
|
789
|
-
d365fo://entities/CustomersV3 # Customer entity with metadata and sample data
|
790
|
-
d365fo://entities/SalesOrders # Sales order entity information
|
791
|
-
d365fo://entities/Products # Product entity details
|
792
|
-
```
|
971
|
+
# Get comprehensive entity information
|
972
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels --output yaml
|
793
973
|
|
794
|
-
|
795
|
-
|
796
|
-
```
|
797
|
-
d365fo://metadata/entities # All data entities metadata (V2 cache)
|
798
|
-
d365fo://metadata/actions # Available OData actions
|
799
|
-
d365fo://metadata/enumerations # System enumerations
|
800
|
-
d365fo://metadata/labels # System labels and translations
|
801
|
-
```
|
974
|
+
# Search for calculation actions
|
975
|
+
d365fo-client metadata actions --pattern "calculate|compute" --output table
|
802
976
|
|
803
|
-
|
804
|
-
|
805
|
-
```
|
806
|
-
d365fo://environment/status # Environment health and connectivity
|
807
|
-
d365fo://environment/version # Version information (app, platform, build)
|
808
|
-
d365fo://environment/cache # Cache status and statistics V2
|
977
|
+
# Test environment connectivity
|
978
|
+
d365fo-client version app --verbose
|
809
979
|
```
|
810
980
|
|
811
|
-
|
812
|
-
Access predefined and templated queries:
|
813
|
-
```
|
814
|
-
d365fo://queries/customers_recent # Recent customers query template
|
815
|
-
d365fo://queries/sales_summary # Sales summary query with parameters
|
816
|
-
```
|
981
|
+
For a complete command reference:
|
817
982
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
d365fo
|
822
|
-
d365fo://database/actions # Action discovery with metadata
|
823
|
-
d365fo://database/statistics # Cache and performance statistics
|
983
|
+
```bash
|
984
|
+
d365fo-client --help
|
985
|
+
d365fo-client entities --help
|
986
|
+
d365fo-client metadata --help
|
824
987
|
```
|
988
|
+
### Basic Usage
|
825
989
|
|
826
|
-
|
990
|
+
```python
|
991
|
+
import asyncio
|
992
|
+
from d365fo_client import D365FOClient, FOClientConfig
|
827
993
|
|
828
|
-
|
994
|
+
async def main():
|
995
|
+
# Simple configuration with default credentials
|
996
|
+
config = FOClientConfig(
|
997
|
+
base_url="https://your-fo-environment.dynamics.com",
|
998
|
+
use_default_credentials=True # Uses Azure Default Credential
|
999
|
+
)
|
1000
|
+
|
1001
|
+
async with D365FOClient(config) as client:
|
1002
|
+
# Test connection
|
1003
|
+
if await client.test_connection():
|
1004
|
+
print("✅ Connected successfully!")
|
1005
|
+
|
1006
|
+
# Get environment information
|
1007
|
+
env_info = await client.get_environment_info()
|
1008
|
+
print(f"Environment: {env_info.application_version}")
|
1009
|
+
|
1010
|
+
# Search for entities (uses metadata cache v2)
|
1011
|
+
customer_entities = await client.search_entities("customer")
|
1012
|
+
print(f"Found {len(customer_entities)} customer entities")
|
1013
|
+
|
1014
|
+
# Get customers with query options
|
1015
|
+
from d365fo_client import QueryOptions
|
1016
|
+
options = QueryOptions(
|
1017
|
+
select=["CustomerAccount", "Name", "SalesCurrencyCode"],
|
1018
|
+
top=10,
|
1019
|
+
orderby=["Name"]
|
1020
|
+
)
|
1021
|
+
|
1022
|
+
customers = await client.get_data("/data/CustomersV3", options)
|
1023
|
+
print(f"Retrieved {len(customers['value'])} customers")
|
829
1024
|
|
830
|
-
|
831
|
-
|
832
|
-
"tool": "d365fo_query_entities",
|
833
|
-
"arguments": {
|
834
|
-
"entityName": "CustomersV3",
|
835
|
-
"select": ["CustomerAccount", "Name", "Email"],
|
836
|
-
"filter": "CustomerGroup eq 'VIP'",
|
837
|
-
"top": 10
|
838
|
-
}
|
839
|
-
}
|
1025
|
+
if __name__ == "__main__":
|
1026
|
+
asyncio.run(main())
|
840
1027
|
```
|
841
1028
|
|
842
|
-
|
1029
|
+
### Using Convenience Function
|
843
1030
|
|
844
|
-
```
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
"
|
850
|
-
"resolveLabels": true,
|
851
|
-
"language": "en-US"
|
852
|
-
}
|
853
|
-
}
|
1031
|
+
```python
|
1032
|
+
from d365fo_client import create_client
|
1033
|
+
|
1034
|
+
# Quick client creation with enhanced defaults
|
1035
|
+
async with create_client("https://your-fo-environment.dynamics.com") as client:
|
1036
|
+
customers = await client.get_data("/data/CustomersV3", top=5)
|
854
1037
|
```
|
855
1038
|
|
856
|
-
|
1039
|
+
## Configuration
|
857
1040
|
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
1041
|
+
### Environment Variable Management (New in v0.3.0)
|
1042
|
+
|
1043
|
+
The d365fo-client now includes a comprehensive **Pydantic settings model** for type-safe environment variable management:
|
1044
|
+
|
1045
|
+
```python
|
1046
|
+
from d365fo_client import D365FOSettings, get_settings
|
1047
|
+
|
1048
|
+
# Get type-safe settings instance
|
1049
|
+
settings = get_settings()
|
1050
|
+
|
1051
|
+
# Access settings with full IntelliSense support
|
1052
|
+
print(f"Base URL: {settings.base_url}")
|
1053
|
+
print(f"Log Level: {settings.log_level}")
|
1054
|
+
print(f"Cache Directory: {settings.cache_dir}")
|
1055
|
+
|
1056
|
+
# Check configuration state
|
1057
|
+
if settings.has_client_credentials():
|
1058
|
+
print("Client credentials configured")
|
1059
|
+
|
1060
|
+
startup_mode = settings.get_startup_mode() # "profile_only", "default_auth", "client_credentials"
|
1061
|
+
|
1062
|
+
# Convert to environment dictionary for external tools
|
1063
|
+
env_vars = settings.to_env_dict()
|
863
1064
|
```
|
864
1065
|
|
865
|
-
|
1066
|
+
**Key Benefits:**
|
1067
|
+
- **Type Safety**: Automatic validation and type conversion for all 35+ environment variables
|
1068
|
+
- **IDE Support**: Full IntelliSense and autocompletion for configuration options
|
1069
|
+
- **Environment Files**: Support for `.env` files in development
|
1070
|
+
- **Comprehensive Defaults**: Sensible defaults for all configuration options
|
1071
|
+
- **Validation**: Built-in validation for URLs, ports, timeouts, and other settings
|
866
1072
|
|
867
|
-
|
868
|
-
Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
|
1073
|
+
### Authentication Options
|
869
1074
|
|
870
|
-
```
|
871
|
-
|
872
|
-
|
873
|
-
|
1075
|
+
```python
|
1076
|
+
from d365fo_client import FOClientConfig
|
1077
|
+
|
1078
|
+
# Option 1: Default Azure credentials (recommended)
|
1079
|
+
config = FOClientConfig(
|
1080
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1081
|
+
use_default_credentials=True
|
1082
|
+
)
|
1083
|
+
|
1084
|
+
# Option 2: Client credentials
|
1085
|
+
config = FOClientConfig(
|
1086
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1087
|
+
client_id="your-client-id",
|
1088
|
+
client_secret="your-client-secret",
|
1089
|
+
tenant_id="your-tenant-id",
|
1090
|
+
use_default_credentials=False
|
1091
|
+
)
|
1092
|
+
|
1093
|
+
# Option 3: Azure Key Vault integration (New in v0.2.3)
|
1094
|
+
config = FOClientConfig(
|
1095
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1096
|
+
credential_source="keyvault", # Use Azure Key Vault for credentials
|
1097
|
+
keyvault_url="https://your-keyvault.vault.azure.net/"
|
1098
|
+
)
|
1099
|
+
|
1100
|
+
# Option 4: With custom settings
|
1101
|
+
config = FOClientConfig(
|
1102
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1103
|
+
use_default_credentials=True,
|
1104
|
+
verify_ssl=False, # For development environments
|
1105
|
+
timeout=60, # Request timeout in seconds
|
1106
|
+
metadata_cache_dir="./my_cache", # Custom cache directory
|
1107
|
+
use_label_cache=True, # Enable label caching
|
1108
|
+
label_cache_expiry_minutes=120 # Cache for 2 hours
|
1109
|
+
)
|
874
1110
|
```
|
875
1111
|
|
876
|
-
|
877
|
-
|
1112
|
+
### Legacy Configuration Migration (New in v0.3.0)
|
1113
|
+
|
1114
|
+
The d365fo-client automatically detects and migrates legacy configuration files:
|
1115
|
+
|
1116
|
+
- **Automatic Detection**: Identifies legacy configuration patterns (missing `verify_ssl`, outdated field names)
|
1117
|
+
- **Field Migration**: Updates `cache_dir` → `metadata_cache_dir`, `auth_mode` → `use_default_credentials`
|
1118
|
+
- **Backup Creation**: Creates backup of original configuration before migration
|
1119
|
+
- **Seamless Upgrade**: Ensures smooth transition from older versions without manual intervention
|
1120
|
+
|
1121
|
+
```python
|
1122
|
+
# Legacy configurations are automatically migrated when FastMCP server starts
|
1123
|
+
# No manual intervention required - migration happens transparently
|
1124
|
+
```
|
1125
|
+
|
1126
|
+
## Core Operations
|
1127
|
+
|
1128
|
+
### CRUD Operations
|
1129
|
+
|
1130
|
+
```python
|
1131
|
+
async with D365FOClient(config) as client:
|
1132
|
+
# CREATE - Create new customer (supports composite keys)
|
1133
|
+
new_customer = {
|
1134
|
+
"CustomerAccount": "US-999",
|
1135
|
+
"Name": "Test Customer",
|
1136
|
+
"SalesCurrencyCode": "USD"
|
1137
|
+
}
|
1138
|
+
created = await client.create_data("/data/CustomersV3", new_customer)
|
1139
|
+
|
1140
|
+
# READ - Get single customer by key
|
1141
|
+
customer = await client.get_data("/data/CustomersV3('US-001')")
|
1142
|
+
|
1143
|
+
# UPDATE - Update customer with optimistic concurrency
|
1144
|
+
updates = {"Name": "Updated Customer Name"}
|
1145
|
+
updated = await client.update_data("/data/CustomersV3('US-001')", updates)
|
1146
|
+
|
1147
|
+
# DELETE - Delete customer
|
1148
|
+
success = await client.delete_data("/data/CustomersV3('US-999')")
|
1149
|
+
print(f"Delete successful: {success}")
|
1150
|
+
```
|
1151
|
+
|
1152
|
+
### Advanced Querying
|
1153
|
+
|
1154
|
+
```python
|
1155
|
+
from d365fo_client import QueryOptions
|
1156
|
+
|
1157
|
+
# Complex query with multiple options
|
1158
|
+
options = QueryOptions(
|
1159
|
+
select=["CustomerAccount", "Name", "SalesCurrencyCode", "CustomerGroupId"],
|
1160
|
+
filter="SalesCurrencyCode eq 'USD' and contains(Name, 'Corp')",
|
1161
|
+
expand=["CustomerGroup"],
|
1162
|
+
orderby=["Name desc", "CustomerAccount"],
|
1163
|
+
top=50,
|
1164
|
+
skip=10,
|
1165
|
+
count=True
|
1166
|
+
)
|
1167
|
+
|
1168
|
+
result = await client.get_data("/data/CustomersV3", options)
|
1169
|
+
print(f"Total count: {result.get('@odata.count')}")
|
1170
|
+
```
|
1171
|
+
|
1172
|
+
### Action Execution
|
1173
|
+
|
1174
|
+
```python
|
1175
|
+
# Unbound action
|
1176
|
+
result = await client.post_data("/data/calculateTax", {
|
1177
|
+
"amount": 1000.00,
|
1178
|
+
"taxGroup": "STANDARD"
|
1179
|
+
})
|
1180
|
+
|
1181
|
+
# Bound action on entity set
|
1182
|
+
result = await client.post_data("/data/CustomersV3/calculateBalances", {
|
1183
|
+
"asOfDate": "2024-12-31"
|
1184
|
+
})
|
1185
|
+
|
1186
|
+
# Bound action on specific entity instance
|
1187
|
+
result = await client.post_data("/data/CustomersV3('US-001')/calculateBalance", {
|
1188
|
+
"asOfDate": "2024-12-31"
|
1189
|
+
})
|
1190
|
+
```
|
1191
|
+
|
1192
|
+
### Metadata Operations
|
1193
|
+
|
1194
|
+
```python
|
1195
|
+
# Intelligent metadata synchronization (v2 system)
|
1196
|
+
sync_manager = await client.get_sync_manager()
|
1197
|
+
await sync_manager.smart_sync()
|
1198
|
+
|
1199
|
+
# Search entities with enhanced filtering
|
1200
|
+
sales_entities = await client.search_entities("sales")
|
1201
|
+
print("Sales-related entities:", [e.name for e in sales_entities])
|
1202
|
+
|
1203
|
+
# Get detailed entity information with labels
|
1204
|
+
entity_info = await client.get_public_entity_info("CustomersV3")
|
1205
|
+
if entity_info:
|
1206
|
+
print(f"Entity: {entity_info.name}")
|
1207
|
+
print(f"Label: {entity_info.label_text}")
|
1208
|
+
print(f"Data Service Enabled: {entity_info.data_service_enabled}")
|
1209
|
+
|
1210
|
+
# Search actions with caching
|
1211
|
+
calc_actions = await client.search_actions("calculate")
|
1212
|
+
print("Calculation actions:", [a.name for a in calc_actions])
|
1213
|
+
|
1214
|
+
# Get enumeration information
|
1215
|
+
enum_info = await client.get_public_enumeration_info("NoYes")
|
1216
|
+
if enum_info:
|
1217
|
+
print(f"Enum: {enum_info.name}")
|
1218
|
+
for member in enum_info.members:
|
1219
|
+
print(f" {member.name} = {member.value}")
|
1220
|
+
```
|
1221
|
+
|
1222
|
+
### Label Operations
|
1223
|
+
|
1224
|
+
```python
|
1225
|
+
# Get specific label (v2 caching system)
|
1226
|
+
label_text = await client.get_label_text("@SYS13342")
|
1227
|
+
print(f"Label text: {label_text}")
|
1228
|
+
|
1229
|
+
# Get multiple labels efficiently
|
1230
|
+
labels = await client.get_labels_batch([
|
1231
|
+
"@SYS13342", "@SYS9490", "@GLS63332"
|
1232
|
+
])
|
1233
|
+
for label_id, text in labels.items():
|
1234
|
+
print(f"{label_id}: {text}")
|
1235
|
+
|
1236
|
+
# Enhanced entity info with resolved labels
|
1237
|
+
entity_info = await client.get_public_entity_info_with_labels("CustomersV3")
|
1238
|
+
if entity_info.label_text:
|
1239
|
+
print(f"Entity display name: {entity_info.label_text}")
|
1240
|
+
|
1241
|
+
# Access enhanced properties with labels
|
1242
|
+
for prop in entity_info.enhanced_properties[:5]:
|
1243
|
+
if hasattr(prop, 'label_text') and prop.label_text:
|
1244
|
+
print(f"{prop.name}: {prop.label_text}")
|
1245
|
+
```
|
1246
|
+
|
1247
|
+
## Error Handling
|
1248
|
+
|
1249
|
+
```python
|
1250
|
+
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
1251
|
+
|
1252
|
+
try:
|
1253
|
+
async with D365FOClient(config) as client:
|
1254
|
+
customer = await client.get_data("/data/CustomersV3('NON-EXISTENT')")
|
1255
|
+
except ConnectionError as e:
|
1256
|
+
print(f"Connection failed: {e}")
|
1257
|
+
except AuthenticationError as e:
|
1258
|
+
print(f"Authentication failed: {e}")
|
1259
|
+
except D365FOClientError as e:
|
1260
|
+
print(f"Client operation failed: {e}")
|
1261
|
+
print(f"Status code: {e.status_code}")
|
1262
|
+
print(f"Response: {e.response_text}")
|
1263
|
+
```
|
1264
|
+
|
1265
|
+
## Development
|
1266
|
+
|
1267
|
+
### Setting up Development Environment
|
878
1268
|
|
879
1269
|
```bash
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
1270
|
+
# Clone the repository
|
1271
|
+
git clone https://github.com/mafzaal/d365fo-client.git
|
1272
|
+
cd d365fo-client
|
1273
|
+
|
1274
|
+
# Install with development dependencies using uv
|
1275
|
+
uv sync --dev
|
1276
|
+
|
1277
|
+
# Run tests
|
1278
|
+
uv run pytest
|
1279
|
+
|
1280
|
+
# Run integration tests
|
1281
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
1282
|
+
|
1283
|
+
# Format code
|
1284
|
+
uv run black .
|
1285
|
+
uv run isort .
|
1286
|
+
|
1287
|
+
# Type checking
|
1288
|
+
uv run mypy src/
|
1289
|
+
|
1290
|
+
# Quality checks
|
1291
|
+
.\make.ps1 quality-check # Windows PowerShell
|
1292
|
+
# or
|
1293
|
+
make quality-check # Unix/Linux/macOS
|
885
1294
|
```
|
886
1295
|
|
887
|
-
|
888
|
-
|
1296
|
+
### Project Structure
|
1297
|
+
|
1298
|
+
```
|
1299
|
+
d365fo-client/
|
1300
|
+
├── src/
|
1301
|
+
│ └── d365fo_client/
|
1302
|
+
│ ├── __init__.py # Public API exports
|
1303
|
+
│ ├── main.py # CLI entry point
|
1304
|
+
│ ├── cli.py # CLI command handlers
|
1305
|
+
│ ├── client.py # Enhanced D365FOClient class
|
1306
|
+
│ ├── config.py # Configuration management
|
1307
|
+
│ ├── auth.py # Authentication management
|
1308
|
+
│ ├── session.py # HTTP session management
|
1309
|
+
│ ├── crud.py # CRUD operations
|
1310
|
+
│ ├── query.py # OData query utilities
|
1311
|
+
│ ├── metadata.py # Legacy metadata operations
|
1312
|
+
│ ├── metadata_api.py # Metadata API client
|
1313
|
+
│ ├── metadata_cache.py # Metadata caching layer V2
|
1314
|
+
│ ├── metadata_sync.py # Metadata synchronization V2 with session management
|
1315
|
+
│ ├── sync_session.py # Enhanced sync session management (New in v0.2.3)
|
1316
|
+
│ ├── credential_manager.py # Credential source management (New in v0.2.3)
|
1317
|
+
│ ├── labels.py # Label operations V2
|
1318
|
+
│ ├── profiles.py # Profile data models
|
1319
|
+
│ ├── profile_manager.py # Profile management
|
1320
|
+
│ ├── models.py # Data models and configurations
|
1321
|
+
│ ├── output.py # Output formatting
|
1322
|
+
│ ├── utils.py # Utility functions
|
1323
|
+
│ ├── exceptions.py # Custom exceptions
|
1324
|
+
│ └── mcp/ # Model Context Protocol server
|
1325
|
+
│ ├── __init__.py # MCP server exports
|
1326
|
+
│ ├── main.py # MCP server entry point
|
1327
|
+
│ ├── server.py # Core MCP server implementation
|
1328
|
+
│ ├── client_manager.py# D365FO client connection pooling
|
1329
|
+
│ ├── models.py # MCP-specific data models
|
1330
|
+
│ ├── tools/ # MCP tool implementations (12 tools)
|
1331
|
+
│ │ ├── connection_tools.py
|
1332
|
+
│ │ ├── crud_tools.py
|
1333
|
+
│ │ ├── metadata_tools.py
|
1334
|
+
│ │ └── label_tools.py
|
1335
|
+
│ ├── resources/ # MCP resource handlers (4 types)
|
1336
|
+
│ │ ├── entity_handler.py
|
1337
|
+
│ │ ├── metadata_handler.py
|
1338
|
+
│ │ ├── environment_handler.py
|
1339
|
+
│ │ └── query_handler.py
|
1340
|
+
│ └── prompts/ # MCP prompt templates
|
1341
|
+
├── tests/ # Comprehensive test suite
|
1342
|
+
│ ├── unit/ # Unit tests (pytest-based)
|
1343
|
+
│ ├── integration/ # Multi-tier integration testing
|
1344
|
+
│ │ ├── mock_server/ # Mock D365 F&O API server
|
1345
|
+
│ │ ├── test_mock_server.py # Mock server tests
|
1346
|
+
│ │ ├── test_sandbox.py # Sandbox environment tests ✅
|
1347
|
+
│ │ ├── test_live.py # Live environment tests
|
1348
|
+
│ │ ├── conftest.py # Shared pytest fixtures
|
1349
|
+
│ │ ├── test_runner.py # Python test execution engine
|
1350
|
+
│ │ └── integration-test-simple.ps1 # PowerShell automation
|
1351
|
+
│ └── test_mcp_server.py # MCP server unit tests ✅
|
1352
|
+
├── scripts/ # Metadata discovery scripts
|
1353
|
+
│ ├── search_data_entities.ps1 # PowerShell entity search
|
1354
|
+
│ ├── get_data_entity_schema.ps1 # PowerShell schema retrieval
|
1355
|
+
│ ├── search_enums.py # Python enumeration search
|
1356
|
+
│ ├── get_enumeration_info.py # Python enumeration info
|
1357
|
+
│ ├── search_actions.ps1 # PowerShell action search
|
1358
|
+
│ └── get_action_info.py # Python action information
|
1359
|
+
├── docs/ # Comprehensive documentation
|
1360
|
+
├── pyproject.toml # Project configuration
|
1361
|
+
└── README.md # This file
|
1362
|
+
```
|
1363
|
+
|
1364
|
+
## Configuration Options
|
1365
|
+
|
1366
|
+
| Option | Type | Default | Description |
|
1367
|
+
|--------|------|---------|-------------|
|
1368
|
+
| `base_url` | str | Required | D365 F&O base URL |
|
1369
|
+
| `client_id` | str | None | Azure AD client ID |
|
1370
|
+
| `client_secret` | str | None | Azure AD client secret |
|
1371
|
+
| `tenant_id` | str | None | Azure AD tenant ID |
|
1372
|
+
| `use_default_credentials` | bool | True | Use Azure Default Credential |
|
1373
|
+
| `credential_source` | str | "environment" | Credential source: "environment", "keyvault" |
|
1374
|
+
| `keyvault_url` | str | None | Azure Key Vault URL for credential storage |
|
1375
|
+
| `verify_ssl` | bool | False | Verify SSL certificates |
|
1376
|
+
| `timeout` | int | 30 | Request timeout in seconds |
|
1377
|
+
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
|
1378
|
+
| `use_label_cache` | bool | True | Enable label caching V2 |
|
1379
|
+
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
|
1380
|
+
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |
|
1381
|
+
|
1382
|
+
### Cache Directory Behavior
|
1383
|
+
|
1384
|
+
By default, the client uses platform-appropriate user cache directories:
|
889
1385
|
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
export D365FO_KEYVAULT_URL="https://your-keyvault.vault.azure.net/"
|
894
|
-
d365fo-mcp-server
|
895
|
-
```
|
1386
|
+
- **Windows**: `%LOCALAPPDATA%\d365fo-client` (e.g., `C:\Users\username\AppData\Local\d365fo-client`)
|
1387
|
+
- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
|
1388
|
+
- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)
|
896
1389
|
|
897
|
-
|
1390
|
+
You can override this by explicitly setting `metadata_cache_dir`:
|
898
1391
|
|
899
|
-
|
1392
|
+
```python
|
1393
|
+
from d365fo_client import FOClientConfig
|
900
1394
|
|
901
|
-
|
902
|
-
|
903
|
-
|
1395
|
+
# Use custom cache directory
|
1396
|
+
config = FOClientConfig(
|
1397
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1398
|
+
metadata_cache_dir="/custom/cache/path"
|
1399
|
+
)
|
904
1400
|
|
905
|
-
#
|
906
|
-
|
1401
|
+
# Or get the default cache directory programmatically
|
1402
|
+
from d365fo_client import get_user_cache_dir
|
907
1403
|
|
908
|
-
#
|
909
|
-
|
910
|
-
|
1404
|
+
cache_dir = get_user_cache_dir("my-app") # Platform-appropriate cache dir
|
1405
|
+
config = FOClientConfig(
|
1406
|
+
base_url="https://your-fo-environment.dynamics.com",
|
1407
|
+
metadata_cache_dir=str(cache_dir)
|
1408
|
+
)
|
911
1409
|
```
|
912
1410
|
|
913
|
-
|
1411
|
+
## Testing
|
914
1412
|
|
915
|
-
|
1413
|
+
This project includes comprehensive testing at multiple levels to ensure reliability and quality.
|
916
1414
|
|
917
|
-
|
918
|
-
Add to your Claude Desktop configuration:
|
1415
|
+
### Unit Tests
|
919
1416
|
|
920
|
-
|
921
|
-
{
|
922
|
-
"mcpServers": {
|
923
|
-
"d365fo": {
|
924
|
-
"command": "d365fo-mcp-server",
|
925
|
-
"env": {
|
926
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com" //Optional
|
927
|
-
}
|
928
|
-
}
|
929
|
-
}
|
930
|
-
}
|
931
|
-
```
|
1417
|
+
Run standard unit tests for core functionality:
|
932
1418
|
|
933
|
-
|
1419
|
+
```bash
|
1420
|
+
# Run all unit tests
|
1421
|
+
uv run pytest
|
934
1422
|
|
935
|
-
|
936
|
-
|
1423
|
+
# Run with coverage
|
1424
|
+
uv run pytest --cov=d365fo_client --cov-report=html
|
937
1425
|
|
938
|
-
|
939
|
-
|
940
|
-
"servers": {
|
941
|
-
"d365fo-mcp-server": {
|
942
|
-
"type": "stdio",
|
943
|
-
"command": "uvx",
|
944
|
-
"args": [
|
945
|
-
"--from",
|
946
|
-
"d365fo-client",
|
947
|
-
"d365fo-mcp-server"
|
948
|
-
],
|
949
|
-
"env": {
|
950
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
951
|
-
"D365FO_LOG_LEVEL": "INFO"
|
952
|
-
}
|
953
|
-
}
|
954
|
-
}
|
955
|
-
}
|
1426
|
+
# Run specific test file
|
1427
|
+
uv run pytest tests/test_client.py -v
|
956
1428
|
```
|
957
1429
|
|
958
|
-
|
959
|
-
For environments requiring service principal authentication:
|
1430
|
+
### Integration Tests
|
960
1431
|
|
961
|
-
|
962
|
-
{
|
963
|
-
"servers": {
|
964
|
-
"d365fo-mcp-server": {
|
965
|
-
"type": "stdio",
|
966
|
-
"command": "uvx",
|
967
|
-
"args": [
|
968
|
-
"--from",
|
969
|
-
"d365fo-client",
|
970
|
-
"d365fo-mcp-server"
|
971
|
-
],
|
972
|
-
"env": {
|
973
|
-
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
974
|
-
"D365FO_LOG_LEVEL": "DEBUG",
|
975
|
-
"D365FO_CLIENT_ID": "${input:client_id}",
|
976
|
-
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
977
|
-
"D365FO_TENANT_ID": "${input:tenant_id}"
|
978
|
-
}
|
979
|
-
}
|
980
|
-
},
|
981
|
-
"inputs": [
|
982
|
-
{
|
983
|
-
"id": "tenant_id",
|
984
|
-
"type": "promptString",
|
985
|
-
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
986
|
-
"password": true
|
987
|
-
},
|
988
|
-
{
|
989
|
-
"id": "client_id",
|
990
|
-
"type": "promptString",
|
991
|
-
"description": "Azure AD Client ID for D365 F&O authentication",
|
992
|
-
"password": true
|
993
|
-
},
|
994
|
-
{
|
995
|
-
"id": "client_secret",
|
996
|
-
"type": "promptString",
|
997
|
-
"description": "Azure AD Client Secret for D365 F&O authentication",
|
998
|
-
"password": true
|
999
|
-
}
|
1000
|
-
]
|
1001
|
-
}
|
1002
|
-
```
|
1432
|
+
The project includes a sophisticated multi-tier integration testing framework:
|
1003
1433
|
|
1004
|
-
|
1005
|
-
- Always uses the latest version from the repository
|
1006
|
-
- No local installation required
|
1007
|
-
- Automatic dependency management
|
1008
|
-
- Works across different environments
|
1434
|
+
#### Quick Start
|
1009
1435
|
|
1010
|
-
|
1011
|
-
|
1436
|
+
```bash
|
1437
|
+
# Run sandbox integration tests (recommended)
|
1438
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
1012
1439
|
|
1013
|
-
|
1014
|
-
|
1440
|
+
# Run mock server tests (no external dependencies)
|
1441
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
1015
1442
|
|
1016
|
-
|
1017
|
-
|
1018
|
-
tools = await client.list_tools()
|
1019
|
-
|
1020
|
-
# Execute operations
|
1021
|
-
result = await client.call_tool(
|
1022
|
-
"d365fo_query_entities",
|
1023
|
-
{"entityName": "Customers", "top": 5}
|
1024
|
-
)
|
1443
|
+
# Run with verbose output
|
1444
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox -VerboseOutput
|
1025
1445
|
```
|
1026
1446
|
|
1027
|
-
|
1447
|
+
#### Test Levels
|
1028
1448
|
|
1029
|
-
|
1030
|
-
-
|
1031
|
-
-
|
1032
|
-
-
|
1033
|
-
- **Error Context**: Detailed error information for troubleshooting
|
1449
|
+
1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API
|
1450
|
+
- No external dependencies
|
1451
|
+
- Complete API simulation
|
1452
|
+
- Ideal for CI/CD pipelines
|
1034
1453
|
|
1035
|
-
|
1036
|
-
-
|
1037
|
-
-
|
1038
|
-
-
|
1039
|
-
- **Well Documented**: Complete API documentation and examples
|
1454
|
+
2. **Sandbox Tests** ⭐ *(Default)* - Tests against real D365 F&O test environments
|
1455
|
+
- Validates authentication
|
1456
|
+
- Tests real API behavior
|
1457
|
+
- Requires test environment access
|
1040
1458
|
|
1041
|
-
|
1042
|
-
-
|
1043
|
-
-
|
1044
|
-
-
|
1045
|
-
- **Maintenance Friendly**: Clear architecture and comprehensive test coverage
|
1459
|
+
3. **Live Tests** - Optional tests against production environments
|
1460
|
+
- Final validation
|
1461
|
+
- Performance benchmarking
|
1462
|
+
- Use with caution
|
1046
1463
|
|
1047
|
-
|
1464
|
+
#### Configuration
|
1048
1465
|
|
1049
|
-
|
1466
|
+
Set up integration testing with environment variables:
|
1050
1467
|
|
1051
|
-
**Connection Failures**
|
1052
1468
|
```bash
|
1053
|
-
#
|
1054
|
-
|
1469
|
+
# Copy the template and configure
|
1470
|
+
cp tests/integration/.env.template tests/integration/.env
|
1055
1471
|
|
1056
|
-
#
|
1057
|
-
|
1472
|
+
# Edit .env file with your settings:
|
1473
|
+
INTEGRATION_TEST_LEVEL=sandbox
|
1474
|
+
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
|
1475
|
+
D365FO_CLIENT_ID=your-client-id
|
1476
|
+
D365FO_CLIENT_SECRET=your-client-secret
|
1477
|
+
D365FO_TENANT_ID=your-tenant-id
|
1058
1478
|
```
|
1059
1479
|
|
1060
|
-
|
1480
|
+
#### Available Commands
|
1481
|
+
|
1061
1482
|
```bash
|
1062
|
-
#
|
1063
|
-
|
1483
|
+
# Test environment setup
|
1484
|
+
.\tests\integration\integration-test-simple.ps1 setup
|
1064
1485
|
|
1065
|
-
#
|
1066
|
-
|
1067
|
-
# ... set other variables
|
1068
|
-
d365fo-mcp-server
|
1069
|
-
```
|
1486
|
+
# Dependency checking
|
1487
|
+
.\tests\integration\integration-test-simple.ps1 deps-check
|
1070
1488
|
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1489
|
+
# Run specific test levels
|
1490
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
1491
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
1492
|
+
.\tests\integration\integration-test-simple.ps1 test-live
|
1075
1493
|
|
1076
|
-
#
|
1077
|
-
|
1078
|
-
|
1494
|
+
# Coverage and reporting
|
1495
|
+
.\tests\integration\integration-test-simple.ps1 coverage
|
1496
|
+
|
1497
|
+
# Clean up test artifacts
|
1498
|
+
.\tests\integration\integration-test-simple.ps1 clean
|
1079
1499
|
```
|
1080
1500
|
|
1081
|
-
####
|
1501
|
+
#### Test Coverage
|
1082
1502
|
|
1083
|
-
|
1084
|
-
|
1085
|
-
- **
|
1086
|
-
- **
|
1503
|
+
Integration tests cover:
|
1504
|
+
|
1505
|
+
- ✅ **Connection & Authentication** - Azure AD integration, SSL/TLS validation
|
1506
|
+
- ✅ **Version Methods** - Application, platform, and build version retrieval
|
1507
|
+
- ✅ **Metadata Operations** - Entity discovery, metadata API validation
|
1508
|
+
- ✅ **Data Operations** - CRUD operations, OData query validation
|
1509
|
+
- ✅ **Error Handling** - Network failures, authentication errors, invalid requests
|
1510
|
+
- ✅ **Performance** - Response time validation, concurrent operations
|
1511
|
+
|
1512
|
+
For detailed information, see [Integration Testing Documentation](tests/integration/README.md).
|
1513
|
+
|
1514
|
+
### Test Results
|
1515
|
+
|
1516
|
+
Recent sandbox integration test results:
|
1517
|
+
```
|
1518
|
+
✅ 17 passed, 0 failed, 2 warnings in 37.67s
|
1519
|
+
======================================================
|
1520
|
+
✅ TestSandboxConnection::test_connection_success
|
1521
|
+
✅ TestSandboxConnection::test_metadata_connection_success
|
1522
|
+
✅ TestSandboxVersionMethods::test_get_application_version
|
1523
|
+
✅ TestSandboxVersionMethods::test_get_platform_build_version
|
1524
|
+
✅ TestSandboxVersionMethods::test_get_application_build_version
|
1525
|
+
✅ TestSandboxVersionMethods::test_version_consistency
|
1526
|
+
✅ TestSandboxMetadataOperations::test_download_metadata
|
1527
|
+
✅ TestSandboxMetadataOperations::test_search_entities
|
1528
|
+
✅ TestSandboxMetadataOperations::test_get_data_entities
|
1529
|
+
✅ TestSandboxMetadataOperations::test_get_public_entities
|
1530
|
+
✅ TestSandboxDataOperations::test_get_available_entities
|
1531
|
+
✅ TestSandboxDataOperations::test_odata_query_options
|
1532
|
+
✅ TestSandboxAuthentication::test_authenticated_requests
|
1533
|
+
✅ TestSandboxErrorHandling::test_invalid_entity_error
|
1534
|
+
✅ TestSandboxErrorHandling::test_invalid_action_error
|
1535
|
+
✅ TestSandboxPerformance::test_response_times
|
1536
|
+
✅ TestSandboxPerformance::test_concurrent_operations
|
1537
|
+
```
|
1087
1538
|
|
1088
1539
|
## Contributing
|
1089
1540
|
|