MCP 面试高频考点:给 Java MCP Server 加日志、指标、追踪与审计,你能答到第几层?
面试场景
这是一场偏后端与 AI 基础设施方向的技术面试,岗位要求候选人不仅能把 MCP Server 跑起来,还要能把它接入企业现有的可观测与安全体系。面试官从一个看似普通的需求切入:“我们现在有一个基于 Java MCP SDK 的 MCP Server,暴露了若干 Tools、Resources 和 Prompts,请你设计一套可落地的日志、指标、分布式追踪和审计方案。”
基础问题:先把观测面搭起来
面试官:如果让你给这个 Java MCP Server 增加日志、指标和分布式追踪,你会怎么分层设计?先说结论。
候选人:我的结论是分四层做:协议与传输层、能力执行层、安全审计层、业务适配层。原因是 MCP 本质上是基于 JSON-RPC 的客户端—服务端协议,Host 里的 MCP Client 会和 Server 建立会话,Server 对外暴露 Tools、Resources、Prompts 三类能力[资料2]。如果只在业务方法里打日志,既看不到请求链路,也无法覆盖鉴权失败、参数校验失败、传输异常这类关键问题。
具体分工上: 1.日志:统一走 SLF4J,不直接绑定具体日志后端,因为 Java MCP SDK 的公开 API 本身就是通过 SLF4J 输出日志,且默认不带具体后端实现[资料1]。 2.指标:不在 SDK 内硬编码埋点,而是通过 Micrometer 这类门面在请求入口、能力调用、异常、超时点做计数和耗时统计;这也符合 SDK 对 Micrometer、OpenTelemetry 无缝集成的设计方向[资料1]。 3.分布式追踪:把 correlation ID、trace context 沿着异步边界传递,尤其要注意 Reactor 流水线中的上下文传播,因为 SDK 明确提到观测元数据是通过 Reactor pipeline 流动的[资料1]。 4.审计:单独记录“谁在什么时间调用了哪个 Tool、访问了什么 Resource、作用范围是什么、结果如何”,并和普通应用日志分开存储。
面试官:为什么日志、指标、追踪不能都打到同一个文件里?
候选人:因为三者用途不同。普通日志用于排错,指标用于趋势和告警,追踪用于跨调用链定位,审计日志用于合规和追责。如果混在一起,一方面检索成本高,另一方面审计日志有保留周期、脱敏和访问控制要求,不能和调试日志同权处理。MCP 安全边界里也明确提到,凭据不能出现在日志、Tool 返回值或模型上下文中,审计日志要记录关键信息同时对敏感字段脱敏[资料2]。
第一轮追问:JSON-RPC 与传输层怎么埋点
面试官:你提到协议层。MCP 走 JSON-RPC,那你在协议入口具体记录什么?如果传输方式不同,方案要不要变?
候选人:先说结论:要围绕 JSON-RPC 的方法级语义做统一埋点,而不是只记录 HTTP 或 stdio 的原始字节流。
在协议入口,我会记录这些信息: - 请求到达时间、传输方式(stdio / Streamable HTTP 等); - JSON-RPC 的 method、id、session 或连接标识; - 调用的能力类型:Tool、Resource 还是 Prompt; - 能力名称,例如 tool 名、resource URI、prompt 名; - 结果状态:成功、业务失败、协议错误、鉴权失败、超时、取消; - traceId、spanId、调用方主体标识; - 脱敏后的参数摘要和错误分类。
传输方式不同,埋点位置和注意事项确实不同。stdio 场景下,Server 是由 Host 启动的本地子进程,标准输出必须留给协议消息,调试日志只能写到标准错误,否则会破坏通信[资料2]。因此如果用 stdio,日志框架要确保 console appender 定向到 stderr,不能把 INFO/DEBUG 打到 stdout。Streamable HTTP 更适合远程服务,这时除了 JSON-RPC 层埋点,还要考虑认证、授权、会话管理、限流和超时[资料2],所以 HTTP 层还要补充状态码、请求大小、客户端标识、限流命中情况等指标。
面试官:如果 JSON-RPC 请求解析就失败了,比如方法名不存在、参数格式不合法,这时怎么办?
候选人:这类错误不能漏,而且要和业务异常区分开。我的做法是在协议解码和分发层单独捕获: - 打错误日志,但不打印原始大报文全文,避免泄露敏感数据和把日志打爆; - 记一类专门的协议错误指标,按错误类型打标签,比如method_not_found、invalid_params、parse_error; - 在 tracing 中把这类错误标记为 span error,并附上规范化的错误原因; - 审计日志里也要记,因为异常请求可能是探测或越权尝试。
这里有个取舍:是否记录原始请求体。默认我只记录脱敏后的摘要;只有在采样开关打开且确认不含敏感信息时,才记录有限长度的原始片段。否则凭据、Token、用户数据很容易进入日志,违反最小权限和安全要求[资料2]。
第二轮追问:能力执行、最小权限与审计
面试官:进入 Tool 执行阶段后,怎么把最小权限、审计日志和可观测串起来?
候选人:结论是:先鉴权,后执行;先记审计上下文,后落结果;所有模型传入内容都按不可信输入处理。
MCP 的安全边界说得很明确:Tool 参数 schema 只是结构约束,不能代替服务端校验和授权;模型传入的文本必须视为不可信输入,文件路径、URL、SQL、Shell 参数、资源标识符都要做约束;高风险或不可逆操作要展示具体影响,并在执行前要求用户确认;服务不能因为请求来自 AI 应用就默认可信[资料2]。
所以我会在能力分发链路上放几层拦截器或装饰器: 1.身份识别:从会话或请求上下文中取出调用主体,可能是用户、Host 应用、服务账号;远程部署时每次请求都要做授权检查,不能只判断是否登录[资料2]。 2.权限判定:按 Tool/Resource/Prompt 维度做授权,例如某 Tool 只能读取特定目录,某 Resource 只能访问自己租户的数据。 3.参数校验与约束:schema 校验之后,还要做路径穿越、URL 白名单、SQL 注入风险、Shell 参数转义等服务端校验。 4.审计前置记录:在真正执行前记录“谁、何时、对哪个能力、目标资源范围是什么、请求摘要是什么”,拿到一个审计事件 ID。 5.执行与观测包裹:为每次能力调用创建独立 span,把能力类型、名称、资源 URI、审计事件 ID 作为属性挂上去;同时记录执行耗时、重试次数、外部依赖调用情况。 6.结果落审计:成功、失败、拒绝、用户取消分别记录结果状态和原因;失败时记录错误分类,不记录堆栈和敏感参数原文。
如果用版本无关的伪代码表达,大概是这样:
onMcpRequest(request): startSpan(method=request.method, capability=resolveCapability(request)) putTraceContextIntoReactorContext() principal = authenticate(request) authorize(principal, request) sanitizedArgs = validateAndSanitize(request.params) auditEventId = auditLogger.logStart( principal = principal, capability = request.capabilityName, resourceScope = resolveResourceScope(sanitizedArgs), requestSummary = summarize(sanitizedArgs) ) try: if requiresExplicitConfirmation(request.capabilityName): requireUserConfirmation(principal, request.capabilityName, impactPreview(request)) result = executeCapability(request.capabilityName, sanitizedArgs) metrics.recordSuccess(request.capabilityName, elapsed) auditLogger.logSuccess(auditEventId) return result catch (e): span.recordError(e) metrics.recordFailure(request.capabilityName, classify(e)) auditLogger.logFailure(auditEventId, errorType = classify(e)) throw toJsonRpcError(e)面试官:这里有什么容易踩坑的点?
候选人:至少有三个。
第一,stdio 日志输出打错位置。本地调试时看起来一切正常,但部署后因为日志写到 stdout,JSON-RPC 消息流被污染,客户端解析失败[资料2]。这个问题很隐蔽,因为开发人员往往先在控制台看日志,不会立刻发现协议被破坏。
第二,把 schema 校验当安全校验。很多人以为 Tool 输入 schema 写了字段类型就安全了,但 schema 不能解决越权访问、路径穿越、恶意 URL、SQL 注入这些问题,服务端必须再次校验[资料2]。
第三,异步链路丢上下文。Java MCP SDK 的观测元数据依赖 Reactor pipeline 传播[资料1],如果在执行中切换线程池、用了非 Reactor 的阻塞调用、或者手写了没有上下文捕获的回调,traceId、租户信息、审计主体就可能丢失,最后链路断成两截。
方案设计:指标、追踪与异常处理的取舍
面试官:如果现在要你给出一套可落地的指标和追踪设计,你会选哪些关键指标?异常时怎么处理才不会把问题放大?
候选人:我的原则是少而关键,避免高基数标签把监控系统打穿。
核心指标我会选这几类: - 请求量:按传输方式、能力类型、能力名称、结果状态统计; - 延迟:按能力名称记录直方图,观察 P50、P95、P99; - 错误数:按错误分类区分协议错误、鉴权失败、参数校验失败、业务异常、依赖超时; - 活跃会话数:尤其对 Streamable HTTP 远程部署有意义; - 限流与拒绝数:记录被限流、被权限拒绝、被用户确认拦截的次数; - 外部依赖指标:如果 Tool 会调数据库、HTTP 服务、文件系统,要单独记录依赖耗时和错误。
追踪方面,至少要有: - 传输层 span:接收请求、发送响应; - 鉴权与授权 span; - 能力执行 span; - 外部调用 span; - 异常事件与审计事件关联字段。
异常处理上,有几个取舍: 1.不要把所有异常都返回给模型详细堆栈。对外应返回 JSON-RPC 规范下的错误对象,内部日志保留分类信息,避免泄露实现细节和敏感路径。 2.不要无限重试。是否重试、重试多少次、超时设多长,要结合业务 SLA、下游幂等性和压测结果决定,不能拍脑袋写死。 3.不要对高风险操作自动补偿。比如删除、写入、发消息这类不可逆操作,应优先要求用户确认,并记录审计;失败时让调用方或用户决定是否重试[资料2]。 4.采样策略分层:正常流量可按比例采样 tracing,但鉴权失败、审计事件、高风险 Tool 调用建议全量保留追踪和审计上下文。
面试官:那这套方案的适用边界是什么?
候选人:它适合需要长期运行、接入企业观测体系、有明确权限与合规要求的 MCP Server,尤其是远程部署的 Streamable HTTP 服务。对于本地开发期临时起的 stdio 小工具,全量指标和复杂审计可能过重,这时可以先保留 SLF4J 日志和基础错误计数,等它进入共享服务或生产环境再补齐。
另一个边界是:SDK 本身不提供 metrics 和 tracing 实现,只保证日志通过 SLF4J、观测上下文可沿 Reactor 传播,并方便接入 Micrometer、OpenTelemetry 等系统[资料1]。所以工程上要避免把自己绑死在某一个特定观测后端,应该通过门面和拦截器接入。
面试官点评
考察点: 这道题看起来是在问“怎么加日志”,实际考察四件事: 1. 是否理解 MCP 的客户端—服务端架构、JSON-RPC 通信与三类能力边界[资料2]; 2. 是否知道 Java MCP SDK 在可观测上的默认设计:SLF4J 日志、Reactor 上下文传播、不内置 metrics/tracing 实现[资料1]; 3. 是否能把最小权限、输入校验、审计日志和能力执行链路串起来,而不是把安全当“登录后就放行”; 4. 是否有工程意识,能区分传输方式差异、异常处理、高基数标签、日志泄露等落地问题。
合格回答: - 能说清日志、指标、追踪、审计分层; - 知道 stdio 下日志不能打 stdout,调试日志应走 stderr[资料2]; - 知道 schema 不能替代授权和服务端校验,模型输入不可信[资料2]; - 能设计请求入口到能力执行的统一拦截链路,并区分协议错误、业务错误、权限错误。
加分项: - 能结合 Java MCP SDK 的 SLF4J、Reactor 上下文传播和与 Micrometer/OpenTelemetry 的集成方式设计方案[资料1]; - 能明确审计日志与普通日志分离、敏感字段脱敏、凭据不落日志; - 能指出异步上下文丢失、高基数标签、错误信息外溢、高风险操作未确认等真实坑点; - 能说明方案边界:本地工具可轻量,远程服务必须补认证、授权、限流、超时和审计[资料2]。
总结
给 MCP Server 做可观测,不是简单“多打几行日志”,而是要沿着 JSON-RPC 协议入口、能力执行、安全校验、外部依赖和结果返回这条主链路,把日志、指标、追踪、审计各自放在合适的位置。Java 场景下,要尊重 SDK 的设计边界:日志走 SLF4J,指标和追踪通过 Micrometer、OpenTelemetry 等标准门面接入,观测上下文沿 Reactor 异步链路正确传播[资料1]。安全上要始终假设输入不可信、远程请求每次都要鉴权、高风险操作需确认、审计要可追溯且脱敏[资料2]。真正能落地的方案,不是功能最多,而是在排错效率、性能成本、安全合规和维护复杂度之间找到平衡。
参考资料
- MCP Java SDK
- [MCP 基础知识]
- MCP Python SDK
- MCP TypeScript SDK