重构以加强 abi 的兼容性
This commit is contained in:
gjm committed 2026-10-03 00:24:32 +08:00
1 parent a7eaa8483f
commit c0966ad334
5 files changed
+77 -35

No files matched your search

+58 -5
View File
@@ -5,9 +5,37 @@
class mcp_Tool_Utility class mcp_Tool_Utility
{ {
public: public:
// 1. 纯文本响应
static nlohmann::json make_text_result(const std::string &text) 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 { return {
{ "result", { { "result", {
{ "content", { { "content", {
@@ -31,10 +59,11 @@ public:
} }
// 3. 图文混合响应 (文字汇报结果 + 附带 CAD 视口截图) // 3. 图文混合响应 (文字汇报结果 + 附带 CAD 视口截图)
static nlohmann::json make_mixed_result(const std::string &text static nlohmann::json make_mixed_result(const std::wstring &_text
, const std::string &base64Data , const std::string &base64Data
, const std::string &mimeType = "image/png") , const std::string &mimeType = "image/png")
{ {
std::string text = WStringToUtf8(_text);
return { return {
{ "result", { { "result", {
{ "content", { { "content", {
@@ -45,8 +74,9 @@ public:
}; };
} }
static nlohmann::json make_error(int code, const std::string &message) static nlohmann::json make_error(int code, const std::wstring &_message)
{ {
std::string message = WStringToUtf8(_message);
return { return {
{ "error",{ { "error",{
{ "code", code }, { "code", code },
@@ -60,7 +90,7 @@ class mcp_Tool
{ {
protected: protected:
nlohmann::json m_config; nlohmann::json m_config;
std::string m_abiBuffer; // 存在于对象实例自己的堆内存中,确保指针生命周期安全
public: public:
virtual ~mcp_Tool() = default; virtual ~mcp_Tool() = default;
@@ -97,6 +127,29 @@ public:
// 整个类唯一一个纯虚函数:全心全意干活! // 整个类唯一一个纯虚函数:全心全意干活!
virtual nlohmann::json Execute(const nlohmann::json &args) = 0; 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) \ #define EXPORT_MCP_TOOL(ToolClass, FactoryName) \
+4 -3
View File
@@ -122,7 +122,9 @@ void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std::
if (pRealTool) if (pRealTool)
{ {
// 给真正的 DLL 实例也注入配置,以防第三方需要在内部读取 // 给真正的 DLL 实例也注入配置,以防第三方需要在内部读取
pRealTool->SetConfig(toolConfig); std::string cfgStr = toolConfig.dump();
pRealTool->SetConfigAbiSafe(cfgStr.c_str());
//pRealTool->SetConfig(toolConfig);
// 实例化代理类,接管生命周期 // 实例化代理类,接管生命周期
auto pProxy = std::make_shared<mcp_Tool_DllProxy>(hMod, pRealTool, destroyFn); auto pProxy = std::make_shared<mcp_Tool_DllProxy>(hMod, pRealTool, destroyFn);
@@ -145,7 +147,6 @@ void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std::
#pragma endregion mcp_ConfigParser #pragma endregion mcp_ConfigParser
#pragma region mcp_Tools #pragma region mcp_Tools
// 注册工具 // 注册工具
void mcp_Tools::Register(std::shared_ptr<mcp_Tool> pTool) void mcp_Tools::Register(std::shared_ptr<mcp_Tool> pTool)
@@ -177,7 +178,7 @@ nlohmann::json mcp_Tools::Call(const std::string &name, const nlohmann::json &ar
} }
// 找不到工具时,返回标准的 JSON-RPC 错误对象 // 找不到工具时,返回标准的 JSON-RPC 错误对象
return mcp_Tool_Utility::make_error(-32601, "Specified tool not found: " + name); return mcp_Tool_Utility::make_error(-32601, L"Specified tool not found: " + mcp_utils::utf8ToWString(name));
} }
void mcp_Tools::print() void mcp_Tools::print()
+10 -7
View File
@@ -46,14 +46,14 @@ nlohmann::json mcp_Tool_LispInline::Execute(const nlohmann::json &args)
std::wstring finalScript; std::wstring finalScript;
if (!ReplaceTemplateArgs(tpl, args, finalScript)) if (!ReplaceTemplateArgs(tpl, args, finalScript))
return mcp_Tool_Utility::make_error(-32603, "Script template replacement failed."); return mcp_Tool_Utility::make_error(-32603, L"Script template replacement failed.");
finalScript += _T(" "); // 尾部加空格,等同于在 CAD 命令行敲回车 finalScript += _T(" "); // 尾部加空格,等同于在 CAD 命令行敲回车
// 异步推送到 AutoCAD 命令行执行 // 异步推送到 AutoCAD 命令行执行
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str()); acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
return mcp_Tool_Utility::make_text_result("LISP inline command issued for execution (asynchronous)"); return mcp_Tool_Utility::make_text_result(L"LISP inline command issued for execution (asynchronous)");
} }
#pragma endregion LispInline #pragma endregion LispInline
@@ -66,7 +66,7 @@ nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
if (!fs::exists(filePath)) if (!fs::exists(filePath))
{ {
return mcp_Tool_Utility::make_error(-32603, "LISP file does not exist: " + filePath.string()); return mcp_Tool_Utility::make_error(-32603, L"LISP file does not exist: " + mcp_utils::utf8ToWString(filePath.string()));
} }
std::string callTpl = m_config["backend"].value("call_template", ""); std::string callTpl = m_config["backend"].value("call_template", "");
@@ -76,7 +76,7 @@ nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
std::wstring executeCmd; std::wstring executeCmd;
if (!ReplaceTemplateArgs(callTpl, args, executeCmd)) if (!ReplaceTemplateArgs(callTpl, args, executeCmd))
{ {
return mcp_Tool_Utility::make_error(-32603, "Script template replacement failed."); return mcp_Tool_Utility::make_error(-32603, L"Script template replacement failed.");
} }
executeCmd += _T(" "); executeCmd += _T(" ");
@@ -84,7 +84,7 @@ nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str()); acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
return mcp_Tool_Utility::make_text_result("LISP external file loaded and issued for execution (asynchronous)"); return mcp_Tool_Utility::make_text_result(L"LISP external file loaded and issued for execution (asynchronous)");
} }
#pragma endregion LispFile #pragma endregion LispFile
@@ -106,9 +106,12 @@ nlohmann::json mcp_Tool_DllProxy::Execute(const nlohmann::json &args)
{ {
if (m_pRealTool) if (m_pRealTool)
{ {
return m_pRealTool->Execute(args); // 完美转发 std::string argStr = args.dump();
//return m_pRealTool->Execute(args); // 完美转发
const char* retStr = m_pRealTool->ExecuteAbiSafe(argStr.c_str());
return nlohmann::json::parse(retStr);
} }
return mcp_Tool_Utility::make_error(-32603, "Underlying DLL tool instance is invalid"); return mcp_Tool_Utility::make_error(-32603, L"Underlying DLL tool instance is invalid");
} }
#pragma endregion DllProxy #pragma endregion DllProxy
+3 -18
View File
@@ -3,33 +3,18 @@
#include <Windows.h> #include <Windows.h>
#include <tchar.h> #include <tchar.h>
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
#include "mcp_plugin_api.h"
namespace mcp_utils namespace mcp_utils
{ {
std::wstring utf8ToWString(const std::string &utf8Str) std::wstring utf8ToWString(const std::string &utf8Str)
{ {
if (utf8Str.empty()) return std::wstring(); return mcp_Tool_Utility::utf8ToWString(utf8Str);
// 第一次调用获取需要的宽字符缓冲区大小
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;
} }
std::string WStringToUtf8(const std::wstring &wstr) std::string WStringToUtf8(const std::wstring &wstr)
{ {
if (wstr.empty()) return std::string(); return mcp_Tool_Utility::WStringToUtf8(wstr);
// 第一次调用获取需要的 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;
} }
bool mcp_utils::hasFileExist(const std::wstring &filePath) { return _taccess(filePath.c_str(), 0) == 0; } bool mcp_utils::hasFileExist(const std::wstring &filePath) { return _taccess(filePath.c_str(), 0) == 0; }
+2 -2
View File
@@ -61,11 +61,11 @@ public:
// 5. 返回标准 MCP 结果,如果失败可以根据 es 的值返回 make_error // 5. 返回标准 MCP 结果,如果失败可以根据 es 的值返回 make_error
if (es == Acad::eOk) if (es == Acad::eOk)
{ {
return mcp_Tool_Utility::make_text_result("C++底层接口已成功在模型空间生成圆形!"); return mcp_Tool_Utility::make_text_result(L"C++底层接口已成功在模型空间生成圆形!");
} }
else else
{ {
return mcp_Tool_Utility::make_error((int)es, "ObjectARX 内部错误"); return mcp_Tool_Utility::make_error((int)es, L"ObjectARX 内部错误");
} }
} }
}; };