Dynamics 365 CRM 全功能 HTTP MCP服务器(145 个工具)使用指南 SKILL

Dynamics 365 CRM 全功能 HTTP 服务器(145 个工具)使用指南 SKILL

1. 环境信息

项目 值
服务器脚本 D:\Users\PC\Documents\LINQPad Queries\AMCP\CRM_AllTools_HttpServer.linq
LINQPad Premium v5.37.6 (AnyCPU),lprun.exe 位于 D:\Program Files (x86)\LINQPad Premium v5.37.6 (AnyCPU)
默认地址 http://localhost:8790/(可用环境变量 CRM_HTTP_PORT 改端口)
连接环境 默认 Dynamic365.GetService_Dev()(XXX 开发环境);按请求 env 字段懒切换 UAT/PROD
连接用户 # D365-CRM,WhoAmI user_id 0e48b980-c11e-f111-8342-70a8a504f378
协议 HTTP REST(GET/POST),支持 CORS
语言 错误消息为中文

2. 启动服务器

# 方式一:直接运行(Ctrl+C 停止)
& "D:\Program Files (x86)\LINQPad Premium v5.37.6 (AnyCPU)\lprun.exe" -format=text "AMCP\CRM_AllTools_HttpServer.linq"

# 方式二:用启动脚本
& "AMCP\Start-AllToolsHttp.ps1"

2.1 看门狗(Watchdog)自动守护

