d365fo-client


Named365fo-client JSON
Version 0.2.2 PyPI version JSON
download
home_pageNone
SummaryMicrosoft Dynamics 365 Finance & Operations client
upload_time2025-08-28 03:10:20
maintainerNone
docs_urlNone
authorNone
requires_python>=3.13
licenseNone
keywords dynamics365 d365 finance operations erp microsoft
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Dynamics 365 Finance & Operations Client and MCP Server

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.

## Features

- ๐Ÿ”— **OData Client**: Full CRUD operations on D365 F&O data entities with composite key support
- ๐Ÿ“Š **Metadata Management V2**: Enhanced caching system with intelligent synchronization and FTS5 search
- ๐Ÿท๏ธ **Label Operations V2**: Multilingual label caching with performance improvements and async support
- ๐Ÿ” **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)
- โšก **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling
- ๐Ÿ”’ **Authentication**: Azure AD integration with default credentials and service principal support
- ๐Ÿ’พ **Intelligent Caching**: Cross-environment cache sharing with module-based version detection
- ๐ŸŒ **Async/Await**: Modern async/await patterns with optimized session management
- ๐Ÿ“ **Type Hints**: Full type annotation support with enhanced data models
- ๐Ÿค– **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types
- ๐Ÿ–ฅ๏ธ **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations
- ๐Ÿงช **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)
- ๐Ÿ“‹ **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery

## Installation

```bash
# Install from PyPI
pip install d365fo-client

# Or install from source
git clone https://github.com/mafzaal/d365fo-client.git
cd d365fo-client
uv sync  # Installs with exact dependencies from uv.lock
```

**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.

## Quick Start

## Command Line Interface (CLI)

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.

### Usage

```bash
# Use the installed CLI command
d365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]

# Alternative: Module execution
python -m d365fo_client.main [OPTIONS] COMMAND [ARGS]
```

### Command Categories

#### Entity Operations
```bash
# List entities with filtering
d365fo-client entities list --pattern "customer" --limit 10

# Get entity details and schema
d365fo-client entities get CustomersV3 --properties --keys --labels

# CRUD operations
d365fo-client entities create Customers --data '{"CustomerAccount":"US-999","Name":"Test"}'
d365fo-client entities update Customers US-999 --data '{"Name":"Updated Name"}'
d365fo-client entities delete Customers US-999
```

#### Metadata Operations
```bash
# Search and discover entities
d365fo-client metadata entities --search "sales" --output json

# Get available actions
d365fo-client metadata actions --pattern "calculate" --limit 5

# Enumerate system enumerations
d365fo-client metadata enums --search "status" --output table

# Synchronize metadata cache
d365fo-client metadata sync --force-refresh
```

#### Version Information
```bash
# Get application versions
d365fo-client version app
d365fo-client version platform  
d365fo-client version build
```

#### Label Operations
```bash
# Resolve single label
d365fo-client labels resolve "@SYS13342"

# Search labels by pattern
d365fo-client labels search "customer" --language "en-US"
```

### Global Options

- `--base-url URL` โ€” Specify D365 F&O environment URL
- `--profile NAME` โ€” Use named configuration profile  
- `--output FORMAT` โ€” Output format: json, table, csv, yaml (default: table)
- `--verbose` โ€” Enable verbose output for debugging
- `--timeout SECONDS` โ€” Request timeout (default: 30)

### Configuration Profiles

Create reusable configurations in `~/.d365fo-client/config.yaml`:

```yaml
profiles:
  production:
    base_url: "https://prod.dynamics.com"
    use_default_credentials: true
    timeout: 60
    
  development:
    base_url: "https://dev.dynamics.com" 
    client_id: "${AZURE_CLIENT_ID}"
    client_secret: "${AZURE_CLIENT_SECRET}"
    tenant_id: "${AZURE_TENANT_ID}"
    use_cache_first: true

default_profile: "development"
```

### Examples

```bash
# Quick entity discovery
d365fo-client entities list --pattern "cust.*" --output json

# Get comprehensive entity information
d365fo-client entities get CustomersV3 --properties --keys --labels --output yaml

# Search for calculation actions
d365fo-client metadata actions --pattern "calculate|compute" --output table

# Test environment connectivity
d365fo-client version app --verbose
```

For a complete command reference:

```bash
d365fo-client --help
d365fo-client entities --help
d365fo-client metadata --help
```
### Basic Usage

```python
import asyncio
from d365fo_client import D365FOClient, FOClientConfig

async def main():
    # Simple configuration with default credentials
    config = FOClientConfig(
        base_url="https://your-fo-environment.dynamics.com",
        use_default_credentials=True  # Uses Azure Default Credential
    )
    
    async with D365FOClient(config) as client:
        # Test connection
        if await client.test_connection():
            print("โœ… Connected successfully!")
        
        # Get environment information
        env_info = await client.get_environment_info()
        print(f"Environment: {env_info.application_version}")
        
        # Search for entities (uses metadata cache v2)
        customer_entities = await client.search_entities("customer")
        print(f"Found {len(customer_entities)} customer entities")
        
        # Get customers with query options
        from d365fo_client import QueryOptions
        options = QueryOptions(
            select=["CustomerAccount", "Name", "SalesCurrencyCode"],
            top=10,
            orderby=["Name"]
        )
        
        customers = await client.get_data("/data/CustomersV3", options)
        print(f"Retrieved {len(customers['value'])} customers")

if __name__ == "__main__":
    asyncio.run(main())
```

### Using Convenience Function

```python
from d365fo_client import create_client

# Quick client creation with enhanced defaults
async with create_client("https://your-fo-environment.dynamics.com") as client:
    customers = await client.get_data("/data/CustomersV3", top=5)
```

## Configuration

### Authentication Options

```python
from d365fo_client import FOClientConfig

# Option 1: Default Azure credentials (recommended)
config = FOClientConfig(
    base_url="https://your-fo-environment.dynamics.com",
    use_default_credentials=True
)

# Option 2: Client credentials
config = FOClientConfig(
    base_url="https://your-fo-environment.dynamics.com",
    client_id="your-client-id",
    client_secret="your-client-secret", 
    tenant_id="your-tenant-id",
    use_default_credentials=False
)

# Option 3: With custom settings
config = FOClientConfig(
    base_url="https://your-fo-environment.dynamics.com",
    use_default_credentials=True,
    verify_ssl=False,  # For development environments
    timeout=60,  # Request timeout in seconds
    metadata_cache_dir="./my_cache",  # Custom cache directory
    use_label_cache=True,  # Enable label caching
    label_cache_expiry_minutes=120  # Cache for 2 hours
)
```

## Core Operations

### CRUD Operations

```python
async with D365FOClient(config) as client:
    # CREATE - Create new customer (supports composite keys)
    new_customer = {
        "CustomerAccount": "US-999",
        "Name": "Test Customer",
        "SalesCurrencyCode": "USD"
    }
    created = await client.create_data("/data/CustomersV3", new_customer)
    
    # READ - Get single customer by key
    customer = await client.get_data("/data/CustomersV3('US-001')")
    
    # UPDATE - Update customer with optimistic concurrency
    updates = {"Name": "Updated Customer Name"}
    updated = await client.update_data("/data/CustomersV3('US-001')", updates)
    
    # DELETE - Delete customer
    success = await client.delete_data("/data/CustomersV3('US-999')")
    print(f"Delete successful: {success}")
```

### Advanced Querying

```python
from d365fo_client import QueryOptions

# Complex query with multiple options
options = QueryOptions(
    select=["CustomerAccount", "Name", "SalesCurrencyCode", "CustomerGroupId"],
    filter="SalesCurrencyCode eq 'USD' and contains(Name, 'Corp')",
    expand=["CustomerGroup"],
    orderby=["Name desc", "CustomerAccount"],
    top=50,
    skip=10,
    count=True
)

result = await client.get_data("/data/CustomersV3", options)
print(f"Total count: {result.get('@odata.count')}")
```

### Action Execution

```python
# Unbound action
result = await client.post_data("/data/calculateTax", {
    "amount": 1000.00,
    "taxGroup": "STANDARD"
})

# Bound action on entity set
result = await client.post_data("/data/CustomersV3/calculateBalances", {
    "asOfDate": "2024-12-31"
})

# Bound action on specific entity instance  
result = await client.post_data("/data/CustomersV3('US-001')/calculateBalance", {
    "asOfDate": "2024-12-31"
})
```

### Metadata Operations

```python
# Intelligent metadata synchronization (v2 system)
sync_manager = await client.get_sync_manager()
await sync_manager.smart_sync()

# Search entities with enhanced filtering
sales_entities = await client.search_entities("sales")
print("Sales-related entities:", [e.name for e in sales_entities])

# Get detailed entity information with labels
entity_info = await client.get_public_entity_info("CustomersV3")
if entity_info:
    print(f"Entity: {entity_info.name}")
    print(f"Label: {entity_info.label_text}")
    print(f"Data Service Enabled: {entity_info.data_service_enabled}")

# Search actions with caching
calc_actions = await client.search_actions("calculate")
print("Calculation actions:", [a.name for a in calc_actions])

# Get enumeration information
enum_info = await client.get_public_enumeration_info("NoYes")
if enum_info:
    print(f"Enum: {enum_info.name}")
    for member in enum_info.members:
        print(f"  {member.name} = {member.value}")
```

