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,706 @@
|
|
1
|
+
"""Profile management tools for MCP server."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
from typing import Any, Dict, List
|
6
|
+
|
7
|
+
from mcp import Tool
|
8
|
+
from mcp.types import TextContent
|
9
|
+
|
10
|
+
from ...profile_manager import EnvironmentProfile, ProfileManager
|
11
|
+
from ..client_manager import D365FOClientManager
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class ProfileTools:
|
17
|
+
"""Profile management tools for the MCP server."""
|
18
|
+
|
19
|
+
def __init__(self, client_manager: D365FOClientManager):
|
20
|
+
"""Initialize profile tools.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
client_manager: D365FO client manager instance
|
24
|
+
"""
|
25
|
+
self.client_manager = client_manager
|
26
|
+
self.profile_manager = ProfileManager()
|
27
|
+
|
28
|
+
def get_tools(self) -> List[Tool]:
|
29
|
+
"""Get list of profile management tools.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
List of Tool definitions
|
33
|
+
"""
|
34
|
+
return [
|
35
|
+
self._get_list_profiles_tool(),
|
36
|
+
self._get_get_profile_tool(),
|
37
|
+
self._get_create_profile_tool(),
|
38
|
+
self._get_update_profile_tool(),
|
39
|
+
self._get_delete_profile_tool(),
|
40
|
+
self._get_set_default_profile_tool(),
|
41
|
+
self._get_get_default_profile_tool(),
|
42
|
+
self._get_validate_profile_tool(),
|
43
|
+
self._get_test_profile_connection_tool(),
|
44
|
+
]
|
45
|
+
|
46
|
+
def _get_list_profiles_tool(self) -> Tool:
|
47
|
+
"""Get list profiles tool definition."""
|
48
|
+
return Tool(
|
49
|
+
name="d365fo_list_profiles",
|
50
|
+
description="List all available D365FO environment profiles",
|
51
|
+
inputSchema={"type": "object", "properties": {}},
|
52
|
+
)
|
53
|
+
|
54
|
+
def _get_get_profile_tool(self) -> Tool:
|
55
|
+
"""Get profile tool definition."""
|
56
|
+
return Tool(
|
57
|
+
name="d365fo_get_profile",
|
58
|
+
description="Get details of a specific D365FO environment profile",
|
59
|
+
inputSchema={
|
60
|
+
"type": "object",
|
61
|
+
"properties": {
|
62
|
+
"profileName": {
|
63
|
+
"type": "string",
|
64
|
+
"description": "Name of the profile to retrieve",
|
65
|
+
}
|
66
|
+
},
|
67
|
+
"required": ["profileName"],
|
68
|
+
},
|
69
|
+
)
|
70
|
+
|
71
|
+
def _get_create_profile_tool(self) -> Tool:
|
72
|
+
"""Get create profile tool definition."""
|
73
|
+
return Tool(
|
74
|
+
name="d365fo_create_profile",
|
75
|
+
description="Create a new D365FO environment profile",
|
76
|
+
inputSchema={
|
77
|
+
"type": "object",
|
78
|
+
"properties": {
|
79
|
+
"name": {"type": "string", "description": "Profile name"},
|
80
|
+
"baseUrl": {"type": "string", "description": "D365FO base URL"},
|
81
|
+
"authMode": {
|
82
|
+
"type": "string",
|
83
|
+
"description": "Authentication mode",
|
84
|
+
"enum": ["default", "client_credentials"],
|
85
|
+
"default": "default",
|
86
|
+
},
|
87
|
+
"clientId": {
|
88
|
+
"type": "string",
|
89
|
+
"description": "Azure client ID (for client_credentials auth)",
|
90
|
+
},
|
91
|
+
"clientSecret": {
|
92
|
+
"type": "string",
|
93
|
+
"description": "Azure client secret (for client_credentials auth)",
|
94
|
+
},
|
95
|
+
"tenantId": {
|
96
|
+
"type": "string",
|
97
|
+
"description": "Azure tenant ID (for client_credentials auth)",
|
98
|
+
},
|
99
|
+
"verifySsl": {
|
100
|
+
"type": "boolean",
|
101
|
+
"description": "Whether to verify SSL certificates",
|
102
|
+
"default": True,
|
103
|
+
},
|
104
|
+
"timeout": {
|
105
|
+
"type": "integer",
|
106
|
+
"description": "Request timeout in seconds",
|
107
|
+
"minimum": 1,
|
108
|
+
"maximum": 300,
|
109
|
+
"default": 60,
|
110
|
+
},
|
111
|
+
"useLabelCache": {
|
112
|
+
"type": "boolean",
|
113
|
+
"description": "Whether to enable label caching",
|
114
|
+
"default": True,
|
115
|
+
},
|
116
|
+
"labelCacheExpiryMinutes": {
|
117
|
+
"type": "integer",
|
118
|
+
"description": "Label cache expiry in minutes",
|
119
|
+
"minimum": 1,
|
120
|
+
"default": 60,
|
121
|
+
},
|
122
|
+
"language": {
|
123
|
+
"type": "string",
|
124
|
+
"description": "Default language code",
|
125
|
+
"default": "en-US",
|
126
|
+
},
|
127
|
+
"cacheDir": {
|
128
|
+
"type": "string",
|
129
|
+
"description": "Cache directory path",
|
130
|
+
},
|
131
|
+
"description": {
|
132
|
+
"type": "string",
|
133
|
+
"description": "Profile description",
|
134
|
+
},
|
135
|
+
"setAsDefault": {
|
136
|
+
"type": "boolean",
|
137
|
+
"description": "Set as default profile",
|
138
|
+
"default": False,
|
139
|
+
},
|
140
|
+
},
|
141
|
+
"required": ["name", "baseUrl"],
|
142
|
+
},
|
143
|
+
)
|
144
|
+
|
145
|
+
def _get_update_profile_tool(self) -> Tool:
|
146
|
+
"""Get update profile tool definition."""
|
147
|
+
return Tool(
|
148
|
+
name="d365fo_update_profile",
|
149
|
+
description="Update an existing D365FO environment profile",
|
150
|
+
inputSchema={
|
151
|
+
"type": "object",
|
152
|
+
"properties": {
|
153
|
+
"name": {"type": "string", "description": "Profile name"},
|
154
|
+
"baseUrl": {"type": "string", "description": "D365FO base URL"},
|
155
|
+
"authMode": {
|
156
|
+
"type": "string",
|
157
|
+
"description": "Authentication mode",
|
158
|
+
"enum": ["default", "client_credentials"],
|
159
|
+
},
|
160
|
+
"clientId": {"type": "string", "description": "Azure client ID"},
|
161
|
+
"clientSecret": {
|
162
|
+
"type": "string",
|
163
|
+
"description": "Azure client secret",
|
164
|
+
},
|
165
|
+
"tenantId": {"type": "string", "description": "Azure tenant ID"},
|
166
|
+
"verifySsl": {
|
167
|
+
"type": "boolean",
|
168
|
+
"description": "Whether to verify SSL certificates",
|
169
|
+
},
|
170
|
+
"timeout": {
|
171
|
+
"type": "integer",
|
172
|
+
"description": "Request timeout in seconds",
|
173
|
+
"minimum": 1,
|
174
|
+
"maximum": 300,
|
175
|
+
},
|
176
|
+
"useLabelCache": {
|
177
|
+
"type": "boolean",
|
178
|
+
"description": "Whether to enable label caching",
|
179
|
+
},
|
180
|
+
"labelCacheExpiryMinutes": {
|
181
|
+
"type": "integer",
|
182
|
+
"description": "Label cache expiry in minutes",
|
183
|
+
"minimum": 1,
|
184
|
+
},
|
185
|
+
"language": {
|
186
|
+
"type": "string",
|
187
|
+
"description": "Default language code",
|
188
|
+
},
|
189
|
+
"cacheDir": {
|
190
|
+
"type": "string",
|
191
|
+
"description": "Cache directory path",
|
192
|
+
},
|
193
|
+
"description": {
|
194
|
+
"type": "string",
|
195
|
+
"description": "Profile description",
|
196
|
+
},
|
197
|
+
},
|
198
|
+
"required": ["name"],
|
199
|
+
},
|
200
|
+
)
|
201
|
+
|
202
|
+
def _get_delete_profile_tool(self) -> Tool:
|
203
|
+
"""Get delete profile tool definition."""
|
204
|
+
return Tool(
|
205
|
+
name="d365fo_delete_profile",
|
206
|
+
description="Delete a D365FO environment profile",
|
207
|
+
inputSchema={
|
208
|
+
"type": "object",
|
209
|
+
"properties": {
|
210
|
+
"profileName": {
|
211
|
+
"type": "string",
|
212
|
+
"description": "Name of the profile to delete",
|
213
|
+
}
|
214
|
+
},
|
215
|
+
"required": ["profileName"],
|
216
|
+
},
|
217
|
+
)
|
218
|
+
|
219
|
+
def _get_set_default_profile_tool(self) -> Tool:
|
220
|
+
"""Get set default profile tool definition."""
|
221
|
+
return Tool(
|
222
|
+
name="d365fo_set_default_profile",
|
223
|
+
description="Set the default D365FO environment profile",
|
224
|
+
inputSchema={
|
225
|
+
"type": "object",
|
226
|
+
"properties": {
|
227
|
+
"profileName": {
|
228
|
+
"type": "string",
|
229
|
+
"description": "Name of the profile to set as default",
|
230
|
+
}
|
231
|
+
},
|
232
|
+
"required": ["profileName"],
|
233
|
+
},
|
234
|
+
)
|
235
|
+
|
236
|
+
def _get_get_default_profile_tool(self) -> Tool:
|
237
|
+
"""Get default profile tool definition."""
|
238
|
+
return Tool(
|
239
|
+
name="d365fo_get_default_profile",
|
240
|
+
description="Get the current default D365FO environment profile",
|
241
|
+
inputSchema={"type": "object", "properties": {}},
|
242
|
+
)
|
243
|
+
|
244
|
+
def _get_validate_profile_tool(self) -> Tool:
|
245
|
+
"""Get validate profile tool definition."""
|
246
|
+
return Tool(
|
247
|
+
name="d365fo_validate_profile",
|
248
|
+
description="Validate a D365FO environment profile configuration",
|
249
|
+
inputSchema={
|
250
|
+
"type": "object",
|
251
|
+
"properties": {
|
252
|
+
"profileName": {
|
253
|
+
"type": "string",
|
254
|
+
"description": "Name of the profile to validate",
|
255
|
+
}
|
256
|
+
},
|
257
|
+
"required": ["profileName"],
|
258
|
+
},
|
259
|
+
)
|
260
|
+
|
261
|
+
def _get_test_profile_connection_tool(self) -> Tool:
|
262
|
+
"""Get test profile connection tool definition."""
|
263
|
+
return Tool(
|
264
|
+
name="d365fo_test_profile_connection",
|
265
|
+
description="Test connection for a specific D365FO environment profile",
|
266
|
+
inputSchema={
|
267
|
+
"type": "object",
|
268
|
+
"properties": {
|
269
|
+
"profileName": {
|
270
|
+
"type": "string",
|
271
|
+
"description": "Name of the profile to test",
|
272
|
+
}
|
273
|
+
},
|
274
|
+
"required": ["profileName"],
|
275
|
+
},
|
276
|
+
)
|
277
|
+
|
278
|
+
async def execute_list_profiles(self, arguments: dict) -> List[TextContent]:
|
279
|
+
"""Execute list profiles tool.
|
280
|
+
|
281
|
+
Args:
|
282
|
+
arguments: Tool arguments
|
283
|
+
|
284
|
+
Returns:
|
285
|
+
List of TextContent responses
|
286
|
+
"""
|
287
|
+
try:
|
288
|
+
profiles = self.profile_manager.list_profiles()
|
289
|
+
default_profile = self.profile_manager.get_default_profile()
|
290
|
+
|
291
|
+
profile_list = []
|
292
|
+
for name, profile in profiles.items():
|
293
|
+
profile_info = {
|
294
|
+
"name": profile.name,
|
295
|
+
"baseUrl": profile.base_url,
|
296
|
+
"authMode": profile.auth_mode,
|
297
|
+
"verifySsl": profile.verify_ssl,
|
298
|
+
"language": profile.language,
|
299
|
+
"isDefault": default_profile and default_profile.name == name,
|
300
|
+
"description": profile.description,
|
301
|
+
}
|
302
|
+
profile_list.append(profile_info)
|
303
|
+
|
304
|
+
response = {
|
305
|
+
"profiles": profile_list,
|
306
|
+
"totalCount": len(profile_list),
|
307
|
+
"defaultProfile": default_profile.name if default_profile else None,
|
308
|
+
}
|
309
|
+
|
310
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
311
|
+
|
312
|
+
except Exception as e:
|
313
|
+
logger.error(f"List profiles failed: {e}")
|
314
|
+
error_response = {"error": str(e), "tool": "d365fo_list_profiles"}
|
315
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
316
|
+
|
317
|
+
async def execute_get_profile(self, arguments: dict) -> List[TextContent]:
|
318
|
+
"""Execute get profile tool.
|
319
|
+
|
320
|
+
Args:
|
321
|
+
arguments: Tool arguments
|
322
|
+
|
323
|
+
Returns:
|
324
|
+
List of TextContent responses
|
325
|
+
"""
|
326
|
+
try:
|
327
|
+
profile_name = arguments["profileName"]
|
328
|
+
profile = self.profile_manager.get_profile(profile_name)
|
329
|
+
|
330
|
+
if not profile:
|
331
|
+
error_response = {
|
332
|
+
"error": f"Profile not found: {profile_name}",
|
333
|
+
"tool": "d365fo_get_profile",
|
334
|
+
}
|
335
|
+
return [
|
336
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
337
|
+
]
|
338
|
+
|
339
|
+
# Convert profile to dict, excluding sensitive data
|
340
|
+
profile_dict = {
|
341
|
+
"name": profile.name,
|
342
|
+
"baseUrl": profile.base_url,
|
343
|
+
"authMode": profile.auth_mode,
|
344
|
+
"verifySsl": profile.verify_ssl,
|
345
|
+
"timeout": profile.timeout,
|
346
|
+
"useLabelCache": profile.use_label_cache,
|
347
|
+
"labelCacheExpiryMinutes": profile.label_cache_expiry_minutes,
|
348
|
+
"language": profile.language,
|
349
|
+
"cacheDir": profile.cache_dir,
|
350
|
+
"description": profile.description,
|
351
|
+
}
|
352
|
+
|
353
|
+
# Add auth details if available (but not secrets)
|
354
|
+
if profile.client_id:
|
355
|
+
profile_dict["clientId"] = profile.client_id
|
356
|
+
if profile.tenant_id:
|
357
|
+
profile_dict["tenantId"] = profile.tenant_id
|
358
|
+
|
359
|
+
return [TextContent(type="text", text=json.dumps(profile_dict, indent=2))]
|
360
|
+
|
361
|
+
except Exception as e:
|
362
|
+
logger.error(f"Get profile failed: {e}")
|
363
|
+
error_response = {
|
364
|
+
"error": str(e),
|
365
|
+
"tool": "d365fo_get_profile",
|
366
|
+
"arguments": arguments,
|
367
|
+
}
|
368
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
369
|
+
|
370
|
+
async def execute_create_profile(self, arguments: dict) -> List[TextContent]:
|
371
|
+
"""Execute create profile tool.
|
372
|
+
|
373
|
+
Args:
|
374
|
+
arguments: Tool arguments
|
375
|
+
|
376
|
+
Returns:
|
377
|
+
List of TextContent responses
|
378
|
+
"""
|
379
|
+
try:
|
380
|
+
# Extract parameters
|
381
|
+
name = arguments["name"]
|
382
|
+
base_url = arguments["baseUrl"]
|
383
|
+
auth_mode = arguments.get("authMode", "default")
|
384
|
+
client_id = arguments.get("clientId")
|
385
|
+
client_secret = arguments.get("clientSecret")
|
386
|
+
tenant_id = arguments.get("tenantId")
|
387
|
+
verify_ssl = arguments.get("verifySsl", True)
|
388
|
+
timeout = arguments.get("timeout", 60)
|
389
|
+
use_label_cache = arguments.get("useLabelCache", True)
|
390
|
+
label_cache_expiry_minutes = arguments.get("labelCacheExpiryMinutes", 60)
|
391
|
+
language = arguments.get("language", "en-US")
|
392
|
+
cache_dir = arguments.get("cacheDir")
|
393
|
+
description = arguments.get("description")
|
394
|
+
set_as_default = arguments.get("setAsDefault", False)
|
395
|
+
|
396
|
+
# Create profile
|
397
|
+
success = self.profile_manager.create_profile(
|
398
|
+
name=name,
|
399
|
+
base_url=base_url,
|
400
|
+
auth_mode=auth_mode,
|
401
|
+
client_id=client_id,
|
402
|
+
client_secret=client_secret,
|
403
|
+
tenant_id=tenant_id,
|
404
|
+
verify_ssl=verify_ssl,
|
405
|
+
timeout=timeout,
|
406
|
+
use_label_cache=use_label_cache,
|
407
|
+
label_cache_expiry_minutes=label_cache_expiry_minutes,
|
408
|
+
language=language,
|
409
|
+
cache_dir=cache_dir,
|
410
|
+
description=description,
|
411
|
+
)
|
412
|
+
|
413
|
+
if not success:
|
414
|
+
error_response = {
|
415
|
+
"error": f"Failed to create profile: {name}",
|
416
|
+
"tool": "d365fo_create_profile",
|
417
|
+
}
|
418
|
+
return [
|
419
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
420
|
+
]
|
421
|
+
|
422
|
+
# Set as default if requested
|
423
|
+
if set_as_default:
|
424
|
+
self.profile_manager.set_default_profile(name)
|
425
|
+
|
426
|
+
response = {
|
427
|
+
"success": True,
|
428
|
+
"profileName": name,
|
429
|
+
"message": f"Profile '{name}' created successfully",
|
430
|
+
"isDefault": set_as_default,
|
431
|
+
}
|
432
|
+
|
433
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
434
|
+
|
435
|
+
except Exception as e:
|
436
|
+
logger.error(f"Create profile failed: {e}")
|
437
|
+
error_response = {
|
438
|
+
"error": str(e),
|
439
|
+
"tool": "d365fo_create_profile",
|
440
|
+
"arguments": arguments,
|
441
|
+
}
|
442
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
443
|
+
|
444
|
+
async def execute_update_profile(self, arguments: dict) -> List[TextContent]:
|
445
|
+
"""Execute update profile tool.
|
446
|
+
|
447
|
+
Args:
|
448
|
+
arguments: Tool arguments
|
449
|
+
|
450
|
+
Returns:
|
451
|
+
List of TextContent responses
|
452
|
+
"""
|
453
|
+
try:
|
454
|
+
name = arguments["name"]
|
455
|
+
|
456
|
+
# Remove name from update parameters
|
457
|
+
update_params = {k: v for k, v in arguments.items() if k != "name"}
|
458
|
+
|
459
|
+
# Convert parameter names to match profile manager
|
460
|
+
param_mapping = {
|
461
|
+
"baseUrl": "base_url",
|
462
|
+
"authMode": "auth_mode",
|
463
|
+
"clientId": "client_id",
|
464
|
+
"clientSecret": "client_secret",
|
465
|
+
"tenantId": "tenant_id",
|
466
|
+
"verifySsl": "verify_ssl",
|
467
|
+
"useLabelCache": "use_label_cache",
|
468
|
+
"labelCacheExpiryMinutes": "label_cache_expiry_minutes",
|
469
|
+
"cacheDir": "cache_dir",
|
470
|
+
}
|
471
|
+
|
472
|
+
mapped_params = {}
|
473
|
+
for key, value in update_params.items():
|
474
|
+
mapped_key = param_mapping.get(key, key)
|
475
|
+
mapped_params[mapped_key] = value
|
476
|
+
|
477
|
+
success = self.profile_manager.update_profile(name, **mapped_params)
|
478
|
+
|
479
|
+
if not success:
|
480
|
+
error_response = {
|
481
|
+
"error": f"Failed to update profile: {name}",
|
482
|
+
"tool": "d365fo_update_profile",
|
483
|
+
}
|
484
|
+
return [
|
485
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
486
|
+
]
|
487
|
+
|
488
|
+
response = {
|
489
|
+
"success": True,
|
490
|
+
"profileName": name,
|
491
|
+
"message": f"Profile '{name}' updated successfully",
|
492
|
+
"updatedFields": list(update_params.keys()),
|
493
|
+
}
|
494
|
+
|
495
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
496
|
+
|
497
|
+
except Exception as e:
|
498
|
+
logger.error(f"Update profile failed: {e}")
|
499
|
+
error_response = {
|
500
|
+
"error": str(e),
|
501
|
+
"tool": "d365fo_update_profile",
|
502
|
+
"arguments": arguments,
|
503
|
+
}
|
504
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
505
|
+
|
506
|
+
async def execute_delete_profile(self, arguments: dict) -> List[TextContent]:
|
507
|
+
"""Execute delete profile tool.
|
508
|
+
|
509
|
+
Args:
|
510
|
+
arguments: Tool arguments
|
511
|
+
|
512
|
+
Returns:
|
513
|
+
List of TextContent responses
|
514
|
+
"""
|
515
|
+
try:
|
516
|
+
profile_name = arguments["profileName"]
|
517
|
+
success = self.profile_manager.delete_profile(profile_name)
|
518
|
+
|
519
|
+
if not success:
|
520
|
+
error_response = {
|
521
|
+
"error": f"Profile not found or failed to delete: {profile_name}",
|
522
|
+
"tool": "d365fo_delete_profile",
|
523
|
+
}
|
524
|
+
return [
|
525
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
526
|
+
]
|
527
|
+
|
528
|
+
response = {
|
529
|
+
"success": True,
|
530
|
+
"profileName": profile_name,
|
531
|
+
"message": f"Profile '{profile_name}' deleted successfully",
|
532
|
+
}
|
533
|
+
|
534
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
535
|
+
|
536
|
+
except Exception as e:
|
537
|
+
logger.error(f"Delete profile failed: {e}")
|
538
|
+
error_response = {
|
539
|
+
"error": str(e),
|
540
|
+
"tool": "d365fo_delete_profile",
|
541
|
+
"arguments": arguments,
|
542
|
+
}
|
543
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
544
|
+
|
545
|
+
async def execute_set_default_profile(self, arguments: dict) -> List[TextContent]:
|
546
|
+
"""Execute set default profile tool.
|
547
|
+
|
548
|
+
Args:
|
549
|
+
arguments: Tool arguments
|
550
|
+
|
551
|
+
Returns:
|
552
|
+
List of TextContent responses
|
553
|
+
"""
|
554
|
+
try:
|
555
|
+
profile_name = arguments["profileName"]
|
556
|
+
success = self.profile_manager.set_default_profile(profile_name)
|
557
|
+
|
558
|
+
if not success:
|
559
|
+
error_response = {
|
560
|
+
"error": f"Profile not found: {profile_name}",
|
561
|
+
"tool": "d365fo_set_default_profile",
|
562
|
+
}
|
563
|
+
return [
|
564
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
565
|
+
]
|
566
|
+
|
567
|
+
response = {
|
568
|
+
"success": True,
|
569
|
+
"profileName": profile_name,
|
570
|
+
"message": f"Default profile set to '{profile_name}'",
|
571
|
+
}
|
572
|
+
|
573
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
574
|
+
|
575
|
+
except Exception as e:
|
576
|
+
logger.error(f"Set default profile failed: {e}")
|
577
|
+
error_response = {
|
578
|
+
"error": str(e),
|
579
|
+
"tool": "d365fo_set_default_profile",
|
580
|
+
"arguments": arguments,
|
581
|
+
}
|
582
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
583
|
+
|
584
|
+
async def execute_get_default_profile(self, arguments: dict) -> List[TextContent]:
|
585
|
+
"""Execute get default profile tool.
|
586
|
+
|
587
|
+
Args:
|
588
|
+
arguments: Tool arguments
|
589
|
+
|
590
|
+
Returns:
|
591
|
+
List of TextContent responses
|
592
|
+
"""
|
593
|
+
try:
|
594
|
+
default_profile = self.profile_manager.get_default_profile()
|
595
|
+
|
596
|
+
if not default_profile:
|
597
|
+
response = {"defaultProfile": None, "message": "No default profile set"}
|
598
|
+
else:
|
599
|
+
response = {
|
600
|
+
"defaultProfile": {
|
601
|
+
"name": default_profile.name,
|
602
|
+
"baseUrl": default_profile.base_url,
|
603
|
+
"authMode": default_profile.auth_mode,
|
604
|
+
"description": default_profile.description,
|
605
|
+
},
|
606
|
+
"message": f"Default profile is '{default_profile.name}'",
|
607
|
+
}
|
608
|
+
|
609
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
610
|
+
|
611
|
+
except Exception as e:
|
612
|
+
logger.error(f"Get default profile failed: {e}")
|
613
|
+
error_response = {"error": str(e), "tool": "d365fo_get_default_profile"}
|
614
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
615
|
+
|
616
|
+
async def execute_validate_profile(self, arguments: dict) -> List[TextContent]:
|
617
|
+
"""Execute validate profile tool.
|
618
|
+
|
619
|
+
Args:
|
620
|
+
arguments: Tool arguments
|
621
|
+
|
622
|
+
Returns:
|
623
|
+
List of TextContent responses
|
624
|
+
"""
|
625
|
+
try:
|
626
|
+
profile_name = arguments["profileName"]
|
627
|
+
profile = self.profile_manager.get_profile(profile_name)
|
628
|
+
|
629
|
+
if not profile:
|
630
|
+
error_response = {
|
631
|
+
"error": f"Profile not found: {profile_name}",
|
632
|
+
"tool": "d365fo_validate_profile",
|
633
|
+
}
|
634
|
+
return [
|
635
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
636
|
+
]
|
637
|
+
|
638
|
+
validation_errors = self.profile_manager.validate_profile(profile)
|
639
|
+
|
640
|
+
response = {
|
641
|
+
"profileName": profile_name,
|
642
|
+
"isValid": len(validation_errors) == 0,
|
643
|
+
"errors": validation_errors,
|
644
|
+
"message": (
|
645
|
+
"Profile is valid"
|
646
|
+
if not validation_errors
|
647
|
+
else "Profile has validation errors"
|
648
|
+
),
|
649
|
+
}
|
650
|
+
|
651
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
652
|
+
|
653
|
+
except Exception as e:
|
654
|
+
logger.error(f"Validate profile failed: {e}")
|
655
|
+
error_response = {
|
656
|
+
"error": str(e),
|
657
|
+
"tool": "d365fo_validate_profile",
|
658
|
+
"arguments": arguments,
|
659
|
+
}
|
660
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|
661
|
+
|
662
|
+
async def execute_test_profile_connection(
|
663
|
+
self, arguments: dict
|
664
|
+
) -> List[TextContent]:
|
665
|
+
"""Execute test profile connection tool.
|
666
|
+
|
667
|
+
Args:
|
668
|
+
arguments: Tool arguments
|
669
|
+
|
670
|
+
Returns:
|
671
|
+
List of TextContent responses
|
672
|
+
"""
|
673
|
+
try:
|
674
|
+
profile_name = arguments["profileName"]
|
675
|
+
profile = self.profile_manager.get_profile(profile_name)
|
676
|
+
|
677
|
+
if not profile:
|
678
|
+
error_response = {
|
679
|
+
"error": f"Profile not found: {profile_name}",
|
680
|
+
"tool": "d365fo_test_profile_connection",
|
681
|
+
}
|
682
|
+
return [
|
683
|
+
TextContent(type="text", text=json.dumps(error_response, indent=2))
|
684
|
+
]
|
685
|
+
|
686
|
+
# Test connection using the client manager
|
687
|
+
success = await self.client_manager.test_connection(profile_name)
|
688
|
+
|
689
|
+
response = {
|
690
|
+
"profileName": profile_name,
|
691
|
+
"success": success,
|
692
|
+
"baseUrl": profile.base_url,
|
693
|
+
"authMode": profile.auth_mode,
|
694
|
+
"message": "Connection successful" if success else "Connection failed",
|
695
|
+
}
|
696
|
+
|
697
|
+
return [TextContent(type="text", text=json.dumps(response, indent=2))]
|
698
|
+
|
699
|
+
except Exception as e:
|
700
|
+
logger.error(f"Test profile connection failed: {e}")
|
701
|
+
error_response = {
|
702
|
+
"error": str(e),
|
703
|
+
"tool": "d365fo_test_profile_connection",
|
704
|
+
"arguments": arguments,
|
705
|
+
}
|
706
|
+
return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
|