MCP面试知识点
- 1、概念与定位
-
- 1.1、什么是MCP?它解决什么问题?
- 1.2、MCP的核心架构有哪几部分组成?
- [1.3、MCP 与 Function Calling 有什么区别?](#1.3、MCP 与 Function Calling 有什么区别?)
- [1.4、MCP相比直接提供Restful API 给模型用的优势在哪?](#1.4、MCP相比直接提供Restful API 给模型用的优势在哪?)
- 2、协议与通信
-
- 2.1、MCP的底层协议是什么?
- 2.2、MCP的传输方式有哪些?
- [2.3、stdio和Streamable HTTP如何选型](#2.3、stdio和Streamable HTTP如何选型)
- 3、核心原语
- 4、工程实践
-
- [4.1、生产环境用 MCP 要注意哪些问题?](#4.1、生产环境用 MCP 要注意哪些问题?)
- 4.2、工具数量很多时,如何避免塞爆上下文?
- [4.3、如何做 MCP Server 的鉴权?](#4.3、如何做 MCP Server 的鉴权?)
- 5、开放题
-
- [5.1、让你设计一个"数据库查询 MCP Server",你会怎么设计?](#5.1、让你设计一个"数据库查询 MCP Server",你会怎么设计?)
本文由浅入深的介绍在面试中MCP的考察知识点。
1、概念与定位
1.1、什么是MCP?它解决什么问题?
MCP(Model Context Protocol) 是Anthropic在2024年11月开源的模型上下文的开放标准,旨在 制定模型与外部资源、工具的接入标准。
解决的核心问题 是:工具M*N的集成爆炸 问题。在MCP出现之前,我们需要为每个AI应用对接的每个数据源/工具做定制开发(M个应用 * N个工具);在MCP出现后,MCP定义统一标准,我们只需要开发一次工具即可,将集成复杂度降为M+N。
MCP可以类比为"AI应用的Type-C接口"。
1.2、MCP的核心架构有哪几部分组成?
MCP的核心架构主要由 Host(宿主应用) 、MCP Client(客户端) 、MCP Server(服务端) 以及 LLM(大语言模型) 四部分组成。
- Host(宿主应用): 负责承载 AI 应用并管理会话
- MCP Client(客户端): 负责与 MCP Server 建立连接、发送请求
- MCP Server(服务端): 负责暴露工具、资源和提示词能力
- LLM(大语言模型): 负责理解用户意图并决定调用哪些工具
下面是 AI 应用、MCP Server、MCP Client、LLM、Host 之间的交互时序图:
"LLM" "MCP Server" "MCP Client" "Host(AI应用)" "用户" "LLM" "MCP Server" "MCP Client" "Host(AI应用)" "用户" #mermaid-svg-rEBqO6OrnaYOxcH2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rEBqO6OrnaYOxcH2 .error-icon{fill:#552222;}#mermaid-svg-rEBqO6OrnaYOxcH2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rEBqO6OrnaYOxcH2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rEBqO6OrnaYOxcH2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rEBqO6OrnaYOxcH2 .marker.cross{stroke:#333333;}#mermaid-svg-rEBqO6OrnaYOxcH2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rEBqO6OrnaYOxcH2 p{margin:0;}#mermaid-svg-rEBqO6OrnaYOxcH2 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rEBqO6OrnaYOxcH2 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rEBqO6OrnaYOxcH2 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-rEBqO6OrnaYOxcH2 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-rEBqO6OrnaYOxcH2 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-rEBqO6OrnaYOxcH2 .sequenceNumber{fill:white;}#mermaid-svg-rEBqO6OrnaYOxcH2 #sequencenumber{fill:#333;}#mermaid-svg-rEBqO6OrnaYOxcH2 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-rEBqO6OrnaYOxcH2 .messageText{fill:#333;stroke:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rEBqO6OrnaYOxcH2 .labelText,#mermaid-svg-rEBqO6OrnaYOxcH2 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .loopText,#mermaid-svg-rEBqO6OrnaYOxcH2 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rEBqO6OrnaYOxcH2 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-rEBqO6OrnaYOxcH2 .noteText,#mermaid-svg-rEBqO6OrnaYOxcH2 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-rEBqO6OrnaYOxcH2 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rEBqO6OrnaYOxcH2 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rEBqO6OrnaYOxcH2 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rEBqO6OrnaYOxcH2 .actorPopupMenu{position:absolute;}#mermaid-svg-rEBqO6OrnaYOxcH2 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-rEBqO6OrnaYOxcH2 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rEBqO6OrnaYOxcH2 .actor-man circle,#mermaid-svg-rEBqO6OrnaYOxcH2 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-rEBqO6OrnaYOxcH2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MCP初始化(握手) 发送 initialize 初始化请求(携带自身能力) 返回协议版本与自身能力列表 客户端确认握手完成 输入自然语言请求 发送用户请求与上下文(含工具tool列表) 返回意图理解结果(需调用工具) 请求调用指定工具 发送 tools/call 工具调用请求 执行工具逻辑(访问外部资源) 返回工具执行结果 返回工具结果 将工具结果回传给 LLM 生成最终回复 输出最终答案
MCP初始化(握手)是在连接建立以后,任何业务请求之前。在初始化过程中,会进行能力协商 。
能力协商是双向的,Client和Server各自声明支持哪些能力,双方根据对方的能力声明调整行为。
1.3、MCP 与 Function Calling 有什么区别?
MCP是协议层标准,Function Calling是模型侧能力,两者互补而非替代。
- Function Calling :模型的基础能力,让模型输出结构化的工具调用请求(工具名 + 参数)。它解决的是"模型如何表达要调用什么"问题,但不负责工具执行,具体工具执行由Host应用完成。
- MCP :开放协议标准,标准化外部工具/资源的发现、描述、调用和传输,让工具能跨应用、跨模型复用,解决"工具统一接入"的问题。
- 两者关系:互补而非替代关系,MCP Server暴露的工具,经Host应用注入后,仍由模型通过Function Calling机制决定并生成调用。MCP提供"工具的定义和通道",Function Calling提供"模型的决策和参数"。
1.4、MCP相比直接提供Restful API 给模型用的优势在哪?
核心优势不是"REST 做不到",而是把"模型调用外部能力"这条链路工程化了。
MCP 的四个核心优势:
-
能力动态发现,而非硬编码
提示词方案里,API 列表写死在提示词里,新增接口要改提示词、同步所有 client。MCP 的 listTools 让 client 动态发现 server 暴露了哪些工具和 schema,加工具不用改客户端代码。
-
结构化 schema,而非文本解析
提示词方案中,模型要自己解析文本格式,弱模型容易错。MCP 用标准 JSON Schema,模型直接消费,且一次定义、所有支持 MCP 的 client 通用。
-
能力协商 + 权限分级
提示词方案假设所有 API 都可用,但实际可能没权限。MCP 有 capabilities 协商,server 声明支持什么,client 只暴露可用的;Tools(有副作用)/ Resources(只读)/ Prompts(纯文本)分离,安全策略可差异化。
-
鉴权、传输、错误处理标准化
提示词方案这些全要自己造轮子。MCP 规范定义了 OAuth 2.1、能力协商、错误处理,且同一 server 可跑 stdio 或远程 HTTP,client 不用改。
适用边界:
- 接口少、变化慢、单 client → 提示词描述 REST 更轻,够用
- 接口多、变化快、多 client 复用 → MCP 的工程化优势体现出来
一句话总结 :
MCP 把"描述 API → 模型调用"从手写适配层变成了标准协议,接口多、变化快、多 client 时优势明显;简单场景直接用 REST 更轻。
2、协议与通信
2.1、MCP的底层协议是什么?
基于 JSON-RPC 2.0 ,消息为请求、响应、通知三类。所有能力调用(tools/call、resources/read 等)都是标准 JSON-RPC 方法。
2.2、MCP的传输方式有哪些?
stdio :本地进程,通过标准输入输出通信,适合本地 Server。
Streamable HTTP :远程 Server,支持流式响应与无状态/有状态模式。
旧版 HTTP+SSE 已逐步被 Streamable HTTP 替代。(2025-03 规范起,取代旧版 HTTP+SSE)
2.3、stdio和Streamable HTTP如何选型
选型核心是 部署边界 ,即 Host和所依赖的工具/资源等是否在同机上。
- 同机 -> stdio(零网络开销、天然安全、低延迟)
- 跨机 -> Streamable HTTP(必须接受网络开销和鉴权成本)
具体的对比参考维度如下:
| 维度 | stdio | Streamable HTTP |
|---|---|---|
| 通信方式 | 子进程输入输出(管道) | HTTP请求/响应 + SSE流式 |
| 部署位置 | 与 Host 同进程/同机器 | 可跨机器、跨网络 |
| 延迟 | 极低(进程内管道,无网络开销) | 有网络延迟(取决于 RTT) |
| 安全性 | 天然隔离------子进程权限由 Host 控制,无网络暴露面 | 需额外鉴权(OAuth/Bearer Token)、TLS、防 SSRF |
| 生命周期 | 随 Host 启动/关闭,1:1 绑定 | 独立部署,可多 Host 共享 |
| 适用场景 | 本地工具(文件系统、本地数据库、代码分析、CLI 封装) | 远程服务(云 API、共享数据源、多客户端接入) |
| 状态管理 | 进程内保持状态简单 | 无状态设计,需外部存储 |
| 资源开销 | 每个 Host 启动一个子进程 | 独立进程/容器,可水平扩展 |
| 调试难度 | 低(本地日志 + stdout/stderr) | 中(需网络抓包、鉴权调试) |
3、核心原语
3.1、核心原语有哪些?
核心原语有Tools 、Resources 、Prompts,对比如下:
| 原语 | 是什么 | 副作用 | 谁控制 | 典型场景 |
|---|---|---|---|---|
| Tools | 可执行的动作 | 有(执行动作,写数据),风险高 | 模型(LLM决定何时调用) | 查天气、执行 SQL、发消息、调用 API |
| Resources | 可读的数据/内容 | 无(只读数据),风险低 | 应用(Host/用户控制) | 文件内容、数据库 schema、文档、配置 |
| Prompts | 预置的提示词模版 | 无,风险中(影响模型输出方向) | 用户(用户主动选择) | 代码审查模板、周报模板、翻译模板 |
三者的核心区别点 是:控制方不同
- Tools:模型控制,LLM根据当前上下文决定"要不要调用,调用哪个"
- Resources:应用控制,由Host/用户主动读取,模型不主动读取
- Prompts:用户控制,用户显示控制使用哪个模版
Tools 是模型"主动去拿"的能力,Resources 是应用"主动给"的数据,Prompts 是用户"主动选"的模板。
3.2、为啥分开而不是统一为一个原语?
统一成一个原语做不到,因为三种交互模式本质不同:
- Tools:模型主动调------应用无法预知调用时机,所以不能"主动给"
- Resources:应用主动给------模型不需要决策"要不要读",由应用控制
- Prompts:用户主动选------行为模板必须用户把关
如果强行统一:
- 全部模型主动调 → Resources 和 Prompts 白白占用模型决策和上下文
- 全部应用主动给 → Tools 这条路走不通,因为应用没法替模型决定"什么时候调什么工具"
所以分开设计不是"谁控制谁负责"的哲学选择,而是交互模式的客观差异决定了必须分开。
3.3、Resources和Prompts都属于内容,为啥拆开?
虽然 Prompts 和 Resources 在形式上都是"内容",但它们在语义角色 、控制方 、影响方式上本质不同,不能合并。
- 语义角色不同 :Resources 是"数据",Prompts 是"行为指令"。
Resources 给模型提供的是知识------文件内容、数据库 schema、文档,它回答"模型应该知道什么";Prompts 定义的是行为------任务模板、输出格式、思考框架,它回答"模型应该怎么做"。一个是喂信息,一个是定规矩。 - 控制方不同
Resources 由应用控制------应用决定喂什么数据给模型,模型被动接收;Prompts 由用户控制------用户显式选择用哪个模板。 - 参数化能力不同
Prompts 是可实例化的模板,支持 arguments 参数------用户选择模板时传入参数,生成具体的消息序列;Resources 是已存在的内容,直接读取即可,没有参数化过程。Prompts 更像"函数/宏",Resources 更像"文件"。 - 影响方式不同
Resources 的内容直接注入上下文,影响模型的"知识面";Prompts 返回的是带 role 的消息模板,影响模型的"行为模式"。前者是输入数据,后者是执行逻辑。
为什么 Prompts 必须用户控制?
因为 Prompts 影响的是模型行为,风险更高。如果 Prompts 被当作 Resources 由应用自动注入,模型就可能被任意"程序化"引导------应用注入什么模板,模型就按什么方式思考,用户完全失去对模型行为的控制权。所以 Prompts 必须用户显式选择,这是安全设计。
Prompts 独立出来,本质是因为它控制的是模型行为而非模型知识,必须由用户把关,且支持参数化复用。把它算作 Resources,会丢失"用户显式控制"这个安全边界。
4、工程实践
4.1、生产环境用 MCP 要注意哪些问题?
生产环境用 MCP,核心要关注四个维度:安全 、可靠性 、上下文管理 、可观测性。
- 安全:最小权限原则、提示词防注入、鉴权
- 可靠性:超时、幂等、健康检查
- 上下文管理:工具筛选、返回值控制
- 可观测性:调用链、审计、监控
具体说明如下:
- 安全
| 风险 | 说明 | 应对 |
|---|---|---|
| 工具权限过大 | Server 暴露了不该暴露的工具(如 rm -rf、drop table) | 最小权限原则:每个工具单独鉴权,只暴露业务需要的 |
| Prompt Injection | 恶意数据通过工具返回值注入模型,诱导模型执行危险操作 | 输出过滤、敏感操作二次确认、工具返回值做无害化处理 |
| 远程 Server 鉴权 | HTTP 传输的 Server 可能被未授权调用 | OAuth 2.1 / Bearer Token + TLS,每个请求鉴权 |
| SSRF 风险 | 工具内部可能被诱导访问内网服务 | 工具内部做目标地址白名单,禁止访问内网 IP |
| 数据泄露 | 工具返回值可能包含敏感信息(密码、密钥) | 返回值脱敏、审计日志脱敏 |
- 可靠性
| 问题 | 说明 | 应对 |
|---|---|---|
| Server 崩溃 | stdio Server 随子进程退出,HTTP Server 可能宕机 | 健康检查 + 自动重启;HTTP 做负载均衡 |
| 超时 | 工具执行慢,模型等太久 | 设置合理的超时时间,超时后返回错误而非卡死 |
| 幂等性 | 模型可能重试调用,导致重复执行 | 工具设计成幂等的(尤其是写操作),或做去重 |
| Server 状态丢失 | stdio Server 重启后内存状态丢失 | 有状态操作要持久化,或设计成无状态 |
- 上下文管理
| 问题 | 说明 | 应对 |
|---|---|---|
| 工具列表膨胀 | 暴露几十上百个工具,schema 占满上下文 | 动态筛选:按任务相关性只暴露需要的工具;分层暴露:先暴露高频工具,低频的按需加载 |
| 工具返回值过大 | 查询返回上万行数据,直接爆上下文 | 分页、截断、摘要;用 Resources 替代大段返回 |
| 工具调用链过长 | 模型连续调多个工具,上下文被历史调用占满 | 压缩历史、丢弃中间结果、只保留关键信息 |
- 可观测性
| 需求 | 说明 | 应对 |
|---|---|---|
| 调用链追踪 | 模型调了哪些工具、参数是什么、结果如何 | 每个工具调用记录 trace_id,串联请求链路 |
| 审计日志 | 谁在什么时候调了什么工具,结果是什么 | 全量记录,支持回溯 |
| 性能监控 | 工具响应时间、失败率、调用频率 | 打点上报,设置告警 |
| 调试能力 | 工具返回了意外结果,需要排查 | 保留完整请求/响应日志,支持回放 |
4.2、工具数量很多时,如何避免塞爆上下文?
核心思路是"源头控制 + 中间压缩",而不是让模型硬扛。
- 动态筛选 :最有效的手段
在 MCP Server 的 listTools 中,根据当前对话的意图,只返回相关的工具子集。比如用户问"查一下订单状态",Server 只暴露订单相关的 5 个工具,而不是全部 100 个。这样模型看到的工具数从 100+ 降到 10-20,上下文占用大幅减少,模型选择准确率也会提升。 - 精简工具描述
工具描述和参数 schema 是上下文的大头。可以只保留一句话描述和必填参数,省略可选参数的详细说明。模型需要更多细节时,再通过单独的接口获取完整 schema。 - 工具分组
按领域给工具打标签(如 db、log),Server 根据对话上下文只暴露匹配 scope 的工具。 - 结果压缩
工具返回大结果时,Server 自动截断或摘要,避免工具结果撑爆上下文。
总结:源头控制(动态筛选 + 分组)是最有效的,精简描述和结果压缩是辅助手段。
4.3、如何做 MCP Server 的鉴权?
MCP Server 鉴权要分场景看,不能一概而论。
-
本地 stdio 传输通常不需要鉴权
因为 server 是客户端本地启动的子进程,进程隔离本身就是安全边界,鉴权是多余的。
-
远程传输(Streamable HTTP / SSE)才需要鉴权
这里分两层:
- 传输层:用 TLS/mTLS 保证通道加密和双向身份认证,防止中间人攻击。
- 应用层 : MCP 规范推荐 OAuth 2.1(Authorization Code + PKCE),这是远程 MCP server 的标准做法。流程是:
- 客户端通过动态客户端注册(RFC 7591)注册自己
- 用户授权后拿到 access token
- 后续请求带 token 访问 server
- 用 RFC 8707 资源指示器区分不同资源
对于企业内部或简单场景,也可以用 API key / Bearer token,在请求头里校验。但 API key 安全性弱于 OAuth,适合内部低风险场景。
-
鉴权之外还要做授权
鉴权解决"你是谁",授权解决"你能做什么"。MCP 的三大原语权限要求不同:
- Tools(有副作用)→ 严格授权 + 审批
- Resources(只读)→ 宽松授权
- Prompts(纯文本)→ 无需授权
可以在 server 端做 RBAC,按用户角色控制哪些工具/资源可见。
5、开放题
5.1、让你设计一个"数据库查询 MCP Server",你会怎么设计?
我会从"工具边界、安全、性能、可观测"四个维度来设计。
-
工具设计(先定义暴露什么)
我会暴露一组职责单一的工具,而不是一个"万能查询"工具:
- list_tables:列出可访问的表(只读)
- get_schema:获取表结构(只读)
- query:执行只读 SQL 查询(SELECT)
- execute:执行写操作(INSERT/UPDATE/DELETE)------单独一个工具,走审批
关键设计:读写分离,查询 server 默认只读,写操作必须显式走 execute 工具,且标记为 IRREVERSIBLE,触发审批流程。这样模型不会"顺手"执行危险操作。
-
安全设计
- SQL 注入防护:所有查询走参数化查询,禁止拼接 SQL。模型生成的 SQL 也要做白名单校验(只允许 SELECT、禁止 DROP/TRUNCATE 等)。
- 权限控制(RBAC):按用户角色控制可访问的表和操作。比如普通用户只能查业务表,管理员才能查配置表。
- 敏感数据脱敏:对手机号、身份证、银行卡等字段自动脱敏(如 1381234)。可以在 server 端配置脱敏规则,按字段名匹配。
- 鉴权:内网用 API key,外网用 OAuth 2.1(Authorization Code + PKCE)。凭证不硬编码,用环境变量或密钥管理服务。
- 审计日志:记录"谁在什么时间调用了什么工具、执行了什么 SQL、返回了多少行",写入 EventStore,可追溯。
-
性能与稳定性
- 强制 LIMIT:所有查询强制加 LIMIT(如 100 行),防止模型生成全表扫描。
- 超时控制:查询设置超时(如 10s),超时返回错误,避免慢查询拖垮连接池。
- 结果集大小限制:返回结果超过阈值(如 100KB)时截断或摘要,避免撑爆上下文。
- 连接池:复用数据库连接,避免每次查询新建连接。
- 分页:大数据量查询支持分页,但优先用 LIMIT + 条件过滤,而不是深分页。
-
错误处理
- SQL 错误友好返回:把数据库错误转成模型能理解的错误信息(如"表不存在""字段名错误"),而不是裸抛异常。
- 空结果处理:返回"无数据"而不是空数组,避免模型误判。
- 降级:数据库不可用时返回明确错误,不静默失败。