### Label Operations

```python
# Get specific label (v2 caching system)
label_text = await client.get_label_text("@SYS13342")
print(f"Label text: {label_text}")

# Get multiple labels efficiently
labels = await client.get_labels_batch([
    "@SYS13342", "@SYS9490", "@GLS63332"
])
for label_id, text in labels.items():
    print(f"{label_id}: {text}")

# Enhanced entity info with resolved labels
entity_info = await client.get_public_entity_info_with_labels("CustomersV3")
if entity_info.label_text:
    print(f"Entity display name: {entity_info.label_text}")

# Access enhanced properties with labels
for prop in entity_info.enhanced_properties[:5]:
    if hasattr(prop, 'label_text') and prop.label_text:
        print(f"{prop.name}: {prop.label_text}")
```

## Error Handling

```python
from d365fo_client import D365FOClientError, AuthenticationError, ConnectionError

try:
    async with D365FOClient(config) as client:
        customer = await client.get_data("/data/CustomersV3('NON-EXISTENT')")
except ConnectionError as e:
    print(f"Connection failed: {e}")
except AuthenticationError as e:
    print(f"Authentication failed: {e}")
except D365FOClientError as e:
    print(f"Client operation failed: {e}")
    print(f"Status code: {e.status_code}")
    print(f"Response: {e.response_text}")
```

## Development

### Setting up Development Environment

```bash
# Clone the repository
git clone https://github.com/mafzaal/d365fo-client.git
cd d365fo-client

# Install with development dependencies using uv
uv sync --dev

# Run tests
uv run pytest

# Run integration tests
.\tests\integration\integration-test-simple.ps1 test-sandbox

# Format code
uv run black .
uv run isort .

# Type checking
uv run mypy src/

# Quality checks
.\make.ps1 quality-check  # Windows PowerShell
# or
make quality-check       # Unix/Linux/macOS
```

### Project Structure

```
d365fo-client/
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ d365fo_client/
โ”‚       โ”œโ”€โ”€ __init__.py          # Public API exports
โ”‚       โ”œโ”€โ”€ main.py              # CLI entry point  
โ”‚       โ”œโ”€โ”€ cli.py               # CLI command handlers
โ”‚       โ”œโ”€โ”€ client.py            # Enhanced D365FOClient class
โ”‚       โ”œโ”€โ”€ config.py            # Configuration management
โ”‚       โ”œโ”€โ”€ auth.py              # Authentication management
โ”‚       โ”œโ”€โ”€ session.py           # HTTP session management
โ”‚       โ”œโ”€โ”€ crud.py              # CRUD operations
โ”‚       โ”œโ”€โ”€ query.py             # OData query utilities
โ”‚       โ”œโ”€โ”€ metadata.py          # Legacy metadata operations
โ”‚       โ”œโ”€โ”€ metadata_api.py      # Metadata API client
โ”‚       โ”œโ”€โ”€ metadata_cache.py    # Metadata caching layer V2
โ”‚       โ”œโ”€โ”€ metadata_sync.py     # Metadata synchronization V2
โ”‚       โ”œโ”€โ”€ labels.py            # Label operations V2
โ”‚       โ”œโ”€โ”€ profiles.py          # Profile data models
โ”‚       โ”œโ”€โ”€ profile_manager.py   # Profile management
โ”‚       โ”œโ”€โ”€ models.py            # Data models and configurations
โ”‚       โ”œโ”€โ”€ output.py            # Output formatting
โ”‚       โ”œโ”€โ”€ utils.py             # Utility functions
โ”‚       โ”œโ”€โ”€ exceptions.py        # Custom exceptions
โ”‚       โ””โ”€โ”€ mcp/                 # Model Context Protocol server
โ”‚           โ”œโ”€โ”€ __init__.py      # MCP server exports
โ”‚           โ”œโ”€โ”€ main.py          # MCP server entry point
โ”‚           โ”œโ”€โ”€ server.py        # Core MCP server implementation
โ”‚           โ”œโ”€โ”€ client_manager.py# D365FO client connection pooling
โ”‚           โ”œโ”€โ”€ models.py        # MCP-specific data models
โ”‚           โ”œโ”€โ”€ tools/           # MCP tool implementations (12 tools)
โ”‚           โ”‚   โ”œโ”€โ”€ connection_tools.py
โ”‚           โ”‚   โ”œโ”€โ”€ crud_tools.py
โ”‚           โ”‚   โ”œโ”€โ”€ metadata_tools.py
โ”‚           โ”‚   โ””โ”€โ”€ label_tools.py
โ”‚           โ”œโ”€โ”€ resources/       # MCP resource handlers (4 types)
โ”‚           โ”‚   โ”œโ”€โ”€ entity_handler.py
โ”‚           โ”‚   โ”œโ”€โ”€ metadata_handler.py
โ”‚           โ”‚   โ”œโ”€โ”€ environment_handler.py
โ”‚           โ”‚   โ””โ”€โ”€ query_handler.py
โ”‚           โ””โ”€โ”€ prompts/         # MCP prompt templates
โ”œโ”€โ”€ tests/                       # Comprehensive test suite
โ”‚   โ”œโ”€โ”€ unit/                    # Unit tests (pytest-based)
โ”‚   โ”œโ”€โ”€ integration/             # Multi-tier integration testing
โ”‚   โ”‚   โ”œโ”€โ”€ mock_server/         # Mock D365 F&O API server
โ”‚   โ”‚   โ”œโ”€โ”€ test_mock_server.py  # Mock server tests
โ”‚   โ”‚   โ”œโ”€โ”€ test_sandbox.py      # Sandbox environment tests โœ…
โ”‚   โ”‚   โ”œโ”€โ”€ test_live.py         # Live environment tests
โ”‚   โ”‚   โ”œโ”€โ”€ conftest.py          # Shared pytest fixtures
โ”‚   โ”‚   โ”œโ”€โ”€ test_runner.py       # Python test execution engine
โ”‚   โ”‚   โ””โ”€โ”€ integration-test-simple.ps1 # PowerShell automation
โ”‚   โ””โ”€โ”€ test_mcp_server.py       # MCP server unit tests โœ…
โ”œโ”€โ”€ scripts/                     # Metadata discovery scripts
โ”‚   โ”œโ”€โ”€ search_data_entities.ps1 # PowerShell entity search
โ”‚   โ”œโ”€โ”€ get_data_entity_schema.ps1 # PowerShell schema retrieval
โ”‚   โ”œโ”€โ”€ search_enums.py          # Python enumeration search
โ”‚   โ”œโ”€โ”€ get_enumeration_info.py  # Python enumeration info
โ”‚   โ”œโ”€โ”€ search_actions.ps1       # PowerShell action search
โ”‚   โ””โ”€โ”€ get_action_info.py       # Python action information
โ”œโ”€โ”€ docs/                        # Comprehensive documentation
โ”œโ”€โ”€ pyproject.toml               # Project configuration
โ””โ”€โ”€ README.md                    # This file
```

## Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `base_url` | str | Required | D365 F&O base URL |
| `client_id` | str | None | Azure AD client ID |
| `client_secret` | str | None | Azure AD client secret |
| `tenant_id` | str | None | Azure AD tenant ID |
| `use_default_credentials` | bool | True | Use Azure Default Credential |
| `verify_ssl` | bool | False | Verify SSL certificates |
| `timeout` | int | 30 | Request timeout in seconds |
| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |
| `use_label_cache` | bool | True | Enable label caching V2 |
| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |
| `use_cache_first` | bool | False | Enable cache-first mode with background sync |

### Cache Directory Behavior

By default, the client uses platform-appropriate user cache directories:

- **Windows**: `%LOCALAPPDATA%\d365fo-client` (e.g., `C:\Users\username\AppData\Local\d365fo-client`)
- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)
- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)

You can override this by explicitly setting `metadata_cache_dir`:

```python
from d365fo_client import FOClientConfig

# Use custom cache directory
config = FOClientConfig(
    base_url="https://your-fo-environment.dynamics.com",
    metadata_cache_dir="/custom/cache/path"
)

# Or get the default cache directory programmatically
from d365fo_client import get_user_cache_dir

cache_dir = get_user_cache_dir("my-app")  # Platform-appropriate cache dir
config = FOClientConfig(
    base_url="https://your-fo-environment.dynamics.com", 
    metadata_cache_dir=str(cache_dir)
)
```

## Testing

This project includes comprehensive testing at multiple levels to ensure reliability and quality.

### Unit Tests

Run standard unit tests for core functionality:

```bash
# Run all unit tests
uv run pytest

# Run with coverage
uv run pytest --cov=d365fo_client --cov-report=html

# Run specific test file
uv run pytest tests/test_client.py -v
```