项目 值
脚本 AMCP\CrmHttpWatchdog.py
行为 每 10 秒探测 http://127.0.0.1:8790/,端口关闭时自动重新拉起 lprun 跑 CRM_AllTools_HttpServer.linq(DETACHED 分离进程)
日志 C:\Users\PC\AppData\Local\Temp\opencode\watchdog-8790.log(追加写入,含 8790 down, launching / 8790 up 等)
检查进程 `Get-Process
  • 主动重启新版代码:直接 Stop-Process -Id <lprunId> -Force 杀掉 lprun 即可,看门狗会在数秒内自动用最新代码拉起新进程,无需手动重启。
  • 端口被占导致启动失败:若 Start-AllToolsHttp.ps1 报"已有一个服务在运行",说明看门狗已拉起过实例;此时应停掉旧 lprun 让看门狗接管,而不是再启一个。
  • 看门狗进程本身是长驻 python,若被误杀需重新手动启动(python AMCP\CrmHttpWatchdog.py)。

2.2 服务健康检查(内置自动重连)

CRM_AllTools_HttpServer.linq 内置定时健康检查:侦听器启动后每 2 分钟 对 DEV / UAT / PROD 逐一执行 WhoAmIRequest 探活;若 service 为空或抛出异常(如"未将对象引用设置到对象的实例"),自动重新获取对应环境的 service(Dynamic365.GetService_Dev/Uat/Prod)并再次验证。日志输出到 lprun 控制台([HH:mm:ss] <ENV> 已重新获取 service 并验证通过)。

3. 调用方式

3.1 健康检查

Invoke-RestMethod http://localhost:8790/

3.2 获取工具列表

Invoke-RestMethod http://localhost:8790/tools

3.3 执行工具(POST /call)

请求体格式:

{"tool":"dataverse_xxx","env":"dev|uat|prod","arguments":{...}}

响应格式:

{"ok":true,"result":{...}}

或

{"ok":false,"error":{"code":-32603,"message":"..."}}

PowerShell 调用:

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_whoami"}' http://localhost:8790/call

3.4 环境切换

通过请求体 env 字段切换目标环境(省略则默认 dev):

  • "dev" — DEV 环境(GetService_Dev,默认)
  • "uat" — GetService_Uat
  • "prod" — GetService_Prod
# 查询 UAT 环境
Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_list_solutions","env":"uat","arguments":{"search":"XXX"}}' http://localhost:8790/call

3.5 OData REST 代理(GET /crmproxy/{env}/api/data/v9.2/...)

直接以浏览器/curl 方式访问 Dataverse Web API,完整 OData 语法($filter/$expand/$apply/any() 内 eq or startswith endswith 等),原样转发并返回 Dataverse 原始 JSON(含 CORS,支持跨域):

GET  http://localhost:8790/crmproxy/dev/api/data/v9.2/accounts?$top=3&$select=name,accountid&$filter=...
POST http://localhost:8790/crmproxy/dev/api/data/v9.2/accounts                       (body=JSON)
PATCH http://localhost:8790/crmproxy/{env}/api/data/v9.2/accounts(guid)              (body=JSON)
DELETE http://localhost:8790/crmproxy/{env}/api/data/v9.2/accounts(guid)
  • {env} 为 dev/uat/prod(省略默认 dev)
  • 路径也可省略 /api/data/v9.2/ 前缀(如 /crmproxy/dev/accounts?...)
  • POST/PATCH 需 Content-Type: application/json;PATCH 是 upsert 语义(记录不存在会创建)
  • 也可省略前缀直接用 accounts?$filter=... 形式

4. 工具分类总览

工具名一律以 dataverse_ 前缀。当前共 145 个工具(以 GET /tools 实际返回数为准,下表分类明细可能滞后),按 29 类组织:

# 分类 工具数
1 Auth 1
2 Metadata 10
3 Query 3
4 CRUD 6
5 Relations 4
6 Actions 3
7 Dependencies 2
8 Batch 1
9 Change Tracking 1
10 Solutions 2
11 Impersonation 1
12 Environment 5
13 Trace 2
14 Audit 1
15 Quality 1
16 Annotations 2
17 Users 2
18 Views 10
19 Forms 6
20 Org 2
21 RBAC 7
22 Workflows 13
23 Assistance 2
24 Attributes 14
25 Schema 写入 3
26 Record Access 4
27 Web Resources 5
28 Assembly 17
29 Email 1

5. 工具参考(参数详解)

1. Auth

  • dataverse_whoami — 无参数。返回 userId、业务部门、组织名。

2. Metadata

  • dataverse_list_tables — include_system_tables(boolean,默认false), search(string)
  • dataverse_get_table_metadata — logical_name(必填), include_attributes(boolean,默认true)。返回完整字段元数据(含 type/options/lookup_targets/is_custom/is_required/max_length 等)
  • dataverse_get_relationships — logical_name(必填), relationship_type(OneToMany/ManyToOne/ManyToMany/All,默认All,也接受 1:N/N:1/N:N)
  • dataverse_update_entity_display_name — entity_logical_name, confirm(必填),可选 display_name_cn(中文, 2052)/display_name_en(英文, 1033)。更新实体显示名(中英文可分别指定)
  • dataverse_list_global_option_sets — 无参数
  • dataverse_get_option_set — name(必填)
  • dataverse_get_entity_key — table_name(必填)
  • dataverse_get_attribute_option_set — entity_logical_name, attribute_logical_name(均必填)
  • dataverse_update_entity — entity_logical_name, confirm(必填),可选 has_notes/change_tracking_enabled/is_audit_enabled
  • dataverse_resolve_entity_name — name(必填),逻辑名↔实体集名双向解析

3. Query

  • dataverse_execute_fetchxml — fetch_xml(必填)。注意参数名是 fetch_xml 不是 fetchxml
  • dataverse_odata_proxy — OData Web API 代理,原样转发到 /api/data/v9.2/,无解析限制。url(必填, 相对路径如 accounts?$select=name&$filter=... 或 accounts(guid)), method(GET/POST/PATCH/DELETE, 默认GET), body(object, POST/PATCH), prefer_return(bool, 加 Prefer: return=representation)。返回 {status, ok, body},body 为 Dataverse 原始 JSON。支持完整 OData 语法(any() 内 eq/or/startswith/endswith、$expand、$apply 等)。注意:PATCH 是 upsert 语义(记录不存在会创建)
  • dataverse_retrieve_multiple_with_paging — logical_name(必填), select(array), filter(OData风格), orderby, max_total(默认5000,上限50000)。陷阱:orderby 传 "name asc" 这种带方向的形式会报错 entity doesn't contain attribute with Name = 'name asc'——需要排序时改用 dataverse_execute_fetchxml(fetchxml 里 <order attribute="name" descending="true"/>)更稳。

4. CRUD

  • dataverse_get — logical_name, id(必填), select(array)。注意响应嵌套:记录字段在 result.record 下,不是 result 直接平铺:{"ok":true,"result":{"logical_name":...,"id":...,"record":{<字段名>:<值>,...}}}。取字段用 $r.result.record.<attr>。二进制字段(如 pluginassembly 的 content)经 JSON 序列化后是 base64 字符串。
  • dataverse_create — logical_name, data(object)必填。返回新记录 GUID
  • dataverse_update — logical_name, id, data(均必填),仅更新传入字段
  • dataverse_delete — logical_name, id, confirm=true(必填)才执行
  • dataverse_upsert — logical_name, alternate_key, alternate_key_value, data,mode(upsert/createOnly/updateOnly)
  • dataverse_merge_records — logical_name, target_id, source_id(必填),可选 update_content。合并两条同类型记录(源并入目标并停用源)。

5. Relations

  • dataverse_associate — logical_name, id, relationship_name, related_logical_name, related_id(均必填)
  • dataverse_disassociate — logical_name, id, relationship_name(必填),N:N 时 related_logical_name/related_id 必填
  • dataverse_associate_bulk — logical_name, id, relationship_name, related_logical_name, related_ids(array 1-200)
  • dataverse_query_associations — logical_name, id, navigation_property(必填), select(逗号分隔), top(默认50)

6. Actions

  • dataverse_execute_action — action_name(必填), parameters(object)
  • dataverse_execute_bound_action — logical_name, id, action_name(必填), parameters
  • dataverse_list_custom_actions — top(默认100,最大500), name_filter

7. Dependencies

  • dataverse_list_dependencies — table_name(必填), component_type(array: Workflow/Flow/BusinessRule/Action/Plugin)
  • dataverse_retrieve_dependencies_for_delete — component_type(number: 1=实体,2=字段,29=流程,91=程序集), object_id

8. Batch

  • dataverse_batch_execute — operations(array,每项 {logical_name, method(Create/Update/Delete), id?, data?}),continue_on_error(默认false)。SDK ExecuteMultiple 实现

9. Change Tracking

  • dataverse_change_detection — logical_name(必填,需启用变更跟踪), delta_token(首次省略), select(array)。返回变更记录 + next_page_cookie

10. Solutions

  • dataverse_publish_customizations — 无参数。发布所有自定义项。异步执行:立即返回 async_job_id,调用 get_async_job_status 轮询(statecode 3=完成,statuscode 30=成功)
  • dataverse_create_sitemap — sitemap_name, site_map_xml(必填)
  • dataverse_list_solutions — top(默认50,上限100), search(按 uniquename 模糊搜索)。返回唯一名/友好名/版本/创建时间/是否托管/ID
  • dataverse_get_today_solution — 无参数。返回今日方案名 XXX_yyyyMMdd 及是否存在
  • dataverse_create_solution — solution_name(可选,默认 XXX_日期)。已存在则忽略;publisher 固定为 HandVyung
  • dataverse_add_solution_component — solution_name, component_type(61=Web资源/91=程序集/92=步骤/29=流程/1=实体/60=表单), component_id(必填),可选 add_required(是否自动加依赖)
  • dataverse_search_solution_components — component_type(必填 61/91/92), keyword, top(默认10,上限50)。按名称搜索可添加的组件
  • dataverse_export_solution — solution_name(必填)。从指定环境导出未托管 .zip 到 D:\Users\PC\Desktop\项目\英飞特项目\solution\,文件名为 <name>_1.0.0.0.zip;方案不存在时自动创建。导出环境由请求 env 字段决定(dev/uat/prod),省略默认 dev;方案自动创建/导出都在该环境下完成。注意:同名 zip 会直接覆盖。
  • dataverse_import_solution — solution_name, target_env(UAT/PROD)必填。读取导出目录的 zip,异步导入并返回 job_id(PublishWorkflows=true)
  • dataverse_publish_solution — target_env(UAT/PROD)必填。在目标环境异步发布全部自定义项,返回 job_id
  • dataverse_get_async_job_status — job_id, target_env(DEV/UAT/PROD)必填。返回 statecode(0就绪/1暂停/2进行中/3完成)、statuscode、is_completed/is_success/is_failed
  • dataverse_get_solution_components — solution_name(必填)。列出方案内组件(类型编号+名称+objectid)
  • dataverse_remove_solution_component — solution_name, component_type, component_id(必填)。从方案移除组件
  • dataverse_send_migration_email — solution_name, target_env, step(import/publish), success(必填);失败时传 job_id 自动抓取错误详情,可选 export_duration_sec/import_duration_sec/publish_duration_sec。调用 Email.SendMailToAdmin 发送 HTML 报告
  • dataverse_get_job_error_detail — job_id, target_env(必填)。自动解析 asyncoperation.message 中的 <MissingDependencies> XML 为友好中文错误

11. Impersonation

  • dataverse_impersonate — caller_id, tool_name, tool_args。已知限制:当前应用用户连接下 CallerId 不生效,工具会先验证并返回 warning + effective_user_id,不静默执行

12. Environment

  • dataverse_get_environment_variable — schema_name(必填)
  • dataverse_set_environment_variable — schema_name, value(必填)
  • dataverse_create_environment_variable — schema_name(含前缀), display_name, type(String/Number/Boolean/JSON), value, confirm=true
  • dataverse_environment_capabilities — 无参数。身份/组织设置/解决方案数快照
  • dataverse_list_connection_references — top(默认50,最大200), name_filter。列出解决方案中的连接引用

13. Trace

  • dataverse_get_plugin_trace_logs — top(默认50,最大200), plugin_type_filter, entity_filter, exceptions_only
  • dataverse_get_workflow_trace_logs — top(默认50,最大200), failed_only

14. Audit

  • dataverse_get_audit_log — logical_name, record_id, top(默认50,最大500), operation

15. Quality

  • dataverse_detect_duplicates — logical_name, record(object)必填,不保存只校验

16. Annotations

  • dataverse_get_annotations — logical_name, record_id(必填), include_content(base64,默认false), top(默认20)
  • dataverse_create_annotation — logical_name, record_id(必填),note_text 与 document_base64 二选一;附件时 file_name/mime_type

17. Users

  • dataverse_list_users — search, top(默认20,最大100)
  • dataverse_get_user_roles — user_id(必填)

18. Views

  • dataverse_list_views — entity_logical_name(必填), top(默认20)
  • dataverse_get_view — view_id(与 entity_logical_name+name 二选一), entity_logical_name, name。获取视图基本信息(含 fetchxml/layoutxml)。
  • dataverse_get_view_xml — view_id(必填), include_fetchxml(默认true), include_layoutxml(默认true)。获取视图完整 FetchXML 与 LayoutXML。
  • dataverse_update_view_fetchxml — view_id, fetch_xml, confirm=true(必填)。更新视图 FetchXML。
  • dataverse_update_view_layoutxml — view_id, layout_xml, confirm=true(必填)。更新视图 LayoutXML。
  • dataverse_create_view — entity_logical_name, name, fetch_xml, confirm=true(必填);可选 layout_xml(不传按 fetchxml 属性自动生成)、description。创建自定义视图。
  • dataverse_delete_view — view_id, confirm=true(必填)。删除视图。
  • dataverse_set_view_default — view_id, entity_logical_name, confirm=true(必填)。设为实体的默认视图(自动取消原默认)。
  • dataverse_reorder_view_columns — view_id, field_names(array), confirm=true(必填)。调整视图列顺序。
  • dataverse_update_entity_active_view — entity_logical_name, field_names(array), confirm=true(必填)。给实体 Active 视图(querytype=0)添加列,同时更新 fetchxml(attribute) 和 layoutxml(grid cell),确保视图列在 UI 中可见。编辑已保存,需调用 dataverse_publish_entity 或 dataverse_publish_customizations 发布后生效
  • dataverse_publish_entity — entity_logical_name(单个) 或 entity_logical_names(array,多个,优先)。发布指定实体(PublishXmlRequest,使表单/视图等改动生效)。同步执行:等待发布完成才返回(PublishXmlRequest 不支持 ExecuteAsync 异步,会报 "can not be executed in async");大实体发布可能较慢,调用方设 -TimeoutSec 600 防客户端超时误判。

19. Forms

  • dataverse_list_entity_forms — entity_logical_name(必填), top(默认50), form_type(可选:2=主表单,6=快速视图,7=快速创建,10=仪表板)。列出实体的所有表单(systemform,自动把逻辑名解析为 OTC 过滤),返回 formid/名称/类型/描述等。
  • dataverse_get_form_xml — form_id(与 name 二选一), name+entity_logical_name。获取指定表单完整 FormXML。
  • dataverse_update_form_xml — form_id, form_xml, confirm=true(必填)。更新完整 FormXML。
  • dataverse_backup_form_xml — form_id(必填), restore(true=恢复最近备份,默认false=备份当前), note(备份备注), confirm=true(恢复时必须)。备份 FormXML 到本地文件(D:\Users\PC\Desktop\项目\英飞特项目\formbackup\<formid>_<时间戳>.xml,按时间戳保留多版本),恢复时自动取最新备份回写;不创建任何 CRM 组件
  • dataverse_enable_disable_form — form_id, enable(true=启用,false=停用), confirm=true(必填)。启用/停用表单。
  • dataverse_update_entity_main_form — entity_logical_name, field_names(array), confirm=true(必填)。给实体主表单(type=2)添加字段,返回新增数。编辑已保存,需调用 dataverse_publish_entity 或 dataverse_publish_customizations 发布后生效

20. Org

  • dataverse_list_business_units — top(默认50), include_disabled
  • dataverse_list_teams — top(默认50), team_type(Owner/Access/AAD)

21. RBAC

  • dataverse_list_roles — name_filter, top(默认50)
  • dataverse_assign_role_to_user — user_id, role_id(必填),幂等
  • dataverse_remove_role_from_user — user_id, role_id(必填)
  • dataverse_assign_role_to_team — team_id, role_id, confirm=true
  • dataverse_get_role_privileges — role_id(必填)。返回全部特权及深度
  • dataverse_add_role_privileges — role_id, privileges(array [{privilege_id, depth}]),depth: None/Basic/Local/Deep/Global
  • dataverse_replace_role_privileges — role_id, privileges, confirm=true(破坏性,原子替换全部特权)

22. Workflows

  • dataverse_list_workflows — top(默认50), name_filter
  • dataverse_get_workflow — workflow_id(必填)
  • dataverse_get_workflow_xaml — workflow_id(必填)。获取工作流完整 XAML 定义。
  • dataverse_update_workflow_xaml — workflow_id, xaml, confirm=true(必填)。更新工作流 XAML(需停用状态)。
  • dataverse_create_workflow — name, primary_entity, confirm=true(必填);可选 category(0=Workflow,1=Dialog,2=BusinessRule,3=Action,4=BusinessProcessFlow)、xaml。创建工作流草稿。
  • dataverse_delete_workflow — workflow_id, confirm=true(必填)。删除工作流。
  • dataverse_activate_workflow — workflow_id, confirm=true(必填)。激活工作流。
  • dataverse_deactivate_workflow — workflow_id, confirm=true(必填)。停用工作流。
  • dataverse_set_workflow_state — workflow_id, activate(bool)必填。激活或停用经典工作流。
  • dataverse_list_cloud_flows — top(默认50), name_filter, state_filter(All/Active/Inactive)。列出云流(Power Automate)。
  • dataverse_get_cloud_flow — workflow_id(必填)。获取云流详情。
  • dataverse_enable_cloud_flow — workflow_id, confirm=true(必填)。启用云流。
  • dataverse_disable_cloud_flow — workflow_id, confirm=true(必填)。停用云流。

23. Assistance

  • dataverse_suggest_tools — task(必填),按自然语言推荐工具
  • dataverse_list_tool_tags — 无参数

24. Attributes(字段)

  • dataverse_create_attribute — entity_logical_name, schema_name, attribute_type(String/Memo/Integer/Float/Decimal/Money/DateTime/Boolean/Picklist/MultiSelectPicklist/Lookup), display_name, confirm=true(必填);可选 max_length/options/precision/lookup_target。注意:Picklist 的 options 每项 {value,label} 必须提供;Boolean 必须提供 true/false 两项
  • dataverse_update_attribute — entity_logical_name, attribute_logical_name, confirm=true(必填);可选 display_name/description
  • dataverse_delete_attribute — entity_logical_name, attribute_logical_name, confirm=true(必填)
  • dataverse_create_lookup_attribute — entity_logical_name, schema_name, display_name, lookup_target, confirm=true。自动创建 N:1 关系
  • dataverse_get_attribute — entity_logical_name, attribute_logical_name(必填)。获取字段完整元数据(类型/选项/长度/精度/必填/搜索/审计等)。
  • dataverse_update_attribute_required_level — entity_logical_name, attribute_logical_name, required_level(None/ApplicationRequired/Recommended), confirm=true(必填)。设置字段必填级别。
  • dataverse_update_attribute_description — entity_logical_name, attribute_logical_name, description, confirm=true(必填)。更新字段描述。
  • dataverse_update_attribute_max_length — entity_logical_name, attribute_logical_name, max_length, confirm=true(必填)。更新字符串字段最大长度。
  • dataverse_update_attribute_precision — entity_logical_name, attribute_logical_name, precision, confirm=true(必填)。更新数字字段精度。
  • dataverse_update_attribute_auditing — entity_logical_name, attribute_logical_name, auditing, confirm=true(必填)。启用/禁用字段审计。
  • dataverse_update_choice_options — entity_logical_name, attribute_logical_name, options(array [{value,label}]), confirm=true(必填)。整体替换选项集字段所有选项。
  • dataverse_add_choice_option — value, label, confirm=true(必填);可选 entity_logical_name+attribute_logical_name(本地选项集) 或 option_set_name(全局选项集)。添加选项。
  • dataverse_update_choice_option — value, label, confirm=true(必填);可选 entity_logical_name+attribute_logical_name 或 option_set_name。修改选项标签。
  • dataverse_delete_choice_option — value, confirm=true(必填);可选 entity_logical_name+attribute_logical_name 或 option_set_name。删除选项。

25. Schema 写入(实体/关系)

  • dataverse_create_table — schema_name(含前缀), display_name, primary_attribute_schema_name, confirm=true(必填);可选 display_collection_name/primary_attribute_display_name/ownership/has_notes
  • dataverse_create_relationship — referencing_entity, referenced_entity, schema_name, referencing_attribute, confirm=true(必填);可选 referencing_attribute_display_name
  • dataverse_delete_table — logical_name, confirm=true(必填)。注意:DeleteEntityRequest 不支持 ExecuteAsync 异步(报 "can not be executed in async"),需同步执行;删除耗时可能超 2 分钟,调用方必须设足够超时(PowerShell 建议 -TimeoutSec 600);完成后用 get_table_metadata/list_tables 复查确认删除

26. Record Access

  • dataverse_check_record_access — logical_name, id(必填), user_id
  • dataverse_grant_access — logical_name, id, principal_type(systemuser/team), principal_id, access_mask(array: Read/Write/Delete/Append/AppendTo/Assign/Share)
  • dataverse_revoke_access — logical_name, id, principal_type, principal_id
  • dataverse_assign — logical_name, id, owner_type(systemuser/team), owner_id。将记录重新分配给用户或团队。

27. Web Resources

  • dataverse_create_web_resource — name(含前缀), display_name, web_resource_type(1=HTML,2=CSS,3=JS,4=XML,5=PNG,6=JPG,7=GIF,8=XAP,9=XSL,10=ICO,11=SVG), content_base64(必填)
  • dataverse_update_web_resource — name(精确匹配), content_base64(必填),可选 display_name/description(不传则不变)。按名称查找 webresource 后更新 content。⚠️ 更新后需发布才生效:调用 dataverse_publish_web_resource 发布,或 dataverse_publish_customizations 发布全部。
  • dataverse_set_table_icon — entity_logical_name, icon_web_resource_name, confirm=true
  • dataverse_delete_web_resource — name, confirm=true
  • dataverse_publish_web_resource — name(与 id 二选一), id(与 name 二选一,优先)。按名称或 GUID 发布单个 Web 资源(PublishXmlRequest)。同步执行:等待发布完成才返回(PublishXmlRequest 不支持 ExecuteAsync 异步,会报 "can not be executed in async"),调用方设 -TimeoutSec 600 防客户端超时误判。

28. Assembly(程序集)

  • dataverse_update_assembly — assembly_name(pluginassembly.name 精确匹配), content_base64(必填);可选 isolation_mode(默认2=Sandbox), refresh_steps(默认true,通过 dummy update 触发步骤缓存刷新,参考 根据目录更新程序集插件.linq)。更新程序集 DLL。
  • dataverse_list_plugin_assemblies — top(默认50), name_filter。列出插件程序集。
  • dataverse_get_plugin_assembly — assembly_id(与 assembly_name 二选一), include_content(默认false)。获取程序集详情。
  • dataverse_create_plugin_assembly — name, content_base64, confirm=true(必填);可选 source_type(0=Database)、isolation_mode(2=Sandbox)。注册新程序集。
  • dataverse_update_plugin_assembly — assembly_id, confirm=true(必填);可选 content_base64/name/isolation_mode。更新程序集。
  • dataverse_delete_plugin_assembly — assembly_id(与 assembly_name 二选一), confirm=true(必填)。删除程序集。
  • dataverse_list_plugin_types — assembly_id(可选), top(默认100)。列出程序集下的插件类型。
  • dataverse_register_plugin_type — assembly_id, type_name(含命名空间), confirm=true(必填);可选 friendly_name。注册插件类型。
  • dataverse_delete_plugin_type — plugin_type_id, confirm=true(必填)。删除插件类型。
  • dataverse_list_plugin_steps — top(默认100,最大500), active_only(默认true), entity_logical_name。列出插件步骤注册信息。
  • dataverse_create_plugin_step — plugin_type_id, message(如Create/Update), entity_logical_name, name, stage(10/20/40), confirm=true(必填);可选 mode(0=同步,1=异步)/rank/filtering_attributes/description。注册插件步骤。
  • dataverse_update_plugin_step — step_id, confirm=true(必填);可选 name/stage/mode/rank/description。更新步骤。
  • dataverse_delete_plugin_step — step_id, confirm=true(必填)。删除步骤。
  • dataverse_enable_plugin_step — step_id, confirm=true(必填)。启用步骤。
  • dataverse_disable_plugin_step — step_id, confirm=true(必填)。停用步骤。
  • dataverse_create_plugin_step_image — step_id, name, confirm=true(必填);可选 image_type(0=PreImage,1=PostImage)/attributes/message_property_name。添加步骤镜像。
  • dataverse_delete_plugin_step_image — image_id, confirm=true(必填)。删除镜像。
  • dataverse_update_assembly — assembly_name(pluginassembly.name 精确匹配), content_base64(必填);可选 isolation_mode(默认2=Sandbox), refresh_steps(默认true,通过 dummy update 触发步骤缓存刷新,参考 根据目录更新程序集插件.linq)。更新程序集 DLL。

29. Email

  • dataverse_send_email — subject(必填), body(必填,HTML 格式),可选 to(收件人邮箱,多个用 ;/, 分隔,默认发给管理员)。复用 Email.SendMailToAdmin 的 SMTP 配置(smtphz.qiye.163.com,管理员邮箱 1005928472@qq.com)。返回 {success:true, message:"邮件已发送: <subject> (发给管理员)"};失败返回 -32603。已知限制:正文通过 SMTP 发送,HTML 中可内嵌 data:image/...;base64 图片(客户端需支持内嵌显示,否则显示为空白,可改附件方式)
    • ⚠️ 中文/emoji 乱码避坑:从 PowerShell 脚本构造 HTML 正文时,若脚本文件编码导致中文/emoji 损坏,将所有中文和 emoji 转为 HTML 实体编码(如 长沙 → &#38271;&#27801;,☀️ → &#9728;&#65039;)即可。HTML 实体全是 ASCII 字符,在任何编码下都不会损坏,邮件客户端渲染时自动还原。在线转换工具:https://www.qqxiuzi.cn/bianma/zifushiti.php

5.1 插件程序集(DLL)跨环境下载与反编译对比

插件 DLL 存在 pluginassembly 实体(component_type 91),content 字段是程序集二进制(base64)。DEV/PROD 中同名程序集 ID 相同,可跨环境按 ID 下载。

下载两个环境的 DLL:

$id = '<pluginassemblyid>'   # 可用 fetchxml 按 name 查询:<condition attribute="name" operator="eq" value="Vyung.Crm.Inventronics.Api"/>
foreach ($env in @('dev','prod')) {
  $body = '{"tool":"dataverse_get","env":"' + $env + '","arguments":{"logical_name":"pluginassembly","id":"' + $id + '","select":["name","content","modifiedon","version"]}}'
  $r = Invoke-RestMethod -Method Post -ContentType 'application/json' -Body $body http://localhost:8790/call
  $c = [string]$r.result.record.content        # 注意是 .record.content,不是 .content
  [IO.File]::WriteAllBytes("$env.dll", [Convert]::FromBase64String($c))
}

dnSpy 反编译(对比差异用):

& "D:\Users\PC\Desktop\常用\dnSpy\dnSpy.Console.exe" -o "decompiled_dev"  "dev.dll"
& "D:\Users\PC\Desktop\常用\dnSpy\dnSpy.Console.exe" -o "decompiled_prod" "prod.dll"
git diff --no-index --stat "decompiled_prod" "decompiled_dev"   # 或直接 diff 目录

对比时的关键陷阱:

  1. dnSpy 在每个类型/属性/方法上输出 // Token: / // (get) Token: / // (set) Token: 元数据注释。只要新增一个类,后续所有类型 RID 偏移,几乎每个文件都会"不同"——比对前必须先过滤这类行:
    Get-Content $f | Where-Object { $_ -notmatch '^\s*//\s*(\((get|set)\)\s+)?Token:' }
  2. 编译器生成的 -PrivateImplementationDetails-.cs(字符串哈希表)、solution.sln/.csproj 属于噪音,忽略。
  3. CustomApi.cs 等 switch 分发类里,新增 tcode 会让编译器重新计算字符串哈希、重排 if (num <= ...) 分支并改 IL 标签名(如 IL_400→IL_485)——这类"差异"是编译器优化产物,真正的改动是新增/删除的字符串分支本身。
  4. 两个环境 DLL 因编译时间/编译器版本不同,即便源码相同 hash 也不同;判断真实差异必须以"过滤 Token 行后的源码 diff"为准。

6. 关键注意事项

  1. confirm 保护:删除/更新/创建等破坏性操作必须传 confirm:true,否则返回错误 -32602 confirm 必须为 true 才执行...
  2. 并发处理:服务器已改为每请求独立线程并发处理(Task.Run),长操作不再阻塞健康检查或看门狗。异步/同步分工:publish_customizations/import_solution 为异步(返回 job_id,用 get_async_job_status 轮询);delete_table/publish_entity/publish_web_resource 为同步(平台拒绝其异步执行,报 "can not be executed in async"),调用方须设足够 HTTP 超时(PowerShell 建议 -TimeoutSec 600),否则客户端超时报错但服务端仍在执行,易误判失败。
  3. MaxConnectionTimeout:服务器已在 Main() 中把 CrmServiceClient.MaxConnectionTimeout 设为 10 分钟(默认 2 分钟),长操作以轮询方式处理。
  4. 属性数量差异:get_table_metadata(RetrieveEntity,含虚拟字段)返回字段数可能比 crm-entity 等工具的字段数多(如 incident 237 vs 221),属于正常差异
  5. 关系可见性延迟:create_relationship 后父实体侧立即可见,子实体侧可能需要等待 + publish_customizations 后才在 get_relationships 中出现
  6. impersonate 不生效:当前应用用户连接类型下 CallerId 无效,工具返回 warning 而非静默执行
  7. 服务器需保持运行:HTTP 服务器是常驻进程,退出后需重启(lprun 或 Start-AllToolsHttp.ps1);通常由看门狗 CrmHttpWatchdog.py 自动拉起,杀掉 lprun 后数秒内自动恢复。服务器内置每 2 分钟健康检查,service 失效会自动重连。
  8. 中文编码(实体/字段/视图名称防乱码,强制执行):创建或更新 display_name/display_name_cn/name(savedquery) 时,若通过 PowerShell 拼 JSON 字符串,必须使用 \uXXXX 转义传中文(例如 异步任务 -> \u5f02\u6b65\u4efb\u52a1),避免被控制台代码页写成 ????。尤其是 dataverse_create_table、dataverse_create_attribute、dataverse_update_entity_display_name、dataverse_update_attribute、dataverse_update(savedquery)。
  9. 中文标签验收(防二次事故):完成实体/字段/视图创建后,必须二次校验元数据:
    • 用 dataverse_odata_proxy 查 EntityDefinitions(...)/Attributes?$select=LogicalName,DisplayName 与 savedquery 名称;
    • 结果中若出现 ?/???? 视为失败,立即修复并重新发布;
    • 不要只看 PowerShell 控制台显示,必要时用字符码点确认(如“异步任务”应为 24322,27493,20219,21153)。
  10. 乱码修复流程(若已写坏):按顺序执行 dataverse_update_entity_display_name -> dataverse_update_attribute(逐字段)-> dataverse_update(更新 savedquery.name)-> dataverse_publish_customizations,再复查。
  11. list_views 无过滤:该 org 中 savedquery.isuserdefined 全部为 true,工具已不做过滤
  12. 测试命名约定:创建自定义实体/字段时 schema 名需含 vy_ 前缀(该 org 已注册前缀),如 vy_mcptest_xxx
  13. 方案命名约定:每日解决方案名为 XXX_日期(如 XXX_20260802),publisher 固定为 HandVyung
  14. 导入/发布是异步的:import_solution/publish_solution 只返回 job_id,需用 get_async_job_status 轮询(statecode 3=完成,statuscode 30=成功);target_env 有白名单校验(仅接受 DEV/UAT/PROD),非法值(含拼写错误)直接报 -32602 target_env 必须是 DEV / UAT / PROD(收到: xxx),不会静默落到其他环境(防误连 PROD 护栏);PROD 连接失败时工具会返回明确错误
  15. 导出路径:export_solution 写入 D:\Users\PC\Desktop\项目\英飞特项目\solution\,import_solution 从同目录读取 <名称>_1.0.0.0.zip,必须先导出再导入
  16. 用 env 字段切换环境:所有工具(含 dataverse_export_solution)的目标环境都由请求 env 字段决定(dev/uat/prod)。import_solution/publish_solution/get_async_job_status 还需传 target_env 参数与 env 一致。导出会写入统一目录 solution\,导出前先确认 env 正确,否则会把错误环境的 zip 覆盖到同名文件。
  17. dataverse_get 返回嵌套:记录在 result.record 下($r.result.record.<attr>),不是 result.<attr>;二进制字段返回 base64 字符串。详见 §5.1
  18. 排序别用 dataverse_retrieve_multiple_with_paging 的 orderby:传 "name asc" 会报错(被当成属性名解析);需要排序一律用 dataverse_execute_fetchxml(fetchxml 内 <order attribute="name" descending="true"/>)
  19. 跨环境对比 DLL:用 §5.1 的流程下载反编译,过滤 // Token: 元数据行后再 diff,否则几乎全部文件都会误报"不同"
  20. 更新 Web 资源后必须发布才生效:dataverse_update_web_resource 更新 content 后,modifiedon 会变但 content 在未发布前检索到的仍是旧内容。必须调用 dataverse_publish_web_resource(单个,同步执行)或 dataverse_publish_customizations(全部,异步返回 async_job_id)发布后,新内容才生效/可见。若更新后 content 仍不变:改用 dataverse_odata_proxy 直接 PATCH webresourceset(<id>) 的 content 字段(body 传 object,不是 JSON 字符串,否则会被二次序列化),再发布。
  21. PublishXml 类发布必须同步:dataverse_publish_web_resource / dataverse_publish_entity(PublishXmlRequest)不支持 ExecuteAsyncRequest 异步(报 Request of type 'PublishXml' can not be executed in async),已改为同步执行,直接返回成功;删除实体 dataverse_delete_table(DeleteEntityRequest)同理同步。同步长操作务必给 HTTP 客户端设 -TimeoutSec 600。

7. 常见使用示例

查询实体元数据

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_get_table_metadata","arguments":{"logical_name":"incident","include_attributes":true}}' http://localhost:8790/call

查询单条记录

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_get","arguments":{"logical_name":"incident","id":"<GUID>","select":["title","ticketnumber","statecode"]}}' http://localhost:8790/call

创建记录

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_create","arguments":{"logical_name":"account","data":{"name":"Test Account","telephone1":"123456"}}}' http://localhost:8790/call

删除记录(需确认)

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_delete","arguments":{"logical_name":"account","id":"<GUID>","confirm":true}}' http://localhost:8790/call

发布 Web 资源(异步,需轮询确认)

# 按名称发布(异步返回 job_id)
$r = Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_publish_web_resource","arguments":{"name":"vy_/js/account/new_businesspurpose.js"}}' http://localhost:8790/call
$jobId = $r.result.async_job_id

# 轮询发布结果(statecode 3=完成 + statuscode 30=成功)
do {
  Start-Sleep 5
  $s = Invoke-RestMethod -Method Post -ContentType 'application/json' `
    -Body ('{"tool":"dataverse_get_async_job_status","arguments":{"job_id":"' + $jobId + '","target_env":"DEV"}}') http://localhost:8790/call
  $s = $s.result
} while (-not $s.is_completed)

