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 | 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 实体编码(如
长沙→长沙,☀️→☀️)即可。HTML 实体全是 ASCII 字符,在任何编码下都不会损坏,邮件客户端渲染时自动还原。在线转换工具:https://www.qqxiuzi.cn/bianma/zifushiti.php
- ⚠️ 中文/emoji 乱码避坑:从 PowerShell 脚本构造 HTML 正文时,若脚本文件编码导致中文/emoji 损坏,将所有中文和 emoji 转为 HTML 实体编码(如
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 目录
对比时的关键陷阱:
- dnSpy 在每个类型/属性/方法上输出
// Token:/// (get) Token:/// (set) Token:元数据注释。只要新增一个类,后续所有类型 RID 偏移,几乎每个文件都会"不同"——比对前必须先过滤这类行:
Get-Content $f | Where-Object { $_ -notmatch '^\s*//\s*(\((get|set)\)\s+)?Token:' } - 编译器生成的
-PrivateImplementationDetails-.cs(字符串哈希表)、solution.sln/.csproj属于噪音,忽略。 CustomApi.cs等 switch 分发类里,新增 tcode 会让编译器重新计算字符串哈希、重排if (num <= ...)分支并改 IL 标签名(如IL_400→IL_485)——这类"差异"是编译器优化产物,真正的改动是新增/删除的字符串分支本身。- 两个环境 DLL 因编译时间/编译器版本不同,即便源码相同 hash 也不同;判断真实差异必须以"过滤 Token 行后的源码 diff"为准。
6. 关键注意事项
- confirm 保护:删除/更新/创建等破坏性操作必须传
confirm:true,否则返回错误-32602 confirm 必须为 true 才执行... - 并发处理:服务器已改为每请求独立线程并发处理(
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),否则客户端超时报错但服务端仍在执行,易误判失败。 - MaxConnectionTimeout:服务器已在
Main()中把CrmServiceClient.MaxConnectionTimeout设为 10 分钟(默认 2 分钟),长操作以轮询方式处理。 - 属性数量差异:
get_table_metadata(RetrieveEntity,含虚拟字段)返回字段数可能比crm-entity等工具的字段数多(如 incident 237 vs 221),属于正常差异 - 关系可见性延迟:
create_relationship后父实体侧立即可见,子实体侧可能需要等待 +publish_customizations后才在get_relationships中出现 - impersonate 不生效:当前应用用户连接类型下 CallerId 无效,工具返回
warning而非静默执行 - 服务器需保持运行:HTTP 服务器是常驻进程,退出后需重启(
lprun或Start-AllToolsHttp.ps1);通常由看门狗CrmHttpWatchdog.py自动拉起,杀掉 lprun 后数秒内自动恢复。服务器内置每 2 分钟健康检查,service 失效会自动重连。 - 中文编码(实体/字段/视图名称防乱码,强制执行):创建或更新
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)。 - 中文标签验收(防二次事故):完成实体/字段/视图创建后,必须二次校验元数据:
- 用
dataverse_odata_proxy查EntityDefinitions(...)/Attributes?$select=LogicalName,DisplayName与savedquery名称; - 结果中若出现
?/????视为失败,立即修复并重新发布; - 不要只看 PowerShell 控制台显示,必要时用字符码点确认(如“异步任务”应为
24322,27493,20219,21153)。
- 用
- 乱码修复流程(若已写坏):按顺序执行
dataverse_update_entity_display_name->dataverse_update_attribute(逐字段)->dataverse_update(更新savedquery.name)->dataverse_publish_customizations,再复查。 - list_views 无过滤:该 org 中
savedquery.isuserdefined全部为 true,工具已不做过滤 - 测试命名约定:创建自定义实体/字段时 schema 名需含
vy_前缀(该 org 已注册前缀),如vy_mcptest_xxx - 方案命名约定:每日解决方案名为
XXX_日期(如XXX_20260802),publisher 固定为HandVyung - 导入/发布是异步的:
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 连接失败时工具会返回明确错误 - 导出路径:
export_solution写入D:\Users\PC\Desktop\项目\英飞特项目\solution\,import_solution从同目录读取<名称>_1.0.0.0.zip,必须先导出再导入 - 用
env字段切换环境:所有工具(含dataverse_export_solution)的目标环境都由请求env字段决定(dev/uat/prod)。import_solution/publish_solution/get_async_job_status还需传target_env参数与env一致。导出会写入统一目录solution\,导出前先确认env正确,否则会把错误环境的 zip 覆盖到同名文件。 - dataverse_get 返回嵌套:记录在
result.record下($r.result.record.<attr>),不是result.<attr>;二进制字段返回 base64 字符串。详见 §5.1 - 排序别用
dataverse_retrieve_multiple_with_paging的orderby:传"name asc"会报错(被当成属性名解析);需要排序一律用dataverse_execute_fetchxml(fetchxml 内<order attribute="name" descending="true"/>) - 跨环境对比 DLL:用 §5.1 的流程下载反编译,过滤
// Token:元数据行后再 diff,否则几乎全部文件都会误报"不同" - 更新 Web 资源后必须发布才生效:
dataverse_update_web_resource更新 content 后,modifiedon会变但content在未发布前检索到的仍是旧内容。必须调用dataverse_publish_web_resource(单个,同步执行)或dataverse_publish_customizations(全部,异步返回async_job_id)发布后,新内容才生效/可见。若更新后 content 仍不变:改用dataverse_odata_proxy直接 PATCHwebresourceset(<id>)的content字段(body 传 object,不是 JSON 字符串,否则会被二次序列化),再发布。 - 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()— 生产环境

浙公网安备 33010602011771号