#include "mcp_ConfigParser.h" #include "KTCad.h" #include #include "mcp_tool.h" #include "mcp_utils.h" #include "ghc/filesystem.hpp" #include "rxregsvc.h" #pragma region mcp_ConfigParser bool mcp_ConfigParser::LoadAndRegister(const std::wstring &jsonFilePath, mcp_Tools ®istry) { std::ifstream ifs(jsonFilePath); if (!ifs.is_open()) return false; nlohmann::json config; try { ifs >> config; } catch (...) { return false; } // 防御非法 JSON fs::path pluginPath = mcp_utils::relative2absolute(config["global_settings"].value("plugin_directory", "./mcp_plugins")); fs::path lispPath = mcp_utils::relative2absolute(config["global_settings"].value("lisp_directory", "./mcp_lisp")); if (!fs::exists(lispPath)) AfxMessageBox(CString(_T("[MCP Server]Warning: The LISP script directory does not exist; the associated tools will not load!\n")) + pluginPath.c_str()); if (!fs::exists(pluginPath)) AfxMessageBox(CString(_T("[MCP Server]Warning: The plugin DLL directory does not exist; the associated tools will not load!\n")) + pluginPath.c_str()); for (const auto &toolConfig : config["tools"]) { std::string type = toolConfig["backend"].value("type", ""); if (type == "lisp_inline") { auto pTool = std::make_shared(); pTool->SetConfig(toolConfig); registry.Register(pTool); } else if (type == "lisp_file" && fs::exists(lispPath)) { auto pTool = std::make_shared(lispPath); pTool->SetConfig(toolConfig); registry.Register(pTool); } else if (type == "c++_dll" && fs::exists(pluginPath)) { LoadDllTool(toolConfig, pluginPath, registry); } } return true; } // 中望平台用的是 zcrx* 家族的符号(ARX 下 acrxEntryPoint 会被 SDK 宏改名, // 但这里显式声明的版本号函数不会,所以按平台分开) #ifdef ZWCAD extern "C" int zcrxGetApiVersion(); #define MCP_OWN_API_VERSION() zcrxGetApiVersion() #else extern "C" int acrxGetApiVersion(); #define MCP_OWN_API_VERSION() acrxGetApiVersion() #endif void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std::wstring &pluginDir, mcp_Tools ®istry) { fs::path dllPath = mcp_utils::relative2absolute(toolConfig["backend"].value("dll_name", "") , fs::path(pluginDir)); std::string factoryName = toolConfig["backend"].value("factory_function", ""); if (!fs::exists(dllPath)) { AfxMessageBox(CString(_T("[MCP Server]Warning: The DLL file does not exist, the associated tools will not load!\n")) + dllPath.c_str()); return; } HMODULE hMod = LoadLibrary(dllPath.c_str()); CString verStr; verStr.Format(_T("Major ver:%d, Minor ver:%d"), ARX, SUB_ARX); CString ver_errMsg; ver_errMsg.Format( CString(_T("[MCP Server]Error: Failed to load the DLL. ")) + CString(_T("Please check whether the version of the ObjectARX SDK used by ")) + CString("the DLL matches the current ARX version(%s).\n"), verStr ); if (!hMod) { AfxMessageBox(ver_errMsg); return; } // 1. 定义版本校验函数指针 typedef int (*GetApiVersionFunc)(); #ifdef ZWCAD GetApiVersionFunc getVerFn = (GetApiVersionFunc)GetProcAddress(hMod, "zcrxGetApiVersion"); #else GetApiVersionFunc getVerFn = (GetApiVersionFunc)GetProcAddress(hMod, "acrxGetApiVersion"); #endif // 2. 核心拦截逻辑 if (!getVerFn) { // 连这个函数都没导出,说明根本不是合法的 ObjectARX/MCP 插件 AfxMessageBox(CString( CString(_T("The ObjectARX version of the DLL could not be determined.")) + CString(_T("Please ensure that the acrxGetApiVersion function is exported.")) )); FreeLibrary(hMod); return; } // 拿 DLL 编译时的 SDK 版本,和基座当前运行的 SDK 版本进行比对 int arx_version(MCP_OWN_API_VERSION()); int dll_version(getVerFn()); if (arx_version != dll_version) { // 平台大版本不匹配,强行加载必定闪崩,果断踢掉! FreeLibrary(hMod); AfxMessageBox(ver_errMsg); return; } typedef mcp_Tool *(*CreateFunc)(); typedef void (*DestroyFunc)(mcp_Tool *); std::string destroyName = "Destroy_" + factoryName; CreateFunc createFn = (CreateFunc)GetProcAddress(hMod, factoryName.c_str()); DestroyFunc destroyFn = (DestroyFunc)GetProcAddress(hMod, destroyName.c_str()); if (createFn && destroyFn) { mcp_Tool *pRealTool = createFn(); if (pRealTool) { // 给真正的 DLL 实例也注入配置,以防第三方需要在内部读取 std::string cfgStr = toolConfig.dump(); pRealTool->SetConfigAbiSafe(cfgStr.c_str()); //pRealTool->SetConfig(toolConfig); // 实例化代理类,接管生命周期 auto pProxy = std::make_shared(hMod, pRealTool, destroyFn); // 代理类继承自基类,也需要配置来处理 GetName 和 ToLLM pProxy->SetConfig(toolConfig); registry.Register(pProxy); } else { FreeLibrary(hMod); } } else { FreeLibrary(hMod); // 导出函数找不到,直接卸载 } } #pragma endregion mcp_ConfigParser #pragma region mcp_Tools // 注册工具 void mcp_Tools::Register(std::shared_ptr pTool) { if (pTool != nullptr) { m_tools[pTool->GetName()] = pTool; } } // 组装所有工具的描述,返回 JSON 数组 (用于 tools/list) nlohmann::json mcp_Tools::ToLLM() const { nlohmann::json arr = nlohmann::json::array(); for (const auto &pair : m_tools) { arr.push_back(pair.second->ToLLM()); } return arr; } // 路由分发执行指令 (用于 tools/call) nlohmann::json mcp_Tools::Call(const std::string &name, const nlohmann::json &args) { auto it = m_tools.find(name); if (it != m_tools.end()) { return it->second->Execute(args); } // 找不到工具时,返回标准的 JSON-RPC 错误对象 return mcp_Tool_Utility::make_error(-32601, L"Specified tool not found: " + mcp_utils::utf8ToWString(name)); } void mcp_Tools::print() { for (const auto &pair : m_tools) { acutPrintf(_T("Tool: %s Already registered.\n"), mcp_utils::utf8ToWString(pair.first.c_str()).c_str()); } } void mcp_Tools::clear() { m_tools.clear(); } #pragma endregion mcp_Tools