☰
MCP 学习笔记:从核心概念到安全边界,一次搞懂 Model Context Protocol 架构
2026/10/8 17:46:19 网站建设 项目流程

1. 先搞清楚 MCP 到底解决什么问题

MCP 全称 Model Context Protocol,是一个开放协议,用来把大语言模型应用和外部工具、数据源、上下文系统连起来。它不替代大模型,也不替代普通 API,而是给 AI 应用提供一套标准化方式,让模型应用能一致地发现、访问、调用外部能力。适合谁?适合刚接触 MCP、想搞懂 Host/Client/Server 三层架构、并且想亲手跑通一次工具调用的开发者。

在没有 MCP 的时候,一个智能助手要访问文件系统、数据库、搜索服务,开发者往往得分别写三套封装。工具少还能忍,工具一多就出问题:不同 AI 应用重复封装相同工具,复用性差;工具描述、参数格式、返回格式、错误格式没有统一标准;权限控制分散,难以统一审计;模型上下文里要塞大量工具定义,上下文成本高;本地工具、远程服务、企业内部系统之间缺少统一连接方式。

MCP 的思路是把这层连接抽象出来。AI 应用不再直接面向每个外部系统写死集成逻辑,而是通过 MCP Client 连接不同的 MCP Server,Server 再以统一协议暴露工具、资源和提示模板。这样工具接入就变得可发现、可复用、可授权、可审计。

我试过把一个本地文件查询工具从“直接写死在应用里”改成 MCP Server 暴露,最大的感受是:模型侧看到的工具 schema 变干净了,权限声明也集中到了一处,排查问题时不用在应用代码和工具代码之间来回跳。

这一篇会按“概念—架构—最小配置—调用验证—安全边界”的顺序走一遍,重点放在能复制、能跑通、能排错。你跟着做完,至少能得到一个本地 MCP Server 的最小可运行配置,以及一次真实的工具调用结果。

2. TaoToken 前置准备:把模型侧入口先配好

MCP 本身是协议层,它不提供模型推理能力。你要验证一次完整的工具调用链路,除了 MCP Server,还需要一个能发起对话、能识别工具调用的模型入口。这里我用 TaoToken 来做模型侧接入,原因是它的接口形态和常见 OpenAI 兼容接口一致,配置成本低,适合拿来跑通 MCP 的工具调用验证。

先把三个关键信息准备好:Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api,API Key 在控制台的 API Keys 页面创建,Model ID 按你实际要验证的模型填。这三个东西后面在 MCP Client 的配置里会反复出现,建议先记在一个临时文件里。

创建 Key 的入口在这里:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_learning_notes

如果你只是想先看看模型对话效果,不急着接 MCP,可以先用模型对话页面确认 Key 能用:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_learning_notes

接入文档在这里,遇到参数不确定时对照看:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_learning_notes

这里要强调一点:MCP 和 tool calling 不是同一个层级的概念。Tool calling 关注的是模型如何以结构化方式请求调用某个工具;MCP 关注的是工具、资源、提示模板如何以标准化协议暴露给不同 AI 应用。Function calling 解决“模型如何调用工具”,MCP 解决“工具如何被发现、连接、复用和管理”。两者是互补关系,不是替代关系。

在实际系统里,MCP 暴露出来的 tools 最终仍会被转换成模型可用的 tool schema。模型生成 tool call 后,Host 再通过 MCP Client 调用对应 MCP Server。所以你在配置时,模型侧只需要一个能识别 tool schema 的接口,MCP 侧负责工具发现和转发。

如果你打算长期做编码类或 Agent 类任务,可以考虑 Coding Plan,它在多轮工具调用场景下更省心:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_learning_notes

3. 可复制配置:本地 MCP Server 最小实现

这一节给你一份可以直接复制运行的本地 MCP Server 最小配置。我用 Python 的 FastMCP 来写,因为它把 tool、resource、prompt 的声明都封装成了装饰器,适合用来理解 MCP Server 的核心职责:声明并提供能力。

先装依赖:

pip install mcp

然后新建一个文件example_server.py:

from mcp.server.fastmcp import FastMCP import sys mcp = FastMCP("example-server") @mcp.tool() def get_weather(city: str) -> str: """Get weather information for a city. Args: city: City name. """ return f"Weather information for {city}." @mcp.resource("config://application") def get_application_config() -> str: """Return application configuration as a resource.""" return "application configuration content" @mcp.prompt() def summarize_prompt(topic: str) -> str: """Return a reusable prompt template.""" return f"Please summarize the following topic: {topic}" if __name__ == "__main__": print("server started", file=sys.stderr) mcp.run(transport="stdio")

