修改了目录和部分源文件名称
This commit is contained in:
gjm committed 2026-10-02 21:02:40 +08:00
1 parent 1435b864f8
commit cf87cfe93d
21 files changed
+8 -7

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
// StdAfx.cpp : source file that includes just the standard includes
// StdAfx.pch will be the pre-compiled header
// StdAfx.obj will contain the pre-compiled type information
#include "StdAfx.h"
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define WINVER 0x0600
#pragma warning(disable: 4786)
//- 'DEBUG workaround' below prevents the MFC or ATL #include-s from pulling
//- in "Afx.h" that would force the debug CRT through #pragma-s.
#if defined(_DEBUG) && !defined(AC_FULL_DEBUG)
#define _DEBUG_WAS_DEFINED
#undef _DEBUG
#pragma message ("Compiling MFC / STL / ATL header files in release mode.")
#endif
#pragma pack (push, 8)
#pragma warning(disable: 4786 4996)
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxole.h> // MFC OLE classes
#include <afxodlgs.h> // MFC OLE dialog classes
#include <afxdisp.h> // MFC Automation classes
#endif
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows Common Controls
#endif
#include <map>
#include <string>
#include <vector>
#include <afxdhtml.h>
#include <afxwin.h>
extern HINSTANCE _hdllInstance;
#ifdef _DEBUG_WAS_DEFINED
#define _DEBUG
#undef _DEBUG_WAS_DEFINED
#endif
+95
View File
@@ -0,0 +1,95 @@
{
"version": "1.0.0",
"global_settings": {
"plugin_directory": "./mcp_plugins",
"lisp_directory": "./mcp_plugins",
"hot_reload": true
},
"tools": [
{
"name": "draw_circle_inline",
"description": "在指定的WCS坐标绘制一个圆。适用于简单的标注场景。",
"inputSchema": {
"type": "object",
"properties": {
"x": {
"type": "number",
"description": "圆心X坐标"
},
"y": {
"type": "number",
"description": "圆心Y坐标"
},
"r": {
"type": "number",
"description": "半径"
}
},
"required": [
"x",
"y",
"r"
]
},
"backend": {
"type": "lisp_inline",
"script_template": "(command \"_.CIRCLE\" \"{x},{y}\" \"{r}\")"
}
},
{
"name": "auto_dimension_room",
"description": "调用外部复杂的 LISP 脚本,根据指定的房间ID自动生成四周的尺寸标注。",
"inputSchema": {
"type": "object",
"properties": {
"room_id": {
"type": "string",
"description": "房间的标识符,例如 ROOM-101"
},
"scale": {
"type": "number",
"description": "标注全局比例因子"
},
"layer": {
"type": "string",
"description": "标注存放的图层"
}
},
"required": [
"room_id",
"scale"
]
},
"backend": {
"type": "lisp_file",
"file_name": "auto_dimension.lsp",
"call_template": "(mcp-auto-dimension \"{room_id}\" {scale} \"{layer}\")"
}
},
{
"name": "check_interference",
"description": "调用 C++ 底层插件,进行3D实体的高性能干涉检查,返回碰撞点坐标数组。",
"inputSchema": {
"type": "object",
"properties": {
"tolerance": {
"type": "number",
"description": "干涉容差值"
},
"layer_filter": {
"type": "string",
"description": "需要检查的图层名,支持通配符"
}
},
"required": [
"tolerance"
]
},
"backend": {
"type": "c++_dll",
"dll_name": "mcp_hardcore_tools.dll",
"factory_function": "CreateInterferenceTool"
}
}
]
}
+80
View File
@@ -0,0 +1,80 @@
#include "StdAfx.h"
#include "resource.h"
#include "cadsdk.h"
#include "mcp_server.h"
HINSTANCE _hdllInstance = NULL ;
#ifndef _ttof
#define _ttof(pszVal) _tcstod((pszVal), NULL)
#endif
void AddCommand(const TCHAR* cmdGroup, const TCHAR* cmdInt, const TCHAR* cmdLoc,
const int cmdFlags, const AcRxFunctionPtr cmdProc, const int idLocal = -1);
void InitApplication();
void UnloadApplication();
AC_IMPLEMENT_EXTENSION_MODULE(EarthWormDLL);
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_hdllInstance = hInstance;
EarthWormDLL.AttachInstance(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
EarthWormDLL.DetachInstance();
}
return TRUE;
}
extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
{
switch (msg)
{
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(pkt);
acrxDynamicLinker->registerAppMDIAware(pkt);
if (!mcp::init_mcp_server())
return AcRx::kRetError;
InitApplication();
break;
case AcRx::kUnloadAppMsg:
mcp::close_mcp_server();
UnloadApplication();
break;
}
return AcRx::kRetOK;
}
void CR_SCGXCmdFnc();
void InitApplication()
{
acrxBuildClassHierarchy();
AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc);
}
void UnloadApplication()
{
acedRegCmds->removeGroup(_T("SHCRACKTOOLS"));
}
void AddCommand(const TCHAR* cmdGroup, const TCHAR* cmdInt, const TCHAR* cmdLoc,
const int cmdFlags, const AcRxFunctionPtr cmdProc, const int idLocal)
{
TCHAR cmdLocRes[65];
if (idLocal != -1)
{
::LoadString(_hdllInstance, idLocal, cmdLocRes, 64);
acedRegCmds->addCommand(cmdGroup, cmdInt, cmdLocRes, cmdFlags, cmdProc);
}
else
{
acedRegCmds->addCommand(cmdGroup, cmdInt, cmdLoc, cmdFlags, cmdProc);
}
}
+7
View File
@@ -0,0 +1,7 @@
LIBRARY "AutoAgent.arx"
EXPORTS
acrxEntryPoint PRIVATE
acrxGetApiVersion PRIVATE
+110
View File
@@ -0,0 +1,110 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Chinese (Simplified, PRC) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"#include ""res\\AutoAgent.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "AutoAgent ObjectARX application"
VALUE "FileVersion", "1, 0, 0, 1"
VALUE "InternalName", "AutoAgent"
VALUE "LegalCopyright", "Copyright 2014"
VALUE "OriginalFilename", "AutoAgent.arx"
VALUE "ProductName", "AutoAgent Application"
VALUE "ProductVersion", "1, 0, 0, 1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_PROJNAME "AutoAgent"
END
#endif // Chinese (Simplified, PRC) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#include "res\AutoAgent.rc2" // non-Microsoft Visual C++ edited resources
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
+34
View File
@@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 3.16)
project(cad_mcp_frame)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(ghc_filesystem CONFIG REQUIRED)
set(SOURCES
"cad_mcp_frame.cpp"
"command.cpp"
"stdafx.cpp"
"cad_mcp_frame.rc"
"mcp_server.cpp"
"mcp_tool.cpp"
"mcp_ConfigParser.cpp"
"mcp_utils.cpp"
)
add_def_file_name(SOURCES "${PROJECT_NAME}")
add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_precompile_headers(${PROJECT_NAME} PRIVATE StdAfx.h)
target_link_objectarx(${PROJECT_NAME} AECRXBASE_EXPORT)
target_set_name_and_suffix(${PROJECT_NAME} "${PROJECT_NAME}" ".arx")
target_compile_options(${PROJECT_NAME} PRIVATE /Zc:forScope)
target_include_directories(${PROJECT_NAME} INTERFACE "${CMAKE_SOURCE_DIR}/inc")
target_link_libraries(${PROJECT_NAME} PRIVATE
nlohmann_json::nlohmann_json
)
+5
View File
@@ -0,0 +1,5 @@
#include "StdAfx.h"
void CR_SCGXCmdFnc()
{
}
+154
View File
@@ -0,0 +1,154 @@
#include "mcp_ConfigParser.h"
#include "cadsdk.h"
#include <windows.h>
#include "mcp_tool.h"
#include "mcp_utils.h"
#include "ghc/filesystem.hpp"
#pragma region mcp_ConfigParser
bool mcp_ConfigParser::LoadAndRegister(const std::wstring &jsonFilePath, mcp_Tools &registry)
{
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<mcp_Tool_LispInline>();
pTool->SetConfig(toolConfig);
registry.Register(pTool);
}
else if (type == "lisp_file" && fs::exists(lispPath))
{
auto pTool = std::make_shared<mcp_Tool_LispFile>(lispPath);
pTool->SetConfig(toolConfig);
registry.Register(pTool);
}
else if (type == "c++_dll" && fs::exists(pluginPath))
{
LoadDllTool(toolConfig, pluginPath, registry);
}
}
return true;
}
void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std::wstring &pluginDir, mcp_Tools &registry)
{
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());
if (!hMod) 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 实例也注入配置,以防第三方需要在内部读取
pRealTool->SetConfig(toolConfig);
// 实例化代理类,接管生命周期
auto pProxy = std::make_shared<mcp_Tool_DllProxy>(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<mcp_Tool> 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, "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
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <fstream>
#include <unordered_map>
#include "mcp_plugin_api.h"
class mcp_Tools
{
private:
std::unordered_map<std::string, std::shared_ptr<mcp_Tool>> m_tools;
public:
// 注册工具
void Register(std::shared_ptr<mcp_Tool> pTool);
// 组装所有工具的描述,返回 JSON 数组 (用于 tools/list)
nlohmann::json ToLLM() const;
// 路由分发执行指令 (用于 tools/call)
nlohmann::json Call(const std::string &name, const nlohmann::json &args);
void print();
void clear();
};
class mcp_ConfigParser
{
public:
static bool LoadAndRegister(const std::wstring &jsonFilePath, mcp_Tools &registry);
private:
static void LoadDllTool(const nlohmann::json &toolConfig, const std::wstring &pluginDir, mcp_Tools &registry);
};
+273
View File
@@ -0,0 +1,273 @@
#include "StdAfx.h"
#include "cadsdk.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <atomic>
#include <nlohmann/json.hpp>
#include "mcp_ConfigParser.h"
#include "mcp_utils.h"
#pragma comment(lib, "ws2_32.lib")
#define WM_MCP_EXECUTE_TASK (WM_USER + 2048)
extern HINSTANCE _hdllInstance;
#define MCP_SERVER_PORT 8080
#define MCP_SERVER_CONFIG _T("mcp_config.json")
namespace
{
std::thread g_serverThread;
std::atomic<bool> g_bRunning(false);
SOCKET g_listenSocket = INVALID_SOCKET;
HWND g_hMsgWnd = NULL;
const wchar_t *g_szMsgClassName = L"AutoAgentMcpMsgWndClass";
// 声明一个全局工具集对象
mcp_Tools g_mcpTools;
// 用于跨线程传递数据的结构体
struct TaskData
{
~TaskData()
{
if (clientSocket != INVALID_SOCKET)
{
::closesocket(clientSocket);
clientSocket = INVALID_SOCKET;
}
}
SOCKET clientSocket = INVALID_SOCKET;
std::string rawJson;
};
// 处理工具查询请求
std::string HandleToolsList(int msgId)
{
nlohmann::json resp;
resp["jsonrpc"] = "2.0";
resp["id"] = msgId;
// 直接向全局工具集要数据
resp["result"]["tools"] = g_mcpTools.ToLLM();
return resp.dump();
}
// 处理工具调用请求
std::string HandleToolsCall(int msgId, const nlohmann::json &params)
{
nlohmann::json resp;
resp["jsonrpc"] = "2.0";
resp["id"] = msgId;
std::string toolName = params.value("name", "");
nlohmann::json args = params.value("arguments", nlohmann::json::object());
// 让工具集去执行,返回 payload 可能是 {"result": ...} 也可能是 {"error": ...}
nlohmann::json payload = g_mcpTools.Call(toolName, args);
// 将 payload 注入到外层信封中
resp.update(payload);
return resp.dump();
}
// 此函数在 AutoCAD 主 UI 线程运行,绝对安全.
void ExecuteTaskInMainThread(TaskData *pTask)
{
if (pTask == nullptr) return;
std::string responseStr;
// 关闭 nlohmann 的异常抛出机制,安全解析
nlohmann::json req = nlohmann::json::parse(pTask->rawJson, nullptr, false);
if (req.is_discarded())
{
// 非法 JSON 报文
responseStr = R"({"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null})";
}
else
{
std::string method = req.value("method", "");
int msgId = req.value("id", 0);
if (method == "tools/list")
{
responseStr = HandleToolsList(msgId);
}
else if (method == "tools/call")
{
responseStr = HandleToolsCall(msgId, req.value("params", nlohmann::json::object()));
}
else
{
// 找不到方法
nlohmann::json err;
err["jsonrpc"] = "2.0";
err["id"] = msgId;
err["error"] = { {"code", -32601}, {"message", "Method not found"} };
responseStr = err.dump();
}
}
// 通过传递过来的 Socket 将结果发送回大模型端
if (pTask->clientSocket != INVALID_SOCKET)
{
send(pTask->clientSocket, responseStr.c_str(), responseStr.length(), 0);
}
}
// 隐式窗口的回调函数 (属于主线程)
LRESULT CALLBACK McpWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MCP_EXECUTE_TASK)
{
TaskData *pTask = reinterpret_cast<TaskData *>(lParam);
if (pTask)
{
ExecuteTaskInMainThread(pTask);
delete pTask; // 执行完毕,清理内存
}
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
// TCP 监听后台线程
void TcpServerLoop()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
g_listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in serverAddr = { 0 };
serverAddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = htons(MCP_SERVER_PORT);
bind(g_listenSocket, (sockaddr *)&serverAddr, sizeof(serverAddr));
listen(g_listenSocket, SOMAXCONN);
while (g_bRunning)
{
// 使用 select 模型实现带超时的轮询,确保插件卸载时线程能正常退出
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(g_listenSocket, &readSet);
timeval timeout = { 1, 0 }; // 1秒超时机制
int ret = select(0, &readSet, nullptr, nullptr, &timeout);
if (ret > 0 && FD_ISSET(g_listenSocket, &readSet))
{
SOCKET clientSocket = accept(g_listenSocket, nullptr, nullptr);
if (clientSocket != INVALID_SOCKET)
{
char recvBuf[2048] = { 0 };
int bytesReceived = recv(clientSocket, recvBuf, sizeof(recvBuf) - 1, 0);
if (bytesReceived > 0)
{
// 将数据拷贝到堆内存,准备派发给主线程
TaskData *pTask = new TaskData;
pTask->clientSocket = clientSocket; // 记录 Socket
pTask->rawJson = recvBuf;
// 关键转变:通过 PostMessage 发送给主线程拥有的隐式窗口
if (g_hMsgWnd != NULL)
{
PostMessage(g_hMsgWnd, WM_MCP_EXECUTE_TASK, 0, reinterpret_cast<LPARAM>(pTask));
}
else
{
delete pTask;
}
}
}
else
{
closesocket(clientSocket);
}
}
}
closesocket(g_listenSocket);
WSACleanup();
}
}
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()
{
// 1. 在主线程注册并创建 Message-Only 窗口
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = McpWndProc;
wc.hInstance = _hdllInstance;
wc.lpszClassName = g_szMsgClassName;
RegisterClassEx(&wc);
g_hMsgWnd = CreateWindowEx(
0,
g_szMsgClassName,
L"AutoAgentMcpMsgWindow",
0, 0, 0, 0, 0,
HWND_MESSAGE, // 纯消息窗口,无界面、无感知
NULL,
_hdllInstance,
NULL
);
g_bRunning = true;
try
{
g_serverThread = std::thread(TcpServerLoop);
acutPrintf(_T("\n[MCP Server] Background TCP listening thread started (Port: 8080)...\n"));
}
catch (const std::system_error &e)
{
// 线程创建失败,重置状态标志并输出错误
g_bRunning = false;
// e.what() 返回的是 const char*,所以在 acutPrintf 中要用 %hs 而不是 %s 或 %ls
acutPrintf(_T("\n[MCP Server] Fatal error: Unable to create background listening thread! Reason: %hs\n"), e.what());
}
// 如果监听跑起来了,则注册tools
if (g_bRunning)
{
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;
}
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
namespace mcp
{
bool init_mcp_server();
bool close_mcp_server();
}
+114
View File
@@ -0,0 +1,114 @@
#include "cadsdk.h"
#include "mcp_tool.h"
#include "mcp_utils.h"
namespace
{
bool ReplaceTemplateArgs(const std::string &_tpl, const nlohmann::json &args, std::wstring &outScript)
{
if (_tpl.empty()) return false;
std::string tpl(_tpl);
for (auto it = args.begin(); it != args.end(); ++it)
{
std::string placeholder = "{" + it.key() + "}";
std::string valueStr;
if (it.value().is_string())
{
valueStr = it.value().get<std::string>();
}
else if (it.value().is_boolean())
{
valueStr = it.value().get<bool>() ? "T" : "nil"; // 适配 AutoLISP 的布尔逻辑
}
else
{
valueStr = it.value().dump(); // 数字类型直接转字符串
}
size_t pos = 0;
while ((pos = tpl.find(placeholder, pos)) != std::string::npos)
{
tpl.replace(pos, placeholder.length(), valueStr);
pos += valueStr.length();
}
}
outScript = mcp_utils::utf8ToWString(tpl);
return true;
}
}
#pragma region mcp_Tool_LispInline
nlohmann::json mcp_Tool_LispInline::Execute(const nlohmann::json &args)
{
std::string tpl = m_config["backend"].value("script_template", "");
std::wstring finalScript;
if (!ReplaceTemplateArgs(tpl, args, finalScript))
return mcp_Tool_Utility::make_error(-32603, "Script template replacement failed.");
finalScript += _T(" "); // 尾部加空格,等同于在 CAD 命令行敲回车
// 异步推送到 AutoCAD 命令行执行
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
return mcp_Tool_Utility::make_text_result("LISP inline command issued for execution (asynchronous)");
}
#pragma endregion mcp_Tool_LispInline
#pragma region mcp_Tool_LispFile
nlohmann::json mcp_Tool_LispFile::Execute(const nlohmann::json &args)
{
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", "");
// 组装前置加载命令,末尾的 nil 是为了防止文件不存在时 LISP 引擎报错弹窗
std::wstring loadCmd = std::wstring(_T("(load \"")) + filePath.generic_wstring() + _T("\" nil) ");
std::wstring executeCmd;
if (!ReplaceTemplateArgs(callTpl, args, executeCmd))
{
return mcp_Tool_Utility::make_error(-32603, "Script template replacement failed.");
}
executeCmd += _T(" ");
std::wstring finalScript = loadCmd + executeCmd;
acDocManager->sendStringToExecute(acDocManager->curDocument(), finalScript.c_str());
return mcp_Tool_Utility::make_text_result("LISP external file loaded and issued for execution (asynchronous)");
}
#pragma endregion mcp_Tool_LispFile
#pragma region mcp_Tool_DllProxy
// RAII:基座销毁工具时,自动通知 DLL 释放内存,并卸载模块
mcp_Tool_DllProxy::~mcp_Tool_DllProxy()
{
if (m_pRealTool && m_destroyFunc)
{
m_destroyFunc(m_pRealTool);
}
if (m_hModule)
{
FreeLibrary(m_hModule);
}
}
nlohmann::json mcp_Tool_DllProxy::Execute(const nlohmann::json &args)
{
if (m_pRealTool)
{
return m_pRealTool->Execute(args); // 完美转发
}
return mcp_Tool_Utility::make_error(-32603, "Underlying DLL tool instance is invalid");
}
#pragma endregion mcp_Tool_DllProxy
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <string>
#include <memory>
#include <nlohmann/json.hpp>
#include "mcp_plugin_api.h"
class mcp_Tool_LispInline : public mcp_Tool
{
public:
virtual nlohmann::json Execute(const nlohmann::json &args) override;
};
class mcp_Tool_LispFile : public mcp_Tool
{
private:
std::wstring m_lispDir;
public:
mcp_Tool_LispFile(const std::wstring& lispDir) : m_lispDir(lispDir) {}
virtual nlohmann::json Execute(const nlohmann::json &args) override;
};
class mcp_Tool_DllProxy : public mcp_Tool
{
private:
HMODULE m_hModule;
mcp_Tool* m_pRealTool; // 真正由第三方 DLL 创建的工具对象
typedef void (*DestroyFunc)(mcp_Tool*);
DestroyFunc m_destroyFunc;
public:
mcp_Tool_DllProxy(HMODULE hMod, mcp_Tool* pReal, DestroyFunc pDestroy)
: m_hModule(hMod), m_pRealTool(pReal), m_destroyFunc(pDestroy) {}
// RAII:基座销毁工具时,自动通知 DLL 释放内存,并卸载模块
virtual ~mcp_Tool_DllProxy();
virtual nlohmann::json Execute(const nlohmann::json &args) override;
};
+49
View File
@@ -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();
}
}
+22
View File
@@ -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());
}
+13
View File
@@ -0,0 +1,13 @@
//
// AutoAgent.RC2 - resources Microsoft Visual C++ does not edit directly
//
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Microsoft Visual C++
#endif //APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
// Add manually edited resources here...
/////////////////////////////////////////////////////////////////////////////
+16
View File
@@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by newProject.rc
//
#define IDS_PROJNAME 100
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 426
#define _APS_NEXT_COMMAND_VALUE 32768
#define _APS_NEXT_CONTROL_VALUE 438
#define _APS_NEXT_SYMED_VALUE 122
#endif
#endif