D365 Odata MCP
MCP Server for Microsoft Dynamics 365 OData API (Dataverse & Finance/Operations)
Ask AI about D365 Odata MCP
Powered by Claude Β· Grounded in docs
I know everything about D365 Odata MCP. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
D365 OData MCP Server
An MCP (Model Context Protocol) server that enables AI assistants to query Microsoft Dynamics 365 data via OData API. Supports both Dataverse and Finance & Operations (F&O).
For AI Assistants
If you want an AI assistant to understand this repository before helping with changes, ask it to read AI_CONTEXT.md first. That file gives a compact overview of the architecture, runtime flow, MCP tools, safety rules, and release process.
Features
- β
Full OData query support:
$filter,$select,$orderby,$top,$skip,$expand,$count - β Cross-company queries (F&O)
- β Azure AD authentication (Cloud D365)
- β ADFS authentication (On-premise D365)
- β Automatic token refresh
- β Retry with exponential backoff
- β Metadata caching with configurable TTL for improved performance
- β Works with OpenAI Codex, Claude Desktop, Claude Code, and other MCP clients
Quick Start
Step 1: Install
cargo install d365-odata-mcp
Upgrade to Latest Version
cargo install d365-odata-mcp --force
Step 2: Create Azure AD App
- Go to Azure Portal
- Navigate to Microsoft Entra ID β App registrations β New registration
- Name your app (e.g.,
D365 MCP) - Note down:
- Tenant ID (from Overview)
- Client ID (Application ID)
- Go to Certificates & secrets β New client secret β Copy the Secret Value
- Go to API permissions β Add a permission:
- For Dataverse:
Dynamics CRMβuser_impersonation - For F&O:
Dynamics ERPβCustomService.FullAccess
- For Dataverse:
- Click Grant admin consent
Step 3: Configure Your AI Client
Choose your AI client below:
Configuration for OpenAI Codex
Edit ~/.codex/config.toml:
[mcp_servers.d365]
command = "d365-odata-mcp"
[mcp_servers.d365.env]
TENANT_ID = "your-tenant-id"
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"
ENDPOINT = "https://your-org.crm.dynamics.com/api/data/v9.2/"
PRODUCT = "dataverse"
For F&O:
[mcp_servers.d365]
command = "d365-odata-mcp"
[mcp_servers.d365.env]
TENANT_ID = "your-tenant-id"
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"
ENDPOINT = "https://your-org.sandbox.operations.dynamics.com/data/"
PRODUCT = "finops"
Verify installation:
codex mcp list
Configuration for Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"d365": {
"command": "d365-odata-mcp",
"env": {
"TENANT_ID": "your-tenant-id",
"CLIENT_ID": "your-client-id",
"CLIENT_SECRET": "your-client-secret",
"ENDPOINT": "https://your-org.crm.dynamics.com/api/data/v9.2/",
"PRODUCT": "dataverse"
}
}
}
}
Configuration for Gemini (Antigravity)
Add workflow file .agent/workflows/d365-query.md to your project:
---
description: How to query D365 Finance & Operations data via d365-odata-mcp
---
# D365 OData Query
Query D365 data via command line:
\```bash
export TENANT_ID="your-tenant-id"
export CLIENT_ID="your-client-id"
export CLIENT_SECRET="your-client-secret"
export ENDPOINT="https://your-org.sandbox.operations.dynamics.com/data/"
export PRODUCT="finops"
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_entity","arguments":{"entity":"CustomersV3","top":"10"}}}' | d365-odata-mcp 2>/dev/null | jq '.result.content[0].text' -r
\```
Available Tools
1. list_entities
List all available D365 entities:
"List all D365 entities"
2. query_entity
Query data with full OData support:
| Parameter | Description | Required |
|---|---|---|
entity | Entity name, e.g., CustomersV3 | β |
filter | OData filter, e.g., dataAreaId eq 'bc' | β |
select | Fields to return, e.g., Name,Id | β |
orderby | Sort order, e.g., CreatedDate desc | β |
top | Max records (default: 50, max: 1000) | β |
skip | Records to skip (pagination) | β |
expand | Navigation properties to expand | β |
cross_company | true for cross-company (F&O only) | β |
count | true to include total count | β |
Examples:
"Query CustomersV3, show first 10 records"
"Query SalesOrderHeaders where dataAreaId is 'bc', order by SalesOrderNumber desc"
"Get inventory where warehouse is 'WH01' with count"
3. get_entity_schema
Get available fields for an entity:
"Show schema for SalesOrderHeaders"
4. get_record
Get a single record by ID:
"Get customer record with ID 'CUS-001'"
5. delete_record
Delete a single record by OData key. This tool requires confirm to be exactly DELETE.
| Parameter | Description | Required |
|---|---|---|
entity | Entity name, e.g., CustomersV3 | β |
key | OData key expression without parentheses, e.g., dataAreaId='bc',CustomerAccount='CUS-001' | β |
id | Simple record ID/key, used when key is not provided | β |
if_match | Optional If-Match header value (default: *) | β |
confirm | Must be exactly DELETE | β |
Example:
"Delete CustomersV3 with key dataAreaId='bc',CustomerAccount='CUS-001' and confirm DELETE"
6. get_environment_info
Get D365 environment information:
"Show D365 environment info"
7. get_metadata
Get entity metadata including properties and navigation properties (expandable fields):
"Get metadata for CustomersV3"
"Show me the schema and expandable fields for SalesOrderHeaders"
8. refresh_metadata
Force refresh the cached metadata (useful when schema changes):
"Refresh metadata cache"
Environment Variables
| Variable | Description | Required |
|---|---|---|
TENANT_ID | Azure AD Tenant ID (or adfs for ADFS) | β |
CLIENT_ID | Azure AD/ADFS Application ID | β |
CLIENT_SECRET | Azure AD/ADFS Client Secret | β |
ENDPOINT | D365 OData endpoint URL | β |
PRODUCT | dataverse or finops | β |
AUTH_TYPE | azure (default) or adfs | β |
TOKEN_URL | Custom token URL (ADFS only) | β |
RESOURCE | Resource/audience (ADFS only) | β |
METADATA_CACHE_TTL | Metadata cache TTL in seconds (default: 900 = 15 min) | β |
INSECURE_SSL | Skip SSL verification for self-signed certs (true/false) | β |
Configuration for On-Premise D365 (ADFS)
For D365 F&O on-premise with ADFS authentication:
# ~/.codex/config.toml
[mcp_servers.d365_onprem]
command = "d365-odata-mcp"
[mcp_servers.d365_onprem.env]
AUTH_TYPE = "adfs"
TENANT_ID = "adfs"
CLIENT_ID = "your-adfs-client-id"
CLIENT_SECRET = "your-adfs-secret"
TOKEN_URL = "https://your-adfs-server.com/adfs/oauth2/token"
RESOURCE = "https://your-d365-onprem.com"
ENDPOINT = "https://your-d365-onprem.com/namespaces/AXSF/data/"
PRODUCT = "finops"
Common F&O Entities
| Entity | Description |
|---|---|
CustomersV3 | Customer master data |
VendorsV2 | Vendor master data |
ProductsV2 | Product master data |
SalesOrderHeaders | Sales order headers |
SalesOrderLines | Sales order lines |
PurchaseOrderHeaders | Purchase order headers |
PurchaseOrderLines | Purchase order lines |
InventoryOnHandAggregatedByWarehouse | Inventory on hand |
OData Filter Syntax
| Operator | Example |
|---|---|
eq | Status eq 'Open' |
ne | Status ne 'Closed' |
gt / ge | Amount gt 1000 |
lt / le | Amount lt 100 |
and / or | Status eq 'Open' and Amount gt 100 |
contains | contains(Name, 'Corp') |
startswith | startswith(Name, 'ABC') |
Testing
Test the server directly:
export TENANT_ID="..." CLIENT_ID="..." CLIENT_SECRET="..." ENDPOINT="..." PRODUCT="finops"
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | d365-odata-mcp
License
MIT License - see LICENSE for details.
Contributing
Contributions welcome! Please open an issue or submit a PR.
