d365fo-client 0.2.2__py3-none-any.whl → 0.2.3__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/models.py CHANGED
@@ -12,6 +12,7 @@ from .utils import get_environment_cache_directory
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from typing import ForwardRef
15
+ from .credential_sources import CredentialSource
15
16
 
16
17
 
17
18
  def _ensure_str_for_json(field):
@@ -65,7 +66,10 @@ class SyncStrategy(StrEnum):
65
66
  FULL = "full"
66
67
  INCREMENTAL = "incremental"
67
68
  ENTITIES_ONLY = "entities_only"
69
+ LABELS_ONLY = "labels_only"
68
70
  SHARING_MODE = "sharing_mode"
71
+ FULL_WITHOUT_LABELS = "full_without_labels"
72
+
69
73
 
70
74
 
71
75
  class Cardinality(StrEnum):
@@ -80,6 +84,7 @@ class FOClientConfig:
80
84
  """Configuration for F&O Client"""
81
85
 
82
86
  base_url: str
87
+ auth_mode: Optional[str] = "defaut" # default | client_credentials
83
88
  client_id: Optional[str] = None
84
89
  client_secret: Optional[str] = None
85
90
  tenant_id: Optional[str] = None
@@ -98,6 +103,8 @@ class FOClientConfig:
98
103
  max_memory_cache_size: int = 1000
99
104
  # Cache-first behavior configuration
100
105
  use_cache_first: bool = True
106
+ # Credential source configuration
107
+ credential_source: Optional["CredentialSource"] = None
101
108
 
102
109
  def __post_init__(self):
103
110
  """Post-initialization to set default cache directory if not provided."""
@@ -738,9 +745,9 @@ class GlobalVersionInfo:
738
745
  first_seen_at: datetime
739
746
  last_used_at: datetime
740
747
  reference_count: int
741
- sample_modules: List[ModuleVersionInfo] = field(
748
+ modules: List[ModuleVersionInfo] = field(
742
749
  default_factory=list
743
- ) # Sample for debugging
750
+ ) # Modules for debugging
744
751
 
745
752
  def to_dict(self) -> Dict[str, Any]:
746
753
  """Convert to dictionary for JSON serialization"""
@@ -751,7 +758,7 @@ class GlobalVersionInfo:
751
758
  "first_seen_at": self.first_seen_at.isoformat(),
752
759
  "last_used_at": self.last_used_at.isoformat(),
753
760
  "reference_count": self.reference_count,
754
- "sample_modules": [module.to_dict() for module in self.sample_modules],
761
+ "modules": [module.to_dict() for module in self.modules],
755
762
  }
756
763
 
757
764
 
@@ -869,3 +876,15 @@ class SyncResult:
869
876
  action_count: int
870
877
  enumeration_count: int
871
878
  label_count: int
879
+
880
+ def to_dict(self) -> dict:
881
+ """Convert to dictionary for JSON serialization"""
882
+ return {
883
+ "success": self.success,
884
+ "error": self.error,
885
+ "duration_ms": self.duration_ms,
886
+ "entity_count": self.entity_count,
887
+ "action_count": self.action_count,
888
+ "enumeration_count": self.enumeration_count,
889
+ "label_count": self.label_count
890
+ }
@@ -7,13 +7,15 @@ import logging
7
7
  import os
8
8
  from dataclasses import asdict
9
9
  from pathlib import Path
10
- from typing import Any, Dict, List, Optional
10
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
11
11
 
12
12
  import yaml
13
13
 
14
14
  from .config import ConfigManager
15
15
  from .models import FOClientConfig
16
16
  from .profiles import Profile
17
+ if TYPE_CHECKING:
18
+ from .credential_sources import CredentialSource
17
19
 
18
20
  logger = logging.getLogger(__name__)
19
21
 
@@ -76,6 +78,8 @@ class ProfileManager:
76
78
  language: str = "en-US",
77
79
  cache_dir: Optional[str] = None,
78
80
  description: Optional[str] = None,
81
+ credential_source: Optional["CredentialSource"] = None
82
+
79
83
  ) -> bool:
