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.
Files changed (51) hide show
  1. d365fo_client/__init__.py +305 -0
  2. d365fo_client/auth.py +93 -0
  3. d365fo_client/cli.py +700 -0
  4. d365fo_client/client.py +1454 -0
  5. d365fo_client/config.py +304 -0
  6. d365fo_client/crud.py +200 -0
  7. d365fo_client/exceptions.py +49 -0
  8. d365fo_client/labels.py +528 -0
  9. d365fo_client/main.py +502 -0
  10. d365fo_client/mcp/__init__.py +16 -0
  11. d365fo_client/mcp/client_manager.py +276 -0
  12. d365fo_client/mcp/main.py +98 -0
  13. d365fo_client/mcp/models.py +371 -0
  14. d365fo_client/mcp/prompts/__init__.py +43 -0
  15. d365fo_client/mcp/prompts/action_execution.py +480 -0
  16. d365fo_client/mcp/prompts/sequence_analysis.py +349 -0
  17. d365fo_client/mcp/resources/__init__.py +15 -0
  18. d365fo_client/mcp/resources/database_handler.py +555 -0
  19. d365fo_client/mcp/resources/entity_handler.py +176 -0
  20. d365fo_client/mcp/resources/environment_handler.py +132 -0
  21. d365fo_client/mcp/resources/metadata_handler.py +283 -0
  22. d365fo_client/mcp/resources/query_handler.py +135 -0
  23. d365fo_client/mcp/server.py +432 -0
  24. d365fo_client/mcp/tools/__init__.py +17 -0
  25. d365fo_client/mcp/tools/connection_tools.py +175 -0
  26. d365fo_client/mcp/tools/crud_tools.py +579 -0
  27. d365fo_client/mcp/tools/database_tools.py +813 -0
  28. d365fo_client/mcp/tools/label_tools.py +189 -0
  29. d365fo_client/mcp/tools/metadata_tools.py +766 -0
  30. d365fo_client/mcp/tools/profile_tools.py +706 -0
  31. d365fo_client/metadata_api.py +793 -0
  32. d365fo_client/metadata_v2/__init__.py +59 -0
  33. d365fo_client/metadata_v2/cache_v2.py +1372 -0
  34. d365fo_client/metadata_v2/database_v2.py +585 -0
  35. d365fo_client/metadata_v2/global_version_manager.py +573 -0
  36. d365fo_client/metadata_v2/search_engine_v2.py +423 -0
  37. d365fo_client/metadata_v2/sync_manager_v2.py +819 -0
  38. d365fo_client/metadata_v2/version_detector.py +439 -0
  39. d365fo_client/models.py +862 -0
  40. d365fo_client/output.py +181 -0
  41. d365fo_client/profile_manager.py +342 -0
  42. d365fo_client/profiles.py +178 -0
  43. d365fo_client/query.py +162 -0
  44. d365fo_client/session.py +60 -0
  45. d365fo_client/utils.py +196 -0
  46. d365fo_client-0.1.0.dist-info/METADATA +1084 -0
  47. d365fo_client-0.1.0.dist-info/RECORD +51 -0
  48. d365fo_client-0.1.0.dist-info/WHEEL +5 -0
  49. d365fo_client-0.1.0.dist-info/entry_points.txt +3 -0
  50. d365fo_client-0.1.0.dist-info/licenses/LICENSE +21 -0
  51. d365fo_client-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,189 @@
