Files
envi-code/SourceCode/Code2026/inc/mcp_plugin_api.h
T
company-desktop ecb8436720 feat: 移植 cad_mcp_plugins,并对齐中望平台的符号与依赖
插件工程在 CadProject/src/cad_mcp_plugins(8 个 cpp/h),本次一并整合进 code2026。

移植改动:
- 源码拷到 SourceCode/Code2026/cad_mcp_plugins/;cadsdk.h -> KTCad.h
- 输出是普通 DLL(.dll),由 cad_mcp_frame 运行时 LoadLibrary,不注册 ARX 命令
- 新增 cad_mcp_plugins/cmakelists.txt:按 code2026 惯例
  (target_set_name_and_suffix ".dll" / target_link_objectarx / 预编译头 / SUB_ARX)
- mcp_plugin_api.h 从 cad_mcp_frame/inc 上移到 SourceCode/Code2026/inc:
  frame 与 plugins 共用,且在 ShsGlobalIncludes 的 include 路径里两边都可见;
  frame 的 CMake 去掉已删除的本地 inc 目录
- 顶层在同一个 C++11+ 条件里挂载 add_custom_module_2(cad_mcp_plugins CadMcpPlugins)
- 中望适配:源码里的 #pragma comment(lib,"rxapi.lib") 在中望下没有这个库(叫 ZwRx.lib),
  该 pragma 与导出名都按 #ifdef ZWCAD 分开(中望导出 zcrxGetApiVersion);
  frame 里 GetProcAddress 查插件版本号的函数名同样按平台分

验证:
- R230:cad_mcp_frame.arx 826 KB + cad_mcp_plugins.dll 866 KB,已自动部署
- Z2026:cad_mcp_frame.zrx 825 KB + cad_mcp_plugins.dll 864 KB,已自动部署
- 配置里 16 个 c++_dll 工具的工厂函数(CreateXxxTool)全部出现在 dll 导出表里
- 所有 .cpp/.h 为 UTF-8 with BOM

未做:没有在 CAD 里实跑。另外配置文件本身没有随构建部署(set_debug_post_build 只拷产物),
需要把 cad-mcp-frame.json(或改名 mcp_config.json)与 LISP 目录放到部署目录,
否则 frame 起来后 LoadAndRegister 失败会直接自关。详见 cad_mcp_frame-review.md。
2026-10-29 00:16:37 +08:00

163 lines
4.5 KiB
C++

#pragma once
#include <string>
#include <nlohmann/json.hpp>
class mcp_Tool_Utility
{
public:
static std::wstring utf8ToWString(const std::string &utf8Str)
{
if (utf8Str.empty()) return std::wstring();
// 第一次调用获取需要的宽字符缓冲区大小
int sizeNeeded = ::MultiByteToWideChar(CP_UTF8, 0, &utf8Str[0], (int)utf8Str.size(), NULL, 0);
std::wstring wstrTo(sizeNeeded, 0);
// 第二次调用真正进行转换
::MultiByteToWideChar(CP_UTF8, 0, &utf8Str[0], (int)utf8Str.size(), &wstrTo[0], sizeNeeded);
return wstrTo;
}
static std::string WStringToUtf8(const std::wstring &wstr)
{
if (wstr.empty()) return std::string();
// 第一次调用获取需要的 UTF-8 缓冲区大小
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo(sizeNeeded, 0);
// 第二次调用真正进行转换
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], sizeNeeded, NULL, NULL);
return strTo;
}
// 1. 纯文本响应
static nlohmann::json make_text_result(const std::wstring &_text)
{
std::string text = WStringToUtf8(_text);
return {
{ "result", {
{ "content", {
{ { "type", "text" }, { "text", text } }
} }
} }
};
}
// 2. 图片响应 (传入 base64 编码的图片数据)
static nlohmann::json make_image_result(const std::string &base64Data
, const std::string &mimeType = "image/png")
{
return {
{ "result", {
{ "content", {
{ { "type", "image" }, { "data", base64Data }, { "mimeType", mimeType } }
} }
} }
};
}
// 3. 图文混合响应 (文字汇报结果 + 附带 CAD 视口截图)
static nlohmann::json make_mixed_result(const std::wstring &_text
, const std::string &base64Data
, const std::string &mimeType = "image/png")
{
std::string text = WStringToUtf8(_text);
return {
{ "result", {
{ "content", {
{ { "type", "text" }, { "text", text } },
{ { "type", "image" }, { "data", base64Data }, { "mimeType", mimeType } }
} }
} }
};
}
static nlohmann::json make_error(int code, const std::wstring &_message)
{
std::string message = WStringToUtf8(_message);
return {
{ "error",{
{ "code", code },
{ "message", message }
} }
};
}
};
class mcp_Tool
{
protected:
nlohmann::json m_config;
std::string m_abiBuffer; // 存在于对象实例自己的堆内存中,确保指针生命周期安全
public:
virtual ~mcp_Tool() = default;
void SetConfig(const nlohmann::json &config) { m_config = config; }
// 兜底三剑客:当 JSON 中不写这些字段时,才调用这里(均可选重载)
virtual std::string private_name() const { return "unnamed_tool"; }
virtual std::string private_description() const { return ""; }
virtual nlohmann::json private_input_schema() const { return nlohmann::json::object(); }
// 统一由框架接管 Name 的获取 (供路由字典 mcp_Tools::Register 调用)
virtual std::string GetName() const final { return m_config.value("name", private_name()); }
// 统一由框架接管 ToLLM 组装
virtual nlohmann::json ToLLM() const final
{
nlohmann::json tool;
tool["name"] = GetName();
std::string desc = m_config.value("description", "");
tool["description"] = desc.empty() ? private_description() : desc;
if (m_config.contains("inputSchema") && !m_config["inputSchema"].empty())
{
tool["inputSchema"] = m_config["inputSchema"];
}
else
{
tool["inputSchema"] = private_input_schema();
}
return tool;
}
// 整个类唯一一个纯虚函数:全心全意干活!
virtual nlohmann::json Execute(const nlohmann::json &args) = 0;
// 安全配置注入
virtual void SetConfigAbiSafe(const char* config_json_str)
{
// 在 DLL 内部的安全环境中完成字符串到 JSON 的反序列化
m_config = nlohmann::json::parse(config_json_str);
}
// 安全执行路由
virtual const char* ExecuteAbiSafe(const char* args_json_str)
{
// 1. 在 DLL 的上下文中,将传入的 C 字符串转为 JSON
nlohmann::json args = nlohmann::json::parse(args_json_str);
// 2. 调用真实的业务逻辑
nlohmann::json result = Execute(args);
// 3. 将结果序列化为字符串,存入属于该 DLL 的内存缓冲区
m_abiBuffer = result.dump();
// 4. 返回安全的 C 指针
return m_abiBuffer.c_str();
}
};
#define EXPORT_MCP_TOOL(ToolClass, FactoryName) \
extern "C" __declspec(dllexport) mcp_Tool* FactoryName() \
{ \
return new ToolClass(); \
} \
extern "C" __declspec(dllexport) void Destroy_##FactoryName(mcp_Tool* pTool) \
{ \
delete pTool; \
}