1. 从一次工具调用失败说起:Agent Framework 接 MCP 到底卡在哪
如果你正在用 Agent Framework 搭智能体,大概率会遇到这样一个场景:模型本身能聊天,但一让它去查文档、读数据库、调内部接口,就开始胡编。原因不复杂——大模型的知识停在训练截止那一刻,它不知道你公司昨天上线的接口长什么样。MCP(Model Context Protocol)就是来解决这件事的:它用一套开放标准,把外部工具和上下文数据以统一方式喂给模型,让 Agent 能真正“动手”。
但真正动手时,问题往往出在链路的最后一段。Agent Framework 里注册 MCP 工具本身不难,McpClient.CreateAsync加一个HttpClientTransport就能把远端 MCP Server 的工具列表拉回来,转成AITool塞给AIAgent。难的是:这些工具调用最终要落到一个模型 endpoint 上,而 endpoint 的鉴权、计费、模型切换如果每个项目各配一套,维护成本会迅速失控。我见过太多团队在 demo 阶段用临时 Key 跑通,一上多环境就乱成一锅粥。
这篇要解决的就是这个“最后一段”。我会用一个可复制的 Console 示例,把 MCP 服务注册成 Agent Framework 可调用工具,同时把请求 endpoint 统一改到 TaoToken 的 Key/API 通道上。TaoToken 在这里扮演的角色是统一入口:一个 Key 覆盖多家模型,Base URL 固定,模型 ID 按需切换,Agent 侧不用为每个模型改代码。适合谁看?正在用 Agent Framework 做工具调用、被多模型 Key 管理折磨、想让 MCP 工具链路稳定跑起来的开发者。下面从环境准备开始,一步步跑通注册到返回结果的完整流程。
2. TaoToken 统一通道前置准备:Base URL、Key 与模型 ID 三件套
在写 Agent 代码之前,先把通道侧的东西备齐。TaoToken 的接入信息就三样:Base URL、API Key、Model ID。这三样在 Agent Framework 里会分别落到HttpClientTransport的 endpoint、请求头的鉴权、以及AIAgent创建时的 deploymentName 上。任何一环对不上,后面就是 401 或者模型找不到。
Base URL 用https://taotoken.net/api,注意这里不带任何查询参数,保持干净。API Key 去控制台生成,路径是 API Keys 页面,生成后只显示一次,复制到环境变量里,别硬编码进源码。Model ID 取决于你要调哪个模型,TaoToken 的模型对话页面能看到当前可用的模型列表,选一个支持工具调用的,比如带 function calling 能力的版本。工具调用对模型有要求,不是所有模型都能稳定解析 tool schema,这点后面排障会细说。
环境变量建议这样设,Windows 用 setx,macOS/Linux 写进 shell profile:
export TAOTOKEN_API_KEY="sk-你的key" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_MODEL="你的模型ID"为什么强调环境变量而不是配置文件?因为 Agent Framework 的示例里经常出现Environment.GetEnvironmentVariable直接读,这样多环境切换只改环境变量,代码零改动。另外提醒一句,Key 不要提交到 Git,.gitignore里加上.env和appsettings.Development.json。
这里有个容易忽略的点:MCP Server 的 endpoint 和模型 endpoint 是两个不同的地址。MCP Server 提供工具定义,模型 endpoint 负责推理和工具调用决策。很多人第一次配的时候把两者混在一起,结果工具列表拉回来了,但模型请求发到了 MCP Server 上,直接报错。记住分工:HttpClientTransport指向 MCP Server,TaoToken 的 Base URL 指向模型服务,两者通过 Agent 的 tools 参数关联起来。
准备好这三件套,就可以进代码了。下一节给完整的可复制配置,包括 MCP 注册和 endpoint 改写。
3. 可复制配置:把 MCP 服务注册为工具并改到 TaoToken 通道
先建一个 Console 项目,加 NuGet 包。核心包是ModelContextProtocol,负责 MCP 客户端;Agent 侧用Microsoft.Agents.AI相关包;如果走 Azure 身份认证还需要Azure.Identity,但既然我们改到 TaoToken 通道,认证就走 API Key,不需要 Azure 那套。包引用如下:
<PackageReference Include="ModelContextProtocol" Version="0.3.0-preview" /> <PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-preview" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />接下来是 MCP 客户端注册。这里用 HTTP 传输方式,连一个远端 MCP Server。示例里用 Microsoft Learn 的公开 MCP Server,你可以换成自己的:
using ModelContextProtocol.Client; using ModelContextProtocol.Protocol.Transport; await using McpClient mcpClient = await McpClient.CreateAsync( new HttpClientTransport(new HttpClientTransportOptions { Endpoint = new Uri("https://learn.microsoft.com/api/mcp"), Name = "Microsoft Learn MCP", })); IList<McpClientTool> mcpTools = await mcpClient.ListToolsAsync(); Console.WriteLine($"可用的 MCP 工具:{string.Join(", ", mcpTools.Select(t => t.Name))}"); List<AITool> wrappedTools = mcpTools.Select(tool => (AITool)tool).ToList();注意HttpClientTransportOptions里的Endpoint是 MCP Server 地址,不是模型地址。ListToolsAsync返回的是工具定义,包含 name、description、input schema,这些会作为 tool schema 传给模型。
然后是关键一步:把模型 endpoint 改到 TaoToken。Agent Framework 里创建AIAgent时,如果用AIProjectClient那套,默认走 Azure 的 endpoint。我们要换成 OpenAI 兼容的客户端,指向 TaoToken 的 Base URL。配置片段如下,用 JSON 存 settings,路径放在项目根目录的appsettings.json:
{ "TaoToken": { "BaseUrl": "https://taotoken.net/api", "ApiKey": "", "ModelId": "你的模型ID" }, "Mcp": { "Endpoint": "https://learn.microsoft.com/api/mcp", "Name": "Microsoft Learn MCP" } }ApiKey 留空,运行时从环境变量注入,避免明文。读取配置并创建 Agent:
using Microsoft.Extensions.Configuration; using OpenAI; using OpenAI.Chat; var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build(); string baseUrl = config["TaoToken:BaseUrl"] ?? "https://taotoken.net/api"; string apiKey = Environment.GetEnvironmentVariable("TAOTOKEN_API_KEY") ?? throw new InvalidOperationException("未设置 TAOTOKEN_API_KEY"); string modelId = config["TaoToken:ModelId"] ?? "你的模型ID"; var client = new ChatClient( model: modelId, credential: new ApiKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint = new Uri(baseUrl) }); AIAgent agent = client.AsAIAgent( instructions: "你是一个可以调用 MCP 工具的助手,优先使用工具获取实时信息。", name: "McpAgent", tools: wrappedTools);这里三件套齐了:Base URL 是https://taotoken.net/api,Key 从环境变量来,Model ID 从配置读。AsAIAgent把工具列表绑上去,模型在推理时会看到这些工具的 schema,需要时发起调用。
如果你用的是 Claude Code 或 Cline 这类工具,配置思路一样,只是文件位置不同。Claude Code 的 settings 里配 Base URL 和 Key,Cline 的 MCP 配置里写 server 地址。核心都是把模型请求指向 TaoToken,把工具定义指向 MCP Server。Codex 的auth.json也是同理,Base URL 填 TaoToken 的地址,Key 填生成的 Key,Model ID 填对应模型。三件套走到哪都是这三样,别被不同工具的配置文件格式绕晕。
配置写完,下一节验证一次真实调用。
4. 验证请求:一次工具调用从发起到返回结果
配置对不对,跑一次就知道。验证分两步:先确认工具列表拉回来了,再确认模型能正确调用工具并返回结果。
第一步,运行程序,看控制台输出的工具列表。正常情况会打印出 MCP Server 提供的工具名,比如microsoft_docs_search、microsoft_code_sample_search之类。如果这里是空的,说明 MCP 连接有问题,先别往下走,去第 5 节排障。
第二步,发一个需要工具才能回答的问题。比如问“如何使用 Azure CLI 创建存储账户”,这个问题模型自己答可能过时,但通过 MCP 工具查 Microsoft Learn 就能拿到最新文档。代码:
string prompt = "如何使用 Azure CLI 创建 Azure 存储账户?请使用工具查询最新文档。"; Console.WriteLine($"用户:{prompt}"); AgentResponse response = await agent.RunAsync(prompt); Console.WriteLine($"智能体:{response}");运行后,观察输出。成功的标志是:模型没有直接编答案,而是先发起 tool call,参数里带上查询关键词,MCP Server 返回文档片段,模型再基于片段组织回答。控制台可能看到类似“正在调用工具 microsoft_docs_search”的日志,最终回答里包含具体的 CLI 命令,比如az storage account create加参数。
再发第二个问题验证多轮:“什么是 Microsoft Agent Framework?”这个问题可能不需要工具,模型直接答。两次调用都走同一个 TaoToken 通道,Key 和 Base URL 不变,只是模型决策不同。这说明通道是通的,工具调用链路也是活的。
如果你想更直观地看请求,可以在OpenAIClientOptions里打开日志,或者用中间件打印 request/response。注意别把 Key 打到日志里。验证通过后,这套配置就可以复制到其他 Agent 项目,只改 Model ID 和 MCP endpoint 即可。
实测下来,最容易出问题的是模型不支持工具调用,或者 tool schema 格式不兼容。下一节把常见报错列出来。
5. 常见报错排查:401、local proxy failed 与 reading choices
工具调用链路跑不通,报错通常集中在几个地方。下面按真实遇到的错误对照排查。
401 Unauthorized。这个最直接,Key 不对或没传。检查三处:环境变量TAOTOKEN_API_KEY是否设置成功,代码里读取的变量名是否一致,请求头里是否真的带上了Authorization: Bearer <key>。有时候配置文件里写了 Key 但环境变量为空,代码优先读环境变量,结果传了空字符串。另外确认 Base URL 是https://taotoken.net/api,不要多加斜杠或路径,路径错了鉴权也会失败。
local proxy failed。这个报错通常出现在网络层,意思是请求没发出去。检查 MCP Server 的 endpoint 是否可达,以及模型 endpoint 是否可达。两个地址分开测:用 curl 直接请求 MCP Server 看返回,用 curl 请求 TaoToken 的 Base URL 看鉴权。如果 MCP Server 是内网地址,确认当前网络能访问。注意不要用任何网络代理工具,直连即可。
reading choices 相关报错。这类错误说明请求发出去了,但响应解析失败。常见原因是模型返回格式和客户端预期不一致,或者模型不支持工具调用。检查 Model ID 是否选对了支持 function calling 的模型。有些模型只支持纯文本,传了 tools 参数后返回结构不对,客户端解析choices时就报错。换一个支持工具调用的模型 ID 再试。
OAuth 相关报错。如果你之前用 Azure 身份认证,切到 TaoToken 后可能残留 OAuth 配置。检查代码里是否还有DefaultAzureCredential或AIProjectClient的调用,这些会尝试走 OAuth 流程。改成 API Key 认证后,这些依赖应该移除。Claude Code 或 Cline 里如果配了 OAuth,也要改成 API Key 模式。
工具列表为空。MCP 连接成功但ListToolsAsync返回空,检查 MCP Server 是否需要额外的初始化参数,或者 endpoint 路径是否正确。有些 MCP Server 的工具列表在/tools路径下,HttpClientTransport的 endpoint 要指到根路径,客户端会自动拼接。
模型不调用工具。工具列表有了,但模型总是直接回答。检查 instructions 里是否明确要求优先使用工具,以及工具的 description 是否清晰。模型靠 description 判断什么时候调用,描述太模糊它就不调。另外确认模型本身支持工具调用,这是硬性前提。
排查顺序建议:先确认 Key 和 Base URL,再确认 MCP endpoint,最后确认模型能力。大部分问题在前两步。
6. 把通道固定下来:Agent 工具调用的长期维护思路
跑通一次不难,难的是长期稳定。我的做法是把 TaoToken 的三件套固定成项目级配置,所有 Agent 共用一套 Base URL 和 Key,Model ID 按 Agent 角色分配。这样新增 Agent 时只改 Model ID,通道侧不动。MCP Server 的注册也抽成工厂方法,传入 endpoint 和 name 就返回工具列表,避免每个 Agent 重复写连接代码。
另一个实用技巧是给工具调用加日志和超时。MCP 工具可能因为网络或服务端问题卡住,Agent 会一直等。在HttpClientTransport上配 HttpClient 的 Timeout,比如 30 秒,超时后让模型走降级回答。日志里记录 tool name、参数、耗时,出问题时能快速定位是哪个工具拖慢了链路。
如果你要长期跑编码类 Agent,Coding Plan 比按量计费更划算,适合高频工具调用的场景。模型对话页面可以随时验证某个模型是否支持工具调用,接入文档里有各语言的完整示例。把这些链接存下来,下次配新项目直接查。
最后说个踩过的坑:别把 MCP Server 直连生产数据库。MCP 工具的能力边界要控制好,只暴露只读查询或受限操作,写操作走审批流程。Agent 再智能,也不该有直接改生产数据的权限。通道统一是为了管理方便,权限收窄是为了安全,两件事都要做。