D365财务与运营MCP服务器
MCP(模型上下文协议)服务器,提供对Microsoft Dynamics 365财务和运营环境的访问。使Claude等AI助手能够探索D365元数据、查询数据,并在非生产环境中执行写入操作。
特性
- 多环境支持 -连接到多个D365环境(生产、UAT、开发)
- 读/写操作 -查询所有环境的数据;仅在非生产环境下创建、更新、删除
- 安全生产 -生产环境的设计始终是只读的
- MCP资源 用于模式发现和元数据探索
- 22专用工具 用于灵活的数据访问、聚合、批处理操作和分析
- 环境仪表板 -健康监测、API统计和操作跟踪
- 安全认证 通过Azure AD客户端凭据
- 自动元数据缓存 (24小时TTL,每种环境)
建筑
资源
| 资源 | URI | 目的 |
|---|---|---|
| 实体列表 | `d365://entities?filter= | |
| ` | 列出所有具有可选通配符筛选的实体 | |
| 实体架构 | d365://entity/{entityName} | 任何实体的完整架构(字段、键、导航属性) |
| 导航属性 | d365://navigation/{entityName} | 实体关系和导航属性 |
| 枚举定义 | d365://enums | 所有枚举类型及其值 |
| 已保存的查询 | d365://queries | 列出已保存的查询模板 |
| 仪表板 | d365://dashboard | 所有环境的JSON指标(运行状况、API统计数据、最近的操作) |
工具
所有工具都支持可选 environment 该参数针对特定的D365环境。
| 工具 | 目的 |
|---|---|
list_environments | 列出所有已配置的D365环境及其连接状态 |
set_environment | 设置当前会话的工作环境 |
describe_entity | 实体的快速架构查找 |
execute_odata | 执行原始OData路径(查询、单个记录、计数) |
aggregate | 对实体数据执行聚合(SUM、AVG、COUNT、MIN、MAX、COUNTDISTINCT、百分位数) |
get_related | 遵循实体关系以检索相关记录 |
export | 将查询结果导出为CSV、JSON或TSV格式 |
compare_periods | 同比、季度、月环比比较及变化计算 |
trending | 基于增长率和移动平均线的时间序列分析 |
save_query | 保存具有参数支持的可重用查询模板 |
execute_saved_query | 使用参数替换执行已保存的查询模板 |
delete_saved_query | 删除已保存的查询模板 |
join_entities | 使用$expand或客户端连接进行跨实体连接 |
batch_query | 并行执行多个查询 |
search_entity | 具有自动回退策略的强大实体搜索 |
analyze_customer | 全面的单次通话客户分析 |
create_record | 创建新记录(仅限非生产环境) |
update_record | 更新现有记录(仅限非生产环境) |
delete_record | 删除记录(仅限非生产环境) |
batch_crud | 在单个批处理请求中执行多个创建/更新/删除操作(仅限非生产) |
compare_schemas | 比较两个环境之间的实体模式以检测模式漂移 |
dashboard | 显示包含运行状况状态、API统计信息和最近操作的环境仪表板 |
安装
来自npm(推荐)
npx @zhound/d365fo-mcp-server或全局安装:
npm install -g @zhound/d365fo-mcp-server
d365fo-mcp源自源头
git clone https://github.com/zhound420/D365FO-claude-connector.git
cd D365FO-claude-connector
npm install
npm run build快速入门(推荐)
运行交互式安装向导:
npm run setup向导将:
- 检查先决条件(Node.js 18+,依赖关系)
- 指导您完成D365环境配置
- 测试与D365环境的连接
- 生成配置文件
- 配置克劳德桌面和/或克劳德代码
安装后,重新启动Claude Desktop(Cmd+Q然后在macOS上重新打开,或在Windows上Ctrl+Q)或启动新的Claude Code会话。
配置
多环境配置(推荐)
创建一个 d365-environments.json 项目根目录或工作目录中的文件:
{
"environments": [
{
"name": "production",
"displayName": "Production",
"type": "production",
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"environmentUrl": "https://your-company.operations.dynamics.com",
"default": true
},
{
"name": "uat",
"displayName": "UAT (Tier 2)",
"type": "non-production",
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"environmentUrl": "https://your-company-uat.sandbox.operations.dynamics.com"
},
{
"name": "dev",
"displayName": "Dev Sandbox",
"type": "non-production",
"tenantId": "your-tenant-id",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"environmentUrl": "https://your-company-dev.sandbox.operations.dynamics.com"
}
]
}环境类型:
type: "production"-只读访问(所有写入操作都被阻止)type: "non-production"-完全读/写访问权限(启用创建、更新、删除)
复制 d365-environments.example.json 作为一个起点。
单一环境(遗留)
服务器还支持以下环境变量(如果没有JSON配置,则回退):
| 变量 | 描述 |
|---|---|
D365_TENANT_ID | Azure AD租户ID |
D365_CLIENT_ID | Azure AD应用程序(客户端)ID |
D365_CLIENT_SECRET | Azure AD客户端机密 |
D365_ENVIRONMENT_URL | D365 F&O环境URL(例如。, https://contoso.operations.dynamics.com) |
D365_ENVIRONMENT_TYPE | 可选:“生产”或“非生产”(安全默认为“生产”) |
可选:
| 变量 | 默认值 | 描述 |
|---|---|---|
D365_TRANSPORT | stdio | 运输方式(stdio 或 http) |
D365_HTTP_PORT | 3000 | HTTP端口(使用HTTP传输时) |
D365_LOG_LEVEL | info | 日志记录级别 |
D365_PAGINATION_TIMEOUT_MS | 60000 | 大型数据集上分页请求的超时(毫秒) |
D365_CONFIG_FILE | 配置文件的路径(如果不在默认位置) |
Azure AD应用程序注册
步骤1:创建Azure AD应用程序
- 首选 Azure 门户 >Azure Active Directory>应用程序注册
- 点击“新建注册”
- 命名它(例如,“D365 MCP服务器”)
- 选择“仅此组织目录中的帐户”
- 点击注册
步骤2:配置API权限
- 转到“API权限”>“添加权限”
- 选择“Dynamics 365财务和运营”
- 选择“应用程序权限”>
CustomService.ReadWrite.All - 单击“授予\[您的组织\]管理员同意”
步骤3:创建客户端密码
- 转到“证书和机密”>“新客户机密”
- 添加描述和有效期
- 立即复制机密值(仅显示一次)
- 记下:
- 租户ID:在概述页面上找到 - 客户端ID:概述页面上的应用程序(客户端)ID - 客户端密钥:您刚才复制的值
步骤4:在D365环境中注册应用程序
重要提示: 此步骤必须在您要连接的每个D365环境(生产、UAT、开发)中完成。
- 在D365 F&O中,导航到:
系统管理>设置>Azure Active Directory应用程序
- 点击“新建”添加记录:
| 字段 | 值 |
|---|---|
| 客户端ID | Azure AD中的应用程序(客户端)ID |
| 名称 | 描述性名称(例如“MCP服务器集成”) |
| 用户ID | 应用程序运行的D365用户帐户 |
- 这 用户ID 确定应用程序可以访问哪些数据:
- 使用具有适当安全角色的服务帐户 - 对于只读访问:分配“查看所有数据”等角色 - 对于非生产环境下的写访问:分配允许创建/更新/删除的角色
- 对要连接的每个环境重复此操作
注: 如果跳过此步骤,即使Azure AD身份验证成功,API调用也将失败,并出现401个未授权错误或403个禁止错误。
设置
克劳德桌面版
添加到您的Claude Desktop配置文件中:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json 视窗: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"Microsoft D365": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_TENANT_ID": "your-tenant-id",
"D365_CLIENT_ID": "your-client-id",
"D365_CLIENT_SECRET": "your-client-secret",
"D365_ENVIRONMENT_URL": "https://your-env.operations.dynamics.com"
}
}
}
}克劳德代码(CLI)
添加 ~/.claude/settings.json:
{
"mcpServers": {
"Microsoft D365": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_TENANT_ID": "your-tenant-id",
"D365_CLIENT_ID": "your-client-id",
"D365_CLIENT_SECRET": "your-client-secret",
"D365_ENVIRONMENT_URL": "https://your-env.operations.dynamics.com"
}
}
}
}添加配置后,重新启动Claude Desktop或Claude Code。
环境可见性配置
使用多个D365环境时,您可以配置它们在Claude中的显示方式:
选项A:每个环境单独的服务器(推荐)
此选项在Claude的侧栏中将每个环境显示为单独的MCP服务器:
{
"mcpServers": {
"D365-production": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json",
"D365_SINGLE_ENV": "production"
}
},
"D365-uat": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json",
"D365_SINGLE_ENV": "uat"
}
},
"D365-dev": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json",
"D365_SINGLE_ENV": "dev"
}
}
}
}赞成的意见:
- 环境在Claude的侧边栏中立即可见
- 查询所针对的环境没有歧义
- 在所有平台上可靠工作
它是如何工作的: 这 D365_SINGLE_ENV 环境变量告诉服务器从 d365-environments.jsonThe D365_CONFIG_FILE 确保无论工作目录如何,都能找到配置。
选项B:单个多环境服务器
使用具有以下功能的单个服务器 environment 每个查询上的参数:
{
"mcpServers": {
"d365": {
"command": "node",
"args": ["/path/to/d365fo-mcp-server/dist/index.js"],
"env": {
"D365_CONFIG_FILE": "/path/to/d365fo-mcp-server/d365-environments.json"
}
}
}
}然后在查询中指定环境:
{ "entity": "CustomersV3", "top": 10, "environment": "uat" }赞成的意见:
- 单服务器进程
- 在一次会话中灵活查询任何环境
交互式设置脚本(node setup.js)可以为您生成任一配置。
与克劳德交谈-示例提示
配置后,您可以向Claude自然语言提问有关D365环境的问题。以下是按能力组织的示例:
发现实体
你: D365中有哪些与客户相关的实体?
克劳德将使用 d365://entities?filter=*Cust* 查找匹配实体的资源。
你: 显示CustomersV3实体的架构
克劳德将使用 describe_entity 或 d365://entity/CustomersV3 资源。
查询数据
你: 给我前10个客户的账号和姓名
克劳德将使用 execute_odata 与路径 CustomersV3?$top=10&$select=CustomerAccount,CustomerName
你: 系统中有多少个销售订单?
克劳德将使用 execute_odata 与路径 SalesOrderHeaders/$count
你: 查找客户组“US”中信用额度超过50000的所有客户
Claude将自动构造OData过滤器查询。
聚合和分析
你: 按总支出计算,我们的前20位客户是谁?
克劳德将使用 aggregate 使用groupBy、orderBy和top:
{
"entity": "SalesOrderLinesV2",
"aggregations": [{"function": "SUM", "field": "LineAmount"}],
"groupBy": ["OrderingCustomerAccountNumber"],
"orderBy": "sum_LineAmount desc",
"top": 20
}你: 订单中值是多少?也给我看看第90和第95百分位数
克劳德将使用 aggregate 使用百分位数函数:
{
"entity": "SalesOrderLinesV2",
"aggregations": [
{"function": "P50", "field": "LineAmount", "alias": "median"},
{"function": "P90", "field": "LineAmount"},
{"function": "P95", "field": "LineAmount"}
],
"accurate": true
}你: 按产品类别细分总收入
克劳德将使用 aggregate 使用groupBy:
{
"entity": "SalesOrderLinesV2",
"aggregations": [{"function": "SUM", "field": "LineAmount"}],
"groupBy": ["ItemGroup"]
}基于时间的分析
你: 显示过去12个月的月度销售趋势和增长率
克劳德将使用 trending:
{
"entity": "SalesOrderLinesV2",
"dateField": "CreatedDateTime",
"valueField": "LineAmount",
"granularity": "month",
"periods": 12,
"includeGrowthRate": true
}你: 将今年的销售额与去年进行比较
克劳德将使用 compare_periods 与去年同期相比:
{
"entity": "SalesOrderLinesV2",
"dateField": "CreatedDateTime",
"comparisonType": "YoY",
"aggregations": [{"function": "SUM", "field": "LineAmount"}]
}你: 第四季度的销售额与第三季度相比如何?
克劳德将使用 compare_periods 与QoQ比较:
{
"entity": "SalesOrderLinesV2",
"dateField": "CreatedDateTime",
"comparisonType": "QoQ",
"aggregations": [{"function": "SUM", "field": "LineAmount"}]
}客户智能
你: 请给我一份完整的客户US-001分析,包括客户概况、订单、支出和趋势
克劳德将使用 analyze_customer 为了进行全面的单次通话分析:
{
"customerAccount": "US-001",
"includeOrders": true,
"includeSpend": true,
"includeTrending": true
}你: 查找名为“S&S Industries”的客户
克劳德将使用 search_entity 它处理违反标准OData的特殊字符:
{
"entity": "CustomersV3",
"searchTerm": "S&S Industries",
"searchField": "CustomerName"
}多查询和联接
你: 给我一个仪表板视图:总客户数、本月总订单数和按销售额排名的前5名产品
克劳德将使用 batch_query 并行运行所有三个查询:
{
"queries": [
{"name": "total_customers", "entity": "CustomersV3", "top": 1},
{"name": "orders_this_month", "entity": "SalesOrderHeadersV2", "filter": "OrderCreatedDateTime ge 2024-01-01"},
{"name": "top_products", "entity": "SalesOrderLinesV2", "top": 5, "orderby": "LineAmount desc"}
]
}你: 显示最近的订单,包括客户名称及其客户群
克劳德将使用 join_entities 将订单与客户详细信息相关联:
{
"primaryEntity": "SalesOrderHeadersV2",
"primaryKey": "OrderingCustomerAccountNumber",
"secondaryEntity": "CustomersV3",
"secondaryKey": "CustomerAccount",
"primarySelect": ["SalesOrderNumber", "OrderCreatedDateTime"],
"secondarySelect": ["CustomerName", "CustomerGroup"]
}数据导出
你: 将信用额度超过10万美元的所有客户导出为CSV
克劳德将使用 export 使用格式和过滤器:
{
"entity": "CustomersV3",
"format": "csv",
"filter": "CreditLimit gt 100000",
"select": ["CustomerAccount", "CustomerName", "CreditLimit"]
}理解枚举
你: 销售订单状态的可能值是什么?
克劳德会检查 d365://enums 资源以查找枚举定义。
最佳结果提示
- 直接问商业问题 -MCP工具为您处理复杂性。只需问:“按支出计算,我们的前20大客户是谁?”或“第四季度与第三季度相比如何?”
- 使用自然日期格式 -克劳德理解“上个月”、“2024年第四季度”、“过去12个月”或“2024月1日”等特定日期
- 不要担心特殊人物 -搜索“S&S Industries”或“O'Brien Corp”会自动运行。这些工具为违反标准OData的角色提供了回退策略。
- 请求趋势和比较 -内置的时间智能可以处理复杂性:“显示每月的销售趋势和增长率”或“将今年的收入与去年进行比较”
- 合并多个问题 -询问仪表板风格的视图:“获取我的总客户数、本月订单数和前5名产品”-查询并行运行。
- 需要时导出数据 -直接请求CSV、JSON或TSV导出:“将信用额度超过10万美元的所有客户导出为CSV”
- 要求解释 -如果你想学习OData语法,请Claude解释查询:“向美国组中的客户展示并解释OData查询”
API 参考
资源
d365://entities
列出具有可选过滤功能的可用D365实体。
查询参数:
filter(可选):通配符模式(*对于任何字符,?对于单个字符)
示例:
d365://entities # List all entities
d365://entities?filter=Cust* # Entities starting with "Cust"
d365://entities?filter=*Header* # Entities containing "Header"d365://entity/{entityName}
获取实体的完整架构。
示例:
d365://entity/CustomersV3
d365://entity/SalesOrderHeaders答复包括:
- 实体名称和描述
- 主要关键字段
- 所有具有类型、约束和枚举引用的字段
- 导航属性(关系)
d365://navigation/{entityName}
获取实体的导航属性(关系)。
示例:
d365://navigation/SalesOrderHeadersV2
d365://navigation/CustomersV3答复包括:
- 导航属性名称
- 目标实体类型
- 关系基数(一对多,多对一)
d365://enums
列出所有枚举类型定义。
答复包括:
- 枚举名称和完整命名空间
- 所有成员值及其数字代码
工具
list_environments
列出所有已配置的D365环境及其连接状态和权限。
参数:
- 无需
例子:
{}答复包括:
- 环境名称和显示名称
- 类型(生产/非生产)
- 连接状态
- 读/写权限
set_environment
设置当前会话的工作环境。默认情况下,后续的工具调用将使用此环境。
参数:
environment(string,必填):要设置为活动的环境的名称
例子:
{
"environment": "uat"
}describe_entity
以人类可读的格式获取实体模式。
参数:
entity(字符串,必填):实体名称
例子:
{
"entity": "CustomersV3"
}execute_odata
对D365执行原始OData路径。
参数:
path(字符串,必填):OData路径附加到/data/
示例:
// Query with parameters
{ "path": "CustomersV3?$top=5&$select=CustomerAccount,CustomerName" }
// Single record by key
{ "path": "CustomersV3('US-001')" }
// Compound key
{ "path": "CustomersV3(DataAreaId='usmf',CustomerAccount='US-001')" }
// Count
{ "path": "CustomersV3/$count" }
// Filtered count
{ "path": "CustomersV3/$count?$filter=CustomerGroup eq 'US'" }
// With expansion
{ "path": "SalesOrderHeaders?$expand=SalesOrderLines&$top=3" }aggregate
对D365实体数据执行聚合。使用速度快 /$count 对于简单的COUNT操作,否则进行客户端聚合。
参数:
entity(字符串,必填):要聚合的实体名称aggregations(array,必填):聚合规范数组:
- function:“SUM”|“AVG”|“COUNT”|“MIN”|“MAX”|“COUNTDISTINCT”|“P50”|“P90”|“p65”|“P99” - field:要聚合的字段(COUNT使用“\*”) - alias (可选):自定义结果名称
filter(字符串,可选):OData$筛选器表达式groupBy(数组,可选):分组依据的字段accurate(布尔值,可选):获取所有记录以获得确切的总计(默认值:false)sampling(布尔值,可选):使用统计抽样对非常大的数据集进行快速估计(默认值:false)orderBy(字符串,可选):按聚合别名对结果进行排序(例如,“sum_LineAmount-desc”)top(数字,可选):排序后仅返回前N个结果
百分位数函数:
P50-中位数(第50百分位)P90-第90百分位数P95-第95百分位数P99-第99百分位数
业绩说明:
- 默认模式上限为5K记录,以便快速估算
accurate=true获取所有记录,每页超时60秒,并自动重试(2次重试,指数回退)sampling=true使用约10K的记录样本对非常大的数据集(100K+条记录)进行统计估计
示例:
// Count all customers
{ "entity": "CustomersV3", "aggregations": [{"function": "COUNT", "field": "*"}] }
// Sum with filter
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "filter": "SalesOrderNumber eq 'SO-001'" }
// Accurate mode for exact totals
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "accurate": true }
// Group by
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "groupBy": ["ItemNumber"] }
// Median order value (requires accurate=true for percentiles)
{ "entity": "SalesOrderLines", "aggregations": [{"function": "P50", "field": "LineAmount"}], "accurate": true }
// Fast estimate on very large dataset (100K+ records)
{ "entity": "BatchJobs", "aggregations": [{"function": "COUNT", "field": "*"}], "sampling": true }
// Top 20 customers by spend
{ "entity": "SalesOrderLines", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "groupBy": ["CustomerAccount"], "orderBy": "sum_LineAmount desc", "top": 20 }get_related
遵循实体关系,在一次调用中检索相关记录。
参数:
entity(字符串,必填):源实体名称key(string | object,必填):源记录的主键relationship(字符串,必填):要遵循的导航属性名称select(string\[\],可选):要从相关实体中包含的字段filter(字符串,可选):应用于相关记录的筛选器top(数字,可选):最大相关记录数(默认值:1000)
示例:
// Get order lines for an order
{ "entity": "SalesOrderHeaders", "key": "SO-001", "relationship": "SalesOrderLines" }
// With compound key
{ "entity": "SalesOrderHeaders", "key": {"DataAreaId": "usmf", "SalesOrderNumber": "SO-001"}, "relationship": "SalesOrderLines" }
// With field selection and filter
{ "entity": "SalesOrderHeaders", "key": "SO-001", "relationship": "SalesOrderLines", "select": ["ItemNumber", "LineAmount"], "filter": "LineAmount gt 1000" }export
将D365实体数据导出为CSV、JSON或TSV格式。
参数:
entity(string,必填):要导出的实体format(“json”|“csv”|“tsv”,可选):输出格式(默认:“json”)select(string\[\],可选):要包含的字段filter(字符串,可选):OData$筛选器表达式orderBy(字符串,可选):OData$orderby表达式maxRecords(数字,可选):最大记录数(默认值:10000)includeHeaders(布尔值,可选):包括CSV/TSV的标题行(默认值:true)
示例:
// JSON export with field selection
{ "entity": "CustomersV3", "format": "json", "select": ["CustomerAccount", "CustomerName"] }
// CSV export with filter
{ "entity": "SalesOrderLines", "format": "csv", "filter": "SalesOrderNumber eq 'SO-001'" }
// TSV with ordering and limit
{ "entity": "Products", "format": "tsv", "orderBy": "ProductName asc", "maxRecords": 500 }compare_periods
比较两个时间段(同比、季度、月环比或自定义范围)之间的聚合。
参数:
entity(string,必填):要分析的实体dateField(字符串,必填):用于筛选的日期/日期时间字段aggregations(数组,必填):与聚合工具相同comparisonType(“YoY”|“QoQ”|“MoM”|“custom”,必填):比较类型referenceDate(字符串,可选):计算参考日期(默认值:今天)period1,period2(对象,可选):自定义期间范围filter(字符串,可选):附加OData筛选器groupBy(string\[\],可选):分组依据的字段
示例:
// Year-over-Year comparison
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "comparisonType": "YoY", "aggregations": [{"function": "SUM", "field": "LineAmount"}] }
// Month-over-Month with grouping
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "comparisonType": "MoM", "aggregations": [{"function": "COUNT", "field": "*"}], "groupBy": ["ItemGroup"] }
// Custom date ranges
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "comparisonType": "custom", "aggregations": [{"function": "SUM", "field": "LineAmount"}], "period1": {"start": "2024-01-01", "end": "2024-03-31"}, "period2": {"start": "2023-01-01", "end": "2023-03-31"} }trending
时间序列分析,包括聚合、增长率和移动平均线。
参数:
entity(string,必填):要分析的实体dateField(字符串,必填):用于分组的日期/日期时间字段valueField(字符串,必填):要聚合的数字字段aggregation(“SUM”|“AVG”|“COUNT”|“MIN”|“MAX”,可选):默认值:“SUM”granularity(“天”|“周”|“月”|“季度”|“年”,可选):默认值:“月”periods(数字,可选):要分析的周期数(默认值:12)endDate(字符串,可选):分析结束日期(默认值:今天)filter(字符串,可选):附加OData筛选器movingAverageWindow(数字,可选):MA计算的窗口大小includeGrowthRate(布尔值,可选):包括增长率(默认值:true)
示例:
// Monthly revenue trend
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "valueField": "LineAmount", "granularity": "month", "periods": 12 }
// Weekly order count with moving average
{ "entity": "SalesOrderHeaders", "dateField": "OrderDate", "valueField": "*", "aggregation": "COUNT", "granularity": "week", "movingAverageWindow": 4 }
// Quarterly with filter
{ "entity": "SalesOrderLines", "dateField": "CreatedDateTime", "valueField": "LineAmount", "granularity": "quarter", "filter": "ItemGroup eq 'Electronics'" }save_query
保存可重用的查询模板以供以后执行。使用 {{paramName}} 对于可替换的参数。
参数:
name(字符串,必填):查询的唯一名称description(字符串,可选):查询描述entity(字符串,必填):要查询的实体select(string\[\],可选):要选择的字段filter(字符串,可选):OData$filter(使用{{paramName}}参数)orderBy(字符串,可选):OData$orderby表达式top(数字,可选):最大记录数expand(字符串,可选):OData$expand表达式
示例:
// Basic query
{ "name": "active_customers", "entity": "CustomersV3", "filter": "IsActive eq true" }
// With parameters
{ "name": "customer_orders", "entity": "SalesOrderHeaders", "filter": "CustomerAccount eq '{{customerId}}'" }
// Complex query with description
{ "name": "recent_sales", "description": "Recent sales for analysis", "entity": "SalesOrderLines", "select": ["ItemNumber", "LineAmount"], "filter": "CreatedDateTime ge {{startDate}}", "orderBy": "CreatedDateTime desc", "top": 100 }execute_saved_query
执行以前保存的查询模板。
参数:
name(字符串,必填):已保存查询的名称params(对象,可选):要替换的参数值fetchAll(boolean,可选):获取所有页面(默认值:false)maxRecords(number,可选):fetchAll=true时的最大记录数(默认值:50000)
示例:
// Simple execution
{ "name": "active_customers" }
// With parameters
{ "name": "customer_orders", "params": {"customerId": "US-001"} }
// Multiple parameters with pagination
{ "name": "date_range_sales", "params": {"startDate": "2024-01-01", "endDate": "2024-12-31"}, "fetchAll": true }delete_saved_query
删除已保存的查询模板。
参数:
name(字符串,必填):要删除的查询的名称
join_entities
使用OData$expand或客户端连接进行跨实体连接。
参数:
primaryEntity(字符串,必填):主要实体名称primaryKey(字符串,必填):要加入的主键字段secondaryEntity(字符串,必填):次要实体名称secondaryKey(字符串,必填):要加入的次要关键字字段primarySelect(string\[\],可选):主实体中的字段secondarySelect(string\[\],可选):来自辅助实体的字段primaryFilter(字符串,可选):主实体筛选器joinType(“内部”|“左侧”,可选):连接类型(默认:“内部”)maxRecords(数字,可选):最大记录数(默认值:5000)
示例:
// Join orders with customers
{ "primaryEntity": "SalesOrderHeadersV2", "primaryKey": "OrderingCustomerAccountNumber", "secondaryEntity": "CustomersV3", "secondaryKey": "CustomerAccount", "primarySelect": ["SalesOrderNumber", "OrderCreatedDateTime"], "secondarySelect": ["CustomerName", "CustomerGroup"] }batch_query
并行执行多个D365 OData查询,在单个响应中返回所有结果。
参数:
queries(array,必填):查询规范数组(1-10个查询):
- name (字符串,可选):此查询结果的标签 - entity (字符串,必填):实体名称 - filter (字符串,可选):OData$筛选器表达式 - select (string\[\],可选):要包含的字段 - top (数字,可选):限制记录(默认值:100) - orderby (字符串,可选):OData$orderby表达式 - fetchAll (布尔值,可选):自动分页所有页面 - maxRecords (number,可选):fetchAll=true时的最大记录数
stopOnError(布尔值,可选):第一次失败时停止(默认值:false)
示例:
// Multiple parallel queries
{
"queries": [
{ "name": "recent_orders", "entity": "SalesOrderHeadersV2", "top": 10, "orderby": "CreatedDateTime desc" },
{ "name": "customers", "entity": "CustomersV3", "filter": "CustomerGroup eq 'US'", "select": ["CustomerAccount", "CustomerName"] },
{ "name": "all_invoices", "entity": "SalesInvoiceHeadersV2", "fetchAll": true, "maxRecords": 1000 }
]
}search_entity
具有自动回退策略的强大实体搜索。处理特殊字符(如 & 在公司名称中)导致标准OData出现问题 contains().
搜索策略(按顺序尝试):
contains()-标准OData文本搜索(最快)startswith()-前缀匹配(在D365上更可靠)exact-精确的字段匹配client_filter-Fetch+客户端过滤器(始终有效)
参数:
entity(字符串,必填):要搜索的实体searchTerm(字符串,必填):要搜索的文本searchField(字符串,必填):要搜索的字段select(string\[\],可选):结果中返回的字段top(数字,可选):最大结果(默认值:10)
示例:
// Search customers with special characters
{ "entity": "CustomersV3", "searchTerm": "S&S", "searchField": "CustomerName" }
// Search with specific fields
{ "entity": "CustomersV3", "searchTerm": "Contoso", "searchField": "CustomerName", "select": ["CustomerAccount", "CustomerName", "CustomerGroup"], "top": 5 }
// Search vendors
{ "entity": "VendorsV3", "searchTerm": "Microsoft", "searchField": "VendorName" }analyze_customer
在一次通话中进行全面的客户分析。运行并行查询以收集配置文件、订单、支出和趋势数据。
特征:
- 客户资料查找(使用回退搜索策略)
- 订单统计(计数、总支出、平均订单价值)
- 订单日期范围(第一个和最后一个订单)
- 最近订单列表
- 月度订单趋势
在线路级别使用高效聚合(SalesOrderLinesV2)为了准确计算支出,避免了标题总计为0美元的问题。
参数:
customerAccount(字符串,可选):客户账号customerName(字符串,可选):要搜索的客户名称(处理特殊字符)includeOrders(布尔值,可选):包括最近的订单列表(默认值:true)includeSpend(布尔值,可选):包括总支出计算(默认值:true)includeTrending(布尔值,可选):包括月度趋势分析(默认值:true)recentOrdersLimit(number,可选):要显示的最近订单数(默认值:10)trendPeriods(数字,可选):趋势的月数(默认值:12)
示例:
// Analyze by account number
{ "customerAccount": "SS0011" }
// Analyze by name (handles special characters like &)
{ "customerName": "S&S" }
// Quick analysis without trending (faster)
{ "customerAccount": "US-001", "includeTrending": false }
// Full analysis with custom periods
{ "customerName": "Contoso", "recentOrdersLimit": 20, "trendPeriods": 24 }输出包括:
- 客户资料(姓名、帐户、组、地址)
- 汇总统计数据(总订单、总支出、平均订单价值、首次/最后一次订单日期)
- 最近订单列表
- 月度订单趋势表,包括订单数量和收入
d365://queries
列出所有已保存查询模板的资源。
答复包括:
- 查询计数和列表
- 每个查询的名称、描述、实体和参数
- 使用说明
dashboard
显示包含运行状况状态、API统计信息和最近操作的环境仪表板。
参数:
checkHealth(布尔值,可选):执行实时连接检查(默认值:false)
例子:
{
"checkHealth": true
}答复包括:
- 根据环境健康状况
- API呼叫统计数据(呼叫总数、成功率)
- 最近的操作日志
- 环境配置摘要
OData查询语法
筛选器示例
// Equality
$filter=CustomerAccount eq 'US-001'
// Comparison
$filter=CreditLimit gt 10000
// String functions
$filter=startswith(CustomerName, 'Contoso')
$filter=contains(CustomerName, 'Inc')
// Logical operators
$filter=CustomerGroup eq 'US' and CreditLimit gt 5000
// Enum values
$filter=Status eq Microsoft.Dynamics.DataEntities.SalesStatus'Invoiced'
// Date comparison
$filter=OrderDate gt 2024-01-01选择并展开
// Select specific fields
$select=CustomerAccount,CustomerName,CreditLimit
// Expand navigation property
$expand=SalesOrderLines
// Expand with nested select
$expand=SalesOrderLines($select=ItemId,Quantity)排序和分页
// Sort ascending
$orderby=CustomerName asc
// Sort descending
$orderby=OrderDate desc
// Multiple sort columns
$orderby=CustomerGroup asc,CustomerName asc
// Pagination
$top=50&$skip=100发展
# Build
npm run build
# Watch mode
npm run dev
# Run directly (requires environment variables)
npm start
# Run tests
npm test
# Run tests in watch mode
npm run test:watch项目结构
src/
├── index.ts # Entry point and server setup
├── config-loader.ts # Configuration loading (JSON + env var fallback)
├── environment-manager.ts # Multi-environment management and write guards
├── auth.ts # Azure AD OAuth2 authentication
├── d365-client.ts # D365 OData API client with read/write methods
├── metadata-cache.ts # EDMX metadata parser and cache (24h TTL)
├── progress.ts # Progress reporting for long operations
├── types.ts # TypeScript type definitions
├── metrics/
│ ├── index.ts # Metrics module exports
│ ├── metrics-tracker.ts # API call statistics tracking
│ ├── health-checker.ts # Environment connectivity health checks
│ └── operation-log.ts # Operation history tracking
├── resources/
│ ├── index.ts # Resource registration
│ ├── entities.ts # d365://entities resource
│ ├── entity.ts # d365://entity/{name} resource
│ ├── navigation.ts # d365://navigation/{name} resource
│ ├── enums.ts # d365://enums resource
│ ├── queries.ts # d365://queries resource
│ └── dashboard.ts # d365://dashboard resource
├── utils/
│ ├── date-utils.ts # Date period calculations
│ ├── csv-utils.ts # CSV/TSV formatting
│ ├── env-utils.ts # Environment variable parsing with validation
│ └── pagination.ts # Shared pagination utilities (fetchPageWithRetry, paginatedFetch)
└── tools/
├── index.ts # Tool registration
├── common.ts # Shared tool utilities and error formatting
├── list-environments.ts
├── set-environment.ts
├── describe-entity.ts
├── execute-odata.ts
├── aggregate.ts
├── get-related.ts
├── export.ts
├── compare-periods.ts
├── trending.ts
├── saved-queries.ts # save/execute/delete query templates
├── join-entities.ts
├── batch-query.ts
├── batch-crud.ts # Batch create/update/delete via $batch (non-production only)
├── compare-schemas.ts # Cross-environment schema comparison
├── search-entity.ts
├── analyze-customer.ts
├── create-record.ts # Write operation (non-production only)
├── update-record.ts # Write operation (non-production only)
├── delete-record.ts # Write operation (non-production only)
└── dashboard.ts
tests/
├── auth.test.ts # Token caching, refresh dedup, invalidation
├── d365-client.test.ts # Retry logic, key formatting, CRUD operations
├── config-loader.test.ts # JSON loading, env var fallback, validation
├── pagination.test.ts # Shared pagination utilities
└── env-utils.test.ts # parseInt validation utility写入操作(仅限非生产)
写入操作仅在以下环境中可用 type: "non-production"生产环境始终是只读的。
create_record
在D365实体中创建新记录。
参数:
entity(字符串,必填):实体名称data(object,必填):新记录的字段值environment(字符串,可选):目标环境
例子:
{
"entity": "CustomersV3",
"data": {
"CustomerAccount": "CUST-001",
"CustomerName": "Contoso Ltd",
"CustomerGroup": "US"
},
"environment": "uat"
}update_record
更新现有记录。
参数:
entity(字符串,必填):实体名称key(string | object,必填):记录键data(对象,必填):要更新的字段值etag(字符串,可选):ETag用于乐观并发environment(字符串,可选):目标环境
例子:
{
"entity": "CustomersV3",
"key": "CUST-001",
"data": {
"CustomerName": "Contoso Corporation"
},
"environment": "dev"
}delete_record
从实体中删除记录。
参数:
entity(字符串,必填):实体名称key(string | object,必填):记录键etag(字符串,可选):ETag用于乐观并发environment(字符串,可选):目标环境
例子:
{
"entity": "CustomersV3",
"key": "CUST-001",
"environment": "dev"
}安全
凭证保护
此项目实现了多个层来保护您的Azure AD凭据:
| 保护 | 说明 |
|---|---|
.gitignore | .env 和 d365-environments.json 被排除在版本控制之外 |
.gitattributes | 敏感文件排除在外 git archive 出口 |
| 预提交钩子 | 在允许提交之前扫描暂存文件中的秘密模式 |
| 山宁泰错误 | Azure AD错误响应会在内部记录,但不会暴露给调用者 |
保护文件
以下文件包含凭据并受保护:
.env-环境变量(传统的单一环境配置)d365-environments.json-具有秘密的多环境配置*.local.json-本地配置覆盖
安全文件 (包含占位符,可以提交):
.env.example-带有占位符值的模板d365-environments.example.json-配置示例
如果凭据被暴露
如果您意外提交或公开凭据:
- 立即轮换Azure AD客户端机密:
- 首选 Azure 门户 >Azure Active Directory>应用程序注册 - 选择您的D365应用程序注册 - 转到“证书和机密” - 创建新的客户端密钥 - 更新您的本地 .env 或 d365-environments.json 新的秘密 - 从Azure AD中删除旧密钥
- 查看Azure AD登录日志:
- 检查是否存在未经授权的访问尝试 - Azure门户>Azure AD>登录日志>按应用程序筛选
- 如果使用git:
- 即使你在新的提交中删除了这个秘密,它也会保留在git历史中 - 考虑使用 git filter-branch 或BFG Repo Cleaner清除历史记录 - 强制推送已清理的存储库(与合作者协调)
旋转Azure AD秘密
最佳做法是定期轮换机密(每90-180天一次):
- 在Azure门户中创建新密钥 (旧版到期前)
- 更新配置文件:
# Edit .env or d365-environments.json with new secret- 测试连接性:
npm start # Verify authentication works- 从Azure门户删除旧密钥
预提交钩子
预提交钩子会扫描以下模式:
- Azure AD客户端机密(后30多个字符串
clientSecret) - 租户ID(UUID格式在后面
tenantId) - 带有机密的环境变量赋值
要绕过(仅适用于误报):
git commit --no-verify运行时保护
- 生产环境只读:生产环境下的写入操作在结构上被阻止
- 非生产写访问:创建、更新、删除仅适用于
type: "non-production"环境 - 无证书风险:凭据在服务器端进行管理
- OData注射预防:参数已正确编码
故障排除
MCP服务器未出现
- 完全重新启动克劳德桌面 -macOS上的Cmd+Q(不仅仅是关闭窗口),然后重新打开。在Windows上,使用Ctrl+Q或退出系统托盘。
- 检查服务器配置 -验证配置文件路径是否正确:
D365_CONFIG_FILE=./d365-environments.json D365_SINGLE_ENV=uat node dist/index.js- 验证配置路径 -确保
D365_CONFIG_FILE在Claude配置中,指向的是d365-environments.json.
- 检查克劳德日志 -在macOS上:
~/Library/Logs/Claude/;在Windows上:%APPDATA%\Claude\logs\
身份验证错误
- 验证租户ID、客户端ID和密钥是否正确
- 确保Azure AD应用程序具有所需的API权限
- 检查是否已授予管理员同意
未找到实体
- 使用
d365://entities发现可用实体 - 实体名称区分大小写
- 某些实体可能不会通过OData公开
超时错误
- 通过以下方式缩小查询范围
$top和$filter - 对于大型数据集,使用分页
$skip - 使用
batch_query并行运行多个查询
大型数据集聚合改进:
- 分页请求现在使用60秒超时,并自动重试(2次重试,指数回退)
- 通过配置超时
D365_PAGINATION_TIMEOUT_MS环境变量 - 对于非常大的数据集(100K+条记录),请使用
sampling=true在aggregate快速统计估计工具 accurate=true如果分页中途中断,模式现在会报告部分结果
许可证
麻省理工学院