### Integration Tests

The project includes a sophisticated multi-tier integration testing framework:

#### Quick Start

```bash
# Run sandbox integration tests (recommended)
.\tests\integration\integration-test-simple.ps1 test-sandbox

# Run mock server tests (no external dependencies)
.\tests\integration\integration-test-simple.ps1 test-mock

# Run with verbose output
.\tests\integration\integration-test-simple.ps1 test-sandbox -VerboseOutput
```

#### Test Levels

1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API
   - No external dependencies
   - Complete API simulation
   - Ideal for CI/CD pipelines

2. **Sandbox Tests** โญ *(Default)* - Tests against real D365 F&O test environments
   - Validates authentication
   - Tests real API behavior
   - Requires test environment access

3. **Live Tests** - Optional tests against production environments
   - Final validation
   - Performance benchmarking
   - Use with caution

#### Configuration

Set up integration testing with environment variables:

```bash
# Copy the template and configure
cp tests/integration/.env.template tests/integration/.env

# Edit .env file with your settings:
INTEGRATION_TEST_LEVEL=sandbox
D365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-client-secret
AZURE_TENANT_ID=your-tenant-id
```

#### Available Commands

```bash
# Test environment setup
.\tests\integration\integration-test-simple.ps1 setup

# Dependency checking
.\tests\integration\integration-test-simple.ps1 deps-check

# Run specific test levels
.\tests\integration\integration-test-simple.ps1 test-mock
.\tests\integration\integration-test-simple.ps1 test-sandbox
.\tests\integration\integration-test-simple.ps1 test-live

# Coverage and reporting
.\tests\integration\integration-test-simple.ps1 coverage

# Clean up test artifacts
.\tests\integration\integration-test-simple.ps1 clean
```

#### Test Coverage

Integration tests cover:

- โœ… **Connection & Authentication** - Azure AD integration, SSL/TLS validation
- โœ… **Version Methods** - Application, platform, and build version retrieval
- โœ… **Metadata Operations** - Entity discovery, metadata API validation
- โœ… **Data Operations** - CRUD operations, OData query validation
- โœ… **Error Handling** - Network failures, authentication errors, invalid requests
- โœ… **Performance** - Response time validation, concurrent operations

For detailed information, see [Integration Testing Documentation](tests/integration/README.md).

### Test Results

Recent sandbox integration test results:
```
โœ… 17 passed, 0 failed, 2 warnings in 37.67s
====================================================== 
โœ… TestSandboxConnection::test_connection_success
โœ… TestSandboxConnection::test_metadata_connection_success  
โœ… TestSandboxVersionMethods::test_get_application_version
โœ… TestSandboxVersionMethods::test_get_platform_build_version
โœ… TestSandboxVersionMethods::test_get_application_build_version
โœ… TestSandboxVersionMethods::test_version_consistency
โœ… TestSandboxMetadataOperations::test_download_metadata
โœ… TestSandboxMetadataOperations::test_search_entities
โœ… TestSandboxMetadataOperations::test_get_data_entities
โœ… TestSandboxMetadataOperations::test_get_public_entities
โœ… TestSandboxDataOperations::test_get_available_entities
โœ… TestSandboxDataOperations::test_odata_query_options
โœ… TestSandboxAuthentication::test_authenticated_requests
โœ… TestSandboxErrorHandling::test_invalid_entity_error
โœ… TestSandboxErrorHandling::test_invalid_action_error
โœ… TestSandboxPerformance::test_response_times
โœ… TestSandboxPerformance::test_concurrent_operations
```

## Model Context Protocol (MCP) Server

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.

### Overview

The MCP server provides:
- **12 functional tools** covering all major D365 F&O operations
- **4 resource types** with comprehensive metadata exposure  
- **Production-ready** implementation with proper error handling and authentication
- **Performance optimization** with connection pooling and intelligent caching V2
- **Comprehensive testing** with 14 unit tests (100% pass rate)
- **Profile support** for multi-environment configurations

### Quick Start

#### Installation and Setup

```bash
# Install d365fo-client with MCP dependencies
pip install d365fo-client

# Set up environment variables
export D365FO_BASE_URL="https://your-environment.dynamics.com"
export AZURE_CLIENT_ID="your-client-id"          # Optional with default credentials
export AZURE_CLIENT_SECRET="your-client-secret"  # Optional with default credentials  
export AZURE_TENANT_ID="your-tenant-id"          # Optional with default credentials

# Start the MCP server
d365fo-mcp-server
```

#### Alternative: Programmatic Usage

```python
from d365fo_client.mcp import D365FOMCPServer

# Create and run server with custom configuration
config = {
    "default_environment": {
        "base_url": "https://your-environment.dynamics.com",
        "use_default_credentials": True
    }
}

server = D365FOMCPServer(config)
await server.run()
```

### MCP Tools

The server provides 12 comprehensive tools organized into functional categories:

#### Connection Tools (2 tools)
- **`d365fo_test_connection`** - Test environment connectivity and health
- **`d365fo_get_environment_info`** - Get comprehensive environment details, versions, and statistics

#### CRUD Operations (5 tools)
- **`d365fo_query_entities`** - Advanced OData querying with filters, selections, and pagination
- **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options
- **`d365fo_create_entity_record`** - Create new entity records with validation
- **`d365fo_update_entity_record`** - Update existing records with optimistic concurrency
- **`d365fo_delete_entity_record`** - Delete entity records with conflict detection

#### Metadata Tools (5 tools)
- **`d365fo_search_entities`** - Search entities by pattern with advanced filtering and FTS5 search
- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties and relationships
- **`d365fo_search_actions`** - Search available OData actions and functions
- **`d365fo_search_enums`** - Search system enumerations with filtering
- **`d365fo_get_enum_info`** - Get detailed enumeration information and values

#### Label Tools (2 tools)  
- **`d365fo_get_label`** - Get single label text by ID with language support
- **`d365fo_get_labels_batch`** - Get multiple labels efficiently in batch operations

### MCP Resources

The server exposes four types of resources for discovery and access:

#### Entity Resources
Access entity metadata and sample data:
```
d365fo://entities/CustomersV3     # Customer entity with metadata and sample data
d365fo://entities/SalesOrders     # Sales order entity information
d365fo://entities/Products        # Product entity details
```

#### Metadata Resources
Access system-wide metadata:
```
d365fo://metadata/entities        # All data entities metadata (V2 cache)
d365fo://metadata/actions         # Available OData actions  
d365fo://metadata/enumerations    # System enumerations
d365fo://metadata/labels          # System labels and translations
```

#### Environment Resources
Access environment status and information:
```
d365fo://environment/status       # Environment health and connectivity
d365fo://environment/version      # Version information (app, platform, build)
d365fo://environment/cache        # Cache status and statistics V2
```

#### Query Resources
Access predefined and templated queries:
```
d365fo://queries/customers_recent # Recent customers query template
d365fo://queries/sales_summary    # Sales summary query with parameters
```

#### Database Resources (New in V2)
Access metadata database queries:
```
d365fo://database/entities        # SQL-based entity searches with FTS5
d365fo://database/actions         # Action discovery with metadata
d365fo://database/statistics      # Cache and performance statistics
```

### Usage Examples

#### Basic Tool Execution

```json
{
  "tool": "d365fo_query_entities",
  "arguments": {
    "entityName": "CustomersV3",
    "select": ["CustomerAccount", "Name", "Email"],
    "filter": "CustomerGroup eq 'VIP'",
    "top": 10
  }
}
```

#### Entity Schema Discovery

```json
{
  "tool": "d365fo_get_entity_schema", 
  "arguments": {
    "entityName": "CustomersV3",
    "includeProperties": true,
    "resolveLabels": true,
    "language": "en-US"
  }
}
```

#### Environment Information

```json
{
  "tool": "d365fo_get_environment_info",
  "arguments": {}
}
```

### Authentication & Configuration

#### Default Credentials (Recommended)
Uses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):

```bash
export D365FO_BASE_URL="https://your-environment.dynamics.com"
# No additional auth environment variables needed
d365fo-mcp-server
```

#### Explicit Credentials
For service principal authentication:

```bash
export D365FO_BASE_URL="https://your-environment.dynamics.com"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"
export AZURE_TENANT_ID="your-tenant-id"
d365fo-mcp-server
```

#### Advanced Configuration

Create a configuration file or set additional environment variables:

```bash
# Optional: Logging configuration
export D365FO_LOG_LEVEL="DEBUG"

# Optional: Cache settings
export D365FO_CACHE_DIR="/custom/cache/path"

# Optional: Performance tuning
export D365FO_CONNECTION_TIMEOUT="60"
export D365FO_MAX_CONCURRENT_REQUESTS="10"
```

### Integration with AI Assistants

The MCP server seamlessly integrates with AI assistants and development tools:

#### Claude Desktop Integration
Add to your Claude Desktop configuration:

```json
{
  "mcpServers": {
    "d365fo": {
      "command": "d365fo-mcp-server",
      "env": {
        "D365FO_BASE_URL": "https://your-environment.dynamics.com" //Optional
      }
    }
  }
}
```

