给个人程序加上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

相关推荐
牛奶1 小时前
2026年大模型怎么选?前端人实用对比
前端·人工智能·ai编程
牛奶1 小时前
前端人为什么要学AI?
前端·人工智能·ai编程
KEEN的创享空间7 小时前
AI编程从0到1之10X提效(Vibe Coding 氛围式编码 )09篇
openai·ai编程
AlienZHOU8 小时前
为 AI Agent 编写高质量 Skill:Claude 官方指南
agent·ai编程·claude
恋猫de小郭9 小时前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
KaneLogger10 小时前
【翻译】打造 Agent Skills 的最佳实践
agent·ai编程·claude
王小酱10 小时前
Everything Claude Code 文档
openai·ai编程·aiops
雮尘11 小时前
如何在非 Claude IDE (TARE、 Cursor、Antigravity 等)下使用 Agent Skills
前端·agent·ai编程
刘贺同学11 小时前
Day12-龙虾哥打工日记:OpenClaw 子 Agent 到底看到了什么?
aigc·ai编程
程序员鱼皮13 小时前
离大谱,我竟然在 VS Code 里做了个视频!
github·aigc·ai编程