refactor
重构 mcp-server 功能
This commit is contained in:
1 parent
5d6f1c74d1
commit
1435b864f8
12 files changed
+232
-95
No files matched your search
@@ -38,9 +38,12 @@ extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
|
|||||||
case AcRx::kInitAppMsg:
|
case AcRx::kInitAppMsg:
|
||||||
acrxDynamicLinker->unlockApplication(pkt);
|
acrxDynamicLinker->unlockApplication(pkt);
|
||||||
acrxDynamicLinker->registerAppMDIAware(pkt);
|
acrxDynamicLinker->registerAppMDIAware(pkt);
|
||||||
|
if (!mcp::init_mcp_server())
|
||||||
|
return AcRx::kRetError;
|
||||||
InitApplication();
|
InitApplication();
|
||||||
break;
|
break;
|
||||||
case AcRx::kUnloadAppMsg:
|
case AcRx::kUnloadAppMsg:
|
||||||
|
mcp::close_mcp_server();
|
||||||
UnloadApplication();
|
UnloadApplication();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -50,8 +53,6 @@ extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
|
|||||||
void CR_SCGXCmdFnc();
|
void CR_SCGXCmdFnc();
|
||||||
void InitApplication()
|
void InitApplication()
|
||||||
{
|
{
|
||||||
mcp::init_mcp_server();
|
|
||||||
|
|
||||||
acrxBuildClassHierarchy();
|
acrxBuildClassHierarchy();
|
||||||
AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc);
|
AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc);
|
||||||
|
|
||||||
@@ -60,7 +61,6 @@ void InitApplication()
|
|||||||
void UnloadApplication()
|
void UnloadApplication()
|
||||||
{
|
{
|
||||||
acedRegCmds->removeGroup(_T("SHCRACKTOOLS"));
|
acedRegCmds->removeGroup(_T("SHCRACKTOOLS"));
|
||||||
mcp::close_mcp_server();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddCommand(const TCHAR* cmdGroup, const TCHAR* cmdInt, const TCHAR* cmdLoc,
|
void AddCommand(const TCHAR* cmdGroup, const TCHAR* cmdInt, const TCHAR* cmdLoc,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"global_settings": {
|
"global_settings": {
|
||||||
"plugin_directory": "./mcp_plugins",
|
"plugin_directory": "./mcp_plugins",
|
||||||
"lisp_directory": "./mcp_lisp",
|
"lisp_directory": "./mcp_plugins",
|
||||||
"hot_reload": true
|
"hot_reload": true
|
||||||
},
|
},
|
||||||
"tools": [
|
"tools": [
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
project(AutoAgent)
|
project(AutoAgent)
|
||||||
|
|
||||||
find_package(nlohmann_json CONFIG REQUIRED)
|
find_package(nlohmann_json CONFIG REQUIRED)
|
||||||
|
find_package(ghc_filesystem CONFIG REQUIRED)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
"AutoAgent.cpp"
|
"AutoAgent.cpp"
|
||||||
@@ -12,6 +13,7 @@ set(SOURCES
|
|||||||
"mcp_server.cpp"
|
"mcp_server.cpp"
|
||||||
"mcp_tool.cpp"
|
"mcp_tool.cpp"
|
||||||
"mcp_ConfigParser.cpp"
|
"mcp_ConfigParser.cpp"
|
||||||
|
"mcp_utils.cpp"
|
||||||
)
|
)
|
||||||
add_def_file_name(SOURCES "${PROJECT_NAME}")
|
add_def_file_name(SOURCES "${PROJECT_NAME}")
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#include "mcp_ConfigParser.h"
|
#include "mcp_ConfigParser.h"
|
||||||
|
#include "cadsdk.h"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "mcp_tool.h"
|
#include "mcp_tool.h"
|
||||||
|
#include "mcp_utils.h"
|
||||||
|
#include "ghc/filesystem.hpp"
|
||||||
|
|
||||||
#pragma region mcp_ConfigParser
|
#pragma region mcp_ConfigParser
|
||||||
bool mcp_ConfigParser::LoadAndRegister(const std::string &jsonFilePath, mcp_Tools ®istry)
|
bool mcp_ConfigParser::LoadAndRegister(const std::wstring &jsonFilePath, mcp_Tools ®istry)
|
||||||
{
|
{
|
||||||
std::ifstream ifs(jsonFilePath);
|
std::ifstream ifs(jsonFilePath);
|
||||||
if (!ifs.is_open()) return false;
|
if (!ifs.is_open()) return false;
|
||||||
@@ -12,8 +15,15 @@ bool mcp_ConfigParser::LoadAndRegister(const std::string &jsonFilePath, mcp_Tool
|
|||||||
try { ifs >> config; }
|
try { ifs >> config; }
|
||||||
catch (...) { return false; } // 防御非法 JSON
|
catch (...) { return false; } // 防御非法 JSON
|
||||||
|
|
||||||
std::string pluginDir = config["global_settings"].value("plugin_directory", "./mcp_plugins");
|
fs::path pluginPath
|
||||||
std::string lispDir = config["global_settings"].value("lisp_directory", "./mcp_lisp");
|
= 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"])
|
for (const auto &toolConfig : config["tools"])
|
||||||
{
|
{
|
||||||
@@ -25,28 +35,35 @@ bool mcp_ConfigParser::LoadAndRegister(const std::string &jsonFilePath, mcp_Tool
|
|||||||
pTool->SetConfig(toolConfig);
|
pTool->SetConfig(toolConfig);
|
||||||
registry.Register(pTool);
|
registry.Register(pTool);
|
||||||
}
|
}
|
||||||
else if (type == "lisp_file")
|
else if (type == "lisp_file" && fs::exists(lispPath))
|
||||||
{
|
{
|
||||||
auto pTool = std::make_shared<mcp_Tool_LispFile>(lispDir);
|
auto pTool = std::make_shared<mcp_Tool_LispFile>(lispPath);
|
||||||
pTool->SetConfig(toolConfig);
|
pTool->SetConfig(toolConfig);
|
||||||
registry.Register(pTool);
|
registry.Register(pTool);
|
||||||
}
|
}
|
||||||
else if (type == "c++_dll")
|
else if (type == "c++_dll" && fs::exists(pluginPath))
|
||||||
{
|
{
|
||||||
LoadDllTool(toolConfig, pluginDir, registry);
|
|
||||||
|
LoadDllTool(toolConfig, pluginPath, registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std::string &pluginDir, mcp_Tools ®istry)
|
void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std::wstring &pluginDir, mcp_Tools ®istry)
|
||||||
{
|
{
|
||||||
std::string dllName = toolConfig["backend"].value("dll_name", "");
|
fs::path dllPath
|
||||||
|
= mcp_utils::relative2absolute(toolConfig["backend"].value("dll_name", "")
|
||||||
|
, fs::path(pluginDir));
|
||||||
std::string factoryName = toolConfig["backend"].value("factory_function", "");
|
std::string factoryName = toolConfig["backend"].value("factory_function", "");
|
||||||
|
|
||||||
std::string fullPath = pluginDir + "\\" + dllName; // Windows 环境下拼接 DLL 路径
|
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 = LoadLibraryA(fullPath.c_str());
|
HMODULE hMod = LoadLibrary(dllPath.c_str());
|
||||||
if (!hMod) return;
|
if (!hMod) return;
|
||||||
|
|
||||||
typedef mcp_Tool *(*CreateFunc)();
|
typedef mcp_Tool *(*CreateFunc)();
|
||||||
@@ -117,7 +134,20 @@ nlohmann::json mcp_Tools::Call(const std::string &name, const nlohmann::json &ar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 找不到工具时,返回标准的 JSON-RPC 错误对象
|
// 找不到工具时,返回标准的 JSON-RPC 错误对象
|
||||||
return mcp_Tool_Utility::make_error(-32601, "未找到指定的 Tool: " + name);
|
return mcp_Tool_Utility::make_error(-32601, "Specified tool not found: " + 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
|
#pragma endregion mcp_Tools
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,17 @@ public:
|
|||||||
|
|
||||||
// 路由分发执行指令 (用于 tools/call)
|
// 路由分发执行指令 (用于 tools/call)
|
||||||
nlohmann::json Call(const std::string &name, const nlohmann::json &args);
|
nlohmann::json Call(const std::string &name, const nlohmann::json &args);
|
||||||
|
|
||||||
|
void print();
|
||||||
|
|
||||||
|
void clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
class mcp_ConfigParser
|
class mcp_ConfigParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool LoadAndRegister(const std::string &jsonFilePath, mcp_Tools ®istry);
|
static bool LoadAndRegister(const std::wstring &jsonFilePath, mcp_Tools ®istry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void LoadDllTool(const nlohmann::json &toolConfig, const std::string &pluginDir, mcp_Tools ®istry);
|
static void LoadDllTool(const nlohmann::json &toolConfig, const std::wstring &pluginDir, mcp_Tools ®istry);
|
||||||
};
|
};
|
||||||
@@ -6,11 +6,15 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include "mcp_ConfigParser.h"
|
#include "mcp_ConfigParser.h"
|
||||||
|
#include "mcp_utils.h"
|
||||||
|
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
#define WM_MCP_EXECUTE_TASK (WM_USER + 2048)
|
#define WM_MCP_EXECUTE_TASK (WM_USER + 2048)
|
||||||
extern HINSTANCE _hdllInstance;
|
extern HINSTANCE _hdllInstance;
|
||||||
|
|
||||||
|
#define MCP_SERVER_PORT 8080
|
||||||
|
#define MCP_SERVER_CONFIG _T("mcp_config.json")
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::thread g_serverThread;
|
std::thread g_serverThread;
|
||||||
@@ -142,7 +146,7 @@ namespace
|
|||||||
sockaddr_in serverAddr = { 0 };
|
sockaddr_in serverAddr = { 0 };
|
||||||
serverAddr.sin_family = AF_INET;
|
serverAddr.sin_family = AF_INET;
|
||||||
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
|
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
|
||||||
serverAddr.sin_port = htons(8080);
|
serverAddr.sin_port = htons(MCP_SERVER_PORT);
|
||||||
|
|
||||||
bind(g_listenSocket, (sockaddr *)&serverAddr, sizeof(serverAddr));
|
bind(g_listenSocket, (sockaddr *)&serverAddr, sizeof(serverAddr));
|
||||||
listen(g_listenSocket, SOMAXCONN);
|
listen(g_listenSocket, SOMAXCONN);
|
||||||
@@ -197,6 +201,27 @@ namespace
|
|||||||
|
|
||||||
namespace mcp
|
namespace mcp
|
||||||
{
|
{
|
||||||
|
bool close_mcp_server()
|
||||||
|
{
|
||||||
|
g_bRunning = false;
|
||||||
|
if (g_serverThread.joinable())
|
||||||
|
{
|
||||||
|
g_serverThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_hMsgWnd != NULL)
|
||||||
|
{
|
||||||
|
DestroyWindow(g_hMsgWnd);
|
||||||
|
UnregisterClass(g_szMsgClassName, _hdllInstance);
|
||||||
|
g_hMsgWnd = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mcpTools.clear();
|
||||||
|
|
||||||
|
acutPrintf(_T("\n[MCP Server] The plugin has been uninstalled, and the thread has been closed.\n"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool init_mcp_server()
|
bool init_mcp_server()
|
||||||
{
|
{
|
||||||
// 1. 在主线程注册并创建 Message-Only 窗口
|
// 1. 在主线程注册并创建 Message-Only 窗口
|
||||||
@@ -214,48 +239,35 @@ namespace mcp
|
|||||||
0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0,
|
||||||
HWND_MESSAGE, // 纯消息窗口,无界面、无感知
|
HWND_MESSAGE, // 纯消息窗口,无界面、无感知
|
||||||
NULL,
|
NULL,
|
||||||
GetModuleHandle(NULL),
|
_hdllInstance,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
g_bRunning = true;
|
g_bRunning = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
g_serverThread = std::thread(TcpServerLoop);
|
g_serverThread = std::thread(TcpServerLoop);
|
||||||
acutPrintf(_T("\n[MCP Server] 后台 TCP 监听线程已启动 (端口: 8080)...\n"));
|
acutPrintf(_T("\n[MCP Server] Background TCP listening thread started (Port: 8080)...\n"));
|
||||||
}
|
}
|
||||||
catch (const std::system_error &e)
|
catch (const std::system_error &e)
|
||||||
{
|
{
|
||||||
// 线程创建失败,重置状态标志并输出错误
|
// 线程创建失败,重置状态标志并输出错误
|
||||||
g_bRunning = false;
|
g_bRunning = false;
|
||||||
// e.what() 返回的是 const char*,所以在 acutPrintf 中要用 %hs 而不是 %s 或 %ls
|
// e.what() 返回的是 const char*,所以在 acutPrintf 中要用 %hs 而不是 %s 或 %ls
|
||||||
acutPrintf(_T("\n[MCP Server] 致命错误:无法创建后台监听线程!原因: %hs\n"), e.what());
|
acutPrintf(_T("\n[MCP Server] Fatal error: Unable to create background listening thread! Reason: %hs\n"), e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果监听跑起来了,则注册tools
|
// 如果监听跑起来了,则注册tools
|
||||||
if (g_bRunning)
|
if (g_bRunning)
|
||||||
{
|
{
|
||||||
mcp_ConfigParser::LoadAndRegister("mcp_config.json", g_mcpTools);
|
if (!mcp_ConfigParser::LoadAndRegister(mcp_utils::getModelDirPath() / MCP_SERVER_CONFIG, g_mcpTools))
|
||||||
|
{
|
||||||
|
acutPrintf(_T("\n[MCP Server] Warning: Failed to load tool configuration!\n[MCP Server] Automatic Shutdown.\n"));
|
||||||
|
g_bRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mcpTools.print();
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_bRunning;
|
return g_bRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool close_mcp_server()
|
|
||||||
{
|
|
||||||
g_bRunning = false;
|
|
||||||
if (g_serverThread.joinable())
|
|
||||||
{
|
|
||||||
g_serverThread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_hMsgWnd != NULL)
|
|
||||||
{
|
|
||||||
DestroyWindow(g_hMsgWnd);
|
|
||||||
UnregisterClass(g_szMsgClassName, _hdllInstance);
|
|
||||||
g_hMsgWnd = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
acutPrintf(_T("\n[MCP Server] 插件已卸载,线程已关闭。\n"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+36
-33
@@ -1,24 +1,14 @@
|
|||||||
#include "mcp_tool.h"
|
#include "cadsdk.h"
|
||||||
#include "cadsdk.h"
|
#include "mcp_tool.h"
|
||||||
|
#include "mcp_utils.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// UTF-8 转 宽字符 (std::wstring)
|
bool ReplaceTemplateArgs(const std::string &_tpl, const nlohmann::json &args, std::wstring &outScript)
|
||||||
std::wstring Utf8ToWString(const std::string &utf8Str)
|
|
||||||
{
|
{
|
||||||
if (utf8Str.empty()) return std::wstring();
|
if (_tpl.empty()) return false;
|
||||||
|
|
||||||
// 第一次调用获取需要的宽字符缓冲区大小
|
std::string tpl(_tpl);
|
||||||
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 ReplaceTemplateArgs(std::string tpl, const nlohmann::json &args)
|
|
||||||
{
|
|
||||||
for (auto it = args.begin(); it != args.end(); ++it)
|
for (auto it = args.begin(); it != args.end(); ++it)
|
||||||
{
|
{
|
||||||
std::string placeholder = "{" + it.key() + "}";
|
std::string placeholder = "{" + it.key() + "}";
|
||||||
@@ -44,7 +34,8 @@ namespace
|
|||||||
pos += valueStr.length();
|
pos += valueStr.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tpl;
|
outScript = mcp_utils::utf8ToWString(tpl);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,36 +43,48 @@ namespace
|
|||||||
nlohmann::json mcp_Tool_LispInline::Execute(const nlohmann::json &args)
|
nlohmann::json mcp_Tool_LispInline::Execute(const nlohmann::json &args)
|
||||||
{
|
{
|
||||||
std::string tpl = m_config["backend"].value("script_template", "");
|
std::string tpl = m_config["backend"].value("script_template", "");
|
||||||
std::string finalScript = ReplaceTemplateArgs(tpl, args) + " "; // 尾部加空格,等同于在 CAD 命令行敲回车
|
std::wstring finalScript;
|
||||||
std::wstring wScript = Utf8ToWString(finalScript);
|
|
||||||
|
if (!ReplaceTemplateArgs(tpl, args, finalScript))
|
||||||
|
return mcp_Tool_Utility::make_error(-32603, "Script template replacement failed.");
|
||||||
|
|
||||||
|
finalScript += _T(" "); // 尾部加空格,等同于在 CAD 命令行敲回车
|
||||||
|
|
||||||
// 异步推送到 AutoCAD 命令行执行
|
// 异步推送到 AutoCAD 命令行执行
|
||||||
acDocManager->sendStringToExecute(acDocManager->curDocument(), wScript.c_str());
|
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
|
||||||
|
|
||||||
return mcp_Tool_Utility::make_text_result("LISP内联指令已下发执行 (异步)");
|
return mcp_Tool_Utility::make_text_result("LISP inline command issued for execution (asynchronous)");
|
||||||
}
|
}
|
||||||
#pragma endregion mcp_Tool_LispInline
|
#pragma endregion mcp_Tool_LispInline
|
||||||
|
|
||||||
#pragma region mcp_Tool_LispFile
|
#pragma region mcp_Tool_LispFile
|
||||||
nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
|
nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
|
||||||
{
|
{
|
||||||
std::string fileName = m_config["backend"].value("file_name", "");
|
fs::path filePath
|
||||||
|
= mcp_utils::relative2absolute(m_config["backend"].value("file_name", "")
|
||||||
|
, fs::path(m_lispDir));
|
||||||
|
|
||||||
|
if (!fs::exists(filePath))
|
||||||
|
{
|
||||||
|
return mcp_Tool_Utility::make_error(-32603, "LISP file does not exist: " + filePath.string());
|
||||||
|
}
|
||||||
|
|
||||||
std::string callTpl = m_config["backend"].value("call_template", "");
|
std::string callTpl = m_config["backend"].value("call_template", "");
|
||||||
|
|
||||||
// 组装文件完整路径,并将反斜杠 \ 转换为 LISP 安全的正斜杠 /
|
|
||||||
std::string fullPath = m_lispDir + "/" + fileName;
|
|
||||||
std::replace(fullPath.begin(), fullPath.end(), '\\', '/');
|
|
||||||
|
|
||||||
// 组装前置加载命令,末尾的 nil 是为了防止文件不存在时 LISP 引擎报错弹窗
|
// 组装前置加载命令,末尾的 nil 是为了防止文件不存在时 LISP 引擎报错弹窗
|
||||||
std::string loadCmd = "(load \"" + fullPath + "\" nil) ";
|
std::wstring loadCmd = std::wstring(_T("(load \"")) + filePath.generic_wstring() + _T("\" nil) ");
|
||||||
std::string executeCmd = ReplaceTemplateArgs(callTpl, args) + " ";
|
std::wstring executeCmd;
|
||||||
|
if (!ReplaceTemplateArgs(callTpl, args, executeCmd))
|
||||||
|
{
|
||||||
|
return mcp_Tool_Utility::make_error(-32603, "Script template replacement failed.");
|
||||||
|
}
|
||||||
|
executeCmd += _T(" ");
|
||||||
|
|
||||||
std::string finalScript = loadCmd + executeCmd;
|
std::wstring finalScript = loadCmd + executeCmd;
|
||||||
std::wstring wScript = Utf8ToWString(finalScript);
|
|
||||||
|
|
||||||
acDocManager->sendStringToExecute(acDocManager->curDocument(), wScript.c_str());
|
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
|
||||||
|
|
||||||
return mcp_Tool_Utility::make_text_result("LISP外部文件已加载并下发执行 (异步)");
|
return mcp_Tool_Utility::make_text_result("LISP external file loaded and issued for execution (asynchronous)");
|
||||||
}
|
}
|
||||||
#pragma endregion mcp_Tool_LispFile
|
#pragma endregion mcp_Tool_LispFile
|
||||||
|
|
||||||
@@ -105,7 +108,7 @@ nlohmann::json mcp_Tool_DllProxy::Execute(const nlohmann::json &args)
|
|||||||
{
|
{
|
||||||
return m_pRealTool->Execute(args); // 完美转发
|
return m_pRealTool->Execute(args); // 完美转发
|
||||||
}
|
}
|
||||||
return mcp_Tool_Utility::make_error(-32603, "底层 DLL 工具实例已失效");
|
return mcp_Tool_Utility::make_error(-32603, "Underlying DLL tool instance is invalid");
|
||||||
}
|
}
|
||||||
#pragma endregion mcp_Tool_DllProxy
|
#pragma endregion mcp_Tool_DllProxy
|
||||||
|
|
||||||
@@ -13,10 +13,10 @@ public:
|
|||||||
class mcp_Tool_LispFile : public mcp_Tool
|
class mcp_Tool_LispFile : public mcp_Tool
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string m_lispDir;
|
std::wstring m_lispDir;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mcp_Tool_LispFile(const std::string& lispDir) : m_lispDir(lispDir) {}
|
mcp_Tool_LispFile(const std::wstring& lispDir) : m_lispDir(lispDir) {}
|
||||||
|
|
||||||
virtual nlohmann::json Execute(const nlohmann::json &args) override;
|
virtual nlohmann::json Execute(const nlohmann::json &args) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mcp_utils.h"
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <ghc/filesystem.hpp>
|
||||||
|
|
||||||
|
namespace mcp_utils
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcp_utils::hasFileExist(const std::wstring &filePath) { return _taccess(filePath.c_str(), 0) == 0; }
|
||||||
|
|
||||||
|
bool mcp_utils::hasDirExist(const std::wstring &filePath) { return _taccess(filePath.c_str(), 0) == 0; }
|
||||||
|
|
||||||
|
std::wstring getModelDir()
|
||||||
|
{
|
||||||
|
TCHAR szPath[MAX_PATH];
|
||||||
|
::GetModuleFileName(_hdllInstance, szPath, MAX_PATH);
|
||||||
|
ghc::filesystem::path path(szPath);
|
||||||
|
return path.parent_path().wstring();
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path getModelDirPath()
|
||||||
|
{
|
||||||
|
return fs::path(getModelDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path relative2absolute(const std::string &relativePath, const fs::path &rootPath)
|
||||||
|
{
|
||||||
|
return relative2absolute(fs::path(utf8ToWString(relativePath)), rootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path relative2absolute(const fs::path &relativePath, const fs::path &rootPath)
|
||||||
|
{
|
||||||
|
fs::path absolutePath = rootPath / relativePath;
|
||||||
|
return absolutePath.lexically_normal().wstring();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "ghc/filesystem.hpp"
|
||||||
|
|
||||||
|
namespace fs = ghc::filesystem;
|
||||||
|
|
||||||
|
namespace mcp_utils
|
||||||
|
{
|
||||||
|
std::wstring utf8ToWString(const std::string &utf8Str);
|
||||||
|
|
||||||
|
bool hasFileExist(const std::wstring &filePath);
|
||||||
|
bool hasDirExist(const std::wstring &filePath);
|
||||||
|
|
||||||
|
std::wstring getModelDir();
|
||||||
|
|
||||||
|
ghc::filesystem::path getModelDirPath();
|
||||||
|
|
||||||
|
fs::path relative2absolute(const std::string &relativePath
|
||||||
|
, const fs::path &rootPath = getModelDirPath());
|
||||||
|
|
||||||
|
fs::path relative2absolute(const fs::path &relativePath
|
||||||
|
, const fs::path &rootPath = getModelDirPath());
|
||||||
|
}
|
||||||
+30
-16
@@ -1,20 +1,34 @@
|
|||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# 模拟大模型生成的 JSON 指令
|
def send_request(req_data):
|
||||||
command = {
|
try:
|
||||||
"cmd": "draw_circle",
|
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
"x": 500.0,
|
client.connect(("127.0.0.1", 8080))
|
||||||
"y": 500.0,
|
client.sendall(json.dumps(req_data).encode('utf-8'))
|
||||||
"r": 250.0
|
response = client.recv(4096)
|
||||||
}
|
print(">>> 收到回复:")
|
||||||
|
print(json.dumps(json.loads(response.decode('utf-8')), indent=2, ensure_ascii=False))
|
||||||
|
client.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"连接失败: {e}")
|
||||||
|
|
||||||
try:
|
if __name__ == "__main__":
|
||||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
print("=== 测试 LISP 外部文件挂载 ===")
|
||||||
client.connect(("127.0.0.1", 8080))
|
request_call = {
|
||||||
# 发送序列化后的 JSON 字符串
|
"jsonrpc": "2.0",
|
||||||
client.sendall(json.dumps(command).encode('utf-8'))
|
"id": 3,
|
||||||
client.close()
|
"method": "tools/call",
|
||||||
print("指令发送成功,请查看 AutoCAD 屏幕。")
|
"params": {
|
||||||
except Exception as e:
|
"name": "draw_conference_table",
|
||||||
print(f"连接失败: {e}")
|
"arguments": {
|
||||||
|
"cx": 1000.0,
|
||||||
|
"cy": 1000.0,
|
||||||
|
"length": 1500.0,
|
||||||
|
"width": 600.0,
|
||||||
|
"chair_count": 4,
|
||||||
|
"layer": "MCP_CONF_ROOM"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send_request(request_call)
|
||||||
+2
-1
@@ -3,7 +3,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"nlohmann-json",
|
"nlohmann-json",
|
||||||
"curl"
|
"curl",
|
||||||
|
"ghc-filesystem"
|
||||||
],
|
],
|
||||||
"builtin-baseline": "127402f1c75bb3d5ff6bce04b285faa4930a5aca"
|
"builtin-baseline": "127402f1c75bb3d5ff6bce04b285faa4930a5aca"
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user