#### VS Code Integration

##### Option 1: Default Credentials (Recommended)
Add to your VS Code `mcp.json` for GitHub Copilot with MCP:

```json
{
  "servers": {
    "d365fo-mcp-server": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "--from",
        "d365fo-client",
        "d365fo-mcp-server"
      ],
      "env": {
        "D365FO_BASE_URL": "https://your-environment.dynamics.com",
        "D365FO_LOG_LEVEL": "INFO"
      }
    }
  }
}
```

##### Option 2: Explicit Credentials
For environments requiring service principal authentication:

```json
{
  "servers": {
    "d365fo-mcp-server": {
      "type": "stdio", 
      "command": "uvx",
      "args": [
        "--from",
        "d365fo-client",
        "d365fo-mcp-server"
      ],
      "env": {
        "D365FO_BASE_URL": "https://your-environment.dynamics.com",
        "D365FO_LOG_LEVEL": "DEBUG",
        "D365FO_CLIENT_ID": "${input:client_id}",
        "D365FO_CLIENT_SECRET": "${input:client_secret}",
        "D365FO_TENANT_ID": "${input:tenant_id}"
      }
    }
  },
  "inputs": [
    {
      "id": "tenant_id",
      "type": "promptString",
      "description": "Azure AD Tenant ID for D365 F&O authentication",
      "password": true
    },
    {
      "id": "client_id", 
      "type": "promptString",
      "description": "Azure AD Client ID for D365 F&O authentication",
      "password": true
    },
    {
      "id": "client_secret",
      "type": "promptString", 
      "description": "Azure AD Client Secret for D365 F&O authentication",
      "password": true
    }
  ]
}
```

**Benefits of uvx approach:**
- Always uses the latest version from the repository
- No local installation required  
- Automatic dependency management
- Works across different environments

#### Custom MCP Clients
Connect using any MCP-compatible client library:

```python
from mcp import Client

async with Client("d365fo-mcp-server") as client:
    # Discover available tools
    tools = await client.list_tools()
    
    # Execute operations
    result = await client.call_tool(
        "d365fo_query_entities",
        {"entityName": "Customers", "top": 5}
    )
```

### Architecture Benefits

#### For AI Assistants
- **Standardized Interface**: Consistent MCP protocol access to D365 F&O
- **Rich Metadata**: Self-describing entities and operations
- **Type Safety**: Schema validation for all operations
- **Error Context**: Detailed error information for troubleshooting

#### For Developers  
- **Minimal Integration**: Standard MCP client libraries
- **Comprehensive Coverage**: Full D365 F&O functionality exposed
- **Performance Optimized**: Efficient connection and caching strategies
- **Well Documented**: Complete API documentation and examples

#### For Organizations
- **Secure Access**: Enterprise-grade authentication (Azure AD, Managed Identity)
- **Audit Logging**: Complete operation tracking and monitoring
- **Scalable Design**: Connection pooling and session management
- **Maintenance Friendly**: Clear architecture and comprehensive test coverage

### Troubleshooting

#### Common Issues

**Connection Failures**
```bash
# Test connectivity
d365fo-client get-version --base-url https://your-environment.dynamics.com

# Check logs
tail -f ~/.d365fo-mcp/logs/mcp-server.log
```

**Authentication Issues**
```bash
# Verify Azure CLI authentication
az account show

# Test with explicit credentials
export AZURE_CLIENT_ID="your-client-id"
# ... set other variables
d365fo-mcp-server
```

**Performance Issues**
```bash
# Enable debug logging
export D365FO_LOG_LEVEL="DEBUG"

# Adjust connection settings
export D365FO_CONNECTION_TIMEOUT="120"
export D365FO_MAX_CONCURRENT_REQUESTS="5"
```

#### Getting Help