if ($s.is_success) { "发布成功" } else { "发布失败: $($s.statuscode)" }

执行 FetchXML

$fetch = '<fetch top="5"><entity name="incident"><attribute name="title"/><attribute name="ticketnumber"/><filter type="and"><condition attribute="statecode" operator="eq" value="0"/></filter></entity></fetch>'
$body = '{"tool":"dataverse_execute_fetchxml","arguments":{"fetch_xml":"' + $fetch.Replace('"','\"') + '"}}'
Invoke-RestMethod -Method Post -ContentType 'application/json' -Body $body http://localhost:8790/call

查询今日解决方案

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_get_today_solution"}' http://localhost:8790/call

查询解决方案内容

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_get_solution_components","arguments":{"solution_name":"XXX_20260807"}}' http://localhost:8790/call

添加解决方案组件

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_add_solution_component","arguments":{"solution_name":"XXX_20260807","component_type":91,"component_id":"<GUID>","add_required":true}}' http://localhost:8790/call

解决方案迁移(导出 → 导入 → 轮询 → 发布 → 邮件)

# 1. 查今日方案
$r = Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_get_today_solution"}' http://localhost:8790/call
$sln = $r.result.today_solution

# 2. 从 DEV 导出(计时)
$sw = [System.Diagnostics.Stopwatch]::StartNew()
Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body ('{"tool":"dataverse_export_solution","arguments":{"solution_name":"' + $sln + '"}}') http://localhost:8790/call
$sw.Stop()
$exportSec = [math]::Round($sw.Elapsed.TotalSeconds)

