d365fo-client 0.1.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 +305 -0
- d365fo_client/auth.py +93 -0
- d365fo_client/cli.py +700 -0
- d365fo_client/client.py +1454 -0
- d365fo_client/config.py +304 -0
- d365fo_client/crud.py +200 -0
- d365fo_client/exceptions.py +49 -0
- d365fo_client/labels.py +528 -0
- d365fo_client/main.py +502 -0
- d365fo_client/mcp/__init__.py +16 -0
- d365fo_client/mcp/client_manager.py +276 -0
- d365fo_client/mcp/main.py +98 -0
- d365fo_client/mcp/models.py +371 -0
- d365fo_client/mcp/prompts/__init__.py +43 -0
- d365fo_client/mcp/prompts/action_execution.py +480 -0
- d365fo_client/mcp/prompts/sequence_analysis.py +349 -0
- d365fo_client/mcp/resources/__init__.py +15 -0
- d365fo_client/mcp/resources/database_handler.py +555 -0
- d365fo_client/mcp/resources/entity_handler.py +176 -0
- d365fo_client/mcp/resources/environment_handler.py +132 -0
- d365fo_client/mcp/resources/metadata_handler.py +283 -0
- d365fo_client/mcp/resources/query_handler.py +135 -0
- d365fo_client/mcp/server.py +432 -0
- d365fo_client/mcp/tools/__init__.py +17 -0
- d365fo_client/mcp/tools/connection_tools.py +175 -0
- d365fo_client/mcp/tools/crud_tools.py +579 -0
- d365fo_client/mcp/tools/database_tools.py +813 -0
- d365fo_client/mcp/tools/label_tools.py +189 -0
- d365fo_client/mcp/tools/metadata_tools.py +766 -0
- d365fo_client/mcp/tools/profile_tools.py +706 -0
- d365fo_client/metadata_api.py +793 -0
- d365fo_client/metadata_v2/__init__.py +59 -0
- d365fo_client/metadata_v2/cache_v2.py +1372 -0
- d365fo_client/metadata_v2/database_v2.py +585 -0
- d365fo_client/metadata_v2/global_version_manager.py +573 -0
- d365fo_client/metadata_v2/search_engine_v2.py +423 -0
- d365fo_client/metadata_v2/sync_manager_v2.py +819 -0
- d365fo_client/metadata_v2/version_detector.py +439 -0
- d365fo_client/models.py +862 -0
- d365fo_client/output.py +181 -0
- d365fo_client/profile_manager.py +342 -0
- d365fo_client/profiles.py +178 -0
- d365fo_client/query.py +162 -0
- d365fo_client/session.py +60 -0
- d365fo_client/utils.py +196 -0
- d365fo_client-0.1.0.dist-info/METADATA +1084 -0
- d365fo_client-0.1.0.dist-info/RECORD +51 -0
- d365fo_client-0.1.0.dist-info/WHEEL +5 -0
- d365fo_client-0.1.0.dist-info/entry_points.txt +3 -0
- d365fo_client-0.1.0.dist-info/licenses/LICENSE +21 -0
- d365fo_client-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1084 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: d365fo-client
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Microsoft Dynamics 365 Finance & Operations client
|
5
|
+
Author-email: Muhammad Afzaal <mo@thedataguy.pro>
|
6
|
+
License-Expression: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/mafzaal/d365fo-client
|
8
|
+
Project-URL: Repository, https://github.com/mafzaal/d365fo-client
|
9
|
+
Project-URL: Documentation, https://github.com/mafzaal/d365fo-client
|
10
|
+
Project-URL: Issues, https://github.com/mafzaal/d365fo-client/issues
|
11
|
+
Project-URL: Changelog, https://github.com/mafzaal/d365fo-client/blob/main/CHANGELOG.md
|
12
|
+
Keywords: dynamics365,d365,finance,operations,erp,microsoft
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
14
|
+
Classifier: Intended Audience :: Developers
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
19
|
+
Requires-Python: >=3.13
|
20
|
+
Description-Content-Type: text/markdown
|
21
|
+
License-File: LICENSE
|
22
|
+
Requires-Dist: aiohttp>=3.10.0
|
23
|
+
Requires-Dist: aiofiles>=24.1.0
|
24
|
+
Requires-Dist: azure-identity>=1.19.0
|
25
|
+
Requires-Dist: requests>=2.32.0
|
26
|
+
Requires-Dist: aiosqlite>=0.19.0
|
27
|
+
Requires-Dist: cachetools>=6.1.0
|
28
|
+
Requires-Dist: diskcache>=5.6.3
|
29
|
+
Requires-Dist: tabulate>=0.9.0
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
31
|
+
Requires-Dist: mcp>=1.13.0
|
32
|
+
Provides-Extra: dev
|
33
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
34
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
35
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
36
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
37
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
38
|
+
Provides-Extra: all
|
39
|
+
Requires-Dist: d365fo-client[dev]; extra == "all"
|
40
|
+
Dynamic: license-file
|
41
|
+
|
42
|
+
# Dynamics 365 Finance & Operations Client and MCP Server
|
43
|
+
|
44
|
+
A comprehensive Python client library and MCP server for Microsoft Dynamics 365 Finance & Operations (D365 F&O) that provides easy access to OData endpoints, metadata operations, label management, and AI assistant integration.
|
45
|
+
|
46
|
+
## Features
|
47
|
+
|
48
|
+
- 🔗 **OData Client**: Full CRUD operations on D365 F&O data entities with composite key support
|
49
|
+
- 📊 **Metadata Management V2**: Enhanced caching system with intelligent synchronization and FTS5 search
|
50
|
+
- 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
|
51
|
+
- 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
|
52
|
+
- ⚡ **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
|
53
|
+
- 🔒 **Authentication**: Azure AD integration with default credentials and service principal support
|
54
|
+
- 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
|
55
|
+
- 🌐 **Async/Await**: Modern async/await patterns with optimized session management
|
56
|
+
- 📝 **Type Hints**: Full type annotation support with enhanced data models
|
57
|
+
- 🤖 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
|
58
|
+
- 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
|
59
|
+
- 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
|
60
|
+
- 📋 **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery
|
61
|
+
|
62
|
+
## Installation
|
63
|
+
|
64
|
+
```bash
|
65
|
+
# Install from PyPI
|
66
|
+
pip install d365fo-client
|
67
|
+
|
68
|
+
# Or install from source
|
69
|
+
git clone https://github.com/mafzaal/d365fo-client.git
|
70
|
+
cd d365fo-client
|
71
|
+
uv sync # Installs with exact dependencies from uv.lock
|
72
|
+
```
|
73
|
+
|
74
|
+
**Note**: The package includes MCP (Model Context Protocol) dependencies by default, enabling AI assistant integration. Both `d365fo-client` CLI and `d365fo-mcp-server` commands will be available after installation.
|
75
|
+
|
76
|
+
## Quick Start
|
77
|
+
|
78
|
+
## Command Line Interface (CLI)
|
79
|
+
|
80
|
+
d365fo-client provides a comprehensive CLI with hierarchical commands for interacting with Dynamics 365 Finance & Operations APIs and metadata. The CLI supports all major operations including entity management, metadata discovery, and system administration.
|
81
|
+
|
82
|
+
### Usage
|
83
|
+
|
84
|
+
```bash
|
85
|
+
# Use the installed CLI command
|
86
|
+
d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]
|
87
|
+
|
88
|
+
# Alternative: Module execution
|
89
|
+
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
|
90
|
+
```
|
91
|
+
|
92
|
+
### Command Categories
|
93
|
+
|
94
|
+
#### Entity Operations
|
95
|
+
```bash
|
96
|
+
# List entities with filtering
|
97
|
+
d365fo-client entities list --pattern "customer" --limit 10
|
98
|
+
|
99
|
+
# Get entity details and schema
|
100
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels
|
101
|
+
|
102
|
+
# CRUD operations
|
103
|
+
d365fo-client entities create Customers --data '{"CustomerAccount":"US-999","Name":"Test"}'
|
104
|
+
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
|
105
|
+
d365fo-client entities delete Customers US-999
|
106
|
+
```
|
107
|
+
|
108
|
+
#### Metadata Operations
|
109
|
+
```bash
|
110
|
+
# Search and discover entities
|
111
|
+
d365fo-client metadata entities --search "sales" --output json
|
112
|
+
|
113
|
+
# Get available actions
|
114
|
+
d365fo-client metadata actions --pattern "calculate" --limit 5
|
115
|
+
|
116
|
+
# Enumerate system enumerations
|
117
|
+
d365fo-client metadata enums --search "status" --output table
|
118
|
+
|
119
|
+
# Synchronize metadata cache
|
120
|
+
d365fo-client metadata sync --force-refresh
|
121
|
+
```
|
122
|
+
|
123
|
+
#### Version Information
|
124
|
+
```bash
|
125
|
+
# Get application versions
|
126
|
+
d365fo-client version app
|
127
|
+
d365fo-client version platform
|
128
|
+
d365fo-client version build
|
129
|
+
```
|
130
|
+
|
131
|
+
#### Label Operations
|
132
|
+
```bash
|
133
|
+
# Resolve single label
|
134
|
+
d365fo-client labels resolve "@SYS13342"
|
135
|
+
|
136
|
+
# Search labels by pattern
|
137
|
+
d365fo-client labels search "customer" --language "en-US"
|
138
|
+
```
|
139
|
+
|
140
|
+
### Global Options
|
141
|
+
|
142
|
+
- `--base-url URL` — Specify D365 F&O environment URL
|
143
|
+
- `--profile NAME` — Use named configuration profile
|
144
|
+
- `--output FORMAT` — Output format: json, table, csv, yaml (default: table)
|
145
|
+
- `--verbose` — Enable verbose output for debugging
|
146
|
+
- `--timeout SECONDS` — Request timeout (default: 30)
|
147
|
+
|
148
|
+
### Configuration Profiles
|
149
|
+
|
150
|
+
Create reusable configurations in `~/.d365fo-client/config.yaml`:
|
151
|
+
|
152
|
+
```yaml
|
153
|
+
profiles:
|
154
|
+
production:
|
155
|
+
base_url: "https://prod.dynamics.com"
|
156
|
+
use_default_credentials: true
|
157
|
+
timeout: 60
|
158
|
+
|
159
|
+
development:
|
160
|
+
base_url: "https://dev.dynamics.com"
|
161
|
+
client_id: "${AZURE_CLIENT_ID}"
|
162
|
+
client_secret: "${AZURE_CLIENT_SECRET}"
|
163
|
+
tenant_id: "${AZURE_TENANT_ID}"
|
164
|
+
use_cache_first: true
|
165
|
+
|
166
|
+
default_profile: "development"
|
167
|
+
```
|
168
|
+
|
169
|
+
### Examples
|
170
|
+
|
171
|
+
```bash
|
172
|
+
# Quick entity discovery
|
173
|
+
d365fo-client entities list --pattern "cust.*" --output json
|
174
|
+
|
175
|
+
# Get comprehensive entity information
|
176
|
+
d365fo-client entities get CustomersV3 --properties --keys --labels --output yaml
|
177
|
+
|
178
|
+
# Search for calculation actions
|
179
|
+
d365fo-client metadata actions --pattern "calculate|compute" --output table
|
180
|
+
|
181
|
+
# Test environment connectivity
|
182
|
+
d365fo-client version app --verbose
|
183
|
+
```
|
184
|
+
|
185
|
+
For a complete command reference:
|
186
|
+
|
187
|
+
```bash
|
188
|
+
d365fo-client --help
|
189
|
+
d365fo-client entities --help
|
190
|
+
d365fo-client metadata --help
|
191
|
+
```
|
192
|
+
### Basic Usage
|
193
|
+
|
194
|
+
```python
|
195
|
+
import asyncio
|
196
|
+
from d365fo_client import D365FOClient, FOClientConfig
|
197
|
+
|
198
|
+
async def main():
|
199
|
+
# Simple configuration with default credentials
|
200
|
+
config = FOClientConfig(
|
201
|
+
base_url="https://your-fo-environment.dynamics.com",
|
202
|
+
use_default_credentials=True # Uses Azure Default Credential
|
203
|
+
)
|
204
|
+
|
205
|
+
async with D365FOClient(config) as client:
|
206
|
+
# Test connection
|
207
|
+
if await client.test_connection():
|
208
|
+
print("✅ Connected successfully!")
|
209
|
+
|
210
|
+
# Get environment information
|
211
|
+
env_info = await client.get_environment_info()
|
212
|
+
print(f"Environment: {env_info.application_version}")
|
213
|
+
|
214
|
+
# Search for entities (uses metadata cache v2)
|
215
|
+
customer_entities = await client.search_entities("customer")
|
216
|
+
print(f"Found {len(customer_entities)} customer entities")
|
217
|
+
|
218
|
+
# Get customers with query options
|
219
|
+
from d365fo_client import QueryOptions
|
220
|
+
options = QueryOptions(
|
221
|
+
select=["CustomerAccount", "Name", "SalesCurrencyCode"],
|
222
|
+
top=10,
|
223
|
+
orderby=["Name"]
|
224
|
+
)
|
225
|
+
|
226
|
+
customers = await client.get_data("/data/CustomersV3", options)
|
227
|
+
print(f"Retrieved {len(customers['value'])} customers")
|
228
|
+
|
229
|
+
if __name__ == "__main__":
|
230
|
+
asyncio.run(main())
|
231
|
+
```
|
232
|
+
|
233
|
+
### Using Convenience Function
|
234
|
+
|
235
|
+
```python
|
236
|
+
from d365fo_client import create_client
|
237
|
+
|
238
|
+
# Quick client creation with enhanced defaults
|
239
|
+
async with create_client("https://your-fo-environment.dynamics.com") as client:
|
240
|
+
customers = await client.get_data("/data/CustomersV3", top=5)
|
241
|
+
```
|
242
|
+
|
243
|
+
## Configuration
|
244
|
+
|
245
|
+
### Authentication Options
|
246
|
+
|
247
|
+
```python
|
248
|
+
from d365fo_client import FOClientConfig
|
249
|
+
|
250
|
+
# Option 1: Default Azure credentials (recommended)
|
251
|
+
config = FOClientConfig(
|
252
|
+
base_url="https://your-fo-environment.dynamics.com",
|
253
|
+
use_default_credentials=True
|
254
|
+
)
|
255
|
+
|
256
|
+
# Option 2: Client credentials
|
257
|
+
config = FOClientConfig(
|
258
|
+
base_url="https://your-fo-environment.dynamics.com",
|
259
|
+
client_id="your-client-id",
|
260
|
+
client_secret="your-client-secret",
|
261
|
+
tenant_id="your-tenant-id",
|
262
|
+
use_default_credentials=False
|
263
|
+
)
|
264
|
+
|
265
|
+
# Option 3: With custom settings
|
266
|
+
config = FOClientConfig(
|
267
|
+
base_url="https://your-fo-environment.dynamics.com",
|
268
|
+
use_default_credentials=True,
|
269
|
+
verify_ssl=False, # For development environments
|
270
|
+
timeout=60, # Request timeout in seconds
|
271
|
+
metadata_cache_dir="./my_cache", # Custom cache directory
|
272
|
+
use_label_cache=True, # Enable label caching
|
273
|
+
label_cache_expiry_minutes=120 # Cache for 2 hours
|
274
|
+
)
|
275
|
+
```
|
276
|
+
|
277
|
+
## Core Operations
|
278
|
+
|
279
|
+
### CRUD Operations
|
280
|
+
|
281
|
+
```python
|
282
|
+
async with D365FOClient(config) as client:
|
283
|
+
# CREATE - Create new customer (supports composite keys)
|
284
|
+
new_customer = {
|
285
|
+
"CustomerAccount": "US-999",
|
286
|
+
"Name": "Test Customer",
|
287
|
+
"SalesCurrencyCode": "USD"
|
288
|
+
}
|
289
|
+
created = await client.create_data("/data/CustomersV3", new_customer)
|
290
|
+
|
291
|
+
# READ - Get single customer by key
|
292
|
+
customer = await client.get_data("/data/CustomersV3('US-001')")
|
293
|
+
|
294
|
+
# UPDATE - Update customer with optimistic concurrency
|
295
|
+
updates = {"Name": "Updated Customer Name"}
|
296
|
+
updated = await client.update_data("/data/CustomersV3('US-001')", updates)
|
297
|
+
|
298
|
+
# DELETE - Delete customer
|
299
|
+
success = await client.delete_data("/data/CustomersV3('US-999')")
|
300
|
+
print(f"Delete successful: {success}")
|
301
|
+
```
|
302
|
+
|
303
|
+
### Advanced Querying
|
304
|
+
|
305
|
+
```python
|
306
|
+
from d365fo_client import QueryOptions
|
307
|
+
|
308
|
+
# Complex query with multiple options
|
309
|
+
options = QueryOptions(
|
310
|
+
select=["CustomerAccount", "Name", "SalesCurrencyCode", "CustomerGroupId"],
|
311
|
+
filter="SalesCurrencyCode eq 'USD' and contains(Name, 'Corp')",
|
312
|
+
expand=["CustomerGroup"],
|
313
|
+
orderby=["Name desc", "CustomerAccount"],
|
314
|
+
top=50,
|
315
|
+
skip=10,
|
316
|
+
count=True
|
317
|
+
)
|
318
|
+
|
319
|
+
result = await client.get_data("/data/CustomersV3", options)
|
320
|
+
print(f"Total count: {result.get('@odata.count')}")
|
321
|
+
```
|
322
|
+
|
323
|
+
### Action Execution
|
324
|
+
|
325
|
+
```python
|
326
|
+
# Unbound action
|
327
|
+
result = await client.post_data("/data/calculateTax", {
|
328
|
+
"amount": 1000.00,
|
329
|
+
"taxGroup": "STANDARD"
|
330
|
+
})
|
331
|
+
|
332
|
+
# Bound action on entity set
|
333
|
+
result = await client.post_data("/data/CustomersV3/calculateBalances", {
|
334
|
+
"asOfDate": "2024-12-31"
|
335
|
+
})
|
336
|
+
|
337
|
+
# Bound action on specific entity instance
|
338
|
+
result = await client.post_data("/data/CustomersV3('US-001')/calculateBalance", {
|
339
|
+
"asOfDate": "2024-12-31"
|
340
|
+
})
|
341
|
+
```
|
342
|
+
|
343
|
+
### Metadata Operations
|
344
|
+
|
345
|
+
```python
|
346
|
+
# Intelligent metadata synchronization (v2 system)
|
347
|
+
sync_manager = await client.get_sync_manager()
|
348
|
+
await sync_manager.smart_sync()
|
349
|
+
|
350
|
+
# Search entities with enhanced filtering
|
351
|
+
sales_entities = await client.search_entities("sales")
|
352
|
+
print("Sales-related entities:", [e.name for e in sales_entities])
|
353
|
+
|
354
|
+
# Get detailed entity information with labels
|
355
|
+
entity_info = await client.get_public_entity_info("CustomersV3")
|
356
|
+
if entity_info:
|
357
|
+
print(f"Entity: {entity_info.name}")
|
358
|
+
print(f"Label: {entity_info.label_text}")
|
359
|
+
print(f"Data Service Enabled: {entity_info.data_service_enabled}")
|
360
|
+
|
361
|
+
# Search actions with caching
|
362
|
+
calc_actions = await client.search_actions("calculate")
|
363
|
+
print("Calculation actions:", [a.name for a in calc_actions])
|
364
|
+
|
365
|
+
# Get enumeration information
|
366
|
+
enum_info = await client.get_public_enumeration_info("NoYes")
|
367
|
+
if enum_info:
|
368
|
+
print(f"Enum: {enum_info.name}")
|
369
|
+
for member in enum_info.members:
|
370
|
+
print(f" {member.name} = {member.value}")
|
371
|
+
```
|
372
|
+
|
373
|
+
### Label Operations
|
374
|
+
|
375
|
+
```python
|
376
|
+
# Get specific label (v2 caching system)
|
377
|
+
label_text = await client.get_label_text("@SYS13342")
|
378
|
+
print(f"Label text: {label_text}")
|
379
|
+
|
380
|
+
# Get multiple labels efficiently
|
381
|
+
labels = await client.get_labels_batch([
|
382
|
+
"@SYS13342", "@SYS9490", "@GLS63332"
|
383
|
+
])
|
384
|
+
for label_id, text in labels.items():
|
385
|
+
print(f"{label_id}: {text}")
|
386
|
+
|
387
|
+
# Enhanced entity info with resolved labels
|
388
|
+
entity_info = await client.get_public_entity_info_with_labels("CustomersV3")
|
389
|
+
if entity_info.label_text:
|
390
|
+
print(f"Entity display name: {entity_info.label_text}")
|
391
|
+
|
392
|
+
# Access enhanced properties with labels
|
393
|
+
for prop in entity_info.enhanced_properties[:5]:
|
394
|
+
if hasattr(prop, 'label_text') and prop.label_text:
|
395
|
+
print(f"{prop.name}: {prop.label_text}")
|
396
|
+
```
|
397
|
+
|
398
|
+
## Error Handling
|
399
|
+
|
400
|
+
```python
|
401
|
+
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError
|
402
|
+
|
403
|
+
try:
|
404
|
+
async with D365FOClient(config) as client:
|
405
|
+
customer = await client.get_data("/data/CustomersV3('NON-EXISTENT')")
|
406
|
+
except ConnectionError as e:
|
407
|
+
print(f"Connection failed: {e}")
|
408
|
+
except AuthenticationError as e:
|
409
|
+
print(f"Authentication failed: {e}")
|
410
|
+
except D365FOClientError as e:
|
411
|
+
print(f"Client operation failed: {e}")
|
412
|
+
print(f"Status code: {e.status_code}")
|
413
|
+
print(f"Response: {e.response_text}")
|
414
|
+
```
|
415
|
+
|
416
|
+
## Development
|
417
|
+
|
418
|
+
### Setting up Development Environment
|
419
|
+
|
420
|
+
```bash
|
421
|
+
# Clone the repository
|
422
|
+
git clone https://github.com/mafzaal/d365fo-client.git
|
423
|
+
cd d365fo-client
|
424
|
+
|
425
|
+
# Install with development dependencies using uv
|
426
|
+
uv sync --dev
|
427
|
+
|
428
|
+
# Run tests
|
429
|
+
uv run pytest
|
430
|
+
|
431
|
+
# Run integration tests
|
432
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
433
|
+
|
434
|
+
# Format code
|
435
|
+
uv run black .
|
436
|
+
uv run isort .
|
437
|
+
|
438
|
+
# Type checking
|
439
|
+
uv run mypy src/
|
440
|
+
|
441
|
+
# Quality checks
|
442
|
+
.\make.ps1 quality-check # Windows PowerShell
|
443
|
+
# or
|
444
|
+
make quality-check # Unix/Linux/macOS
|
445
|
+
```
|
446
|
+
|
447
|
+
### Project Structure
|
448
|
+
|
449
|
+
```
|
450
|
+
d365fo-client/
|
451
|
+
├── src/
|
452
|
+
│ └── d365fo_client/
|
453
|
+
│ ├── __init__.py # Public API exports
|
454
|
+
│ ├── main.py # CLI entry point
|
455
|
+
│ ├── cli.py # CLI command handlers
|
456
|
+
│ ├── client.py # Enhanced D365FOClient class
|
457
|
+
│ ├── config.py # Configuration management
|
458
|
+
│ ├── auth.py # Authentication management
|
459
|
+
│ ├── session.py # HTTP session management
|
460
|
+
│ ├── crud.py # CRUD operations
|
461
|
+
│ ├── query.py # OData query utilities
|
462
|
+
│ ├── metadata.py # Legacy metadata operations
|
463
|
+
│ ├── metadata_api.py # Metadata API client
|
464
|
+
│ ├── metadata_cache.py # Metadata caching layer V2
|
465
|
+
│ ├── metadata_sync.py # Metadata synchronization V2
|
466
|
+
│ ├── labels.py # Label operations V2
|
467
|
+
│ ├── profiles.py # Profile data models
|
468
|
+
│ ├── profile_manager.py # Profile management
|
469
|
+
│ ├── models.py # Data models and configurations
|
470
|
+
│ ├── output.py # Output formatting
|
471
|
+
│ ├── utils.py # Utility functions
|
472
|
+
│ ├── exceptions.py # Custom exceptions
|
473
|
+
│ └── mcp/ # Model Context Protocol server
|
474
|
+
│ ├── __init__.py # MCP server exports
|
475
|
+
│ ├── main.py # MCP server entry point
|
476
|
+
│ ├── server.py # Core MCP server implementation
|
477
|
+
│ ├── client_manager.py# D365FO client connection pooling
|
478
|
+
│ ├── models.py # MCP-specific data models
|
479
|
+
│ ├── tools/ # MCP tool implementations (12 tools)
|
480
|
+
│ │ ├── connection_tools.py
|
481
|
+
│ │ ├── crud_tools.py
|
482
|
+
│ │ ├── metadata_tools.py
|
483
|
+
│ │ └── label_tools.py
|
484
|
+
│ ├── resources/ # MCP resource handlers (4 types)
|
485
|
+
│ │ ├── entity_handler.py
|
486
|
+
│ │ ├── metadata_handler.py
|
487
|
+
│ │ ├── environment_handler.py
|
488
|
+
│ │ └── query_handler.py
|
489
|
+
│ └── prompts/ # MCP prompt templates
|
490
|
+
├── tests/ # Comprehensive test suite
|
491
|
+
│ ├── unit/ # Unit tests (pytest-based)
|
492
|
+
│ ├── integration/ # Multi-tier integration testing
|
493
|
+
│ │ ├── mock_server/ # Mock D365 F&O API server
|
494
|
+
│ │ ├── test_mock_server.py # Mock server tests
|
495
|
+
│ │ ├── test_sandbox.py # Sandbox environment tests ✅
|
496
|
+
│ │ ├── test_live.py # Live environment tests
|
497
|
+
│ │ ├── conftest.py # Shared pytest fixtures
|
498
|
+
│ │ ├── test_runner.py # Python test execution engine
|
499
|
+
│ │ └── integration-test-simple.ps1 # PowerShell automation
|
500
|
+
│ └── test_mcp_server.py # MCP server unit tests ✅
|
501
|
+
├── scripts/ # Metadata discovery scripts
|
502
|
+
│ ├── search_data_entities.ps1 # PowerShell entity search
|
503
|
+
│ ├── get_data_entity_schema.ps1 # PowerShell schema retrieval
|
504
|
+
│ ├── search_enums.py # Python enumeration search
|
505
|
+
│ ├── get_enumeration_info.py # Python enumeration info
|
506
|
+
│ ├── search_actions.ps1 # PowerShell action search
|
507
|
+
│ └── get_action_info.py # Python action information
|
508
|
+
├── docs/ # Comprehensive documentation
|
509
|
+
├── pyproject.toml # Project configuration
|
510
|
+
└── README.md # This file
|
511
|
+
```
|
512
|
+
|
513
|
+
## Configuration Options
|
514
|
+
|
515
|
+
| Option | Type | Default | Description |
|
516
|
+
|--------|------|---------|-------------|
|
517
|
+
| `base_url` | str | Required | D365 F&O base URL |
|
518
|
+
| `client_id` | str | None | Azure AD client ID |
|
519
|
+
| `client_secret` | str | None | Azure AD client secret |
|
520
|
+
| `tenant_id` | str | None | Azure AD tenant ID |
|
521
|
+
| `use_default_credentials` | bool | True | Use Azure Default Credential |
|
522
|
+
| `verify_ssl` | bool | False | Verify SSL certificates |
|
523
|
+
| `timeout` | int | 30 | Request timeout in seconds |
|
524
|
+
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
|
525
|
+
| `use_label_cache` | bool | True | Enable label caching V2 |
|
526
|
+
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
|
527
|
+
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |
|
528
|
+
|
529
|
+
### Cache Directory Behavior
|
530
|
+
|
531
|
+
By default, the client uses platform-appropriate user cache directories:
|
532
|
+
|
533
|
+
- **Windows**: `%LOCALAPPDATA%\d365fo-client` (e.g., `C:\Users\username\AppData\Local\d365fo-client`)
|
534
|
+
- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
|
535
|
+
- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)
|
536
|
+
|
537
|
+
You can override this by explicitly setting `metadata_cache_dir`:
|
538
|
+
|
539
|
+
```python
|
540
|
+
from d365fo_client import FOClientConfig
|
541
|
+
|
542
|
+
# Use custom cache directory
|
543
|
+
config = FOClientConfig(
|
544
|
+
base_url="https://your-fo-environment.dynamics.com",
|
545
|
+
metadata_cache_dir="/custom/cache/path"
|
546
|
+
)
|
547
|
+
|
548
|
+
# Or get the default cache directory programmatically
|
549
|
+
from d365fo_client import get_user_cache_dir
|
550
|
+
|
551
|
+
cache_dir = get_user_cache_dir("my-app") # Platform-appropriate cache dir
|
552
|
+
config = FOClientConfig(
|
553
|
+
base_url="https://your-fo-environment.dynamics.com",
|
554
|
+
metadata_cache_dir=str(cache_dir)
|
555
|
+
)
|
556
|
+
```
|
557
|
+
|
558
|
+
## Testing
|
559
|
+
|
560
|
+
This project includes comprehensive testing at multiple levels to ensure reliability and quality.
|
561
|
+
|
562
|
+
### Unit Tests
|
563
|
+
|
564
|
+
Run standard unit tests for core functionality:
|
565
|
+
|
566
|
+
```bash
|
567
|
+
# Run all unit tests
|
568
|
+
uv run pytest
|
569
|
+
|
570
|
+
# Run with coverage
|
571
|
+
uv run pytest --cov=d365fo_client --cov-report=html
|
572
|
+
|
573
|
+
# Run specific test file
|
574
|
+
uv run pytest tests/test_client.py -v
|
575
|
+
```
|
576
|
+
|
577
|
+
### Integration Tests
|
578
|
+
|
579
|
+
The project includes a sophisticated multi-tier integration testing framework:
|
580
|
+
|
581
|
+
#### Quick Start
|
582
|
+
|
583
|
+
```bash
|
584
|
+
# Run sandbox integration tests (recommended)
|
585
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
586
|
+
|
587
|
+
# Run mock server tests (no external dependencies)
|
588
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
589
|
+
|
590
|
+
# Run with verbose output
|
591
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox -VerboseOutput
|
592
|
+
```
|
593
|
+
|
594
|
+
#### Test Levels
|
595
|
+
|
596
|
+
1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API
|
597
|
+
- No external dependencies
|
598
|
+
- Complete API simulation
|
599
|
+
- Ideal for CI/CD pipelines
|
600
|
+
|
601
|
+
2. **Sandbox Tests** ⭐ *(Default)* - Tests against real D365 F&O test environments
|
602
|
+
- Validates authentication
|
603
|
+
- Tests real API behavior
|
604
|
+
- Requires test environment access
|
605
|
+
|
606
|
+
3. **Live Tests** - Optional tests against production environments
|
607
|
+
- Final validation
|
608
|
+
- Performance benchmarking
|
609
|
+
- Use with caution
|
610
|
+
|
611
|
+
#### Configuration
|
612
|
+
|
613
|
+
Set up integration testing with environment variables:
|
614
|
+
|
615
|
+
```bash
|
616
|
+
# Copy the template and configure
|
617
|
+
cp tests/integration/.env.template tests/integration/.env
|
618
|
+
|
619
|
+
# Edit .env file with your settings:
|
620
|
+
INTEGRATION_TEST_LEVEL=sandbox
|
621
|
+
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
|
622
|
+
AZURE_CLIENT_ID=your-client-id
|
623
|
+
AZURE_CLIENT_SECRET=your-client-secret
|
624
|
+
AZURE_TENANT_ID=your-tenant-id
|
625
|
+
```
|
626
|
+
|
627
|
+
#### Available Commands
|
628
|
+
|
629
|
+
```bash
|
630
|
+
# Test environment setup
|
631
|
+
.\tests\integration\integration-test-simple.ps1 setup
|
632
|
+
|
633
|
+
# Dependency checking
|
634
|
+
.\tests\integration\integration-test-simple.ps1 deps-check
|
635
|
+
|
636
|
+
# Run specific test levels
|
637
|
+
.\tests\integration\integration-test-simple.ps1 test-mock
|
638
|
+
.\tests\integration\integration-test-simple.ps1 test-sandbox
|
639
|
+
.\tests\integration\integration-test-simple.ps1 test-live
|
640
|
+
|
641
|
+
# Coverage and reporting
|
642
|
+
.\tests\integration\integration-test-simple.ps1 coverage
|
643
|
+
|
644
|
+
# Clean up test artifacts
|
645
|
+
.\tests\integration\integration-test-simple.ps1 clean
|
646
|
+
```
|
647
|
+
|
648
|
+
#### Test Coverage
|
649
|
+
|
650
|
+
Integration tests cover:
|
651
|
+
|
652
|
+
- ✅ **Connection & Authentication** - Azure AD integration, SSL/TLS validation
|
653
|
+
- ✅ **Version Methods** - Application, platform, and build version retrieval
|
654
|
+
- ✅ **Metadata Operations** - Entity discovery, metadata API validation
|
655
|
+
- ✅ **Data Operations** - CRUD operations, OData query validation
|
656
|
+
- ✅ **Error Handling** - Network failures, authentication errors, invalid requests
|
657
|
+
- ✅ **Performance** - Response time validation, concurrent operations
|
658
|
+
|
659
|
+
For detailed information, see [Integration Testing Documentation](tests/integration/README.md).
|
660
|
+
|
661
|
+
### Test Results
|
662
|
+
|
663
|
+
Recent sandbox integration test results:
|
664
|
+
```
|
665
|
+
✅ 17 passed, 0 failed, 2 warnings in 37.67s
|
666
|
+
======================================================
|
667
|
+
✅ TestSandboxConnection::test_connection_success
|
668
|
+
✅ TestSandboxConnection::test_metadata_connection_success
|
669
|
+
✅ TestSandboxVersionMethods::test_get_application_version
|
670
|
+
✅ TestSandboxVersionMethods::test_get_platform_build_version
|
671
|
+
✅ TestSandboxVersionMethods::test_get_application_build_version
|
672
|
+
✅ TestSandboxVersionMethods::test_version_consistency
|
673
|
+
✅ TestSandboxMetadataOperations::test_download_metadata
|
674
|
+
✅ TestSandboxMetadataOperations::test_search_entities
|
675
|
+
✅ TestSandboxMetadataOperations::test_get_data_entities
|
676
|
+
✅ TestSandboxMetadataOperations::test_get_public_entities
|
677
|
+
✅ TestSandboxDataOperations::test_get_available_entities
|
678
|
+
✅ TestSandboxDataOperations::test_odata_query_options
|
679
|
+
✅ TestSandboxAuthentication::test_authenticated_requests
|
680
|
+
✅ TestSandboxErrorHandling::test_invalid_entity_error
|
681
|
+
✅ TestSandboxErrorHandling::test_invalid_action_error
|
682
|
+
✅ TestSandboxPerformance::test_response_times
|
683
|
+
✅ TestSandboxPerformance::test_concurrent_operations
|
684
|
+
```
|
685
|
+
|
686
|
+
## Model Context Protocol (MCP) Server
|
687
|
+
|
688
|
+
d365fo-client includes a **production-ready Model Context Protocol (MCP) server** that exposes the full capabilities of the D365 Finance & Operations client to AI assistants and other MCP-compatible tools. This enables sophisticated Dynamics 365 integration workflows through standardized protocol interactions.
|
689
|
+
|
690
|
+
### Overview
|
691
|
+
|
692
|
+
The MCP server provides:
|
693
|
+
- **12 functional tools** covering all major D365 F&O operations
|
694
|
+
- **4 resource types** with comprehensive metadata exposure
|
695
|
+
- **Production-ready** implementation with proper error handling and authentication
|
696
|
+
- **Performance optimization** with connection pooling and intelligent caching V2
|
697
|
+
- **Comprehensive testing** with 14 unit tests (100% pass rate)
|
698
|
+
- **Profile support** for multi-environment configurations
|
699
|
+
|
700
|
+
### Quick Start
|
701
|
+
|
702
|
+
#### Installation and Setup
|
703
|
+
|
704
|
+
```bash
|
705
|
+
# Install d365fo-client with MCP dependencies
|
706
|
+
pip install d365fo-client
|
707
|
+
|
708
|
+
# Set up environment variables
|
709
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
710
|
+
export AZURE_CLIENT_ID="your-client-id" # Optional with default credentials
|
711
|
+
export AZURE_CLIENT_SECRET="your-client-secret" # Optional with default credentials
|
712
|
+
export AZURE_TENANT_ID="your-tenant-id" # Optional with default credentials
|
713
|
+
|
714
|
+
# Start the MCP server
|
715
|
+
d365fo-mcp-server
|
716
|
+
```
|
717
|
+
|
718
|
+
#### Alternative: Programmatic Usage
|
719
|
+
|
720
|
+
```python
|
721
|
+
from d365fo_client.mcp import D365FOMCPServer
|
722
|
+
|
723
|
+
# Create and run server with custom configuration
|
724
|
+
config = {
|
725
|
+
"default_environment": {
|
726
|
+
"base_url": "https://your-environment.dynamics.com",
|
727
|
+
"use_default_credentials": True
|
728
|
+
}
|
729
|
+
}
|
730
|
+
|
731
|
+
server = D365FOMCPServer(config)
|
732
|
+
await server.run()
|
733
|
+
```
|
734
|
+
|
735
|
+
### MCP Tools
|
736
|
+
|
737
|
+
The server provides 12 comprehensive tools organized into functional categories:
|
738
|
+
|
739
|
+
#### Connection Tools (2 tools)
|
740
|
+
- **`d365fo_test_connection`** - Test environment connectivity and health
|
741
|
+
- **`d365fo_get_environment_info`** - Get comprehensive environment details, versions, and statistics
|
742
|
+
|
743
|
+
#### CRUD Operations (5 tools)
|
744
|
+
- **`d365fo_query_entities`** - Advanced OData querying with filters, selections, and pagination
|
745
|
+
- **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options
|
746
|
+
- **`d365fo_create_entity_record`** - Create new entity records with validation
|
747
|
+
- **`d365fo_update_entity_record`** - Update existing records with optimistic concurrency
|
748
|
+
- **`d365fo_delete_entity_record`** - Delete entity records with conflict detection
|
749
|
+
|
750
|
+
#### Metadata Tools (5 tools)
|
751
|
+
- **`d365fo_search_entities`** - Search entities by pattern with advanced filtering and FTS5 search
|
752
|
+
- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties and relationships
|
753
|
+
- **`d365fo_search_actions`** - Search available OData actions and functions
|
754
|
+
- **`d365fo_search_enums`** - Search system enumerations with filtering
|
755
|
+
- **`d365fo_get_enum_info`** - Get detailed enumeration information and values
|
756
|
+
|
757
|
+
#### Label Tools (2 tools)
|
758
|
+
- **`d365fo_get_label`** - Get single label text by ID with language support
|
759
|
+
- **`d365fo_get_labels_batch`** - Get multiple labels efficiently in batch operations
|
760
|
+
|
761
|
+
### MCP Resources
|
762
|
+
|
763
|
+
The server exposes four types of resources for discovery and access:
|
764
|
+
|
765
|
+
#### Entity Resources
|
766
|
+
Access entity metadata and sample data:
|
767
|
+
```
|
768
|
+
d365fo://entities/CustomersV3 # Customer entity with metadata and sample data
|
769
|
+
d365fo://entities/SalesOrders # Sales order entity information
|
770
|
+
d365fo://entities/Products # Product entity details
|
771
|
+
```
|
772
|
+
|
773
|
+
#### Metadata Resources
|
774
|
+
Access system-wide metadata:
|
775
|
+
```
|
776
|
+
d365fo://metadata/entities # All data entities metadata (V2 cache)
|
777
|
+
d365fo://metadata/actions # Available OData actions
|
778
|
+
d365fo://metadata/enumerations # System enumerations
|
779
|
+
d365fo://metadata/labels # System labels and translations
|
780
|
+
```
|
781
|
+
|
782
|
+
#### Environment Resources
|
783
|
+
Access environment status and information:
|
784
|
+
```
|
785
|
+
d365fo://environment/status # Environment health and connectivity
|
786
|
+
d365fo://environment/version # Version information (app, platform, build)
|
787
|
+
d365fo://environment/cache # Cache status and statistics V2
|
788
|
+
```
|
789
|
+
|
790
|
+
#### Query Resources
|
791
|
+
Access predefined and templated queries:
|
792
|
+
```
|
793
|
+
d365fo://queries/customers_recent # Recent customers query template
|
794
|
+
d365fo://queries/sales_summary # Sales summary query with parameters
|
795
|
+
```
|
796
|
+
|
797
|
+
#### Database Resources (New in V2)
|
798
|
+
Access metadata database queries:
|
799
|
+
```
|
800
|
+
d365fo://database/entities # SQL-based entity searches with FTS5
|
801
|
+
d365fo://database/actions # Action discovery with metadata
|
802
|
+
d365fo://database/statistics # Cache and performance statistics
|
803
|
+
```
|
804
|
+
|
805
|
+
### Usage Examples
|
806
|
+
|
807
|
+
#### Basic Tool Execution
|
808
|
+
|
809
|
+
```json
|
810
|
+
{
|
811
|
+
"tool": "d365fo_query_entities",
|
812
|
+
"arguments": {
|
813
|
+
"entityName": "CustomersV3",
|
814
|
+
"select": ["CustomerAccount", "Name", "Email"],
|
815
|
+
"filter": "CustomerGroup eq 'VIP'",
|
816
|
+
"top": 10
|
817
|
+
}
|
818
|
+
}
|
819
|
+
```
|
820
|
+
|
821
|
+
#### Entity Schema Discovery
|
822
|
+
|
823
|
+
```json
|
824
|
+
{
|
825
|
+
"tool": "d365fo_get_entity_schema",
|
826
|
+
"arguments": {
|
827
|
+
"entityName": "CustomersV3",
|
828
|
+
"includeProperties": true,
|
829
|
+
"resolveLabels": true,
|
830
|
+
"language": "en-US"
|
831
|
+
}
|
832
|
+
}
|
833
|
+
```
|
834
|
+
|
835
|
+
#### Environment Information
|
836
|
+
|
837
|
+
```json
|
838
|
+
{
|
839
|
+
"tool": "d365fo_get_environment_info",
|
840
|
+
"arguments": {}
|
841
|
+
}
|
842
|
+
```
|
843
|
+
|
844
|
+
### Authentication & Configuration
|
845
|
+
|
846
|
+
#### Default Credentials (Recommended)
|
847
|
+
Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):
|
848
|
+
|
849
|
+
```bash
|
850
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
851
|
+
# No additional auth environment variables needed
|
852
|
+
d365fo-mcp-server
|
853
|
+
```
|
854
|
+
|
855
|
+
#### Explicit Credentials
|
856
|
+
For service principal authentication:
|
857
|
+
|
858
|
+
```bash
|
859
|
+
export D365FO_BASE_URL="https://your-environment.dynamics.com"
|
860
|
+
export AZURE_CLIENT_ID="your-client-id"
|
861
|
+
export AZURE_CLIENT_SECRET="your-client-secret"
|
862
|
+
export AZURE_TENANT_ID="your-tenant-id"
|
863
|
+
d365fo-mcp-server
|
864
|
+
```
|
865
|
+
|
866
|
+
#### Advanced Configuration
|
867
|
+
|
868
|
+
Create a configuration file or set additional environment variables:
|
869
|
+
|
870
|
+
```bash
|
871
|
+
# Optional: Logging configuration
|
872
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
873
|
+
|
874
|
+
# Optional: Cache settings
|
875
|
+
export D365FO_CACHE_DIR="/custom/cache/path"
|
876
|
+
|
877
|
+
# Optional: Performance tuning
|
878
|
+
export D365FO_CONNECTION_TIMEOUT="60"
|
879
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="10"
|
880
|
+
```
|
881
|
+
|
882
|
+
### Integration with AI Assistants
|
883
|
+
|
884
|
+
The MCP server seamlessly integrates with AI assistants and development tools:
|
885
|
+
|
886
|
+
#### Claude Desktop Integration
|
887
|
+
Add to your Claude Desktop configuration:
|
888
|
+
|
889
|
+
```json
|
890
|
+
{
|
891
|
+
"mcpServers": {
|
892
|
+
"d365fo": {
|
893
|
+
"command": "d365fo-mcp-server",
|
894
|
+
"env": {
|
895
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com" //Optional
|
896
|
+
}
|
897
|
+
}
|
898
|
+
}
|
899
|
+
}
|
900
|
+
```
|
901
|
+
|
902
|
+
#### VS Code Integration
|
903
|
+
|
904
|
+
##### Option 1: Default Credentials (Recommended)
|
905
|
+
Add to your VS Code `mcp.json` for GitHub Copilot with MCP:
|
906
|
+
|
907
|
+
```json
|
908
|
+
{
|
909
|
+
"servers": {
|
910
|
+
"d365fo-mcp-server": {
|
911
|
+
"type": "stdio",
|
912
|
+
"command": "uvx",
|
913
|
+
"args": [
|
914
|
+
"d365fo-mcp-server"
|
915
|
+
],
|
916
|
+
"env": {
|
917
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
918
|
+
"D365FO_LOG_LEVEL": "INFO"
|
919
|
+
}
|
920
|
+
}
|
921
|
+
}
|
922
|
+
}
|
923
|
+
```
|
924
|
+
|
925
|
+
##### Option 2: Explicit Credentials
|
926
|
+
For environments requiring service principal authentication:
|
927
|
+
|
928
|
+
```json
|
929
|
+
{
|
930
|
+
"servers": {
|
931
|
+
"d365fo-mcp-server": {
|
932
|
+
"type": "stdio",
|
933
|
+
"command": "uvx",
|
934
|
+
"args": [
|
935
|
+
"d365fo-mcp-server"
|
936
|
+
],
|
937
|
+
"env": {
|
938
|
+
"D365FO_BASE_URL": "https://your-environment.dynamics.com",
|
939
|
+
"D365FO_LOG_LEVEL": "DEBUG",
|
940
|
+
"D365FO_CLIENT_ID": "${input:client_id}",
|
941
|
+
"D365FO_CLIENT_SECRET": "${input:client_secret}",
|
942
|
+
"D365FO_TENANT_ID": "${input:tenant_id}"
|
943
|
+
}
|
944
|
+
}
|
945
|
+
},
|
946
|
+
"inputs": [
|
947
|
+
{
|
948
|
+
"id": "tenant_id",
|
949
|
+
"type": "promptString",
|
950
|
+
"description": "Azure AD Tenant ID for D365 F&O authentication",
|
951
|
+
"password": true
|
952
|
+
},
|
953
|
+
{
|
954
|
+
"id": "client_id",
|
955
|
+
"type": "promptString",
|
956
|
+
"description": "Azure AD Client ID for D365 F&O authentication",
|
957
|
+
"password": true
|
958
|
+
},
|
959
|
+
{
|
960
|
+
"id": "client_secret",
|
961
|
+
"type": "promptString",
|
962
|
+
"description": "Azure AD Client Secret for D365 F&O authentication",
|
963
|
+
"password": true
|
964
|
+
}
|
965
|
+
]
|
966
|
+
}
|
967
|
+
```
|
968
|
+
|
969
|
+
**Benefits of uvx approach:**
|
970
|
+
- Always uses the latest version from the repository
|
971
|
+
- No local installation required
|
972
|
+
- Automatic dependency management
|
973
|
+
- Works across different environments
|
974
|
+
|
975
|
+
#### Custom MCP Clients
|
976
|
+
Connect using any MCP-compatible client library:
|
977
|
+
|
978
|
+
```python
|
979
|
+
from mcp import Client
|
980
|
+
|
981
|
+
async with Client("d365fo-mcp-server") as client:
|
982
|
+
# Discover available tools
|
983
|
+
tools = await client.list_tools()
|
984
|
+
|
985
|
+
# Execute operations
|
986
|
+
result = await client.call_tool(
|
987
|
+
"d365fo_query_entities",
|
988
|
+
{"entityName": "Customers", "top": 5}
|
989
|
+
)
|
990
|
+
```
|
991
|
+
|
992
|
+
### Architecture Benefits
|
993
|
+
|
994
|
+
#### For AI Assistants
|
995
|
+
- **Standardized Interface**: Consistent MCP protocol access to D365 F&O
|
996
|
+
- **Rich Metadata**: Self-describing entities and operations
|
997
|
+
- **Type Safety**: Schema validation for all operations
|
998
|
+
- **Error Context**: Detailed error information for troubleshooting
|
999
|
+
|
1000
|
+
#### For Developers
|
1001
|
+
- **Minimal Integration**: Standard MCP client libraries
|
1002
|
+
- **Comprehensive Coverage**: Full D365 F&O functionality exposed
|
1003
|
+
- **Performance Optimized**: Efficient connection and caching strategies
|
1004
|
+
- **Well Documented**: Complete API documentation and examples
|
1005
|
+
|
1006
|
+
#### For Organizations
|
1007
|
+
- **Secure Access**: Enterprise-grade authentication (Azure AD, Managed Identity)
|
1008
|
+
- **Audit Logging**: Complete operation tracking and monitoring
|
1009
|
+
- **Scalable Design**: Connection pooling and session management
|
1010
|
+
- **Maintenance Friendly**: Clear architecture and comprehensive test coverage
|
1011
|
+
|
1012
|
+
### Troubleshooting
|
1013
|
+
|
1014
|
+
#### Common Issues
|
1015
|
+
|
1016
|
+
**Connection Failures**
|
1017
|
+
```bash
|
1018
|
+
# Test connectivity
|
1019
|
+
d365fo-client get-version --base-url https://your-environment.dynamics.com
|
1020
|
+
|
1021
|
+
# Check logs
|
1022
|
+
tail -f ~/.d365fo-mcp/logs/mcp-server.log
|
1023
|
+
```
|
1024
|
+
|
1025
|
+
**Authentication Issues**
|
1026
|
+
```bash
|
1027
|
+
# Verify Azure CLI authentication
|
1028
|
+
az account show
|
1029
|
+
|
1030
|
+
# Test with explicit credentials
|
1031
|
+
export AZURE_CLIENT_ID="your-client-id"
|
1032
|
+
# ... set other variables
|
1033
|
+
d365fo-mcp-server
|
1034
|
+
```
|
1035
|
+
|
1036
|
+
**Performance Issues**
|
1037
|
+
```bash
|
1038
|
+
# Enable debug logging
|
1039
|
+
export D365FO_LOG_LEVEL="DEBUG"
|
1040
|
+
|
1041
|
+
# Adjust connection settings
|
1042
|
+
export D365FO_CONNECTION_TIMEOUT="120"
|
1043
|
+
export D365FO_MAX_CONCURRENT_REQUESTS="5"
|
1044
|
+
```
|
1045
|
+
|
1046
|
+
#### Getting Help
|
1047
|
+
|
1048
|
+
- **Logs**: Check `~/.d365fo-mcp/logs/mcp-server.log` for detailed error information
|
1049
|
+
- **Environment**: Use `d365fo_get_environment_info` tool to check system status
|
1050
|
+
- **Documentation**: See [MCP Implementation Summary](docs/MCP_IMPLEMENTATION_SUMMARY.md) for technical details
|
1051
|
+
- **Issues**: Report problems at [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)
|
1052
|
+
|
1053
|
+
## Contributing
|
1054
|
+
|
1055
|
+
1. Fork the repository
|
1056
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
1057
|
+
3. Make your changes
|
1058
|
+
4. Run tests (`uv run pytest`)
|
1059
|
+
5. Run integration tests (`.\tests\integration\integration-test-simple.ps1 test-sandbox`)
|
1060
|
+
6. Format code (`uv run black . && uv run isort .`)
|
1061
|
+
7. Commit changes (`git commit -m 'Add amazing feature'`)
|
1062
|
+
8. Push to branch (`git push origin feature/amazing-feature`)
|
1063
|
+
9. Open a Pull Request
|
1064
|
+
|
1065
|
+
## License
|
1066
|
+
|
1067
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
1068
|
+
|
1069
|
+
## Changelog
|
1070
|
+
|
1071
|
+
See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.
|
1072
|
+
|
1073
|
+
## Support
|
1074
|
+
|
1075
|
+
- 📧 Email: mo@thedataguy.pro
|
1076
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)
|
1077
|
+
|
1078
|
+
|
1079
|
+
## Related Projects
|
1080
|
+
|
1081
|
+
- [Microsoft Dynamics 365](https://dynamics.microsoft.com/)
|
1082
|
+
- [OData](https://www.odata.org/)
|
1083
|
+
- [Azure Identity](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity)
|
1084
|
+
- [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol/python-sdk) - For AI assistant integration
|