[MCP系列2]一小时用C#写一个MCP工具,让大模型访问本地Oracle数据库(完整代码可运行版)

发布时间:2026/10/15 10:49:29
[MCP系列2]一小时用C#写一个MCP工具,让大模型访问本地Oracle数据库(完整代码可运行版) 1. 为什么我要用 C# 写一个 MCP 工具去连 Oracle先说清楚这东西是什么。MCPModel Context Protocol是一套让大模型调用外部工具的通信协议你可以把它理解成大模型和你的本地程序之间的一根“数据线”大模型负责理解你要查什么你的 C# 程序负责真正去 Oracle 里把数据捞出来。能做什么最直接的场景就是——你在支持 MCP 的客户端里打一句“帮我查一下订单表里最近 10 条记录”大模型自动生成 SQL、调用你写的工具、把 Oracle 返回的结果整理成人话给你。适合谁适合手头有本地 Oracle 库、又想让大模型直接读数据的后端和运维同学尤其是 .NET 技术栈的团队。我试过用 Python 和 Node 各写一版最后发现 C# 在 Windows 本地环境里跑 Oracle 反而最省心因为 Oracle.ManagedDataAccess 这个驱动不需要额外装客户端一个 NuGet 包搞定。这篇就按“一小时能跑起来”的节奏来先搭项目骨架再写 MCP Server 的工具定义然后配好 Oracle 连接串最后在客户端里真实调用一次查询并验证返回。中间会给出可直接复制的项目结构、配置片段和启动命令踩过的坑也会标出来。需要提前说明的是MCP 的传输层这里用的是 stdio标准输入输出也就是客户端启动你的 exe双方通过管道收发 JSON-RPC 消息。这个方式最适合本地工具不需要开端口、不需要网络暴露安全性也好。下面所有代码基于 .NET 9.0 SDK 和 ModelContextProtocol 这个官方 C# SDK 的预发布版本实测可编译可运行。2. 前置准备TaoToken 统一 Key 与 MCP 客户端环境在动手写代码之前先把“大模型那一端”准备好。MCP 工具本身只负责查数据真正发起调用的是支持 MCP 的客户端比如 Cursor、Cline 这类。这些客户端背后要接一个大模型来理解你的自然语言而模型调用需要一个统一的 API 通道。我用的是 TaoToken 提供的统一 Key/API 通道官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它把多家模型的调用收敛成一个 Base URL 加一个 Key配置起来不用来回切换。具体怎么拿 Key进控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 在 API Keys 页面新建一个密钥复制出来备用。API 端点固定是 https://taotoken.net/api 注意这个地址后面不加任何 UTM 参数直接填就行。如果你只是想先验证模型通不通可以去模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 发一句话试试如果是长期做编码和 Agent 类任务Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 会更划算。环境这边你需要三样东西。第一.NET 9.0 SDK去官网下安装包装完在命令行敲dotnet --version能看到 9.x 就对了。第二Oracle 数据库的连接信息包括主机、端口、服务名、用户名、密码这些待会要写进连接串。第三一个支持 MCP 的客户端我这里用 Cursor 演示它的 MCP 配置文件是mcp.json路径一般在用户目录下的.cursor文件夹里。依赖包通过 NuGet 装服务端项目目录下执行这两条dotnet add package ModelContextProtocol --prerelease dotnet add package Oracle.ManagedDataAccess.Core第一条是 MCP 的 C# SDK第二条是 Oracle 的托管驱动。注意 SDK 目前还是 prerelease所以要带--prerelease参数否则 NuGet 找不到。装完dotnet restore一下确认没报错。到这一步前置环境就齐了接下来进正题写代码。3. 可复制配置项目结构、Oracle 连接串与 MCP 工具定义先把项目骨架搭出来。整个方案分两个项目OracleMcpServer是真正干活的 MCP 服务端OracleMcpClient是可选的调试客户端如果你直接用 Cursor 调客户端可以不要但留着方便本地测 JSON-RPC。目录结构长这样OracleMcpDemo/ ├── OracleMcpServer/ │ ├── OracleMcpServer.csproj │ ├── Program.cs │ ├── DatabaseTool.cs │ └── appsettings.json └── OracleMcpClient/ ├── OracleMcpClient.csproj └── Program.cs服务端的appsettings.json放连接串别硬编码在代码里{ Oracle: { ConnectionString: User Idyour_user;Passwordyour_pwd;Data Source(DESCRIPTION(ADDRESS(PROTOCOLTCP)(HOST127.0.0.1)(PORT1521))(CONNECT_DATA(SERVICE_NAMEORCLPDB1)));Poolingtrue;Min Pool Size1;Connection Timeout15; } }这里Data Source用的是 EZConnect 的完整描述符写法比简写host:port/service更稳尤其是服务名带特殊字符的时候。Poolingtrue一定要开MCP 工具会被频繁调用每次新建连接开销很大。服务端入口Program.cs负责注册 MCP 服务和工具using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using ModelContextProtocol.Server; var builder Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(options { options.LogToStandardErrorThreshold LogLevel.Trace; }); builder.Services .AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); await builder.Build().RunAsync();关键点是LogToStandardErrorThreshold日志必须走 stderr因为 stdout 被 JSON-RPC 消息占用了日志混进去会直接把协议搞乱这是新手最容易踩的坑。工具定义在DatabaseTool.cs用特性注册using System.ComponentModel; using System.Text.Json; using ModelContextProtocol.Server; using Oracle.ManagedDataAccess.Client; [McpServerToolType] public static class DatabaseTool { [McpServerTool, Description(Query Oracle database and return rows as JSON. Only SELECT statements are allowed.)] public static string QueryOracle( [Description(The SQL SELECT statement to execute)] string sql) { if (!sql.TrimStart().StartsWith(SELECT, StringComparison.OrdinalIgnoreCase)) return JsonSerializer.Serialize(new { error Only SELECT statements are allowed. }); var connStr Environment.GetEnvironmentVariable(ORACLE_CONN) ?? User Idyour_user;Passwordyour_pwd;Data Source127.0.0.1:1521/ORCLPDB1;; try { using var conn new OracleConnection(connStr); conn.Open(); using var cmd new OracleCommand(sql, conn) { CommandTimeout 30 }; using var reader cmd.ExecuteReader(); var rows new ListDictionarystring, object?(); while (reader.Read()) { var row new Dictionarystring, object?(); for (int i 0; i reader.FieldCount; i) row[reader.GetName(i)] reader.IsDBNull(i) ? null : reader.GetValue(i); rows.Add(row); } return JsonSerializer.Serialize(new { count rows.Count, rows }, new JsonSerializerOptions { WriteIndented true }); } catch (Exception ex) { return JsonSerializer.Serialize(new { error ex.Message }); } } }工具名QueryOracle、描述、参数说明都会通过tools/list暴露给大模型模型就是靠这段英文描述来判断什么时候该调它。描述写清楚“只支持 SELECT”能减少模型乱生成 DDL 的概率。连接串优先读环境变量ORACLE_CONN方便在不同机器上切换而不用改代码。4. 验证请求启动服务并在客户端真实调用查询代码写完先编译cd OracleMcpServer dotnet build -c Release产物在bin/Release/net9.0/OracleMcpServer.exe。先别急着接客户端用最原始的方式验证一下协议通不通。手动喂一条初始化请求echo {jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:test,version:1.0}}} | ./OracleMcpServer.exe正常的话你会看到返回里带protocolVersion和serverInfo。接着验证工具列表echo {jsonrpc:2.0,id:2,method:tools/list,params:{}} | ./OracleMcpServer.exe返回里应该能看到QueryOracle这个工具及其 inputSchema。最后直接调一次真实查询echo {jsonrpc:2.0,id:3,method:tools/call,params:{name:QueryOracle,arguments:{sql:SELECT * FROM USER_TABLES WHERE ROWNUM 3}}} | ./OracleMcpServer.exe如果 Oracle 连接正常你会拿到一段 JSON里面有count和rows数组每行是列名到值的映射。这一步能跑通说明服务端完全没问题。接下来配 Cursor。打开mcp.json加入{ mcpServers: { oracle-local: { command: D:/OracleMcpDemo/OracleMcpServer/bin/Release/net9.0/OracleMcpServer.exe, args: [], env: { ORACLE_CONN: User Idyour_user;Passwordyour_pwd;Data Source127.0.0.1:1521/ORCLPDB1; } } } }注意command用正斜杠或双反斜杠Windows 下单反斜杠会被当转义。env里把连接串传进去这样 exe 不用改。保存后重启 Cursor在 MCP 面板里应该能看到oracle-local处于绿色可用状态点开 Tools 能看到QueryOracle。然后在聊天窗口输入“调用 QueryOracle 查一下当前用户下有哪些表最多返回 5 个”。模型会生成类似SELECT TABLE_NAME FROM USER_TABLES WHERE ROWNUM 5的语句并调用工具几秒后返回结果。如果看到表格形式的数据整条链路就通了。实测下来从零到这一步熟练的话四十分钟左右。5. 本篇常见错误排查401、local proxy failed 与 reading choices接入过程里报错基本集中在几个地方我按真实遇到的顺序列一下。第一个是401 Unauthorized。这个通常不是 MCP 服务端的问题而是客户端背后调模型时 Key 不对。检查mcp.json里如果配了模型相关的环境变量确认 Key 是从 TaoToken 控制台复制的完整字符串Base URL 填的是https://taotoken.net/api末尾不要多斜杠也不要带参数。Key 前后有空格也会 401粘贴时注意。第二个是local proxy failed或spawn ENOENT。这是客户端启动你的 exe 失败八成是路径写错了。用绝对路径并且确认 exe 真的在那个位置。如果路径里有空格整个字符串要用引号包住。还有一种情况是 exe 依赖的 DLL 没一起拷过去dotnet build默认会把依赖放同目录但如果你手动挪了 exe 就会缺 DLL用dotnet publish更保险。第三个是Unexpected token或reading choices之类的解析错误。这多半是服务端把日志打到了 stdout污染了 JSON-RPC 流。回去检查Program.cs里LogToStandardErrorThreshold有没有设成LogLevel.Trace以及工具方法里有没有Console.WriteLine这种直接输出。所有调试信息要么走 stderr要么写文件。第四个是 Oracle 连接报ORA-12541: TNS:no listener或ORA-12514。前者是监听没起或端口不对后者是服务名写错。用tnsping或直接用 SQL Developer 先确认连接串本身能连上再往 MCP 里放。托管驱动不需要装 Oracle 客户端但网络必须通。第五个是工具调用返回Only SELECT statements are allowed。这是我在代码里加的白名单校验如果你确实需要执行其他语句自己改判断逻辑但生产环境强烈建议保留防止模型生成危险 SQL。排查顺序建议先手动 echo 测服务端再测客户端启动最后测模型调用。一层层隔离比一上来就在客户端里瞎试快得多。6. 把这条链路用起来从本地工具到日常查询工具跑通之后日常用起来其实很顺手。我现在的习惯是把常用的查询封装成几个固定的工具方法比如“查最近订单”“查用户余额”每个方法内部写死 SQL 模板只留参数这样模型不需要自己拼 SQL既快又安全。MCP 的好处就在这——你注册什么工具模型就能用什么能力边界完全由你控制。如果后面要接更多数据源比如同时连 Oracle 和 MySQL就在DatabaseTool.cs里再加一个[McpServerTool]方法客户端重启后自动发现不用改架构。需要长期跑 Agent 类任务的话模型调用这块可以用 Coding Plan 的额度配合本地 MCP 工具做数据查询整个工作流就闭环了。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 有更细的协议说明遇到 SDK 层面的问题可以去翻。最后提醒一句这套东西目前定位是本地小工具别直接暴露到公网。SQL 白名单、连接串加密、调用日志这些该加就加尤其是连生产库的时候权限给只读账号别用 DBA。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询