# 3. 导入到 UAT
$r = Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body ('{"tool":"dataverse_import_solution","env":"uat","arguments":{"solution_name":"' + $sln + '","target_env":"UAT"}}') http://localhost:8790/call
$jobId = $r.result.job_id

# 4. 轮询导入状态
$pollStart = [System.Diagnostics.Stopwatch]::StartNew()
do {
  Start-Sleep 5
  $r = Invoke-RestMethod -Method Post -ContentType 'application/json' `
    -Body ('{"tool":"dataverse_get_async_job_status","env":"uat","arguments":{"job_id":"' + $jobId + '","target_env":"UAT"}}') http://localhost:8790/call
  $s = $r.result
} while (-not $s.is_completed -and $pollStart.Elapsed.TotalSeconds -lt 600)
$importSec = [math]::Round($pollStart.Elapsed.TotalSeconds)

# 5. 发布 UAT
$r = Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_publish_solution","env":"uat","arguments":{"target_env":"UAT"}}' http://localhost:8790/call
$pubJobId = $r.result.job_id

# 6. 轮询发布状态
$pollStart.Restart()
do {
  Start-Sleep 5
  $r = Invoke-RestMethod -Method Post -ContentType 'application/json' `
    -Body ('{"tool":"dataverse_get_async_job_status","env":"uat","arguments":{"job_id":"' + $pubJobId + '","target_env":"UAT"}}') http://localhost:8790/call
  $s = $r.result
} while (-not $s.is_completed -and $pollStart.Elapsed.TotalSeconds -lt 600)
$publishSec = [math]::Round($pollStart.Elapsed.TotalSeconds)

