# d365-csu

Python MCP tool for D365 Commerce Scale Unit (CSU) REST APIs.

Provides CRUD operations against CSU endpoints and a built-in catalog of available endpoints with sample JSON payloads.

## Installation

```bash
pip install -e .
```

## Configuration

Set the following environment variables:

| Variable | Description |
|---|---|
| `CSU_BASE_URL` | CSU base URL (e.g. `https://myhost.commerce.dynamics.com/Commerce`) |
| `CSU_OUN` | Operating Unit Number |
| `AZURE_TENANT_ID` | Azure AD tenant ID |
| `AZURE_CLIENT_ID` | App registration client ID |
| `AZURE_CLIENT_SECRET` | App registration client secret |
| `AZURE_RESOURCE_ID` | CSU resource ID for token scope |

## Usage

### As an MCP Tool

Add to your MCP client config (e.g. Claude Desktop):

```json
{
  "mcpServers": {
    "d365-csu": {
      "command": "d365-csu",
      "env": {
        "CSU_BASE_URL": "https://myhost.commerce.dynamics.com/Commerce",
        "CSU_OUN": "128",
        "AZURE_TENANT_ID": "your-tenant-id",
        "AZURE_CLIENT_ID": "your-client-id",
        "AZURE_CLIENT_SECRET": "your-client-secret",
        "AZURE_RESOURCE_ID": "your-resource-id"
      }
    }
  }
}
```

### Browse Endpoints (no credentials needed)

```python
from d365_csu.endpoints import list_endpoints, get_sample_payload, list_entities

# List all supported entities
print(list_entities())
# ['Carts', 'Categories', 'Customers', 'OrgUnits', 'Products', 'SalesOrders']

# List all endpoints for an entity
for ep in list_endpoints("Customers"):
    print(f"{ep['method']} {ep['path']} — {ep['description']}")

# Get a sample request body
payload = get_sample_payload("Customers", "Search")
print(payload)
```

### Direct Python Usage

```python
from d365_csu.auth import AzureADAuth
from d365_csu.client import CSUClient
from d365_csu.entities import customers, products

auth = AzureADAuth(
    tenant_id="...",
    client_id="...",
    client_secret="...",
    resource_id="...",
)
client = CSUClient("https://myhost.commerce.dynamics.com/Commerce", "128", auth)

# Search customers
results = customers.search_customers(client, "contoso")

# Search products
results = products.search_products(client, "laptop")
```

## Available MCP Tools

| Tool | Description |
|---|---|
| `csu_list_endpoints` | List available CSU endpoints with sample payloads |
| `csu_list_entities` | List supported entity names |
| `csu_get_sample_payload` | Get sample JSON body for an endpoint |
| `csu_search_customers` | Search customers by keyword |
| `csu_create_customer` | Create a new customer |
| `csu_search_products` | Search product catalog |
| `csu_get_product` | Get a product by ID |
| `csu_manage_cart` | Cart operations (create, add lines, checkout) |
| `csu_search_orders` | Search sales orders |
| `csu_get_stores` | Get org units / stores |
| `csu_get_categories` | Get category hierarchy |
| `csu_raw_request` | Make a raw CSU API call |

## Usage with Claude CLI

Add d365-csu as an MCP server in your project's `.claude/settings.json`:

```json
{
  "mcpServers": {
    "d365-csu": {
      "command": "d365-csu",
      "env": {
        "CSU_BASE_URL": "https://myhost.commerce.dynamics.com/Commerce",
        "CSU_OUN": "128",
        "AZURE_TENANT_ID": "your-tenant-id",
        "AZURE_CLIENT_ID": "your-client-id",
        "AZURE_CLIENT_SECRET": "your-client-secret",
        "AZURE_RESOURCE_ID": "your-resource-id"
      }
    }
  }
}
```

Once configured, Claude CLI can call any of the MCP tools directly. For example:

```
> Search for customers named "contoso"
Claude will call: csu_search_customers(keyword="contoso")

> Create a cart, add product 12345, and checkout
Claude will call: csu_manage_cart(action="create"), then add_line, then checkout
```

You can also add the config to `~/.claude/settings.json` to make it available globally across all projects.

## Usage with GitHub Copilot CLI

GitHub Copilot CLI does not currently support MCP servers. To use d365-csu with Copilot, use it as a Python library directly:

```bash
pip install d365-csu
python -c "from d365_csu.entities import customers; ..."
```

For VS Code with GitHub Copilot Chat, MCP server support may be available via the VS Code MCP extension. Configure it the same way as Claude Desktop (see [As an MCP Tool](#as-an-mcp-tool) above).

## Architecture / Sequence Diagrams

Full diagrams are available in [`docs/sequence-diagrams.md`](docs/sequence-diagrams.md). Key flows shown below.

### Authentication Flow

```mermaid
sequenceDiagram
    participant App
    participant AzureADAuth
    participant AzureAD as Azure AD
    participant CSUClient

    App->>AzureADAuth: AzureADAuth(tenant_id, client_id, client_secret, resource_id)
    App->>CSUClient: CSUClient(base_url, oun, auth)
    App->>CSUClient: client.get("/some/path")
    CSUClient->>AzureADAuth: get_headers(oun)
    AzureADAuth->>AzureAD: acquire_token_for_client(scopes)
    AzureAD-->>AzureADAuth: access_token
    AzureADAuth-->>CSUClient: {Authorization: Bearer <token>, OUN}
    CSUClient->>CSUClient: HTTP request with headers
```

### Cart to Checkout

```mermaid
sequenceDiagram
    participant App
    participant CSUClient
    participant CSU as CSU API

    App->>CSUClient: create_empty_cart(client)
    CSUClient->>CSU: POST /Carts/CreateEmptyCart
    CSU-->>CSUClient: {Id: "cart-123"}
    CSUClient-->>App: cart object

    App->>CSUClient: add_cart_lines(client, "cart-123", lines)
    CSUClient->>CSU: POST /Carts('cart-123')/AddCartLines
    CSU-->>CSUClient: updated cart
    CSUClient-->>App: cart with lines

    App->>CSUClient: checkout(client, "cart-123")
    CSUClient->>CSU: POST /Carts('cart-123')/Checkout
    CSU-->>CSUClient: {SalesOrder}
    CSUClient-->>App: sales order
```

### MCP Tool Call (End-to-End)

```mermaid
sequenceDiagram
    participant Claude as AI Assistant
    participant MCP as MCP Server
    participant Auth as AzureADAuth
    participant AzureAD as Azure AD
    participant CSUClient
    participant CSU as CSU API

    Claude->>MCP: csu_search_customers(keyword="contoso")
    MCP->>MCP: _get_client() reads env vars
    MCP->>Auth: AzureADAuth(...)
    MCP->>CSUClient: CSUClient(base_url, oun, auth)
    MCP->>CSUClient: search_customers(client, "contoso")
    CSUClient->>Auth: get_headers(oun)
    Auth->>AzureAD: acquire_token_for_client(scopes)
    AzureAD-->>Auth: access_token
    Auth-->>CSUClient: headers
    CSUClient->>CSU: POST /Customers/Search
    CSU-->>CSUClient: {value: [...]}
    CSUClient-->>MCP: results
    MCP-->>Claude: JSON response
```
