资讯中心

.NET 开发 MCP 服务器完全指南:用 TaoToken 统一 Key 打造智能数据库查询助手

📅 2026/9/28 6:36:22
.NET 开发 MCP 服务器完全指南:用 TaoToken 统一 Key 打造智能数据库查询助手
1. 从一条 SQL 说起为什么 .NET 开发者需要自己的 MCP 服务器你可能已经习惯了这样的工作流在 Cursor 或 VS Code Copilot 里描述需求AI 帮你生成一段 SQL然后你复制到数据库客户端里执行发现字段名写错了再回来改再复制过去。来回几次半小时就没了。问题不在于 AI 不会写 SQL而在于它看不见你的数据库——不知道有哪些表、字段叫什么类型、数据量多大只能靠猜。MCPModel Context Protocol模型上下文协议解决的正是这件事。它是一套开放标准让 AI 客户端能够以统一方式调用外部工具和数据源。你可以把它理解成 AI 世界的 USB-C 接口不管对面是 Cursor、Claude Desktop 还是 VS Code Copilot只要你的服务实现了 MCP 协议它们就能用同一套方式连接上来。对 .NET 开发者来说这意味着你可以用熟悉的 C#、依赖注入、Dapper 或 EF Core把数据库查询能力封装成 AI 可以直接调用的工具。这篇指南面向的是想从零搭一个数据库查询 MCP 服务器的 .NET 开发者。我会带你走完完整路径建项目、写工具、配连接串、注册服务、本地启动、验证查询链路最后把模型调用的 Key 和通道统一交给 TaoToken 管理避免在多个客户端之间反复切换配置。全程可复制代码能直接跑。2. TaoToken 前置把模型调用的 Key 统一管起来在动手写 MCP 服务器之前先把模型调用这一层理清楚。很多人的痛点是Cursor 里配一个 KeyVS Code 里配一个Claude Desktop 里再配一个用的还是不同厂商的模型改一次配置要翻好几个文件。TaoToken 在这里扮演的角色是统一入口——你只需要在它这里管理 Key 和通道各个客户端都指向同一个地址。具体来说你需要做两件事。第一在 TaoToken 控制台创建一个 API Key这个 Key 就是你所有模型调用的凭证。第二记住两个地址官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。注意 API 地址后面不加任何参数保持干净。如果你后续要做长期编码或者 Agent 类任务可以关注 Coding Plan 这个入口它更适合高频、长时间的模型调用场景。而如果你只是想先验证某个模型能不能用直接去模型对话页面试一下就行。对于本篇的 MCP 服务器来说你暂时不需要在 .NET 代码里直接调用模型——MCP 服务器负责的是数据库工具模型调用发生在 AI 客户端那一侧。但把 Key 统一到 TaoToken意味着你以后换模型、加通道都不用改 MCP 服务器的代码。提示API Key 不要硬编码进 appsettings.json 提交到仓库。本地开发用环境变量或 user-secrets生产环境用密钥管理服务。3. 可复制配置appsettings.json 与 MCP 服务注册骨架现在进入正题。先建项目再配连接串最后写服务注册。这一节的所有代码你都可以直接复制。3.1 创建项目与安装依赖打开终端执行下面这组命令。我们建一个控制台项目用 SQLite 做演示数据库Dapper 做数据访问。dotnet new console -n DbQueryMCP cd DbQueryMCP dotnet add package ModelContextProtocol --prerelease dotnet add package Microsoft.Extensions.Hosting dotnet add package Microsoft.Data.Sqlite dotnet add package Dapper这里选 Dapper 而不是 EF Core原因是 MCP 工具需要执行动态 SQLDapper 在拼接和映射上更直接。如果你团队已经重度使用 EF Core也可以替换但要注意 DbContext 的生命周期管理。3.2 appsettings.json 连接串配置在项目根目录创建 appsettings.json内容如下。连接串指向一个本地 SQLite 文件日志级别把 Microsoft 命名空间压到 Warning避免 MCP 的 stdout 通道被日志污染。{ ConnectionStrings: { DefaultConnection: Data Sourcesample.db }, Logging: { LogLevel: { Default: Information, Microsoft: Warning } } }同时确保 csproj 里把这个文件复制到输出目录ItemGroup None Updateappsettings.json CopyToOutputDirectoryPreserveNewest/CopyToOutputDirectory /None /ItemGroup3.3 MCP 服务注册骨架Program.cs 是整个服务器的入口。关键点有三个日志必须走 stderr数据库服务注册为单例MCP 服务器用 stdio 传输并自动扫描程序集里的工具。using DbQueryMCP.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; var builder Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(options { options.LogToStandardErrorThreshold LogLevel.Trace; }); builder.Services.AddSingletonDatabaseService(); builder.Services .AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); await builder.Build().RunAsync();WithToolsFromAssembly()会自动扫描当前程序集中所有带[McpServerToolType]的类把里面的[McpServerTool]方法注册成 AI 可调用的工具。你不需要手动一个个注册。3.4 数据库服务与工具类DatabaseService 封装连接管理和查询执行。这里给出核心方法重点是安全过滤只允许 SELECT禁止注释和危险关键字默认加行数限制。using System.Data; using System.Text; using System.Text.RegularExpressions; using Dapper; using Microsoft.Data.Sqlite; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace DbQueryMCP.Services; public class DatabaseService : IDisposable { private readonly string _connectionString; private readonly ILoggerDatabaseService _logger; private SqliteConnection? _connection; public DatabaseService(IConfiguration configuration, ILoggerDatabaseService logger) { _connectionString configuration.GetConnectionString(DefaultConnection) ?? Data Sourcesample.db; _logger logger; } private async TaskSqliteConnection GetConnectionAsync() { if (_connection is null || _connection.State ! ConnectionState.Open) { _connection new SqliteConnection(_connectionString); await _connection.OpenAsync(); } return _connection; } public async Taskstring ExecuteQueryAsync(string sql, int maxRows 100) { var trimmed sql.Trim().ToUpperInvariant(); if (!trimmed.StartsWith(SELECT)) throw new InvalidOperationException(仅允许执行 SELECT 查询。); if (trimmed.Contains(--) || trimmed.Contains(/*)) throw new InvalidOperationException(查询中不允许包含 SQL 注释。); string[] forbidden [DROP, DELETE, UPDATE, INSERT, ALTER, CREATE, TRUNCATE, EXEC]; foreach (var kw in forbidden) { if (Regex.IsMatch(trimmed, $\b{kw}\b)) throw new InvalidOperationException($查询中包含不允许的关键字 {kw}。); } var conn await GetConnectionAsync(); if (!trimmed.Contains(LIMIT)) sql ${sql.TrimEnd(;)} LIMIT {maxRows}; _logger.LogInformation(执行查询: {Sql}, sql); var rows (await conn.QueryAsync(sql)).ToList(); return rows.Count 0 ? 查询结果为空。 : FormatAsMarkdown(rows); } private static string FormatAsMarkdown(Listdynamic data) { var sb new StringBuilder(); var headers ((IDictionarystring, object)data[0]).Keys.ToList(); sb.AppendLine(| string.Join( | , headers) |); sb.AppendLine(| string.Join( | , headers.Select(_ ---)) |); foreach (var row in data) { var dict (IDictionarystring, object)row; sb.AppendLine(| string.Join( | , headers.Select(h dict.TryGetValue(h, out var v) ? v?.ToString() ?? NULL : NULL)) |); } return sb.ToString(); } public void Dispose() { _connection?.Dispose(); GC.SuppressFinalize(this); } }工具类用特性暴露给 AI。注意[Description]里的文字会直接传给模型写得越清楚AI 越知道什么时候调用。using System.ComponentModel; using DbQueryMCP.Services; using ModelContextProtocol.Server; namespace DbQueryMCP.Tools; [McpServerToolType] public static class QueryTools { [McpServerTool, Description( 在数据库上执行 SQL SELECT 查询并返回 Markdown 表格结果。 仅支持 SELECT禁止修改操作。默认最多返回 100 行。)] public static async Taskstring ExecuteSelectQuery( DatabaseService dbService, [Description(要执行的 SQL SELECT 语句)] string sql, [Description(最大返回行数默认 100)] int maxRows 100) { try { return await dbService.ExecuteQueryAsync(sql, maxRows); } catch (InvalidOperationException ex) { return $查询被拒绝{ex.Message}; } catch (Exception ex) { return $查询执行失败{ex.Message}; } } }4. 验证请求本地启动并跑通查询链路代码写完接下来验证它真的能工作。分三步造数据、启动服务器、在 AI 客户端里提问。4.1 生成示例数据库单独建一个控制台项目跑初始化脚本生成 sample.db然后复制到 DbQueryMCP 的输出目录。using Microsoft.Data.Sqlite; using Dapper; var conn new SqliteConnection(Data Sourcesample.db); conn.Open(); conn.Execute( CREATE TABLE IF NOT EXISTS Employees ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL, Department TEXT, Salary REAL, IsActive INTEGER DEFAULT 1 ) ); conn.Execute( INSERT INTO Employees (Name, Department, Salary, IsActive) VALUES (张三, 技术研发部, 45000, 1), (李四, 产品设计部, 35000, 1), (王五, 技术研发部, 38000, 1), (赵六, 市场营销部, 30000, 0), (钱七, 技术研发部, 50000, 1) ); Console.WriteLine(示例数据库初始化完成。);运行一次后把生成的 sample.db 放到 DbQueryMCP 项目的 bin 输出目录下。4.2 启动 MCP 服务器在 DbQueryMCP 目录执行dotnet run如果配置正确进程会挂起等待 stdio 输入不会有报错。这说明 MCP 服务器已经就绪。4.3 在 Cursor 中配置并提问在项目根目录创建 .cursor/mcp.json{ mcpServers: { db-query: { command: dotnet, args: [run, --project, D:\\path\\to\\DbQueryMCP\\DbQueryMCP.csproj] } } }重启 Cursor 后在对话里问“帮我查一下技术研发部所有在职员工的姓名和薪资按薪资从高到低排列。”AI 会自动组装 SQL 并调用 ExecuteSelectQuery返回一张 Markdown 表格。你看到的不是一段需要手动执行的 SQL而是直接呈现的查询结果。5. 本篇常见错排查实际跑的时候下面这几个坑我见过太多次。第一个坑日志写到了 stdout。MCP 协议用 stdout 传 JSON-RPC 消息如果你用默认的AddConsole()不加LogToStandardErrorThreshold日志会混进协议通道客户端直接解析失败。表现是 Cursor 里工具列表加载不出来。解决办法就是第 3.3 节里那段配置把阈值设成 Trace强制日志走 stderr。第二个坑连接串路径不对。Data Sourcesample.db是相对路径相对于进程的工作目录。如果你在 Cursor 里用dotnet run --project启动工作目录可能不是项目目录导致找不到数据库文件。稳妥做法是用绝对路径或者在 Program.cs 里把当前目录打印到 stderr 确认一下。第三个坑工具方法不是 static。[McpServerTool]标注的方法必须是 public static参数里的服务从 DI 容器解析基本类型由 AI 传入。如果你写成实例方法扫描时会被跳过AI 看不到这个工具。第四个坑SQL 过滤误伤字段名。比如你有个字段叫CreatedAt里面包含CREATE吗不包含。但如果字段叫UpdateTime正则\bUPDATE\b不会匹配UpdateTime因为\b要求单词边界。真正会误伤的是字段名恰好等于关键字的情况这种建议改字段名或者把过滤逻辑改成基于语法解析。第五个坑SQLite 并发写锁。如果你把 MCP 服务器配成多个客户端同时连SQLite 的单写锁会导致偶发失败。演示够用生产建议换 SQL Server 或 PostgreSQL连接串改一下DatabaseService 里的 PRAGMA 查询换成 INFORMATION_SCHEMA 查询即可。6. 把 Key 和通道收口继续往下走到这里一个能跑的数据库查询 MCP 服务器已经成型。你有了可复制的 appsettings.json、服务注册骨架、安全过滤逻辑也验证了从 AI 客户端到数据库的完整链路。接下来要做的是把模型调用这一层也收口。如果你只是本地玩玩现在的配置够了。但如果你打算把这个 MCP 服务器接到多个客户端、多个模型上建议去 TaoToken 控制台把 API Key 建好后续所有客户端的模型调用都指向同一个入口。需要排障或者接入细节看接入文档想先试试模型效果去模型对话如果是长期编码或 Agent 场景Coding Plan 更合适。API Keys 管理页面在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 模型对话在 https://taotoken.net/chat Coding Plan 在 https://taotoken.net/coding-plan 。把这些地址存下来下次换模型就不用翻聊天记录了。最后留一个实用建议MCP 工具的[Description]值得你花时间打磨。它不是注释是给模型看的说明书。描述里写清楚“什么时候用”“返回什么”“有什么限制”AI 调用工具的准确率会明显不一样。我试过把同一个工具的 Description 从“查询数据”改成“在指定表按条件搜索支持模糊匹配返回 Markdown 表格”模型选错工具的次数少了一大半。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案