1
+ """Label tools for MCP server."""
2
+
3
+ import json
4
+ import logging
5
+ import time
6
+ from typing import List
7
+
8
+ from mcp import Tool
9
+ from mcp.types import TextContent
10
+
11
+ from ..client_manager import D365FOClientManager
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class LabelTools:
17
+ """Label and localization tools for the MCP server."""
18
+
19
+ def __init__(self, client_manager: D365FOClientManager):
20
+ """Initialize label tools.
21
+
22
+ Args:
23
+ client_manager: D365FO client manager instance
24
+ """
25
+ self.client_manager = client_manager
26
+
27
+ def get_tools(self) -> List[Tool]:
28
+ """Get list of label tools.
29
+
30
+ Returns:
31
+ List of Tool definitions
32
+ """
33
+ return [self._get_label_tool(), self._get_labels_batch_tool()]
34
+
35
+ def _get_label_tool(self) -> Tool:
36
+ """Get label tool definition."""
37
+ return Tool(
38
+ name="d365fo_get_label",
39
+ description="Get label text by label ID",
40
+ inputSchema={
41
+ "type": "object",
42
+ "properties": {
43
+ "labelId": {
44
+ "type": "string",
45
+ "description": "Label ID (e.g., @SYS1234)",
46
+ },
47
+ "language": {
48
+ "type": "string",
49
+ "default": "en-US",
50
+ "description": "Language code for label text",
51
+ },
52
+ "fallbackToEnglish": {
53
+ "type": "boolean",
54
+ "default": True,
55
+ "description": "Fallback to English if translation not found",
56
+ },
57
+ "profile": {
58
+ "type": "string",
59
+ "description": "Configuration profile to use (optional - uses default profile if not specified)",
60
+ },
61
+ },
62
+ "required": ["labelId"],
63
+ },
64
+ )
65
+
66
+ def _get_labels_batch_tool(self) -> Tool:
67
+ """Get labels batch tool definition."""
68
+ return Tool(
69
+ name="d365fo_get_labels_batch",
70
+ description="Get multiple labels in a single request",
71
+ inputSchema={
72
+ "type": "object",
73
+ "properties": {
74
+ "labelIds": {
75
+ "type": "array",
76
+ "items": {"type": "string"},
77
+ "description": "List of label IDs to retrieve",
78
+ },
79
+ "language": {
80
+ "type": "string",
81
+ "default": "en-US",
82
+ "description": "Language code for label texts",
83
+ },
84
+ "fallbackToEnglish": {
85
+ "type": "boolean",
86
+ "default": True,
87
+ "description": "Fallback to English if translation not found",
88
+ },
89
+ "profile": {
90
+ "type": "string",
91
+ "description": "Configuration profile to use (optional - uses default profile if not specified)",
92
+ },
93
+ },
94
+ "required": ["labelIds"],
95
+ },
96
+ )
97
+
98
+ async def execute_get_label(self, arguments: dict) -> List[TextContent]:
99
+ """Execute get label tool.
100
+
101
+ Args:
102
+ arguments: Tool arguments
103
+
104
+ Returns:
105
+ List of TextContent responses
106
+ """
107
+ try:
108
+ profile = arguments.get("profile", "default")
109
+ client = await self.client_manager.get_client(profile)
110
+
111
+ label_id = arguments["labelId"]
112
+ language = arguments.get("language", "en-US")
113
+
114
+ # Get label text
115
+ label_text = await client.get_label_text(label_id, language=language)
116
+
117
+ response = {
118
+ "labelId": label_id,
119
+ "text": label_text or f"[{label_id}]",
120
+ "language": language,
121
+ "found": label_text is not None,
122
+ }
123
+
124
+ return [TextContent(type="text", text=json.dumps(response, indent=2))]
125
+
126
+ except Exception as e:
127
+ logger.error(f"Get label failed: {e}")
128
+ error_response = {
129
+ "error": str(e),
130
+ "tool": "d365fo_get_label",
131
+ "arguments": arguments,
132
+ }
133
+ return [TextContent(type="text", text=json.dumps(error_response, indent=2))]
134
+
135
+ async def execute_get_labels_batch(self, arguments: dict) -> List[TextContent]:
136
+ """Execute get labels batch tool.
137
+
138
+ Args:
139
+ arguments: Tool arguments
140
+
141
+ Returns:
142
+ List of TextContent responses
143
+ """
144
+ try:
145
+ profile = arguments.get("profile", "default")
146
+ client = await self.client_manager.get_client(profile)
147
+
148
+ label_ids = arguments["labelIds"]
149
+ language = arguments.get("language", "en-US")
150
+
151
+ start_time = time.time()
152
+
153
+ # Get labels in batch
154
+ labels = {}
155
+ missing_labels = []
156
+
157
+ for label_id in label_ids:
158
+ try:
159
+ label_text = await client.get_label_text(
160
+ label_id, language=language
161
+ )
162
+ if label_text:
163
+ labels[label_id] = label_text
164
+ else:
165
+ missing_labels.append(label_id)
166
+ except Exception:
167
+ missing_labels.append(label_id)
168
+
169
+ retrieval_time = time.time() - start_time
170
+
171
+ response = {
172
+ "labels": labels,
173
+ "missingLabels": missing_labels,
174
+ "retrievalTime": round(retrieval_time, 3),
175
+ "language": language,
176
+ "totalRequested": len(label_ids),
177
+ "foundCount": len(labels),
178
+ }
179
+
180
+ return [TextContent(type="text", text=json.dumps(response, indent=2))]
181
+
182
+ except Exception as e:
183
+ logger.error(f"Get labels batch failed: {e}")
184
+ error_response = {
185
+ "error": str(e),
186
+ "tool": "d365fo_get_labels_batch",
187
+ "arguments": arguments,
188
+ }
189
+ return [TextContent(type="text", text=json.dumps(error_response, indent=2))]