mcpField
D
d365fo-connector

d365fo-connector

本地服务
github.com/zhound420商业与营销1032 周前更新

关于 d365fo-connector

d365fo-connector 是连接 Microsoft Dynamics 365 Finance & Operations 环境的 MCP 服务,供 AI 助手发现元数据、查询数据并执行操作。它支持多环境,并强制生产环境只读。

主要功能

  1. 01连接多个 D365 环境
  2. 02发现实体与架构元数据
  3. 03查询、聚合及分析数据
  4. 04导出与批量执行查询
  5. 05非生产环境记录增删改
  6. 06生产环境强制只读

适用场景

  1. 01检查实体字段和关系
  2. 02分析销售及客户数据
  3. 03对比不同环境的架构
  4. 04在开发或 UAT 环境维护记录

MCP 相关

通过 MCP 向客户端提供 D365 元数据资源及数据访问工具。支持查询和分析;写操作仅对非生产环境开放。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"zhound420-d365fo-connector": {
"command": "npx",
"args": [
"-y",
"@zhound/d365fo-mcp-server"
],
"env": {
"D365_TENANT_ID": "<D365_TENANT_ID>",
"D365_CLIENT_ID": "<D365_CLIENT_ID>",
"D365_CLIENT_SECRET": "<D365_CLIENT_SECRET>",
"D365_ENVIRONMENT_URL": "<D365_ENVIRONMENT_URL>"
}
}
}
}
启动命令npx -y @zhound/d365fo-mcp-server
需要配置的环境变量
D365_TENANT_IDAzure AD tenant ID
D365_CLIENT_IDApp registration client ID
D365_CLIENT_SECRET密钥App registration client secret
D365_ENVIRONMENT_URLD365 F&O environment URL, e.g. https://contoso.operations.dynamics.com

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@zhound/d365fo-mcp-server2.0.1

工具

22

收录的工具列表 · 更新于 2 周前

此工具无需参数。

详细介绍

D365 Finance & Operations MCP 服务

一个 MCP(Model Context Protocol)服务,可访问 Microsoft Dynamics 365 Finance & Operations 环境。支持 Claude 等 AI 助手探索 D365 元数据、查询数据,并在非生产环境中执行写入操作。

功能

  • 多环境支持 - 连接到多个 D365 环境(生产、UAT、开发)
  • 读写操作 - 可在所有环境中查询数据;仅可在非生产环境中创建、更新和删除数据
  • 生产环境安全保障 - 生产环境在设计上始终为只读
  • 用于架构发现和元数据探索的 MCP 资源
  • 22 种专用工具,支持灵活的数据访问、聚合、批量操作和分析
  • 环境仪表板 - 健康监控、API 统计和操作跟踪
  • 通过 Azure AD 客户端凭据进行 安全身份验证
  • 自动元数据缓存(TTL 为 24 小时,按环境分别缓存)

架构

资源
资源URI用途
实体列表d365://entities?filter=<pattern>列出所有实体,并可选用通配符筛选
实体架构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

向导将:

  1. 检查前置条件(Node.js 18+、依赖项)
  2. 指导你配置 D365 环境
  3. 测试与 D365 环境的连接
  4. 生成配置文件
  5. 配置 Claude Desktop 和/或 Claude Code

