给个人程序加上MCP翅膀

背景

最近MCP这个词真是到处都是,看起来特别高大上。我平时没事的时候也一直在关注这方面的技术,知道它是怎么一回事,也懂该怎么去实现。但可惜一直抽不出时间来自己动手搞一个MCP服务。网上关于MCP的教程一搜一大把,但基本上都是些简单的Demo,而且大部分还是拿天气查询来做例子的,没什么新意,看多了都让人觉得无聊。

MCP服务器实现语言有pythonTypeScriptJavaC#Swift这几种语言,好家伙,就是没有C++,但是我大部分底层算法都是基于C++实现。基于此,我只能将我C++的程序封装成上述的语言,个人平时更多的还是封装成C#(之前实现的自主CAD程序已经可以在C#上调用),那就基于C#实现自己的MCP服务器吧。

基于MCP服务我想在自己的CAD程序中加入几个功能,分别是打开Dwg文件绘制图形修改颜色,画话不多说,直接上手。

C# 官方MCP 概述

在.NET 生态系统中,除了官方提供的 C# MCP SDK 外,还有一些其他与 MCP 相关的技术,比如 MCPSharp 和 mcpdotnet 等。这些工具也为开发者提供了丰富的功能和支持。MCPSharp 是一个专为.NET 设计的库,旨在帮助开发者轻松构建符合 Model Context Protocol(MCP)标准的服务器和客户端。它提供了创建合规工具和函数的功能,还支持连接现有的 MCP 服务器,甚至可以将.NET 方法直接暴露为 MCP 端点。而 mcpdotnet 则是一个基于.NET 的模型上下文协议(MCP)实现,能够让.NET 应用程序方便地与 MCP 客户端和服务器进行交互。

不过,相比于这些非官方的实现,C# 官方 MCP SDK 有着显著的优势,尤其是在标准化和兼容性方面。作为官方推出的工具,C# MCP SDK 严格遵循 MCP 协议标准,确保了不同应用程序和服务之间的交互既一致又可靠。而一些非官方实现可能在某些细节上与标准存在偏差,这可能会在集成过程中引发问题。例如,在与不同的 AI 模型集成时,官方 SDK 能够更准确地保证数据传输和工具调用的可靠性,从而减少因协议不一致而导致的错误。

此外,在兼容性方面,C# 官方 MCP SDK 经过了全面的测试和优化,能够与各种主流的.NET 框架和开发工具无缝协作。无论你使用的是最新版本的.NET,还是其他常用的开发工具,官方 SDK 都能轻松集成,为开发者省去不少麻烦。相比之下,其他相关技术在兼容性上可能会有一定的局限性,有时需要开发者额外花时间解决兼容性问题。因此,选择官方 SDK 不仅能让开发过程更加顺畅,还能提升项目的稳定性和效率。

MCP服务实战

编写C# MCP服务端
  1. 在个人的C#程序下打开终端,输入命令
powershell 复制代码
## 如果安装不了,可能要给nuget更换国内镜像
dotnet add package ModelContextProtocol --prerelease
  1. 让cursor给我们编写mcp服务
powershell 复制代码
@https://github.com/ModelContextProtocol/csharp-sdk 基于这份开源库说明,编写一个CADServer类,实现mcp服务,添加一个打开dwg文件的接口
csharp 复制代码
using ModelContextProtocol.Server;
using mx;
using System.ComponentModel;
using System.IO;
using System.Linq;

namespace CimEditor.Common
{
    [McpServerToolType]
    public static class CADServer
    {
        [McpServerTool, Description("打开指定路径的DWG文件,并返回是否成功")]
        public static string OpenDwgFile([Description("DWG文件的完整路径")] string filePath)
        {
            if (!File.Exists(filePath))
            {
                return $"文件不存在: {filePath}";
            }

            try
            {
                var graph = CimEditor.Common.Project.Instance.Graph;
                graph.LoadDwg(filePath);
                return $"DWG文件已成功打开: {filePath}";
            }
            catch (System.Exception ex)
            {
                return $"打开DWG文件失败: {ex.Message}";
            }
        }

        [McpServerTool, Description("绘制多段线,points格式为'x1,y1;x2,y2;...',isClosed表示是否闭合")]
        public static string DrawPolyline(
            [Description("多段线点坐标,格式为'x1,y1;x2,y2;...' ")] string points,
            [Description("是否闭合")] bool isClosed)
        {
            try
            {
                var graph = CimEditor.Common.Project.Instance.Graph;
                // 假设Graph有DrawPolyline方法,参数为点数组和是否闭合
                var pts = points.Split(';').Select(p => {
                    var xy = p.Split(',');
                    return new XDPoint(double.Parse(xy[0]),double.Parse(xy[1]));
                }).ToList();
                
                var polyline = XDPolylineShape.CreateObject();
                polyline.SetVertices(new XDPoints(pts));
                polyline.SetClosed(isClosed);
                graph.AddShape(polyline);
                return $"多段线已成功绘制,共{pts.Count}个点,闭合:{isClosed}";
            }
            catch (System.Exception ex)
            {
                return $"绘制多段线失败: {ex.Message}";
            }
        }