这段代码里,@mcp.tool()声明可调用工具,@mcp.resource()声明可读取资源,@mcp.prompt()声明可复用提示模板,mcp.run(transport="stdio")表示通过 STDIO 方式运行本地 Server。

这里有个容易踩的坑:STDIO transport 下,标准输入输出通常用于承载协议消息。如果你往 stdout 随意打印普通日志,可能破坏协议通信。所以日志要写到 stderr 或日志文件,上面代码里print("server started", file=sys.stderr)就是正确做法。

接下来是 MCP Client 侧的配置。不同 Host 的配置文件路径不一样,但核心字段就三个:Base URL、Key、Model ID。以常见的 JSON 配置为例:

{ "mcpServers": { "example-server": { "command": "python", "args": ["/absolute/path/to/example_server.py"], "env": { "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_MODEL_ID": "你的ModelID" } } } }

注意args里要用绝对路径,相对路径在不同 Host 的工作目录下容易找不到文件。env里的三个变量是给模型侧接入用的,MCP Server 本身不直接消费它们,但 Host 在把工具结果回填给模型时会用到。

如果你用的是 TOML 格式的配置,等价写法是:

[mcp_servers.example-server] command = "python" args = ["/absolute/path/to/example_server.py"] [mcp_servers.example-server.env] TAOTOKEN_BASE_URL = "https://taotoken.net/api" TAOTOKEN_API_KEY = "sk-你的Key" TAOTOKEN_MODEL_ID = "你的ModelID"

配置写完后,先别急着接模型,单独启动一次 Server 看能不能正常跑起来:

python /absolute/path/to/example_server.py

如果终端没有报错,并且 stderr 输出了server started,说明 Server 进程本身没问题。接下来才是让 Host 通过 MCP Client 去连接它。

4. 验证请求:跑通一次工具调用

配置就绪后,下一步是验证整条链路:Host 创建 MCP Client,Client 连接 Server,完成初始化和能力协商,获取 tools/resources/prompts,然后发起一次工具调用。

一个典型的 MCP 交互流程是这样的:Host 构造上下文,MCP Client 连接 MCP Server,Server 返回可用能力列表,Host 把相关工具暴露给模型,模型生成工具调用请求,MCP Client 调用 Server,Server 执行并返回结果,Host 把结果放回模型上下文,模型生成最终回答。

在 Client 侧,逻辑顺序可以概括为:初始化、发现能力、调用工具、处理结果。概念性伪代码长这样:

async def run_client() -> None: async with create_mcp_session("example-server") as session: await session.initialize() tools = await session.list_tools() print("Available tools:", tools) result = await session.call_tool( name="get_weather", arguments={"city": "London"} ) print("Tool result:", result)

这段伪代码省略了具体 SDK 的 transport 创建细节,重点展示调用顺序。实际跑的时候,你要确认三件事:Server 能正常启动、Client 能完成初始化、tools 能被正确发现。

验证时,我建议先单独调list_tools,确认返回的工具列表里有get_weather,并且它的inputSchema里city是必填的 string。确认无误后,再调call_tool,参数传{"city": "London"}。如果返回结果里包含Weather information for London.,说明工具调用链路是通的。

如果你用的是支持远程 MCP 的平台,也可以把远程 MCP Server 作为工具源直接交给模型平台,由平台负责工具发现、调用和结果回填。这种模式下应用侧集成成本低,但具体能力、鉴权方式、审批策略和支持范围取决于对应平台。

还有一种更通用的方式:应用自己实现 MCP Client,先从 MCP Server 获取工具定义,再转换成模型可识别的 function/tool schema。转换函数大概是这样:

def mcp_tool_to_function_tool(mcp_tool: dict) -> dict: return { "type": "function", "function": { "name": mcp_tool["name"], "description": mcp_tool.get("description", ""), "parameters": mcp_tool.get("inputSchema", {"type": "object"}) } }

这种方式更灵活,适合需要精细控制权限、日志、审计和工具执行策略的系统。转换完成后,模型生成 tool call,应用再通过 MCP Client 调用对应 Server,把结果回填到模型上下文。

验证成功后,你会看到模型在回答里引用了工具返回的内容,而不是凭空编造。这一步是整个 MCP 学习里最有成就感的环节,因为它把前面所有概念都串起来了。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

跑 MCP 工具调用时,报错往往集中在几个固定位置。下面按真实报错逐条对照。