设置完成后,重启 Claude Desktop(在 macOS 上按 Cmd+Q 后重新打开,或在 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_IDAzure AD 租户 ID
D365_CLIENT_IDAzure AD 应用程序(客户端)ID
D365_CLIENT_SECRETAzure AD 客户端机密
D365_ENVIRONMENT_URLD365 F&O 环境 URL(例如,https://contoso.operations.dynamics.com)
D365_ENVIRONMENT_TYPE可选:"production" 或 "non-production"(为安全起见,默认为 "production")

可选项:

变量默认值描述
D365_TRANSPORTstdio传输模式(stdio 或 http)
D365_HTTP_PORT3000HTTP 端口(使用 http 传输时)
D365_LOG_LEVELinfo日志级别
D365_PAGINATION_TIMEOUT_MS60000大型数据集分页请求的超时时间(毫秒)
D365_CONFIG_FILE配置文件路径(如果不在默认位置)
Azure AD 应用注册
第 1 步:创建 Azure AD 应用
  1. 前往 Azure 门户 > Azure Active Directory > 应用注册
  2. 点击“新注册”
  3. 输入名称(例如,“D365 MCP Server”)
  4. 选择“仅此组织目录中的帐户”
  5. 点击“注册”
第 2 步:配置 API 权限
  1. 前往“API 权限” > “添加权限”
  2. 选择“Dynamics 365 Finance and Operations”
  3. 选择“应用程序权限” > CustomService.ReadWrite.All
  4. 点击“代表 [your organization] 授予管理员同意”
第 3 步:创建客户端机密
  1. 前往“证书和机密” > “新建客户端机密”
  2. 添加描述并设置有效期
  3. 立即复制机密值(仅显示一次)
  4. 记下:
    • 租户 ID:可在“概述”页面找到
    • 客户端 ID:“概述”页面上的应用程序(客户端)ID
    • 客户端机密:刚刚复制的值
第 4 步:在 D365 环境中注册应用

重要: 必须在你要连接的每个 D365 环境(Production、UAT、Dev)中完成此步骤。

  1. 在 D365 F&O 中,导航至: 系统管理 > 设置 > Azure Active Directory 应用程序

  2. 点击“新建”以添加记录:

    字段值
    客户端 IDAzure AD 中的应用程序(客户端)ID
    名称描述性名称(例如,“MCP Server Integration”)
    用户 ID应用运行时使用的 D365 用户帐户
  3. 用户 ID 决定应用可以访问哪些数据:

    • 使用分配了适当安全角色的服务帐户
    • 对于只读访问:分配“查看所有数据”等角色
    • 对于非生产环境的写入访问:分配允许创建、更新和删除的角色
  4. 对要连接的每个环境重复此操作

注意: 如果跳过此步骤,即使 Azure AD 身份验证成功,API 调用仍会因 401 Unauthorized 或 403 Forbidden 错误而失败。

设置

Claude Desktop

将以下内容添加到 Claude Desktop 配置文件中:

macOS:~/Library/Application Support/Claude/claude_desktop_config.json Windows:%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"
      }
    }
  }
}
Claude Code(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.json 加载指定环境。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 对话——提示词示例

配置完成后,你可以用自然语言向 Claude 询问 D365 环境相关问题。以下是按功能分类的示例:

发现实体

你: D365 中有哪些与客户相关的实体?

Claude 会使用 d365://entities?filter=*Cust* 资源查找匹配的实体。

你: 显示 CustomersV3 实体的架构

Claude 会使用 describe_entity 或 d365://entity/CustomersV3 资源。

查询数据

你: 获取前 10 位客户的账号和名称

Claude 会使用 execute_odata,路径为 CustomersV3?$top=10&$select=CustomerAccount,CustomerName

你: 系统中有多少销售订单?

Claude 会使用 execute_odata,路径为 SalesOrderHeaders/$count

你: 查找所有属于客户组“US”且信用额度超过 50000 的客户

Claude 会自动构建 OData 筛选查询。

聚合与分析

你: 按总消费金额列出前 20 位客户

Claude 会使用 aggregate,并设置 groupBy、orderBy 和 top:

{
  "entity": "SalesOrderLinesV2",
  "aggregations": [{"function": "SUM", "field": "LineAmount"}],
  "groupBy": ["OrderingCustomerAccountNumber"],
  "orderBy": "sum_LineAmount desc",
  "top": 20
}

你: 订单金额的中位数是多少?也显示第 90 和第 95 百分位数

Claude 会使用 aggregate 和百分位函数:

{
  "entity": "SalesOrderLinesV2",
  "aggregations": [
    {"function": "P50", "field": "LineAmount", "alias": "median"},
    {"function": "P90", "field": "LineAmount"},
    {"function": "P95", "field": "LineAmount"}
  ],
  "accurate": true
}

你: 按产品类别拆分总收入

Claude 会使用 aggregate 并设置 groupBy:

{
  "entity": "SalesOrderLinesV2",
  "aggregations": [{"function": "SUM", "field": "LineAmount"}],
  "groupBy": ["ItemGroup"]
}
基于时间的分析

你: 显示过去 12 个月的月度销售趋势和增长率

Claude 会使用 trending:

{
  "entity": "SalesOrderLinesV2",
  "dateField": "CreatedDateTime",
  "valueField": "LineAmount",
  "granularity": "month",
  "periods": 12,
  "includeGrowthRate": true
}

你: 比较今年和去年的销售额

Claude 会使用 compare_periods 进行同比比较:

{
  "entity": "SalesOrderLinesV2",
  "dateField": "CreatedDateTime",
  "comparisonType": "YoY",
  "aggregations": [{"function": "SUM", "field": "LineAmount"}]
}

你: 第四季度的销售额与第三季度相比如何?

Claude 会使用 compare_periods 进行环比比较:

{
  "entity": "SalesOrderLinesV2",
  "dateField": "CreatedDateTime",
  "comparisonType": "QoQ",
  "aggregations": [{"function": "SUM", "field": "LineAmount"}]
}
客户洞察

你: 全面分析客户 US-001,包括档案、订单、消费额和趋势

Claude 会使用 analyze_customer,通过一次调用进行全面分析:

{
  "customerAccount": "US-001",
  "includeOrders": true,
  "includeSpend": true,
  "includeTrending": true
}

你: 查找名为“S&S Industries”的客户

Claude 会使用 search_entity,它可以处理会导致标准 OData 查询失败的特殊字符:

{
  "entity": "CustomersV3",
  "searchTerm": "S&S Industries",
  "searchField": "CustomerName"
}
多查询与联接

你: 获取仪表板视图:客户总数、本月订单总数,以及销量最高的 5 种产品

Claude 会使用 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"}
  ]
}