        [McpServerTool, Description("绘制圆,center格式为'x,y',radius为半径")]
        public static string DrawCircle(
            [Description("圆心坐标,格式为'x,y'")] string center,
            [Description("半径")] double radius)
        {
            try
            {
                var graph = CimEditor.Common.Project.Instance.Graph;
                var xy = center.Split(',');
                double x = double.Parse(xy[0]);
                double y = double.Parse(xy[1]);
                var circle = XDCircleShape.CreateObject(new XDPoint(x, y), radius);
                graph.AddShape(circle);
                return $"圆已成功绘制,圆心:({x},{y}), 半径:{radius}";
            }
            catch (System.Exception ex)
            {
                return $"绘制圆失败: {ex.Message}";
            }
        }

        [McpServerTool, Description("绘制文字,position格式为'x,y',text为内容,fontSize为字号")]
        public static string DrawText(
            [Description("文字位置,格式为'x,y'")] string position,
            [Description("文字内容")] string text,
            [Description("字号")] double fontSize)
        {
            try
            {
                var graph = CimEditor.Common.Project.Instance.Graph;
                var xy = position.Split(',');
                double x = double.Parse(xy[0]);
                double y = double.Parse(xy[1]);
                var text_shape = XDTextShape.CreateObject();
                text_shape.Position = new XDPoint(x, y);
                text_shape.TextSize = fontSize;
                return $"文字已成功绘制,位置:({x},{y}), 内容:'{text}', 字号:{fontSize}";
            }
            catch (System.Exception ex)
            {
                return $"绘制文字失败: {ex.Message}";
            }
        }

        [McpServerTool, Description("设置当前选中图元的颜色,color格式为'R,G,B'")]
        public static string SetSelectedShapesColor(
            [Description("颜色,格式为'R,G,B'")] string color)
        {
            try
            {
                var graph = CimEditor.Common.Project.Instance.Graph;
                var rgb = color.Split(',');
                if (rgb.Length != 3)
                    return "颜色格式错误,应为'R,G,B'";
                int r = int.Parse(rgb[0]);
                int g = int.Parse(rgb[1]);
                int b = int.Parse(rgb[2]);
                var selected_cells = graph.GetSelectionCells(); // 假设有此方法
                int count = 0;
                foreach (var cell in selected_cells)
                {
                    // 假设shape有SetColor方法,参数为r,g,b
                    cell.Geometry.SetColor(new XDColor(r, g, b));
                    count++;
                }
                return $"已成功修改{count}个选中图元的颜色为({r},{g},{b})";
            }
            catch (System.Exception ex)
            {
                return $"修改颜色失败: {ex.Message}";
            }
        }
    }
} 
  1. 在程序主程入口添加
csharp 复制代码
  private void StartMcpServerInBackground()
  {
      Task.Run(async () =>
      {
          var builder = Host.CreateEmptyApplicationBuilder(settings: null);
          builder.Services
              .AddMcpServer()
              .WithStdioServerTransport()
              .WithToolsFromAssembly();
          await builder.Build().RunAsync();
      });
  }
  1. 调整AI生成不合理的地方,编译通过即可。
使用Cursor配置mcp客户端。
csharp 复制代码
{
  "mcpServers": {
    "DwgServer": {
      "command": "D:\\code\\cad\\cimediter\\bin\\CimEditor.exe",
      "args": [],
      "cwd": "D:\\code\\cad\\cimediter\\bin",
      "env": {
        "DOTNET_ENVIRONMENT": "Development"
      }
    }
  }
}
验证成果

mcp.mp4

相关推荐
人猿泰飞6 小时前
Trae IDE和VSCode Trae插件初探
ai编程·trae
zhz521417 小时前
AI数字人融合VR全景:从技术突破到可信场景落地
人工智能·vr·ai编程·ai数字人·ai agent·智能体
冷yan~20 小时前
GitHub文档加载器设计与实现
java·人工智能·spring·ai·github·ai编程
无声旅者2 天前
深度解析 IDEA 集成 Continue 插件:提升开发效率的全流程指南
java·ide·ai·intellij-idea·ai编程·continue·openapi
zhz52142 天前
AI数字人融合VR全景:开启未来营销与交互新篇章
人工智能·ai·交互·vr·ai编程·智能体
hongdou1993 天前
图形语言中间层:重构 AI 编程的未来之路
ai编程
是店小二呀3 天前
Trae 插件 Builder 模式:从 0 到 1 开发天气查询小程序,解锁 AI 编程新体验
人工智能·ai编程·trae
小众AI3 天前
Void: Cursor 的开源平替
人工智能·ai编程
Lilith的AI学习日记3 天前
Claude官方63组提示词模板全解析:从工作到生活的AI应用指南
人工智能·prompt·生活·ai编程·claude