401 Unauthorized:这个最常见,基本是 Key 或 Base URL 配错了。先检查TAOTOKEN_API_KEY是不是完整复制,有没有多余空格;再确认TAOTOKEN_BASE_URL是https://taotoken.net/api,不要漏掉/api。如果 Key 是在别的环境创建的,确认它没有过期或被禁用。排查顺序是:先单独用模型对话页面验证 Key 能用,再回到 MCP 配置里检查。

local proxy failed:这个报错通常出现在 Host 尝试连接本地 MCP Server 时。原因可能是command写错了,比如python不在 PATH 里,或者args里的脚本路径不是绝对路径。先手动在终端跑一遍python /absolute/path/to/example_server.py,确认能启动;如果手动能跑但 Host 报这个错,检查 Host 的工作目录和权限,确保它能访问到脚本文件。

reading choices 相关报错:这类报错一般出现在模型返回结果解析阶段,说明返回结构不符合预期。常见原因是模型侧接口返回的不是标准 tool call 格式,或者 MCP Client 在转换工具 schema 时字段名对不上。检查mcp_tool_to_function_tool里inputSchema的键名是否和模型侧期望的一致,有些接口要求parameters而不是inputSchema。

OAuth 相关报错:远程 MCP Server 如果访问用户数据或受保护系统,会涉及认证授权。报错通常表现为 token 无效、scope 不足或回调地址不匹配。排查时先确认授权范围是否覆盖了你要调用的工具,再检查 token 有没有被放进模型上下文(这是错误做法,token 不应暴露给模型)。授权应该由确定性的安全机制控制,而不是依赖模型判断。

除了这些,还有一类隐蔽问题:STDIO 日志污染协议输出。表现是 Server 能启动,但 Client 初始化失败或工具列表为空。检查 Server 代码里有没有往 stdout 打印普通日志,有的话改成 stderr。

排错时建议按链路顺序走:先确认 Server 能独立启动,再确认 Client 能初始化,再确认 tools 能被发现,最后确认 call_tool 能返回结果。每一步都单独验证,比一次性跑全链路更容易定位问题。

如果你在接入文档里找不到对应参数,可以对照 API Keys 页面重新生成一个 Key 试试:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_learning_notes

6. 安全边界检查清单与后续接入

MCP 让 AI 应用能访问更多外部能力,也放大了安全边界问题。只要工具能读取敏感数据、调用外部 API 或执行有副作用的动作,就必须建立明确的权限边界。下面这份检查清单可以直接拿去对照。

工具权限分级:低风险工具,只读、无副作用、可重复执行,可以自动调用,但要记录日志;中风险工具,可能写入非关键数据或影响用户体验,需要参数校验,必要时请求确认;高风险工具,涉及删除、付款、权限变更、外部发送或生产环境操作,必须人工确认,并保留审计和回滚机制。

认证与授权:只授予完成任务所需的最小权限;区分只读权限和写入权限;敏感操作需要用户确认或管理员审批;访问令牌不应暴露给模型上下文;所有高风险操作应可审计。

Prompt Injection 风险:MCP 工具可能返回网页、文档、邮件、评论、数据库字段等外部内容,这些内容可能包含恶意指令。正确做法是把工具返回内容视为不可信数据,而不是系统指令。错误做法是把外部文档中的指令当作最高优先级指令执行;正确做法是把外部文档内容作为 observation,只用于回答任务问题。

数据外发风险:使用远程 MCP Server 时,工具参数、用户输入、上下文片段或资源内容可能被发送到外部服务。系统应明确哪些数据会发送给远程 Server、远程 Server 的维护方是否可信、是否需要脱敏或最小化传输数据、是否记录外发请求日志、是否允许用户撤销授权。

调试与测试 MCP Server 时,关注点包括:Server 是否能正常启动、Client 是否能完成初始化、tools/resources/prompts 是否能被正确发现、工具 schema 是否清晰稳定可解析、工具调用参数是否被正确校验、错误返回是否结构化、STDIO 日志是否污染协议输出、远程连接是否正确处理认证和授权。测试不应只验证“工具能运行”,还要验证“工具如何暴露给模型应用”,因为工具名称、描述、输入 schema 和错误返回都会影响模型是否能正确使用工具。

把这份清单过一遍后,你就可以把本地 MCP Server 接到真实工作流里了。模型侧入口用 TaoToken 的 API 配置,工具侧按最小权限原则声明,日志和审计单独留一份。后续如果要接更多工具,优先复用已有的 MCP Server 声明方式,而不是每个应用单独封装。这样工具生态才能保持可发现、可复用、可授权、可审计。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询