# 7. 发送迁移邮件(一个环境一封)
Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body ('{"tool":"dataverse_send_migration_email","env":"uat","arguments":{"solution_name":"' + $sln + '","target_env":"UAT","step":"publish","success":true,"export_duration_sec":' + $exportSec + ',"import_duration_sec":' + $importSec + ',"publish_duration_sec":' + $publishSec + '}}') http://localhost:8790/call

发送邮件

Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Body '{"tool":"dataverse_send_email","arguments":{"subject":"Test","body":"<h1>Hello</h1>"}}' http://localhost:8790/call

8. 验证清单

步骤 方法 预期结果
服务器可启动 GET / 返回 ok:true 及 tools 数 145
工具可枚举 GET /tools 返回 145 个工具
连接正常 dataverse_whoami 返回 userId 0e48b980-... 等
元数据可用 dataverse_get_table_metadata 返回实体字段明细
修改已生效 破坏性操作后调用 get_table_metadata/list_tables 复查 反映最新状态
方案列表可用 dataverse_list_solutions 返回解决方案列表
方案导入/发布 import_solution/publish_solution 后 get_async_job_status statecode 3 + statuscode 30

9. 环境切换

通过请求体 env 字段(dev/uat/prod)切换环境,无需修改脚本。solution 管理类工具(import_solution/publish_solution/get_async_job_status/get_job_error_detail/send_migration_email)还需传 target_env 参数与 env 一致:

  • env: "dev" + target_env: "DEV" — 开发环境(默认 _service)
  • env: "uat" + target_env: "UAT" — UAT 环境
  • env: "prod" + target_env: "PROD" — 生产环境

target_env 有白名单校验(DEV/UAT/PROD),非法值直接报错,绝不静默回退其他环境(防误连 PROD 护栏)。

如需将整个服务器默认切换为其他环境,修改脚本 Main() 中 Dynamic365.GetService_Dev() 为:

  • Dynamic365.GetService_Dev() — 开发环境(默认)
  • Dynamic365.GetService_Uat() — UAT 环境
  • Dynamic365.GetService_Prod() — 生产环境
posted @ 2026-08-09 14:40  抢囡囡糖未遂  阅读(71)  评论(0)    收藏  举报