- **Logs**: Check `~/.d365fo-mcp/logs/mcp-server.log` for detailed error information
- **Environment**: Use `d365fo_get_environment_info` tool to check system status
- **Documentation**: See [MCP Implementation Summary](docs/MCP_IMPLEMENTATION_SUMMARY.md) for technical details
- **Issues**: Report problems at [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests (`uv run pytest`)
5. Run integration tests (`.\tests\integration\integration-test-simple.ps1 test-sandbox`)
6. Format code (`uv run black . && uv run isort .`)
7. Commit changes (`git commit -m 'Add amazing feature'`)
8. Push to branch (`git push origin feature/amazing-feature`)
9. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.

## Support

- ๐Ÿ“ง Email: mo@thedataguy.pro
- ๐Ÿ› Issues: [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)


## Related Projects

- [Microsoft Dynamics 365](https://dynamics.microsoft.com/)
- [OData](https://www.odata.org/)
- [Azure Identity](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity)
- [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol/python-sdk) - For AI assistant integration

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "d365fo-client",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.13",
    "maintainer_email": null,
    "keywords": "dynamics365, d365, finance, operations, erp, microsoft",
    "author": null,
    "author_email": "Muhammad Afzaal <mo@thedataguy.pro>",
    "download_url": "https://files.pythonhosted.org/packages/86/12/be3dfccafb62c8a2cd7bb4c6362e52e0a31f8d041812dccc3949307dabfa/d365fo_client-0.2.2.tar.gz",
    "platform": null,
    "description": "# Dynamics 365 Finance & Operations Client and MCP Server\n\nA 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.\n\n## Features\n\n- \ud83d\udd17 **OData Client**: Full CRUD operations on D365 F&O data entities with composite key support\n- \ud83d\udcca **Metadata Management V2**: Enhanced caching system with intelligent synchronization and FTS5 search\n- \ud83c\udff7\ufe0f **Label Operations V2**: Multilingual label caching with performance improvements and async support\n- \ud83d\udd0d **Advanced Querying**: Support for all OData query parameters ($select, $filter, $expand, etc.)\n- \u26a1 **Action Execution**: Execute bound and unbound OData actions with comprehensive parameter handling\n- \ud83d\udd12 **Authentication**: Azure AD integration with default credentials and service principal support\n- \ud83d\udcbe **Intelligent Caching**: Cross-environment cache sharing with module-based version detection\n- \ud83c\udf10 **Async/Await**: Modern async/await patterns with optimized session management\n- \ud83d\udcdd **Type Hints**: Full type annotation support with enhanced data models\n- \ud83e\udd16 **MCP Server**: Production-ready Model Context Protocol server with 12 tools and 4 resource types\n- \ud83d\udda5\ufe0f **Comprehensive CLI**: Hierarchical command-line interface for all D365 F&O operations\n- \ud83e\uddea **Multi-tier Testing**: Mock, sandbox, and live integration testing framework (17/17 tests passing)\n- \ud83d\udccb **Metadata Scripts**: PowerShell and Python utilities for entity, enumeration, and action discovery\n\n## Installation\n\n```bash\n# Install from PyPI\npip install d365fo-client\n\n# Or install from source\ngit clone https://github.com/mafzaal/d365fo-client.git\ncd d365fo-client\nuv sync  # Installs with exact dependencies from uv.lock\n```\n\n**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.\n\n## Quick Start\n\n## Command Line Interface (CLI)\n\nd365fo-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.\n\n### Usage\n\n```bash\n# Use the installed CLI command\nd365fo-client [GLOBAL_OPTIONS] COMMAND [SUBCOMMAND] [OPTIONS]\n\n# Alternative: Module execution\npython -m d365fo_client.main [OPTIONS] COMMAND [ARGS]\n```\n\n### Command Categories\n\n#### Entity Operations\n```bash\n# List entities with filtering\nd365fo-client entities list --pattern \"customer\" --limit 10\n\n# Get entity details and schema\nd365fo-client entities get CustomersV3 --properties --keys --labels\n\n# CRUD operations\nd365fo-client entities create Customers --data '{\"CustomerAccount\":\"US-999\",\"Name\":\"Test\"}'\nd365fo-client entities update Customers US-999 --data '{\"Name\":\"Updated Name\"}'\nd365fo-client entities delete Customers US-999\n```\n\n#### Metadata Operations\n```bash\n# Search and discover entities\nd365fo-client metadata entities --search \"sales\" --output json\n\n# Get available actions\nd365fo-client metadata actions --pattern \"calculate\" --limit 5\n\n# Enumerate system enumerations\nd365fo-client metadata enums --search \"status\" --output table\n\n# Synchronize metadata cache\nd365fo-client metadata sync --force-refresh\n```\n\n#### Version Information\n```bash\n# Get application versions\nd365fo-client version app\nd365fo-client version platform  \nd365fo-client version build\n```\n\n#### Label Operations\n```bash\n# Resolve single label\nd365fo-client labels resolve \"@SYS13342\"\n\n# Search labels by pattern\nd365fo-client labels search \"customer\" --language \"en-US\"\n```\n\n### Global Options\n\n- `--base-url URL` \u2014 Specify D365 F&O environment URL\n- `--profile NAME` \u2014 Use named configuration profile  \n- `--output FORMAT` \u2014 Output format: json, table, csv, yaml (default: table)\n- `--verbose` \u2014 Enable verbose output for debugging\n- `--timeout SECONDS` \u2014 Request timeout (default: 30)\n\n### Configuration Profiles\n\nCreate reusable configurations in `~/.d365fo-client/config.yaml`:\n\n```yaml\nprofiles:\n  production:\n    base_url: \"https://prod.dynamics.com\"\n    use_default_credentials: true\n    timeout: 60\n    \n  development:\n    base_url: \"https://dev.dynamics.com\" \n    client_id: \"${AZURE_CLIENT_ID}\"\n    client_secret: \"${AZURE_CLIENT_SECRET}\"\n    tenant_id: \"${AZURE_TENANT_ID}\"\n    use_cache_first: true\n\ndefault_profile: \"development\"\n```\n\n### Examples\n\n```bash\n# Quick entity discovery\nd365fo-client entities list --pattern \"cust.*\" --output json\n\n# Get comprehensive entity information\nd365fo-client entities get CustomersV3 --properties --keys --labels --output yaml\n\n# Search for calculation actions\nd365fo-client metadata actions --pattern \"calculate|compute\" --output table\n\n# Test environment connectivity\nd365fo-client version app --verbose\n```\n\nFor a complete command reference:\n\n```bash\nd365fo-client --help\nd365fo-client entities --help\nd365fo-client metadata --help\n```\n### Basic Usage\n\n```python\nimport asyncio\nfrom d365fo_client import D365FOClient, FOClientConfig\n\nasync def main():\n    # Simple configuration with default credentials\n    config = FOClientConfig(\n        base_url=\"https://your-fo-environment.dynamics.com\",\n        use_default_credentials=True  # Uses Azure Default Credential\n    )\n    \n    async with D365FOClient(config) as client:\n        # Test connection\n        if await client.test_connection():\n            print(\"\u2705 Connected successfully!\")\n        \n        # Get environment information\n        env_info = await client.get_environment_info()\n        print(f\"Environment: {env_info.application_version}\")\n        \n        # Search for entities (uses metadata cache v2)\n        customer_entities = await client.search_entities(\"customer\")\n        print(f\"Found {len(customer_entities)} customer entities\")\n        \n        # Get customers with query options\n        from d365fo_client import QueryOptions\n        options = QueryOptions(\n            select=[\"CustomerAccount\", \"Name\", \"SalesCurrencyCode\"],\n            top=10,\n            orderby=[\"Name\"]\n        )\n        \n        customers = await client.get_data(\"/data/CustomersV3\", options)\n        print(f\"Retrieved {len(customers['value'])} customers\")\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n### Using Convenience Function\n\n```python\nfrom d365fo_client import create_client\n\n# Quick client creation with enhanced defaults\nasync with create_client(\"https://your-fo-environment.dynamics.com\") as client:\n    customers = await client.get_data(\"/data/CustomersV3\", top=5)\n```\n\n## Configuration\n\n### Authentication Options\n\n```python\nfrom d365fo_client import FOClientConfig\n\n# Option 1: Default Azure credentials (recommended)\nconfig = FOClientConfig(\n    base_url=\"https://your-fo-environment.dynamics.com\",\n    use_default_credentials=True\n)\n\n# Option 2: Client credentials\nconfig = FOClientConfig(\n    base_url=\"https://your-fo-environment.dynamics.com\",\n    client_id=\"your-client-id\",\n    client_secret=\"your-client-secret\", \n    tenant_id=\"your-tenant-id\",\n    use_default_credentials=False\n)\n\n# Option 3: With custom settings\nconfig = FOClientConfig(\n    base_url=\"https://your-fo-environment.dynamics.com\",\n    use_default_credentials=True,\n    verify_ssl=False,  # For development environments\n    timeout=60,  # Request timeout in seconds\n    metadata_cache_dir=\"./my_cache\",  # Custom cache directory\n    use_label_cache=True,  # Enable label caching\n    label_cache_expiry_minutes=120  # Cache for 2 hours\n)\n```\n\n## Core Operations\n\n### CRUD Operations\n\n```python\nasync with D365FOClient(config) as client:\n    # CREATE - Create new customer (supports composite keys)\n    new_customer = {\n        \"CustomerAccount\": \"US-999\",\n        \"Name\": \"Test Customer\",\n        \"SalesCurrencyCode\": \"USD\"\n    }\n    created = await client.create_data(\"/data/CustomersV3\", new_customer)\n    \n    # READ - Get single customer by key\n    customer = await client.get_data(\"/data/CustomersV3('US-001')\")\n    \n    # UPDATE - Update customer with optimistic concurrency\n    updates = {\"Name\": \"Updated Customer Name\"}\n    updated = await client.update_data(\"/data/CustomersV3('US-001')\", updates)\n    \n    # DELETE - Delete customer\n    success = await client.delete_data(\"/data/CustomersV3('US-999')\")\n    print(f\"Delete successful: {success}\")\n```\n\n### Advanced Querying\n\n```python\nfrom d365fo_client import QueryOptions\n\n# Complex query with multiple options\noptions = QueryOptions(\n    select=[\"CustomerAccount\", \"Name\", \"SalesCurrencyCode\", \"CustomerGroupId\"],\n    filter=\"SalesCurrencyCode eq 'USD' and contains(Name, 'Corp')\",\n    expand=[\"CustomerGroup\"],\n    orderby=[\"Name desc\", \"CustomerAccount\"],\n    top=50,\n    skip=10,\n    count=True\n)\n\nresult = await client.get_data(\"/data/CustomersV3\", options)\nprint(f\"Total count: {result.get('@odata.count')}\")\n```\n\n### Action Execution\n\n```python\n# Unbound action\nresult = await client.post_data(\"/data/calculateTax\", {\n    \"amount\": 1000.00,\n    \"taxGroup\": \"STANDARD\"\n})\n\n# Bound action on entity set\nresult = await client.post_data(\"/data/CustomersV3/calculateBalances\", {\n    \"asOfDate\": \"2024-12-31\"\n})\n\n# Bound action on specific entity instance  \nresult = await client.post_data(\"/data/CustomersV3('US-001')/calculateBalance\", {\n    \"asOfDate\": \"2024-12-31\"\n})\n```\n\n### Metadata Operations\n\n```python\n# Intelligent metadata synchronization (v2 system)\nsync_manager = await client.get_sync_manager()\nawait sync_manager.smart_sync()\n\n# Search entities with enhanced filtering\nsales_entities = await client.search_entities(\"sales\")\nprint(\"Sales-related entities:\", [e.name for e in sales_entities])\n\n# Get detailed entity information with labels\nentity_info = await client.get_public_entity_info(\"CustomersV3\")\nif entity_info:\n    print(f\"Entity: {entity_info.name}\")\n    print(f\"Label: {entity_info.label_text}\")\n    print(f\"Data Service Enabled: {entity_info.data_service_enabled}\")\n\n# Search actions with caching\ncalc_actions = await client.search_actions(\"calculate\")\nprint(\"Calculation actions:\", [a.name for a in calc_actions])\n\n# Get enumeration information\nenum_info = await client.get_public_enumeration_info(\"NoYes\")\nif enum_info:\n    print(f\"Enum: {enum_info.name}\")\n    for member in enum_info.members:\n        print(f\"  {member.name} = {member.value}\")\n```\n\n### Label Operations\n\n```python\n# Get specific label (v2 caching system)\nlabel_text = await client.get_label_text(\"@SYS13342\")\nprint(f\"Label text: {label_text}\")\n\n# Get multiple labels efficiently\nlabels = await client.get_labels_batch([\n    \"@SYS13342\", \"@SYS9490\", \"@GLS63332\"\n])\nfor label_id, text in labels.items():\n    print(f\"{label_id}: {text}\")\n\n# Enhanced entity info with resolved labels\nentity_info = await client.get_public_entity_info_with_labels(\"CustomersV3\")\nif entity_info.label_text:\n    print(f\"Entity display name: {entity_info.label_text}\")\n\n# Access enhanced properties with labels\nfor prop in entity_info.enhanced_properties[:5]:\n    if hasattr(prop, 'label_text') and prop.label_text:\n        print(f\"{prop.name}: {prop.label_text}\")\n```\n\n## Error Handling\n\n```python\nfrom d365fo_client import D365FOClientError, AuthenticationError, ConnectionError\n\ntry:\n    async with D365FOClient(config) as client:\n        customer = await client.get_data(\"/data/CustomersV3('NON-EXISTENT')\")\nexcept ConnectionError as e:\n    print(f\"Connection failed: {e}\")\nexcept AuthenticationError as e:\n    print(f\"Authentication failed: {e}\")\nexcept D365FOClientError as e:\n    print(f\"Client operation failed: {e}\")\n    print(f\"Status code: {e.status_code}\")\n    print(f\"Response: {e.response_text}\")\n```\n\n## Development\n\n### Setting up Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/mafzaal/d365fo-client.git\ncd d365fo-client\n\n# Install with development dependencies using uv\nuv sync --dev\n\n# Run tests\nuv run pytest\n\n# Run integration tests\n.\\tests\\integration\\integration-test-simple.ps1 test-sandbox\n\n# Format code\nuv run black .\nuv run isort .\n\n# Type checking\nuv run mypy src/\n\n# Quality checks\n.\\make.ps1 quality-check  # Windows PowerShell\n# or\nmake quality-check       # Unix/Linux/macOS\n```\n\n### Project Structure\n\n```\nd365fo-client/\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 d365fo_client/\n\u2502       \u251c\u2500\u2500 __init__.py          # Public API exports\n\u2502       \u251c\u2500\u2500 main.py              # CLI entry point  \n\u2502       \u251c\u2500\u2500 cli.py               # CLI command handlers\n\u2502       \u251c\u2500\u2500 client.py            # Enhanced D365FOClient class\n\u2502       \u251c\u2500\u2500 config.py            # Configuration management\n\u2502       \u251c\u2500\u2500 auth.py              # Authentication management\n\u2502       \u251c\u2500\u2500 session.py           # HTTP session management\n\u2502       \u251c\u2500\u2500 crud.py              # CRUD operations\n\u2502       \u251c\u2500\u2500 query.py             # OData query utilities\n\u2502       \u251c\u2500\u2500 metadata.py          # Legacy metadata operations\n\u2502       \u251c\u2500\u2500 metadata_api.py      # Metadata API client\n\u2502       \u251c\u2500\u2500 metadata_cache.py    # Metadata caching layer V2\n\u2502       \u251c\u2500\u2500 metadata_sync.py     # Metadata synchronization V2\n\u2502       \u251c\u2500\u2500 labels.py            # Label operations V2\n\u2502       \u251c\u2500\u2500 profiles.py          # Profile data models\n\u2502       \u251c\u2500\u2500 profile_manager.py   # Profile management\n\u2502       \u251c\u2500\u2500 models.py            # Data models and configurations\n\u2502       \u251c\u2500\u2500 output.py            # Output formatting\n\u2502       \u251c\u2500\u2500 utils.py             # Utility functions\n\u2502       \u251c\u2500\u2500 exceptions.py        # Custom exceptions\n\u2502       \u2514\u2500\u2500 mcp/                 # Model Context Protocol server\n\u2502           \u251c\u2500\u2500 __init__.py      # MCP server exports\n\u2502           \u251c\u2500\u2500 main.py          # MCP server entry point\n\u2502           \u251c\u2500\u2500 server.py        # Core MCP server implementation\n\u2502           \u251c\u2500\u2500 client_manager.py# D365FO client connection pooling\n\u2502           \u251c\u2500\u2500 models.py        # MCP-specific data models\n\u2502           \u251c\u2500\u2500 tools/           # MCP tool implementations (12 tools)\n\u2502           \u2502   \u251c\u2500\u2500 connection_tools.py\n\u2502           \u2502   \u251c\u2500\u2500 crud_tools.py\n\u2502           \u2502   \u251c\u2500\u2500 metadata_tools.py\n\u2502           \u2502   \u2514\u2500\u2500 label_tools.py\n\u2502           \u251c\u2500\u2500 resources/       # MCP resource handlers (4 types)\n\u2502           \u2502   \u251c\u2500\u2500 entity_handler.py\n\u2502           \u2502   \u251c\u2500\u2500 metadata_handler.py\n\u2502           \u2502   \u251c\u2500\u2500 environment_handler.py\n\u2502           \u2502   \u2514\u2500\u2500 query_handler.py\n\u2502           \u2514\u2500\u2500 prompts/         # MCP prompt templates\n\u251c\u2500\u2500 tests/                       # Comprehensive test suite\n\u2502   \u251c\u2500\u2500 unit/                    # Unit tests (pytest-based)\n\u2502   \u251c\u2500\u2500 integration/             # Multi-tier integration testing\n\u2502   \u2502   \u251c\u2500\u2500 mock_server/         # Mock D365 F&O API server\n\u2502   \u2502   \u251c\u2500\u2500 test_mock_server.py  # Mock server tests\n\u2502   \u2502   \u251c\u2500\u2500 test_sandbox.py      # Sandbox environment tests \u2705\n\u2502   \u2502   \u251c\u2500\u2500 test_live.py         # Live environment tests\n\u2502   \u2502   \u251c\u2500\u2500 conftest.py          # Shared pytest fixtures\n\u2502   \u2502   \u251c\u2500\u2500 test_runner.py       # Python test execution engine\n\u2502   \u2502   \u2514\u2500\u2500 integration-test-simple.ps1 # PowerShell automation\n\u2502   \u2514\u2500\u2500 test_mcp_server.py       # MCP server unit tests \u2705\n\u251c\u2500\u2500 scripts/                     # Metadata discovery scripts\n\u2502   \u251c\u2500\u2500 search_data_entities.ps1 # PowerShell entity search\n\u2502   \u251c\u2500\u2500 get_data_entity_schema.ps1 # PowerShell schema retrieval\n\u2502   \u251c\u2500\u2500 search_enums.py          # Python enumeration search\n\u2502   \u251c\u2500\u2500 get_enumeration_info.py  # Python enumeration info\n\u2502   \u251c\u2500\u2500 search_actions.ps1       # PowerShell action search\n\u2502   \u2514\u2500\u2500 get_action_info.py       # Python action information\n\u251c\u2500\u2500 docs/                        # Comprehensive documentation\n\u251c\u2500\u2500 pyproject.toml               # Project configuration\n\u2514\u2500\u2500 README.md                    # This file\n```\n\n## Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `base_url` | str | Required | D365 F&O base URL |\n| `client_id` | str | None | Azure AD client ID |\n| `client_secret` | str | None | Azure AD client secret |\n| `tenant_id` | str | None | Azure AD tenant ID |\n| `use_default_credentials` | bool | True | Use Azure Default Credential |\n| `verify_ssl` | bool | False | Verify SSL certificates |\n| `timeout` | int | 30 | Request timeout in seconds |\n| `metadata_cache_dir` | str | Platform-specific user cache | Metadata cache directory |\n| `use_label_cache` | bool | True | Enable label caching V2 |\n| `label_cache_expiry_minutes` | int | 60 | Label cache expiry time |\n| `use_cache_first` | bool | False | Enable cache-first mode with background sync |\n\n### Cache Directory Behavior\n\nBy default, the client uses platform-appropriate user cache directories:\n\n- **Windows**: `%LOCALAPPDATA%\\d365fo-client` (e.g., `C:\\Users\\username\\AppData\\Local\\d365fo-client`)\n- **macOS**: `~/Library/Caches/d365fo-client` (e.g., `/Users/username/Library/Caches/d365fo-client`)\n- **Linux**: `~/.cache/d365fo-client` (e.g., `/home/username/.cache/d365fo-client`)\n\nYou can override this by explicitly setting `metadata_cache_dir`:\n\n```python\nfrom d365fo_client import FOClientConfig\n\n# Use custom cache directory\nconfig = FOClientConfig(\n    base_url=\"https://your-fo-environment.dynamics.com\",\n    metadata_cache_dir=\"/custom/cache/path\"\n)\n\n# Or get the default cache directory programmatically\nfrom d365fo_client import get_user_cache_dir\n\ncache_dir = get_user_cache_dir(\"my-app\")  # Platform-appropriate cache dir\nconfig = FOClientConfig(\n    base_url=\"https://your-fo-environment.dynamics.com\", \n    metadata_cache_dir=str(cache_dir)\n)\n```\n\n## Testing\n\nThis project includes comprehensive testing at multiple levels to ensure reliability and quality.\n\n### Unit Tests\n\nRun standard unit tests for core functionality:\n\n```bash\n# Run all unit tests\nuv run pytest\n\n# Run with coverage\nuv run pytest --cov=d365fo_client --cov-report=html\n\n# Run specific test file\nuv run pytest tests/test_client.py -v\n```\n\n### Integration Tests\n\nThe project includes a sophisticated multi-tier integration testing framework:\n\n#### Quick Start\n\n```bash\n# Run sandbox integration tests (recommended)\n.\\tests\\integration\\integration-test-simple.ps1 test-sandbox\n\n# Run mock server tests (no external dependencies)\n.\\tests\\integration\\integration-test-simple.ps1 test-mock\n\n# Run with verbose output\n.\\tests\\integration\\integration-test-simple.ps1 test-sandbox -VerboseOutput\n```\n\n#### Test Levels\n\n1. **Mock Server Tests** - Fast, isolated tests against a simulated D365 F&O API\n   - No external dependencies\n   - Complete API simulation\n   - Ideal for CI/CD pipelines\n\n2. **Sandbox Tests** \u2b50 *(Default)* - Tests against real D365 F&O test environments\n   - Validates authentication\n   - Tests real API behavior\n   - Requires test environment access\n\n3. **Live Tests** - Optional tests against production environments\n   - Final validation\n   - Performance benchmarking\n   - Use with caution\n\n#### Configuration\n\nSet up integration testing with environment variables:\n\n```bash\n# Copy the template and configure\ncp tests/integration/.env.template tests/integration/.env\n\n# Edit .env file with your settings:\nINTEGRATION_TEST_LEVEL=sandbox\nD365FO_SANDBOX_BASE_URL=https://your-test.dynamics.com\nAZURE_CLIENT_ID=your-client-id\nAZURE_CLIENT_SECRET=your-client-secret\nAZURE_TENANT_ID=your-tenant-id\n```\n\n#### Available Commands\n\n```bash\n# Test environment setup\n.\\tests\\integration\\integration-test-simple.ps1 setup\n\n# Dependency checking\n.\\tests\\integration\\integration-test-simple.ps1 deps-check\n\n# Run specific test levels\n.\\tests\\integration\\integration-test-simple.ps1 test-mock\n.\\tests\\integration\\integration-test-simple.ps1 test-sandbox\n.\\tests\\integration\\integration-test-simple.ps1 test-live\n\n# Coverage and reporting\n.\\tests\\integration\\integration-test-simple.ps1 coverage\n\n# Clean up test artifacts\n.\\tests\\integration\\integration-test-simple.ps1 clean\n```\n\n#### Test Coverage\n\nIntegration tests cover:\n\n- \u2705 **Connection & Authentication** - Azure AD integration, SSL/TLS validation\n- \u2705 **Version Methods** - Application, platform, and build version retrieval\n- \u2705 **Metadata Operations** - Entity discovery, metadata API validation\n- \u2705 **Data Operations** - CRUD operations, OData query validation\n- \u2705 **Error Handling** - Network failures, authentication errors, invalid requests\n- \u2705 **Performance** - Response time validation, concurrent operations\n\nFor detailed information, see [Integration Testing Documentation](tests/integration/README.md).\n\n### Test Results\n\nRecent sandbox integration test results:\n```\n\u2705 17 passed, 0 failed, 2 warnings in 37.67s\n====================================================== \n\u2705 TestSandboxConnection::test_connection_success\n\u2705 TestSandboxConnection::test_metadata_connection_success  \n\u2705 TestSandboxVersionMethods::test_get_application_version\n\u2705 TestSandboxVersionMethods::test_get_platform_build_version\n\u2705 TestSandboxVersionMethods::test_get_application_build_version\n\u2705 TestSandboxVersionMethods::test_version_consistency\n\u2705 TestSandboxMetadataOperations::test_download_metadata\n\u2705 TestSandboxMetadataOperations::test_search_entities\n\u2705 TestSandboxMetadataOperations::test_get_data_entities\n\u2705 TestSandboxMetadataOperations::test_get_public_entities\n\u2705 TestSandboxDataOperations::test_get_available_entities\n\u2705 TestSandboxDataOperations::test_odata_query_options\n\u2705 TestSandboxAuthentication::test_authenticated_requests\n\u2705 TestSandboxErrorHandling::test_invalid_entity_error\n\u2705 TestSandboxErrorHandling::test_invalid_action_error\n\u2705 TestSandboxPerformance::test_response_times\n\u2705 TestSandboxPerformance::test_concurrent_operations\n```\n\n## Model Context Protocol (MCP) Server\n\nd365fo-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.\n\n### Overview\n\nThe MCP server provides:\n- **12 functional tools** covering all major D365 F&O operations\n- **4 resource types** with comprehensive metadata exposure  \n- **Production-ready** implementation with proper error handling and authentication\n- **Performance optimization** with connection pooling and intelligent caching V2\n- **Comprehensive testing** with 14 unit tests (100% pass rate)\n- **Profile support** for multi-environment configurations\n\n### Quick Start\n\n#### Installation and Setup\n\n```bash\n# Install d365fo-client with MCP dependencies\npip install d365fo-client\n\n# Set up environment variables\nexport D365FO_BASE_URL=\"https://your-environment.dynamics.com\"\nexport AZURE_CLIENT_ID=\"your-client-id\"          # Optional with default credentials\nexport AZURE_CLIENT_SECRET=\"your-client-secret\"  # Optional with default credentials  \nexport AZURE_TENANT_ID=\"your-tenant-id\"          # Optional with default credentials\n\n# Start the MCP server\nd365fo-mcp-server\n```\n\n#### Alternative: Programmatic Usage\n\n```python\nfrom d365fo_client.mcp import D365FOMCPServer\n\n# Create and run server with custom configuration\nconfig = {\n    \"default_environment\": {\n        \"base_url\": \"https://your-environment.dynamics.com\",\n        \"use_default_credentials\": True\n    }\n}\n\nserver = D365FOMCPServer(config)\nawait server.run()\n```\n\n### MCP Tools\n\nThe server provides 12 comprehensive tools organized into functional categories:\n\n#### Connection Tools (2 tools)\n- **`d365fo_test_connection`** - Test environment connectivity and health\n- **`d365fo_get_environment_info`** - Get comprehensive environment details, versions, and statistics\n\n#### CRUD Operations (5 tools)\n- **`d365fo_query_entities`** - Advanced OData querying with filters, selections, and pagination\n- **`d365fo_get_entity_record`** - Retrieve specific records by key with expansion options\n- **`d365fo_create_entity_record`** - Create new entity records with validation\n- **`d365fo_update_entity_record`** - Update existing records with optimistic concurrency\n- **`d365fo_delete_entity_record`** - Delete entity records with conflict detection\n\n#### Metadata Tools (5 tools)\n- **`d365fo_search_entities`** - Search entities by pattern with advanced filtering and FTS5 search\n- **`d365fo_get_entity_schema`** - Get detailed entity schemas with properties and relationships\n- **`d365fo_search_actions`** - Search available OData actions and functions\n- **`d365fo_search_enums`** - Search system enumerations with filtering\n- **`d365fo_get_enum_info`** - Get detailed enumeration information and values\n\n#### Label Tools (2 tools)  \n- **`d365fo_get_label`** - Get single label text by ID with language support\n- **`d365fo_get_labels_batch`** - Get multiple labels efficiently in batch operations\n\n### MCP Resources\n\nThe server exposes four types of resources for discovery and access:\n\n#### Entity Resources\nAccess entity metadata and sample data:\n```\nd365fo://entities/CustomersV3     # Customer entity with metadata and sample data\nd365fo://entities/SalesOrders     # Sales order entity information\nd365fo://entities/Products        # Product entity details\n```\n\n#### Metadata Resources\nAccess system-wide metadata:\n```\nd365fo://metadata/entities        # All data entities metadata (V2 cache)\nd365fo://metadata/actions         # Available OData actions  \nd365fo://metadata/enumerations    # System enumerations\nd365fo://metadata/labels          # System labels and translations\n```\n\n#### Environment Resources\nAccess environment status and information:\n```\nd365fo://environment/status       # Environment health and connectivity\nd365fo://environment/version      # Version information (app, platform, build)\nd365fo://environment/cache        # Cache status and statistics V2\n```\n\n#### Query Resources\nAccess predefined and templated queries:\n```\nd365fo://queries/customers_recent # Recent customers query template\nd365fo://queries/sales_summary    # Sales summary query with parameters\n```\n\n#### Database Resources (New in V2)\nAccess metadata database queries:\n```\nd365fo://database/entities        # SQL-based entity searches with FTS5\nd365fo://database/actions         # Action discovery with metadata\nd365fo://database/statistics      # Cache and performance statistics\n```\n\n### Usage Examples\n\n#### Basic Tool Execution\n\n```json\n{\n  \"tool\": \"d365fo_query_entities\",\n  \"arguments\": {\n    \"entityName\": \"CustomersV3\",\n    \"select\": [\"CustomerAccount\", \"Name\", \"Email\"],\n    \"filter\": \"CustomerGroup eq 'VIP'\",\n    \"top\": 10\n  }\n}\n```\n\n#### Entity Schema Discovery\n\n```json\n{\n  \"tool\": \"d365fo_get_entity_schema\", \n  \"arguments\": {\n    \"entityName\": \"CustomersV3\",\n    \"includeProperties\": true,\n    \"resolveLabels\": true,\n    \"language\": \"en-US\"\n  }\n}\n```\n\n#### Environment Information\n\n```json\n{\n  \"tool\": \"d365fo_get_environment_info\",\n  \"arguments\": {}\n}\n```\n\n### Authentication & Configuration\n\n#### Default Credentials (Recommended)\nUses Azure Default Credential chain (Managed Identity, Azure CLI, etc.):\n\n```bash\nexport D365FO_BASE_URL=\"https://your-environment.dynamics.com\"\n# No additional auth environment variables needed\nd365fo-mcp-server\n```\n\n#### Explicit Credentials\nFor service principal authentication:\n\n```bash\nexport D365FO_BASE_URL=\"https://your-environment.dynamics.com\"\nexport AZURE_CLIENT_ID=\"your-client-id\"\nexport AZURE_CLIENT_SECRET=\"your-client-secret\"\nexport AZURE_TENANT_ID=\"your-tenant-id\"\nd365fo-mcp-server\n```\n\n#### Advanced Configuration\n\nCreate a configuration file or set additional environment variables:\n\n```bash\n# Optional: Logging configuration\nexport D365FO_LOG_LEVEL=\"DEBUG\"\n\n# Optional: Cache settings\nexport D365FO_CACHE_DIR=\"/custom/cache/path\"\n\n# Optional: Performance tuning\nexport D365FO_CONNECTION_TIMEOUT=\"60\"\nexport D365FO_MAX_CONCURRENT_REQUESTS=\"10\"\n```\n\n### Integration with AI Assistants\n\nThe MCP server seamlessly integrates with AI assistants and development tools:\n\n#### Claude Desktop Integration\nAdd to your Claude Desktop configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"d365fo\": {\n      \"command\": \"d365fo-mcp-server\",\n      \"env\": {\n        \"D365FO_BASE_URL\": \"https://your-environment.dynamics.com\" //Optional\n      }\n    }\n  }\n}\n```\n\n#### VS Code Integration\n\n##### Option 1: Default Credentials (Recommended)\nAdd to your VS Code `mcp.json` for GitHub Copilot with MCP:\n\n```json\n{\n  \"servers\": {\n    \"d365fo-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\n        \"--from\",\n        \"d365fo-client\",\n        \"d365fo-mcp-server\"\n      ],\n      \"env\": {\n        \"D365FO_BASE_URL\": \"https://your-environment.dynamics.com\",\n        \"D365FO_LOG_LEVEL\": \"INFO\"\n      }\n    }\n  }\n}\n```\n\n##### Option 2: Explicit Credentials\nFor environments requiring service principal authentication:\n\n```json\n{\n  \"servers\": {\n    \"d365fo-mcp-server\": {\n      \"type\": \"stdio\", \n      \"command\": \"uvx\",\n      \"args\": [\n        \"--from\",\n        \"d365fo-client\",\n        \"d365fo-mcp-server\"\n      ],\n      \"env\": {\n        \"D365FO_BASE_URL\": \"https://your-environment.dynamics.com\",\n        \"D365FO_LOG_LEVEL\": \"DEBUG\",\n        \"D365FO_CLIENT_ID\": \"${input:client_id}\",\n        \"D365FO_CLIENT_SECRET\": \"${input:client_secret}\",\n        \"D365FO_TENANT_ID\": \"${input:tenant_id}\"\n      }\n    }\n  },\n  \"inputs\": [\n    {\n      \"id\": \"tenant_id\",\n      \"type\": \"promptString\",\n      \"description\": \"Azure AD Tenant ID for D365 F&O authentication\",\n      \"password\": true\n    },\n    {\n      \"id\": \"client_id\", \n      \"type\": \"promptString\",\n      \"description\": \"Azure AD Client ID for D365 F&O authentication\",\n      \"password\": true\n    },\n    {\n      \"id\": \"client_secret\",\n      \"type\": \"promptString\", \n      \"description\": \"Azure AD Client Secret for D365 F&O authentication\",\n      \"password\": true\n    }\n  ]\n}\n```\n\n**Benefits of uvx approach:**\n- Always uses the latest version from the repository\n- No local installation required  \n- Automatic dependency management\n- Works across different environments\n\n#### Custom MCP Clients\nConnect using any MCP-compatible client library:\n\n```python\nfrom mcp import Client\n\nasync with Client(\"d365fo-mcp-server\") as client:\n    # Discover available tools\n    tools = await client.list_tools()\n    \n    # Execute operations\n    result = await client.call_tool(\n        \"d365fo_query_entities\",\n        {\"entityName\": \"Customers\", \"top\": 5}\n    )\n```\n\n### Architecture Benefits\n\n#### For AI Assistants\n- **Standardized Interface**: Consistent MCP protocol access to D365 F&O\n- **Rich Metadata**: Self-describing entities and operations\n- **Type Safety**: Schema validation for all operations\n- **Error Context**: Detailed error information for troubleshooting\n\n#### For Developers  \n- **Minimal Integration**: Standard MCP client libraries\n- **Comprehensive Coverage**: Full D365 F&O functionality exposed\n- **Performance Optimized**: Efficient connection and caching strategies\n- **Well Documented**: Complete API documentation and examples\n\n#### For Organizations\n- **Secure Access**: Enterprise-grade authentication (Azure AD, Managed Identity)\n- **Audit Logging**: Complete operation tracking and monitoring\n- **Scalable Design**: Connection pooling and session management\n- **Maintenance Friendly**: Clear architecture and comprehensive test coverage\n\n### Troubleshooting\n\n#### Common Issues\n\n**Connection Failures**\n```bash\n# Test connectivity\nd365fo-client get-version --base-url https://your-environment.dynamics.com\n\n# Check logs\ntail -f ~/.d365fo-mcp/logs/mcp-server.log\n```\n\n**Authentication Issues**\n```bash\n# Verify Azure CLI authentication\naz account show\n\n# Test with explicit credentials\nexport AZURE_CLIENT_ID=\"your-client-id\"\n# ... set other variables\nd365fo-mcp-server\n```\n\n**Performance Issues**\n```bash\n# Enable debug logging\nexport D365FO_LOG_LEVEL=\"DEBUG\"\n\n# Adjust connection settings\nexport D365FO_CONNECTION_TIMEOUT=\"120\"\nexport D365FO_MAX_CONCURRENT_REQUESTS=\"5\"\n```\n\n#### Getting Help\n\n- **Logs**: Check `~/.d365fo-mcp/logs/mcp-server.log` for detailed error information\n- **Environment**: Use `d365fo_get_environment_info` tool to check system status\n- **Documentation**: See [MCP Implementation Summary](docs/MCP_IMPLEMENTATION_SUMMARY.md) for technical details\n- **Issues**: Report problems at [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Make your changes\n4. Run tests (`uv run pytest`)\n5. Run integration tests (`.\\tests\\integration\\integration-test-simple.ps1 test-sandbox`)\n6. Format code (`uv run black . && uv run isort .`)\n7. Commit changes (`git commit -m 'Add amazing feature'`)\n8. Push to branch (`git push origin feature/amazing-feature`)\n9. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.\n\n## Support\n\n- \ud83d\udce7 Email: mo@thedataguy.pro\n- \ud83d\udc1b Issues: [GitHub Issues](https://github.com/mafzaal/d365fo-client/issues)\n\n\n## Related Projects\n\n- [Microsoft Dynamics 365](https://dynamics.microsoft.com/)\n- [OData](https://www.odata.org/)\n- [Azure Identity](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity)\n- [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol/python-sdk) - For AI assistant integration\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Microsoft Dynamics 365 Finance & Operations client",
    "version": "0.2.2",
    "project_urls": {
        "Changelog": "https://github.com/mafzaal/d365fo-client/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/mafzaal/d365fo-client",
        "Homepage": "https://github.com/mafzaal/d365fo-client",
        "Issues": "https://github.com/mafzaal/d365fo-client/issues",
        "Repository": "https://github.com/mafzaal/d365fo-client"
    },
    "split_keywords": [
        "dynamics365",
        " d365",
        " finance",
        " operations",
        " erp",
        " microsoft"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1229d6f1025f78e2971a15ad83f1d8371a0da456280aba8d6de5db5f8118cac1",
                "md5": "4e717534f750455d227d7f780e190bc1",
                "sha256": "4bd7aab8c9f422908a8243530d031c82ad881c549ac467aa7fafe5c891641620"
            },
            "downloads": -1,
            "filename": "d365fo_client-0.2.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4e717534f750455d227d7f780e190bc1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.13",
            "size": 157843,
            "upload_time": "2025-08-28T03:10:18",
            "upload_time_iso_8601": "2025-08-28T03:10:18.834649Z",
            "url": "https://files.pythonhosted.org/packages/12/29/d6f1025f78e2971a15ad83f1d8371a0da456280aba8d6de5db5f8118cac1/d365fo_client-0.2.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8612be3dfccafb62c8a2cd7bb4c6362e52e0a31f8d041812dccc3949307dabfa",
                "md5": "61ef2f00e3a5c71100f64edb5497a903",
                "sha256": "2b8c9407ed2bab69730a4e674954432ca2706f4d413b5d67f868ef9ff9994661"
            },
            "downloads": -1,
            "filename": "d365fo_client-0.2.2.tar.gz",
            "has_sig": false,
            "md5_digest": "61ef2f00e3a5c71100f64edb5497a903",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.13",
            "size": 152895,
            "upload_time": "2025-08-28T03:10:20",
            "upload_time_iso_8601": "2025-08-28T03:10:20.587987Z",
            "url": "https://files.pythonhosted.org/packages/86/12/be3dfccafb62c8a2cd7bb4c6362e52e0a31f8d041812dccc3949307dabfa/d365fo_client-0.2.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-28 03:10:20",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mafzaal",
    "github_project": "d365fo-client",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "d365fo-client"
}
        
Elapsed time: 0.98665s