Skip to main content
Glama

AutoCAD MCP Server

by zh19980811
from mcp.server.fastmcp import FastMCP, Context from typing import Optional, List, Dict, Any import win32com.client import sqlite3 import json import random import re # 创建服务器 mcp = FastMCP("AutoCAD-DB-Server") # 初始化 SQLite 数据库 def init_db(): try: conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() # 创建实体表 cursor.execute(''' CREATE TABLE IF NOT EXISTS cad_elements ( id INTEGER PRIMARY KEY, handle TEXT UNIQUE, name TEXT NOT NULL, type TEXT NOT NULL, layer TEXT, properties TEXT ) ''') # 创建文字内容统计表 cursor.execute(''' CREATE TABLE IF NOT EXISTS text_patterns ( id INTEGER PRIMARY KEY, pattern TEXT UNIQUE, count INTEGER DEFAULT 0, drawing TEXT ) ''') conn.commit() conn.close() return True except Exception as e: print(f"数据库初始化失败: {str(e)}") return False # 确保数据库初始化 init_db() # ======= AutoCAD 基础工具 ======= @mcp.tool() def create_new_drawing(ctx: Context, template: Optional[str] = None) -> str: """创建新的 AutoCAD 图纸""" try: # 尝试连接到 AutoCAD acad = win32com.client.Dispatch("AutoCAD.Application") acad.Visible = True # 创建新文档 if template: doc = acad.Documents.Add(template) else: doc = acad.Documents.Add() return f"成功创建新图纸" except Exception as e: return f"创建图纸失败: {str(e)}" @mcp.tool() def draw_line(ctx: Context, start_x: float, start_y: float, end_x: float, end_y: float, layer: Optional[str] = None) -> str: """在AutoCAD中绘制直线 Args: start_x: 起点X坐标 start_y: 起点Y坐标 end_x: 终点X坐标 end_y: 终点Y坐标 layer: 可选的图层名称 """ try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace # 如果指定了图层,先切换或创建图层 if layer: try: # 尝试获取图层 doc.Layers.Item(layer) except: # 图层不存在,创建新图层 doc.Layers.Add(layer) # 设置当前图层 doc.ActiveLayer = doc.Layers.Item(layer) # 创建直线 line = model_space.AddLine( win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x, start_y, 0]), win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [end_x, end_y, 0]) ) # 将线条信息存入数据库 conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() props = { "start_point": [start_x, start_y, 0], "end_point": [end_x, end_y, 0] } cursor.execute( "INSERT INTO cad_elements (handle, name, type, layer, properties) VALUES (?, ?, ?, ?, ?)", (line.Handle, "Line", "AcDbLine", doc.ActiveLayer.Name, json.dumps(props)) ) conn.commit() conn.close() return f"已创建直线,Handle: {line.Handle}, 图层: {doc.ActiveLayer.Name}" except Exception as e: return f"创建直线失败: {str(e)}" @mcp.tool() def draw_circle(ctx: Context, center_x: float, center_y: float, radius: float, layer: Optional[str] = None) -> str: """在AutoCAD中绘制圆 Args: center_x: 圆心X坐标 center_y: 圆心Y坐标 radius: 半径 layer: 可选的图层名称 """ try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace # 如果指定了图层,先切换或创建图层 if layer: try: # 尝试获取图层 doc.Layers.Item(layer) except: # 图层不存在,创建新图层 doc.Layers.Add(layer) # 设置当前图层 doc.ActiveLayer = doc.Layers.Item(layer) # 创建圆 center_point = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [center_x, center_y, 0]) circle = model_space.AddCircle(center_point, radius) # 将圆信息存入数据库 conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() props = { "center_point": [center_x, center_y, 0], "radius": radius } cursor.execute( "INSERT INTO cad_elements (handle, name, type, layer, properties) VALUES (?, ?, ?, ?, ?)", (circle.Handle, "Circle", "AcDbCircle", doc.ActiveLayer.Name, json.dumps(props)) ) conn.commit() conn.close() return f"已创建圆,Handle: {circle.Handle}, 半径: {radius}, 图层: {doc.ActiveLayer.Name}" except Exception as e: return f"创建圆失败: {str(e)}" # ======= 实体扫描和数据库交互 ======= @mcp.tool() def scan_all_entities(ctx: Context) -> str: """扫描当前图纸中的所有实体并保存到数据库""" try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace # 连接数据库 conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() # 清空现有记录(可选) cursor.execute("DELETE FROM cad_elements") # 统计信息 count = 0 entity_types = {} # 遍历所有实体 for i in range(model_space.Count): try: entity = model_space.Item(i) entity_type = entity.ObjectName # 统计类型数量 if entity_type in entity_types: entity_types[entity_type] += 1 else: entity_types[entity_type] = 1 # 获取基本属性 properties = {} if entity_type == "AcDbLine": properties = { "start_point": [entity.StartPoint[0], entity.StartPoint[1], entity.StartPoint[2]], "end_point": [entity.EndPoint[0], entity.EndPoint[1], entity.EndPoint[2]] } elif entity_type == "AcDbCircle": properties = { "center": [entity.Center[0], entity.Center[1], entity.Center[2]], "radius": entity.Radius } elif entity_type == "AcDbText" or entity_type == "AcDbMText": properties = { "text": entity.TextString, "position": [entity.InsertionPoint[0], entity.InsertionPoint[1], entity.InsertionPoint[2]] if hasattr(entity, "InsertionPoint") else None, "height": entity.Height if hasattr(entity, "Height") else None } # 将实体信息存入数据库 cursor.execute( "INSERT OR REPLACE INTO cad_elements (handle, name, type, layer, properties) VALUES (?, ?, ?, ?, ?)", (entity.Handle, entity.ObjectName.replace("AcDb", ""), entity.ObjectName, entity.Layer, json.dumps(properties)) ) count += 1 except Exception as e: print(f"处理实体 {i} 时出错: {str(e)}") conn.commit() conn.close() # 格式化类型统计 type_summary = "\n".join([f"{t}: {c}" for t, c in entity_types.items()]) return f"已扫描并保存 {count} 个实体到数据库。\n\n实体类型统计:\n{type_summary}" except Exception as e: return f"扫描实体失败: {str(e)}" @mcp.tool() def highlight_entity(ctx: Context, handle: str, color: int = 1) -> str: """通过Handle在AutoCAD中高亮显示指定实体 Args: handle: 实体的Handle值 color: 高亮颜色码(1=红色, 2=黄色, 3=绿色, 4=青色, 5=蓝色, 6=洋红色) """ try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument # 保存当前选择集 doc.SelectionSets.Add("TempSS") selection = doc.SelectionSets.Item("TempSS") # 根据Handle选择实体 filter_type = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_I2, [0]) filter_data = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_VARIANT, ["HANDLE", handle]) selection.Select(2, 0, 0, filter_type, filter_data) if selection.Count == 0: selection.Delete() return f"未找到Handle为 {handle} 的实体" # 修改实体颜色 entity = selection.Item(0) original_color = entity.Color entity.Color = color selection.Delete() return f"已高亮实体 {handle},颜色从 {original_color} 改为 {color}" except Exception as e: return f"高亮实体失败: {str(e)}" # ======= 文本分析工具 ======= @mcp.tool() def count_text_patterns(ctx: Context, pattern: str = "PMC-3M") -> str: """统计图纸中文本实体中特定模式的出现次数 Args: pattern: 要搜索的文本模式,默认为"PMC-3M" """ try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace drawing_name = doc.Name # 连接数据库 conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() # 计数器 count = 0 matching_entities = [] # 遍历所有实体 for i in range(model_space.Count): try: entity = model_space.Item(i) # 检查是否为文本实体 if hasattr(entity, "TextString"): text = entity.TextString # 搜索模式 if pattern in text: count += 1 matching_entities.append({ "handle": entity.Handle, "text": text, "layer": entity.Layer, "position": [entity.InsertionPoint[0], entity.InsertionPoint[1]] if hasattr(entity, "InsertionPoint") else None }) except Exception as e: print(f"处理文本实体 {i} 时出错: {str(e)}") # 保存统计结果到数据库 cursor.execute( "INSERT OR REPLACE INTO text_patterns (pattern, count, drawing) VALUES (?, ?, ?)", (pattern, count, drawing_name) ) conn.commit() conn.close() result = f"在图纸 '{drawing_name}' 中找到 {count} 处匹配模式 '{pattern}' 的文本。" # 如果有匹配项,显示详细信息 if count > 0: details = "\n\n匹配详情:" for i, match in enumerate(matching_entities[:10]): # 限制显示前10个 details += f"\n{i+1}. 文本: '{match['text']}', 图层: {match['layer']}, Handle: {match['handle']}" if len(matching_entities) > 10: details += f"\n... 以及其他 {len(matching_entities) - 10} 个匹配项" result += details return result except Exception as e: return f"统计文本模式失败: {str(e)}" @mcp.tool() def highlight_text_matches(ctx: Context, pattern: str = "PMC-3M", color: int = 1) -> str: """高亮显示包含指定文本模式的所有文本实体 Args: pattern: 要搜索的文本模式,默认为"PMC-3M" color: 高亮颜色码(1=红色, 2=黄色, 3=绿色, 4=青色, 5=蓝色, 6=洋红色) """ try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace # 创建选择集 try: # 尝试删除可能存在的选择集 doc.SelectionSets.Item("TextMatches").Delete() except: pass selection = doc.SelectionSets.Add("TextMatches") # 计数器 count = 0 # 遍历所有实体 for i in range(model_space.Count): try: entity = model_space.Item(i) # 检查是否为文本实体 if hasattr(entity, "TextString"): text = entity.TextString # 搜索模式 if pattern in text: # 保存原始颜色 original_color = entity.Color # 修改颜色 entity.Color = color # 添加到选择集 selection.AddItems([entity]) count += 1 except Exception as e: print(f"处理文本实体 {i} 时出错: {str(e)}") if count > 0: # 缩放到选择集 doc.ActiveView.ZoomAll() return f"已高亮显示 {count} 个包含 '{pattern}' 的文本实体" else: selection.Delete() return f"未找到包含 '{pattern}' 的文本实体" except Exception as e: return f"高亮文本匹配失败: {str(e)}" # ======= 数据库查询工具 ======= @mcp.tool() def get_all_tables(ctx: Context) -> str: """获取数据库中的所有表""" try: conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") tables = cursor.fetchall() conn.close() table_list = [table[0] for table in tables] return json.dumps(table_list, indent=2) except Exception as e: return f"获取表列表失败: {str(e)}" @mcp.tool() def get_table_schema(ctx: Context, table_name: str) -> str: """获取指定表的结构信息""" try: conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() cursor.execute(f"PRAGMA table_info({table_name});") columns = cursor.fetchall() conn.close() schema = [] for col in columns: schema.append({ "cid": col[0], "name": col[1], "type": col[2], "notnull": col[3], "default_value": col[4], "pk": col[5] }) return json.dumps(schema, indent=2) except Exception as e: return f"获取表结构失败: {str(e)}" @mcp.tool() def execute_query(ctx: Context, query: str) -> str: """执行自定义数据库查询""" try: conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() cursor.execute(query) # 如果是SELECT查询,获取结果 if query.strip().upper().startswith("SELECT"): columns = [desc[0] for desc in cursor.description] rows = cursor.fetchall() # 将结果转换为字典列表 result = [] for row in rows: result.append(dict(zip(columns, row))) conn.commit() conn.close() return json.dumps(result, indent=2) else: # 非SELECT查询,返回影响的行数 conn.commit() affected = cursor.rowcount conn.close() return f"执行成功,影响了 {affected} 行" except Exception as e: return f"执行查询失败: {str(e)}" @mcp.tool() def query_and_highlight(ctx: Context, sql_query: str, highlight_color: int = 1) -> str: """根据SQL查询结果高亮显示AutoCAD实体 Args: sql_query: 必须是返回handle列的SQL查询 highlight_color: 高亮颜色码(1-255) """ try: # 执行查询 conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() cursor.execute(sql_query) rows = cursor.fetchall() conn.close() if not rows: return "查询未返回任何结果" # 获取列名 column_names = [description[0] for description in cursor.description] # 查找handle列 handle_index = -1 for i, name in enumerate(column_names): if name.lower() == 'handle': handle_index = i break if handle_index == -1: return "查询结果中未找到handle列" # 提取所有handle handles = [row[handle_index] for row in rows] # 高亮实体 acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument # 创建选择集 try: # 尝试删除可能存在的选择集 doc.SelectionSets.Item("QueryResults").Delete() except: pass selection = doc.SelectionSets.Add("QueryResults") # 高亮找到的实体 highlighted_count = 0 for handle in handles: try: # 根据Handle选择实体 filter_type = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_I2, [0]) filter_data = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_VARIANT, ["HANDLE", handle]) # 创建临时选择集 temp_selection = doc.SelectionSets.Add(f"Temp_{random.randint(1000, 9999)}") temp_selection.Select(2, 0, 0, filter_type, filter_data) if temp_selection.Count > 0: # 修改颜色 entity = temp_selection.Item(0) entity.Color = highlight_color # 添加到主选择集 selection.AddItems([entity]) highlighted_count += 1 # 删除临时选择集 temp_selection.Delete() except Exception as e: print(f"处理实体 {handle} 时出错: {str(e)}") if highlighted_count > 0: # 缩放到选择集 doc.ActiveView.ZoomAll() return f"已高亮显示 {highlighted_count} 个实体(共 {len(handles)} 个结果)" else: selection.Delete() return f"未能高亮任何实体" except Exception as e: return f"查询并高亮失败: {str(e)}" # 添加到现有代码中 @mcp.tool() @mcp.tool() def draw_line(ctx: Context, start_x: float, start_y: float, end_x: float, end_y: float, layer: Optional[str] = None) -> str: """在AutoCAD中绘制直线""" try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace # 处理图层... # 创建直线 line = model_space.AddLine( win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x, start_y, 0]), win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [end_x, end_y, 0]) ) # 将线条信息存入数据库(不使用handle字段) conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() props = { "start_point": [start_x, start_y, 0], "end_point": [end_x, end_y, 0] } cursor.execute( "INSERT INTO cad_elements (name, type, layer, properties) VALUES (?, ?, ?, ?)", ("Line", "AcDbLine", doc.ActiveLayer.Name, json.dumps(props)) ) conn.commit() conn.close() return f"已创建直线,从 ({start_x}, {start_y}) 到 ({end_x}, {end_y})" except Exception as e: return f"创建直线失败: {str(e)}" @mcp.tool() def draw_device_connection(ctx: Context, start_device: str, end_device: str, start_x: Optional[float] = None, start_y: Optional[float] = None, end_x: Optional[float] = None, end_y: Optional[float] = None, layer: Optional[str] = None) -> str: """绘制设备之间的连接线 Args: start_device: 起始设备标签,如"P14" end_device: 结束设备标签,如"P02" start_x: 可选的起始点X坐标(如果不提供则自动查找设备) start_y: 可选的起始点Y坐标 end_x: 可选的结束点X坐标 end_y: 可选的结束点Y坐标 layer: 可选的图层名称 """ try: acad = win32com.client.Dispatch("AutoCAD.Application") if acad.Documents.Count == 0: return "无打开的文档,请先创建或打开一个图纸" doc = acad.ActiveDocument model_space = doc.ModelSpace # 如果指定了图层,先切换或创建图层 if layer: try: # 尝试获取图层 doc.Layers.Item(layer) except: # 图层不存在,创建新图层 doc.Layers.Add(layer) # 设置当前图层 doc.ActiveLayer = doc.Layers.Item(layer) # 如果没有提供坐标,尝试从数据库中查找设备 if start_x is None or start_y is None or end_x is None or end_y is None: conn = sqlite3.connect("autocad_data.db") cursor = conn.cursor() # 查找起始设备 cursor.execute( "SELECT properties FROM cad_elements WHERE type = 'CustomDevice' AND json_extract(properties, '$.label') = ?", (start_device,) ) start_result = cursor.fetchone() # 查找结束设备 cursor.execute( "SELECT properties FROM cad_elements WHERE type = 'CustomDevice' AND json_extract(properties, '$.label') = ?", (end_device,) ) end_result = cursor.fetchone() conn.close() if not start_result: return f"未找到标签为 {start_device} 的设备" if not end_result: return f"未找到标签为 {end_device} 的设备" # 解析设备位置和尺寸 start_props = json.loads(start_result[0]) end_props = json.loads(end_result[0]) start_pos = start_props["position"] end_pos = end_props["position"] # 设置连接线起点和终点(设备的左侧连接点) start_x = start_pos[0] - 5 # 设备左侧 start_y = start_pos[1] end_x = end_pos[0] - 5 end_y = end_pos[1] # 创建连接线(水平线段 + 垂直线段 + 水平线段) # 首先创建起始水平线段 line1_start = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x, start_y, 0]) line1_end = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x - 10, start_y, 0]) line1 = model_space.AddLine(line1_start, line1_end) # 创建垂直连接线 line2_start = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x - 10, start_y, 0]) line2_end = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x - 10, end_y, 0]) line2 = model_space.AddLine(line2_start, line2_end) # 创建结束水平线段 line3_start = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [start_x - 10, end_y, 0]) line3_end = win32com.client.VARIANT(win32com.client.pythoncom.VT_ARRAY | win32com.client.pythoncom.VT_R8, [end_x, end_y, 0]) line3 = model_space.AddLine(line3_start, line3_end) return f"已创建从 {start_device} 到 {end_device} 的连接线" except Exception as e: return f"创建连接线失败: {str(e)}" # 启动服务器 if __name__ == "__main__": mcp.run()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zh19980811/Easy-MCP-AutoCad'

If you have feedback or need assistance with the MCP directory API, please join our Discord server