80
84
  """Create a new profile.
81
85
 
@@ -93,6 +97,7 @@ class ProfileManager:
93
97
  language: Default language code
94
98
  cache_dir: Cache directory path
95
99
  description: Profile description (stored separately from CLI profile)
100
+ credential_source: Credential source configuration
96
101
 
97
102
  Returns:
98
103
  True if created successfully
@@ -120,6 +125,7 @@ class ProfileManager:
120
125
  cache_dir=cache_dir,
121
126
  description=description,
122
127
  output_format="table", # Default for CLI compatibility
128
+ credential_source=credential_source
123
129
  )
124
130
 
125
131
  self.config_manager.save_profile(profile)
d365fo_client/profiles.py CHANGED
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from .models import FOClientConfig
9
+ from .credential_sources import CredentialSource
9
10
 
10
11
  logger = logging.getLogger(__name__)
11
12
 
@@ -24,8 +25,10 @@ class Profile:
24
25
  client_id: Optional[str] = None
25
26
  client_secret: Optional[str] = None
26
27
  tenant_id: Optional[str] = None
28
+ use_default_credentials: Optional[bool] = None # None means derive from auth_mode
27
29
  verify_ssl: bool = True
28
30
  timeout: int = 60
31
+ credential_source: Optional["CredentialSource"] = None
29
32
 
30
33
  # Cache settings
31
34
  use_label_cache: bool = True
@@ -43,18 +46,26 @@ class Profile:
43
46
  """Convert profile to FOClientConfig."""
44
47
  from .models import FOClientConfig
45
48
 
49
+ # Determine use_default_credentials: explicit setting takes precedence over auth_mode
50
+ if self.use_default_credentials is not None:
51
+ use_default_creds = self.use_default_credentials
52
+ else:
53
+ use_default_creds = self.auth_mode == "default"
54
+
46
55
  return FOClientConfig(
47
56
  base_url=self.base_url,
57
+ auth_mode=self.auth_mode,
48
58
  client_id=self.client_id,
49
59
  client_secret=self.client_secret,
50
60
  tenant_id=self.tenant_id,
51
- use_default_credentials=self.auth_mode == "default",
61
+ use_default_credentials=use_default_creds,
52
62
  timeout=self.timeout,
53
63
  verify_ssl=self.verify_ssl,
54
64
  use_label_cache=self.use_label_cache,
55
65
  label_cache_expiry_minutes=self.label_cache_expiry_minutes,
56
66
  use_cache_first=self.use_cache_first,
57
67
  metadata_cache_dir=self.cache_dir,
68
+ credential_source=self.credential_source,
58
69
  )
59
70
 
60
71
  def validate(self) -> List[str]:
@@ -100,6 +111,7 @@ class Profile:
100
111
  "client_id": None,
101
112
  "client_secret": None,
102
113
  "tenant_id": None,
114
+ "use_default_credentials": None,
103
115
  "verify_ssl": True,
104
116
  "timeout": 60,
105
117
  "use_label_cache": True,
@@ -108,6 +120,7 @@ class Profile:
108
120
  "cache_dir": None,
109
121
  "language": "en-US",
110
122
  "output_format": "table",
123
+ "credential_source": None,
111
124
  }
112
125
 
113
126
  for key, default_value in defaults.items():
@@ -119,6 +132,16 @@ class Profile:
119
132
  k: v for k, v in migrated_data.items() if k in cls.__dataclass_fields__
120
133
  }
121
134
 
135
+ # Handle credential_source deserialization
136
+ if "credential_source" in valid_params and valid_params["credential_source"] is not None:
137
+ from .credential_sources import CredentialSource
138
+ credential_source_data = valid_params["credential_source"]
139
+ try:
140
+ valid_params["credential_source"] = CredentialSource.from_dict(credential_source_data)
141
+ except Exception as e:
142
+ logger.error(f"Error deserializing credential_source: {e}")
143
+ valid_params["credential_source"] = None
144
+
122
145
  try:
123
146
  return cls(**valid_params)
124
147
  except Exception as e:
@@ -151,6 +174,10 @@ class Profile:
151
174
  data = asdict(self)
152
175
  data.pop("name", None)
153
176
 
177
+ # Handle credential_source serialization
178
+ if self.credential_source is not None:
179
+ data["credential_source"] = self.credential_source.to_dict()
180
+
154
181
  return data
155
182
 
156
183
  def clone(self, name: str, **overrides) -> "Profile":
@@ -0,0 +1,181 @@
1
+ """Enhanced sync models for progress reporting and session management."""
2
+
3
+ import uuid
4
+ from datetime import datetime, timezone
5
+ from dataclasses import dataclass, field
6
+ from enum import StrEnum
7
+ from typing import Dict, List, Optional, Callable, Set
8
+
9
+ from .models import SyncStrategy, SyncResult
10
+
11
+
12
+ class SyncStatus(StrEnum):
13
+ """Sync operation status"""
14
+ PENDING = "pending"
15
+ RUNNING = "running"
16
+ COMPLETED = "completed"
17
+ FAILED = "failed"
18
+ CANCELLED = "cancelled"
19
+ PAUSED = "paused"
20
+
21
+
22
+ class SyncPhase(StrEnum):
23
+ """Detailed sync phases"""
24
+ INITIALIZING = "initializing"
25
+ VERSION_CHECK = "version_check"
26
+ ENTITIES = "entities"
27
+ SCHEMAS = "schemas"
28
+ ENUMERATIONS = "enumerations"
29
+ LABELS = "labels"
30
+ INDEXING = "indexing"
31
+ FINALIZING = "finalizing"
32
+ COMPLETED = "completed"
33
+ FAILED = "failed"
34
+
35
+
36
+ @dataclass
37
+ class SyncActivity:
38
+ """Individual sync activity within a phase"""
39
+ name: str
40
+ status: SyncStatus
41
+ start_time: Optional[datetime] = None
42
+ end_time: Optional[datetime] = None
43
+ progress_percent: float = 0.0
44
+ items_processed: int = 0
45
+ items_total: Optional[int] = None
46
+ current_item: Optional[str] = None
47
+ error: Optional[str] = None
48
+
49
+ def to_dict(self) -> dict:
50
+ """Convert to dictionary for JSON serialization"""
51
+ return {
52
+ "name": self.name,
53
+ "status": self.status,
54
+ "start_time": self.start_time.isoformat() if self.start_time else None,
55
+ "end_time": self.end_time.isoformat() if self.end_time else None,
56
+ "progress_percent": self.progress_percent,
57
+ "items_processed": self.items_processed,
58
+ "items_total": self.items_total,
59
+ "current_item": self.current_item,
60
+ "error": self.error
61
+ }
62
+
63
+
64
+ @dataclass
65
+ class SyncSession:
66
+ """Complete sync session with detailed tracking"""
67
+ session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
68
+ global_version_id: int = 0
69
+ strategy: SyncStrategy = SyncStrategy.FULL
70
+ status: SyncStatus = SyncStatus.PENDING
71
+
72
+ # Overall progress
73
+ start_time: Optional[datetime] = None
74
+ end_time: Optional[datetime] = None
75
+ estimated_completion: Optional[datetime] = None
76
+ progress_percent: float = 0.0
77
+
78
+ # Current state
79
+ current_phase: SyncPhase = SyncPhase.INITIALIZING
80
+ current_activity: Optional[str] = None
81
+
82
+ # Phase tracking
83
+ phases: Dict[SyncPhase, SyncActivity] = field(default_factory=dict)
84
+
85
+ # Results
86
+ result: Optional[SyncResult] = None
87
+ error: Optional[str] = None
88
+
89
+ # Metadata
90
+ initiated_by: str = "system" # user, system, scheduled, mcp
91
+ can_cancel: bool = True
92
+
93
+ # Collected label IDs during sync for efficient label processing
94
+ collected_label_ids: Set[str] = field(default_factory=set)
95
+
96
+ def get_overall_progress(self) -> float:
97
+ """Calculate overall progress across all phases"""
98
+ if not self.phases:
99
+ return 0.0
100
+
101
+ total_weight = len(self.phases)
102
+ completed_weight = sum(
103
+ 1.0 if activity.status == SyncStatus.COMPLETED
104
+ else activity.progress_percent / 100.0
105
+ for activity in self.phases.values()
106
+ )
107
+ return min(100.0, (completed_weight / total_weight) * 100.0)
108
+
109
+ def get_current_activity_detail(self) -> Optional[SyncActivity]:
110
+ """Get current running activity details"""
111
+ if self.current_phase in self.phases:
112
+ return self.phases[self.current_phase]
113
+ return None
114
+
115
+ def estimate_remaining_time(self) -> Optional[int]:
116
+ """Estimate remaining time in seconds"""
117
+ if not self.start_time or self.progress_percent <= 0:
118
+ return None
119
+
120
+ elapsed = (datetime.now(timezone.utc) - self.start_time).total_seconds()
121
+ if self.progress_percent >= 100:
122
+ return 0
123
+
124
+ estimated_total = elapsed / (self.progress_percent / 100.0)
125
+ return max(0, int(estimated_total - elapsed))
126
+
127
+ def to_dict(self) -> dict:
128
+ """Convert to dictionary for JSON serialization"""
129
+ return {
130
+ "session_id": self.session_id,
131
+ "global_version_id": self.global_version_id,
132
+ "strategy": self.strategy,
133
+ "status": self.status,
134
+ "start_time": self.start_time.isoformat() if self.start_time else None,
135
+ "end_time": self.end_time.isoformat() if self.end_time else None,
136
+ "estimated_completion": self.estimated_completion.isoformat() if self.estimated_completion else None,
137
+ "progress_percent": self.progress_percent,
138
+ "current_phase": self.current_phase,
139
+ "current_activity": self.current_activity,
140
+ "phases": {
141
+ phase.value: activity.to_dict()
142
+ for phase, activity in self.phases.items()
143
+ },
144
+ "result": self.result.to_dict() if self.result and hasattr(self.result, 'to_dict') else None,
145
+ "error": self.error,
146
+ "initiated_by": self.initiated_by,
147
+ "can_cancel": self.can_cancel,
148
+ "estimated_remaining_seconds": self.estimate_remaining_time()
149
+ }
150
+
151
+
152
+ @dataclass
153
+ class SyncSessionSummary:
154
+ """Lightweight sync session summary for listing"""
155
+ session_id: str
156
+ global_version_id: int
157
+ strategy: SyncStrategy
158
+ status: SyncStatus
159
+ start_time: Optional[datetime] = None
160
+ end_time: Optional[datetime] = None
161
+ progress_percent: float = 0.0
162
+ current_phase: SyncPhase = SyncPhase.INITIALIZING
163
+ current_activity: Optional[str] = None
164
+ initiated_by: str = "system"
165
+ duration_seconds: Optional[int] = None
166
+
167
+ def to_dict(self) -> dict:
168
+ """Convert to dictionary for JSON serialization"""
169
+ return {
170
+ "session_id": self.session_id,
171
+ "global_version_id": self.global_version_id,
172
+ "strategy": self.strategy,
173
+ "status": self.status,
174
+ "start_time": self.start_time.isoformat() if self.start_time else None,
175
+ "end_time": self.end_time.isoformat() if self.end_time else None,
176
+ "progress_percent": self.progress_percent,
177
+ "current_phase": self.current_phase,
178
+ "current_activity": self.current_activity,
179
+ "initiated_by": self.initiated_by,
180
+ "duration_seconds": self.duration_seconds
181
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d365fo-client
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Microsoft Dynamics 365 Finance & Operations client
5
5
  Author-email: Muhammad Afzaal <mo@thedataguy.pro>
6
6
  License-Expression: MIT
@@ -22,6 +22,7 @@ License-File: LICENSE
22
22
  Requires-Dist: aiohttp>=3.10.0
23
23
  Requires-Dist: aiofiles>=24.1.0
24
24
  Requires-Dist: azure-identity>=1.19.0
25
+ Requires-Dist: azure-keyvault-secrets>=4.8.0
25
26
  Requires-Dist: requests>=2.32.0
26
27
  Requires-Dist: aiosqlite>=0.19.0
27
28
  Requires-Dist: cachetools>=6.1.0
@@ -50,7 +51,7 @@ A comprehensive Python client library and MCP server for Microsoft Dynamics 365
50
51
  - 🏷️ **Label Operations V2**: Multilingual label caching with performance improvements and async support
51
52
  - 🔍 **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
52
53
  - ⚡ **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
+ - 🔒 **Authentication**: Azure AD integration with default credentials, service principal, and Azure Key Vault support
54
55
  - 💾 **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
55
56
  - 🌐 **Async/Await**: Modern async/await patterns with optimized session management
56
57
  - 📝 **Type Hints**: Full type annotation support with enhanced data models
@@ -58,6 +59,8 @@ A comprehensive Python client library and MCP server for Microsoft Dynamics 365
58
59
  - 🖥️ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
59
60
  - 🧪 **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
60
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
61
64
 
62
65
  ## Installation
63
66
 
@@ -73,6 +76,13 @@ uv sync # Installs with exact dependencies from uv.lock
73
76
 
74
77
  **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
78
 
79
+ **Breaking Change in v0.2.3**: Environment variable names have been updated for consistency:
80
+ - `AZURE_CLIENT_ID` → `D365FO_CLIENT_ID`
81
+ - `AZURE_CLIENT_SECRET` → `D365FO_CLIENT_SECRET`
82
+ - `AZURE_TENANT_ID` → `D365FO_TENANT_ID`
83
+
84
+ Please update your environment variables accordingly when upgrading.
85
+
76
86
  ## Quick Start
77
87
 
78
88
  ## Command Line Interface (CLI)
@@ -158,9 +168,9 @@ profiles:
158
168
 
159
169
  development:
160
170
  base_url: "https://dev.dynamics.com"
161
- client_id: "${AZURE_CLIENT_ID}"
162
- client_secret: "${AZURE_CLIENT_SECRET}"
163
- tenant_id: "${AZURE_TENANT_ID}"
171
+ client_id: "${D365FO_CLIENT_ID}"
172
+ client_secret: "${D365FO_CLIENT_SECRET}"
173
+ tenant_id: "${D365FO_TENANT_ID}"
164
174
  use_cache_first: true
165
175
 
166
176
  default_profile: "development"
@@ -262,7 +272,14 @@ config = FOClientConfig(
262
272
  use_default_credentials=False
263
273
  )
264
274
 
265
- # Option 3: With custom settings
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
+ )
281
+
282
+ # Option 4: With custom settings
266
283
  config = FOClientConfig(
267
284
  base_url="https://your-fo-environment.dynamics.com",
268
285
  use_default_credentials=True,
@@ -462,7 +479,9 @@ d365fo-client/
462
479
  │ ├── metadata.py # Legacy metadata operations
463
480
  │ ├── metadata_api.py # Metadata API client
464
481
  │ ├── metadata_cache.py # Metadata caching layer V2
465
- │ ├── metadata_sync.py # Metadata synchronization 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)
466
485
  │ ├── labels.py # Label operations V2
467
486
  │ ├── profiles.py # Profile data models
468
487
  │ ├── profile_manager.py # Profile management
@@ -519,6 +538,8 @@ d365fo-client/
519
538
  | `client_secret` | str | None | Azure AD client secret |
520
539
  | `tenant_id` | str | None | Azure AD tenant ID |
521
540
  | `use_default_credentials` | bool | True | Use Azure Default Credential |
541
+ | `credential_source` | str | "environment" | Credential source: "environment", "keyvault" |
542
+ | `keyvault_url` | str | None | Azure Key Vault URL for credential storage |
522
543
  | `verify_ssl` | bool | False | Verify SSL certificates |
523
544
  | `timeout` | int | 30 | Request timeout in seconds |
524
545
  | `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
@@ -619,9 +640,9 @@ cp tests/integration/.env.template tests/integration/.env
619
640
  # Edit .env file with your settings:
620
641
  INTEGRATION_TEST_LEVEL=sandbox
621
642
  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
643
+ D365FO_CLIENT_ID=your-client-id
644
+ D365FO_CLIENT_SECRET=your-client-secret
645
+ D365FO_TENANT_ID=your-tenant-id
625
646
  ```
626
647
 
627
648
  #### Available Commands
@@ -707,9 +728,9 @@ pip install d365fo-client
707
728
 
708
729
  # Set up environment variables
709
730
  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
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
713
734
 
714
735
  # Start the MCP server
715
736
  d365fo-mcp-server
@@ -857,9 +878,19 @@ For service principal authentication:
857
878
 
858
879
  ```bash
859
880
  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"
881
+ export D365FO_CLIENT_ID="your-client-id"
882
+ export D365FO_CLIENT_SECRET="your-client-secret"
883
+ export D365FO_TENANT_ID="your-tenant-id"
884
+ d365fo-mcp-server
885
+ ```
886
+
887
+ #### Azure Key Vault Integration (New in v0.2.3)
888
+ For secure credential storage using Azure Key Vault:
889
+
890
+ ```bash
891
+ export D365FO_BASE_URL="https://your-environment.dynamics.com"
892
+ export D365FO_CREDENTIAL_SOURCE="keyvault"
893
+ export D365FO_KEYVAULT_URL="https://your-keyvault.vault.azure.net/"
863
894
  d365fo-mcp-server
864
895
  ```
865
896
 
@@ -1032,7 +1063,7 @@ tail -f ~/.d365fo-mcp/logs/mcp-server.log
1032
1063
  az account show
1033
1064
 
1034
1065
  # Test with explicit credentials
1035
- export AZURE_CLIENT_ID="your-client-id"
1066
+ export D365FO_CLIENT_ID="your-client-id"
1036
1067
  # ... set other variables
1037
1068
  d365fo-mcp-server
1038
1069
  ```
@@ -1,25 +1,27 @@
1
1
  d365fo_client/__init__.py,sha256=EH0lGM1TDt_-hVJqnuZK4i7sKSttSEhHapl0tRdRrmE,7752
2
- d365fo_client/auth.py,sha256=nLq1JGCTsuEuAt0a3f8rcjZkKANRqmMk7flR5MkDitI,2889
2
+ d365fo_client/auth.py,sha256=XKt6MJQrnR1v5ahA1OR3Q-1LpHY3xo-mA62a4kvmO1M,4633
3
3
  d365fo_client/cli.py,sha256=fBF6OjbZgj0T--Sii1wazP2fIFZnZnEeznUSti3l194,26118
4
- d365fo_client/client.py,sha256=v_45aonnTJH8T9Wv8feXL3n_LyE1O6vBTiPnRwlJtuI,54732
4
+ d365fo_client/client.py,sha256=00d1V_kz6-upPMvKMSs42jXej2N7lAznna3KxQJYyCo,55326
5
5
  d365fo_client/config.py,sha256=WQxXEHYhgXaM2Ocd98ILiAnJycevKArn1FNp-TGKdKA,10469
6
+ d365fo_client/credential_sources.py,sha256=XJ-DitJD3f0ncTMP4Cj0-O1RW4dFGiicYJojzg4HL_w,17259
6
7
  d365fo_client/crud.py,sha256=YBjYIKqgyWYsLB8uRtI_sjRa2G7d9qtgm5mLGCB2CfA,6701
7
8
  d365fo_client/exceptions.py,sha256=k8tVb5K6C_F4sCY5NVoE5RYpKPjCgPLd3JtcLIuXzTw,733
8
9
  d365fo_client/labels.py,sha256=a7b_YYWDWaXANirDAr-CoodEpBvfaZvRGy76kNg4Z44,18412
9
10
  d365fo_client/main.py,sha256=6jBBcePWHOG-FMJ8HMz9Qz1Tmw486Ef6VGbfvK5oBHg,19045
10
- d365fo_client/metadata_api.py,sha256=ALuAO2A-2-yU-vwvib8n4VI9rD7PZwXz_6X6YUIeYho,38571
11
- d365fo_client/models.py,sha256=9g2gVeqPysbJvrLMUzB7eAamgZdsP0dGUDu5Wpeo6B8,26599
11
+ d365fo_client/metadata_api.py,sha256=1tDkPLxvR3F4St8GjHssn9EAoNa-ZDEP0-pbGa84DLI,41624
12
+ d365fo_client/models.py,sha256=MZoDAiXC0itaGjhgzxAzYR7JxgYwkR3035u3VHgYiNo,27309
12
13
  d365fo_client/output.py,sha256=U-q6_wRHARWUKEhK-OCu16VhgWZ89sofzZuE67hNg1Q,5986
13
- d365fo_client/profile_manager.py,sha256=X8nMr0sTi_x8gEPtB--jl3xb9wjLpPj4JCpZpZltMOc,11344
14
- d365fo_client/profiles.py,sha256=cZg9WKO8wxRfIl6UjZ7k6pTqyw_r9EFWOUwf6QBt4aI,5759
14
+ d365fo_client/profile_manager.py,sha256=w5yV1Ab2hZDpYQjssr2ztcOswXbAYCfy5Bl56YhuVwg,11609
15
+ d365fo_client/profiles.py,sha256=M_PlJ42ahNCkQr5W5eXdile_ljGOnqueHOQJboVwHlo,7147
15
16
  d365fo_client/query.py,sha256=wOZjXEtGzPcd5mRfdkpMZTHZdSId44gLSboJs4LeSaw,5028
16
17
  d365fo_client/session.py,sha256=4OIR61eYQHALPmbXZ446Ko4j5ttvozDxDRnMikogBys,1841
18
+ d365fo_client/sync_models.py,sha256=svQGwf9PDOBot-8bo6jiUHL4L62qtICHrhuM5RXGbOs,6419
17
19
  d365fo_client/utils.py,sha256=9bHSWznuhuOmxbx9Vkd8k9RFctHYUCTiWZCmcUujPqw,6771
18
20
  d365fo_client/mcp/__init__.py,sha256=B6Pw342ejRUKrw0NN5zyMSb1lF2rTICxv8KFfBMlBsU,487
19
- d365fo_client/mcp/client_manager.py,sha256=HpLjn85SnI6z-bQMWIxntMT7xUpQvMTR70Lis1ZzbEE,11705
20
- d365fo_client/mcp/main.py,sha256=UtPRtUg4ZtNecl7m_d0leu9BPCXufSRNXinWmCLQcMs,3248
21
+ d365fo_client/mcp/client_manager.py,sha256=Z8jNm8pjI64-xloJW5mcLe-7tP1JD6iGxwsaynLgyNs,12169
22
+ d365fo_client/mcp/main.py,sha256=mlKnxHHo79NBYAwcNHXuB5ImuE6QDcJvCVBtktbdduM,4269
21
23
  d365fo_client/mcp/models.py,sha256=Tq48Xvh6aXkGAHZ805r1OwPIzftUGXSYE-rt0pY4wI8,7493
22
- d365fo_client/mcp/server.py,sha256=yfNeNOIfFqhM3gYufMN-wjx6-Ntlt2QtMPodRSfIj4E,22249
24
+ d365fo_client/mcp/server.py,sha256=n4BlgfJkMuRZbHD8jshZOS-oPH-Pi2uKBExE4oxeo3U,24964
23
25
  d365fo_client/mcp/prompts/__init__.py,sha256=haa0Cit3f2xWrcqlFkhfQTqff2DfAore_I0il2VIW_0,1104
24
26
  d365fo_client/mcp/prompts/action_execution.py,sha256=gdZcgtXHrn8fUBfk2758RGbxwAcu1qRODOxVqwLoeZA,15587
25
27
  d365fo_client/mcp/prompts/sequence_analysis.py,sha256=AMsb3boD0rdd4xUYrnEqvx_OTYcFIyo6acE9zMJzgOU,12662
@@ -29,24 +31,26 @@ d365fo_client/mcp/resources/entity_handler.py,sha256=wFYMPp_Rny-HZams5NtIFF8vrYU
29
31
  d365fo_client/mcp/resources/environment_handler.py,sha256=iblut_E_0q9Z4gfOeEG5bfodTPOzFcvfiYQFzH3WggE,4469
30
32
  d365fo_client/mcp/resources/metadata_handler.py,sha256=ZPH4yAylASpbAPptWMHazACKS3dhDQKr88o22KBqRsM,11618
31
33
  d365fo_client/mcp/resources/query_handler.py,sha256=NMJ07SolMO_rjdY9l7m53_EV2j0KHQSrckTySnTw1oU,4635
32
- d365fo_client/mcp/tools/__init__.py,sha256=JIYUoHA_RtqvvCMW9tNJvBGFMjF1gmQcn6uqNK9dKQM,412
34
+ d365fo_client/mcp/tools/__init__.py,sha256=kfPPcs2r6biL_KLn-1MqSN2tU2eD5QahocMIun7yfuU,463
33
35
  d365fo_client/mcp/tools/connection_tools.py,sha256=TT4eDF0Bb8gS36nTj5K-AIuuAYCZMB-UjW66w5HLi3I,6872
34
36
  d365fo_client/mcp/tools/crud_tools.py,sha256=YM6TDK41f7-xg9K4d4vs3t5vTwabtru9Z7gnzup3XvE,31229
35
37
  d365fo_client/mcp/tools/database_tools.py,sha256=Kh9g-e0RCTKPbmsuZ99MDK8-ZYaBB2czbElHDaJXPGU,33286
36
38
  d365fo_client/mcp/tools/label_tools.py,sha256=BpUI55MgDjhokN44JhZLh1J46JgiPL0Fh0pfhZrD5I0,6376
37
39
  d365fo_client/mcp/tools/metadata_tools.py,sha256=NHFrQbpL0403EA2mxLqqRE0q7MDPLroZ79JRb3gqxeQ,32260
38
- d365fo_client/mcp/tools/profile_tools.py,sha256=N7-bLI9ZUg0T5GA9RhvBMAlUwrsBBQNVRNBKwJEtCCc,27039
40
+ d365fo_client/mcp/tools/profile_tools.py,sha256=n8-uGbA-K4kFVBZ--yfk-9znyPydiwCoWNEm9EpULwg,42337
41
+ d365fo_client/mcp/tools/sync_tools.py,sha256=nHpwDwGttYTVenrhl4PygWRguZj37IfHeZeqRCqpg6w,22358
39
42
  d365fo_client/metadata_v2/__init__.py,sha256=54VTuWSo5j-BEM-raLbRr3bfxc5lVXp2COajsy-7Oo0,1895
40
- d365fo_client/metadata_v2/cache_v2.py,sha256=h9Z4L3HG-xl5jimvek-5MmYdFgd4V_gGpKKK7sfm5ws,60419
43
+ d365fo_client/metadata_v2/cache_v2.py,sha256=lW9QiPXzv__rjhswNiiRgsoOyistk5tBYhX-WjmhQqE,60476
41
44
  d365fo_client/metadata_v2/database_v2.py,sha256=JX6kB_xGc1QrHSgo0XBdeBWj_k3n_ZwYtx2fsxV5ufI,25584
42
- d365fo_client/metadata_v2/global_version_manager.py,sha256=0xTZFPDTafvSDovbBmeTiQQynTWLKR0nbIpbEvJ5tac,22644
45
+ d365fo_client/metadata_v2/global_version_manager.py,sha256=dbf7ISFGCU88mDK5gtEZa9-q0DLLbWkLiiJIVJ_9W60,22537
43
46
  d365fo_client/metadata_v2/label_utils.py,sha256=-GRagiURJv-ZQCepeOkCRqfo62rhWo8A42271w0Cb6A,3752
44
47
  d365fo_client/metadata_v2/search_engine_v2.py,sha256=s_XVqP3LLog19IAv8DpxVUS7TFRUAjuBevyfe1Ldwps,16525
45
- d365fo_client/metadata_v2/sync_manager_v2.py,sha256=deI5jOQYXtG0HAMGrMCZLSSEA2tVcWyKWaY_Mla2rhA,34067
48
+ d365fo_client/metadata_v2/sync_manager_v2.py,sha256=iNwnKxHdE-kIbPUvBmRYJjrHEbyLMNt___ISXEnz3FM,34060
49
+ d365fo_client/metadata_v2/sync_session_manager.py,sha256=4v3idTQF3Mhaky7DD3R4AY74ZAt1s5mIuHlekIVH9MY,44350
46
50
  d365fo_client/metadata_v2/version_detector.py,sha256=t9mKaeT4SKb13LmIq5fB6PTLOZn5Jp7ZUqQobntNEUg,15791
47
- d365fo_client-0.2.2.dist-info/licenses/LICENSE,sha256=idD7NJAZD7ognzZVyKjDxVYDCmngEIt0WxA_uB1v0iI,1071
48
- d365fo_client-0.2.2.dist-info/METADATA,sha256=08Z35iq6TWrZrWINMwHUJwWppwkW1HqrwIeO-KaGDys,35404
49
- d365fo_client-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
50
- d365fo_client-0.2.2.dist-info/entry_points.txt,sha256=ZZBjH4mQ0XO3ALeNswswa09dh_JGIvD-zCBkKi6qNqA,106
51
- d365fo_client-0.2.2.dist-info/top_level.txt,sha256=ZbvqO90RjhOW0cjFCAEeP8OFyITbhrij2vC3k4bWERQ,14
52
- d365fo_client-0.2.2.dist-info/RECORD,,
51
+ d365fo_client-0.2.3.dist-info/licenses/LICENSE,sha256=idD7NJAZD7ognzZVyKjDxVYDCmngEIt0WxA_uB1v0iI,1071
52
+ d365fo_client-0.2.3.dist-info/METADATA,sha256=0D0rsWk6VAfStAuuoujNQwoDE_0EC9dR3tXHyoHgeKU,36940
53
+ d365fo_client-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
54
+ d365fo_client-0.2.3.dist-info/entry_points.txt,sha256=ZZBjH4mQ0XO3ALeNswswa09dh_JGIvD-zCBkKi6qNqA,106
55
+ d365fo_client-0.2.3.dist-info/top_level.txt,sha256=ZbvqO90RjhOW0cjFCAEeP8OFyITbhrij2vC3k4bWERQ,14
56
+ d365fo_client-0.2.3.dist-info/RECORD,,