你: 显示最近的订单及客户名称和客户组

Claude 会使用 join_entities 将订单与客户详情关联起来:

{
  "primaryEntity": "SalesOrderHeadersV2",
  "primaryKey": "OrderingCustomerAccountNumber",
  "secondaryEntity": "CustomersV3",
  "secondaryKey": "CustomerAccount",
  "primarySelect": ["SalesOrderNumber", "OrderCreatedDateTime"],
  "secondarySelect": ["CustomerName", "CustomerGroup"]
}
数据导出

你: 将所有信用额度超过 $100K 的客户导出为 CSV

Claude 会使用 export 并指定格式和筛选条件:

{
  "entity": "CustomersV3",
  "format": "csv",
  "filter": "CreditLimit gt 100000",
  "select": ["CustomerAccount", "CustomerName", "CreditLimit"]
}
了解枚举

你: 销售订单状态有哪些可能的值?

Claude 会检查 d365://enums 资源以查找枚举定义。

最佳效果提示

  1. 直接询问业务问题 - MCP 工具会为你处理复杂操作。只需提问:“按支出列出我们的前 20 大客户。”或“Q4 与 Q3 相比如何?”

  2. 使用自然的日期格式 - Claude 能理解“上个月”、“Q4 2024”、“过去 12 个月”,或“January 1, 2024”这类具体日期。

  3. 无需担心特殊字符 - 搜索“S&S Industries”或“O'Brien Corp”会自动生效。遇到会导致标准 OData 出错的字符时,工具会采用备用策略。

  4. 请求趋势和对比 - 内置的时间智能功能会处理复杂操作:“显示每月销售趋势及增长率”或“比较今年与去年的收入”。

  5. 组合多个问题 - 请求类似仪表板的视图:“获取客户总数、本月订单数和排名前 5 的产品”——查询会并行运行。

  6. 需要时导出数据 - 直接请求导出为 CSV、JSON 或 TSV:“将所有信用额度超过 $100K 的客户导出为 CSV”。

  7. 请求解释 - 如果想了解 OData 语法,可以让 Claude 解释查询:“显示 US 组中的客户,并解释 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(字符串,必填):要设为活动环境的名称

示例:

{
  "environment": "uat"
}
describe_entity

以易读格式获取实体架构。

参数:

  • entity(字符串,必填):实体名称

示例:

{
  "entity": "CustomersV3"
}
execute_odata

针对 D365 执行原始 OData 路径。

参数:

  • path(字符串,必填):追加到 /data/ 后的 OData 路径

示例:

// 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(数组,必填):聚合规格数组:
    • function:"SUM" | "AVG" | "COUNT" | "MIN" | "MAX" | "COUNTDISTINCT" | "P50" | "P90" | "P95" | "P99"
    • field:要聚合的字段(COUNT 使用 "*")
    • alias(可选):自定义结果名称
  • filter(字符串,可选):OData $filter 表达式
  • groupBy(数组,可选):用于分组的字段
  • accurate(布尔值,可选):获取所有记录以计算精确总数(默认值:false)
  • sampling(布尔值,可选):对超大型数据集进行统计抽样,以快速估算(默认值:false)
  • orderBy(字符串,可选):按聚合别名对结果排序(例如:"sum_LineAmount desc")
  • top(数字,可选):排序后仅返回前 N 个结果

百分位函数:

  • P50 - 中位数(第 50 百分位)
  • P90 - 第 90 百分位
  • P95 - 第 95 百分位
  • P99 - 第 99 百分位

性能说明:

  • 默认模式最多处理 5K 条记录,以快速估算
  • accurate=true 会获取所有记录,每页超时时间为 60s,并自动重试(采用指数退避,重试 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(字符串 | 对象,必填):源记录的主键
  • relationship(字符串,必填):要沿用的导航属性名称
  • select(字符串[],可选):要从关联实体中包含的字段
  • 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(字符串,必填):要导出的实体
  • format("json" | "csv" | "tsv",可选):输出格式(默认值:"json")
  • select(字符串[],可选):要包含的字段
  • filter(字符串,可选):OData $filter 表达式
  • 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

比较两个时间段的聚合结果(YoY、QoQ、MoM 或自定义范围)。

参数:

  • entity(字符串,必填):要分析的实体
  • dateField(字符串,必填):用于筛选的日期/日期时间字段
  • aggregations(数组,必填):与 aggregate 工具相同
  • comparisonType("YoY" | "QoQ" | "MoM" | "custom",必填):比较类型
  • referenceDate(字符串,可选):计算所用的参考日期(默认值:今天)
  • period1、period2(对象,可选):自定义时间段范围
  • filter(字符串,可选):附加的 OData 筛选条件
  • groupBy(字符串[],可选):用于分组的字段

示例:

// 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(string,必需):用于分桶的日期/日期时间字段
  • valueField(string,必需):要聚合的数值字段
  • aggregation("SUM" | "AVG" | "COUNT" | "MIN" | "MAX",可选):默认值:"SUM"
  • granularity("day" | "week" | "month" | "quarter" | "year",可选):默认值:"month"
  • periods(number,可选):要分析的周期数(默认值:12)
  • endDate(string,可选):分析的结束日期(默认值:今天)
  • filter(string,可选):额外的 OData 筛选条件
  • movingAverageWindow(number,可选):计算 MA 的窗口大小
  • includeGrowthRate(boolean,可选):包含增长率(默认值: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(string,必需):查询的唯一名称
  • description(string,可选):查询描述
  • entity(string,必需):要查询的实体
  • select(string[],可选):要选择的字段
  • filter(string,可选):OData $filter(使用 {{paramName}} 表示参数)
  • orderBy(string,可选):OData $orderby 表达式
  • top(number,可选):最大记录数
  • expand(string,可选):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(string,必需):已保存查询的名称
  • params(object,可选):要替换的参数值
  • 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(string,必需):要删除的查询名称
join_entities

使用 OData $expand 或客户端联接跨实体进行联接。

参数:

  • primaryEntity(string,必需):主实体名称
  • primaryKey(string,必需):用于联接的主键字段
  • secondaryEntity(string,必需):次实体名称
  • secondaryKey(string,必需):用于联接的次键字段
  • primarySelect(string[],可选):主实体中的字段
  • secondarySelect(string[],可选):次实体中的字段
  • primaryFilter(string,可选):主实体的筛选条件
  • joinType("inner" | "left",可选):联接类型(默认值:"inner")
  • maxRecords(number,可选):最大记录数(默认值: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(string,可选):此查询结果的标签
    • entity(string,必需):实体名称
    • filter(string,可选):OData $filter 表达式
    • select(string[],可选):要包含的字段
    • top(number,可选):记录数上限(默认值:100)
    • orderby(string,可选):OData $orderby 表达式
    • fetchAll(boolean,可选):自动获取所有分页结果
    • maxRecords(number,可选):当 fetchAll=true 时的最大记录数
  • stopOnError(boolean,可选):遇到第一个错误时停止(默认值: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() 遇到的问题。

搜索策略(按顺序尝试):

  1. contains() - 标准 OData 文本搜索(速度最快)
  2. startswith() - 前缀匹配(在 D365 上更可靠)
  3. exact - 字段精确匹配
  4. client_filter - 获取数据并在客户端筛选(始终有效)

参数:

  • entity(字符串,必填):要搜索的实体
  • searchTerm(字符串,必填):要搜索的文本
  • searchField(字符串,必填):要搜索的字段
  • select(字符串数组,可选):结果中要返回的字段
  • 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(数字,可选):显示的最近订单数量(默认值: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(对象,必填):新记录的字段值
  • environment(字符串,可选):目标环境

示例:

{
  "entity": "CustomersV3",
  "data": {
    "CustomerAccount": "CUST-001",
    "CustomerName": "Contoso Ltd",
    "CustomerGroup": "US"
  },
  "environment": "uat"
}
update_record

更新现有记录。

参数:

  • entity(字符串,必填):实体名称
  • key(字符串 | 对象,必填):记录键
  • data(对象,必填):要更新的字段值
  • etag(字符串,可选):用于乐观并发控制的 ETag
  • environment(字符串,可选):目标环境

示例:

{
  "entity": "CustomersV3",
  "key": "CUST-001",
  "data": {
    "CustomerName": "Contoso Corporation"
  },
  "environment": "dev"
}
delete_record

从实体中删除记录。

参数:

  • entity(字符串,必填):实体名称
  • key(字符串 | 对象,必填):记录键
  • 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 - 示例配置
凭据泄露时的处理方式

如果你不慎提交或泄露凭据:

  1. 立即轮换 Azure AD 客户端机密:

    • 前往 Azure Portal > Azure Active Directory > App registrations
    • 选择你的 D365 应用注册
    • 前往“Certificates & secrets”
    • 创建新的客户端机密
    • 使用新机密更新本地 .env 或 d365-environments.json
    • 从 Azure AD 中删除旧机密
  2. 查看 Azure AD 登录日志:

    • 检查是否存在未经授权的访问尝试
    • Azure Portal > Azure AD > Sign-in logs > 按你的应用筛选
  3. 如果已提交到 git:

    • 即使在新提交中移除机密,它仍会保留在 git 历史记录中
    • 可考虑使用 git filter-branch 或 BFG Repo-Cleaner 清除历史记录
    • 强制推送清理后的仓库(请与协作者协调)
轮换 Azure AD 机密

最佳做法是定期轮换机密(每 90-180 天):

  1. 在 Azure Portal 中创建新机密(在旧机密过期之前)
  2. 更新配置文件:
    # Edit .env or d365-environments.json with new secret
    
  3. 测试连接:
    npm start  # Verify authentication works
    
  4. 从 Azure Portal 中删除旧机密
提交前钩子

提交前钩子会扫描以下模式:

  • Azure AD 客户端机密(clientSecret 后的 30 个或更多字符组成的字符串)
  • 租户 ID(tenantId 后的 UUID 格式字符串)
  • 含机密信息的环境变量赋值

仅在误报时,可通过以下方式绕过:

git commit --no-verify
运行时保护
  • 生产环境只读:在结构上禁止对生产环境执行写入操作
  • 非生产环境写入权限:仅 type: "non-production" 环境支持创建、更新和删除操作
  • 不暴露凭据:凭据由服务端管理
  • 防止 OData 注入:参数会经过适当编码

故障排除

MCP 服务未显示
  1. 完全重启 Claude Desktop - 在 macOS 上按 Cmd+Q(不要只关闭窗口),然后重新打开。在 Windows 上,按 Ctrl+Q 或从系统托盘退出。

  2. 检查服务配置 - 确认配置文件路径正确:

    D365_CONFIG_FILE=./d365-environments.json D365_SINGLE_ENV=uat node dist/index.js
    
  3. 验证配置路径 - 确保 Claude 配置中的 D365_CONFIG_FILE 指向 d365-environments.json 的实际位置。

  4. 查看 Claude 日志 - 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 环境变量配置超时时间
  • 对于超大型数据集(10 万条以上记录),在 aggregate 工具中使用 sampling=true,快速获取统计估算结果
  • 如果在分页过程中中断,accurate=true 模式现在会报告部分结果

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。