在无状态规范变更后于EC2上部署MCP服务器
将MCP服务器从本地主机迁移到EC2:流式HTTP、nginx以及承载令牌认证,那么在实际部署中无状态会话和OAuth 2.1又意味着什么。
来源: MCP Servers Explained: A Complete Guide to What I Learned Deploying One to AWS EC2
从本地主机到真实服务器
关于MCP的教程往往较为抽象。它们通常会展示客户端与服务器并排的图示,将该协议比作USB-C接口,或者仅在本地主机上运行五行代码的示例,而这些示例从未离开过开发者的笔记本电脑。真正的挑战在于此之后:需要在真实的服务器上、通过真正的反向代理来运行在本地编写的工具,这样才能让真正的客户端能够通过公共互联网调用它。这是一个截然不同的问题,却很少被提及。
此处描述的部署流程是从本地的MCP服务器开始,将其迁移到EC2实例上,并通过可流式HTTP传输协议进行服务,同时nginx会要求提前提供承载令牌。这一流程运行得相当顺利。不久之后,该协议便被纳入其管理之下。2026年7月28日,MCP规范迄今为止最大规模的修订版从候选版本正式成为最终版本,这使得该协议具备了核心的无状态特性。各团队通常会将“MCP支持”视为一个独立项,但实际上它包含三个方面的选择:传输方式、认证机制,以及此次更新后新增的扩展性考量。如果在这些方面出现任何错误决策,就会导致工具从演示级产品沦为无法在生产环境中可靠使用的工具。
本文涵盖了多个问题:MCP定义了问题的哪些部分,哪些部分仍未明确;为何流式HTTP取代了基于SSE的传输方式;nginx和承载令牌如何让EC2上的本地服务器得以暴露;无状态重写会带来哪些变化;为何静态令牌虽被视为具有认证功能,却仍不符合规范要求;以及为跟上发展步伐,部署过程中哪些部分需要修改。
MCP标准化了什么,又未标准化什么
从理论上讲,Model Context Protocol只规定了单一事项:即人工智能应用如何通过定义好的传输方式发送JSON-RPC消息,从而找到并调用由独立服务器进程提供的工具,以及读取该进程提供的资源。工具逻辑、智能体设计与部署均不在其规范范围内。它是一种连接标准,而非框架。此前,每个需要访问内部API之类资源的人工智能应用都必须进行定制化的集成。而在MCP架构下,一台服务器即可为所有符合标准的客户端提供服务。
从这个角度来看,就能明白为何部署会如此复杂。该协议仅描述了消息结构,对于服务器是否只能从某台笔记本电脑访问还是可从任何地方访问、是否有防护措施,以及是否能够同时处理多个客户端等问题均未作出规定。这些问题的答案都由系统运营方决定。
为何公开部署有所不同
- 本地服务器几乎不暴露任何信息,而公共服务器则会暴露大量信息。拥有公共URL后,该服务器就会变成与其他服务器一样的需身份验证的API,因此令牌处理和输入验证同样重要。
- 此前,会话管理及扩展规则并不明确。如果判断失误,要么会导致服务器在并发负载下悄无声息地崩溃,要么会让服务器承担本不需要的粘性会话负担。
- 相关规范也在不断更新。基于六个月前假设构建的系统可能会在没有任何错误的情况下不再符合规范,因为客户端会自动降级到更旧的协议版本。
其架构如下所示。
AI Client ── HTTPS POST ──▶ nginx (TLS termination, auth check, reverse proxy)
│
▼
MCP Server Process
(Streamable HTTP transport)
│
┌────────────┴────────────┐
Tools Resources
所有这些都有其核心原则。一旦MCP服务器获得了公共URL,就应像处理EC2上提供的任何已认证API一样来对待它,因为从结构上来看二者并无区别。MCP仅是上层的信息格式,而底层的安全性与扩展性问题则与任何后端系统完全相同。
1. 可流式HTTP传输
可流式HTTP是一个能够接收携带JSON-RPC消息的POST请求的HTTP端点。它取代了早期的HTTP+SSE传输方式,后者需要在常规HTTP请求之外再维护一个持续的服务器发送事件流。
对于任何反向代理、负载均衡器或API网关而言,单个POST端点都是十分常见的配置,因此通过nginx或ALB维持长时运行的数据流并不会带来困难。此类流量看起来与普通HTTP API并无不同,nginx的location块也只需简单配置即可。
location /mcp {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header Authorization $http_authorization;
proxy_http_version 1.1;
proxy_read_timeout 300s;
}
一个针对生产环境的建议:即便使用单端点传输方式,也应将代理的读取超时时间设置得足够长。某些工具调用所需的处理时间确实可能超过普通API请求的时间,过短的超时时间会同时终止那些正常但耗时较长的调用以及真正卡住的调用。
2. 承载令牌认证
在这种机制中,固定的令牌会被放在Authorization请求头中,在请求到达MCP服务器进程之前——无论是在nginx中还是在应用程序中间件中——就会对其进行验证。对于通过公共URL访问的场景,这是最低限度的可接受安全措施,也可作为合理的初步方案。不过该令牌永远不会过期,也不与特定服务器绑定,如果被记录到日志中则无法提供任何保护。它仅能表明该请求携带了认证凭证。
一个FastAPI中间件的示例代码始于导入语句。
from fastapi import Request, HTTPException
接着就是支票本身了。
VALID_TOKEN = "your-rotated-secret-token"async def verify_bearer(request: Request):
auth = request.headers.get("authorization", "")
if auth != f"Bearer {VALID_TOKEN}":
raise HTTPException(status_code=401, detail="Unauthorized")
另一个建议:不要将原始的 Authorization 请求头包含在访问日志中。默认的 Nginx 和应用程序日志格式很容易让这一内容被忽略,而一旦它出现在日志中,该日志文件就会变成泄露的凭证。
3. 无状态性——2026年7月重写的核心
根据2026-07-28的规范,协议级的会话将不复存在。不再有 Mcp-Session-Id 请求头,传输层也会放弃早期版本为保持客户端与服务器在对话过程中的同步而使用的长期有效的 GET 流。每个请求都会携带其所需的所有信息。
扩展性才是关键。旧模型允许服务器在多次调用之间记住客户端的相关信息,而当多个实例部署在负载均衡器后时,这些记忆就需要通过粘性会话来实现。在没有协议级状态的情况下,任何实例都可以处理任意请求,从而实现真正的水平扩展,同时让客户端不必绑定在某台特定机器上。
在实际应用中,那些在多次调用之间在内存中保存客户端状态的服务器将无法符合新规范。所有重要的状态都应移至请求载荷中,或存储在如 Redis 这样的外部存储中,以便所有实例都能读取。
4. 多次往返请求
即便是无状态协议,服务器也需要某种方式在工具调用过程中向客户端提出问题,比如请求确认或补充缺失的参数。新的规范通过多轮往返请求解决了这一问题。服务器不会保持连接开启状态,而是返回一个InputRequiredResult,其中列出了所需的问题以及一个不可见的requestState值。客户端收集这些回复后再次发送原始请求,同时附上这些回复,而状态保持不变。
正是这一点使得真正的无状态设计得以实现。由于服务器恢复工作所需的所有信息都包含在客户端返回的负载中,因此任何实例——不一定是最初发起请求的那个——都可以接管重试并完成该操作。
客户端应勿触碰requestState:既不可查看也不可修改。应原封不动地保留它并原样传回,否则依赖其完整性的恢复流程可能会失败。
5. 权限强化
修订后的规范要求远程MCP服务器使用带PKCE功能的OAuth 2.1协议,并需采用资源标识符(RFC 8707),以此将已发放的令牌与其预定的服务器绑定。
需要防范重放攻击。如果静态承载令牌泄露,任何接受该令牌的服务器都可能成为攻击目标。将令牌与特定资源绑定即可弥补这一漏洞,因此为某个MCP服务器发放的令牌无法在另一个服务器上使用,即便两者都依赖相同的身份提供方。
许多人最初采用的承载令牌机制虽然能够满足“该端点需要身份验证”的要求,但却无法达到新规范对合规远程服务器所期望的互操作性及重放保护标准。在该URL被核心团队之外的人使用之前,必须填补这一差距。
6. 废弃策略与扩展功能
该规范中的各项功能现在都遵循活跃、废弃和移除的正式生命周期,从废弃到移除之间至少有十二个月的间隔。诸如服务器渲染的界面或长时间运行的任务等新功能,会首先以可选的扩展功能形式出现,而非直接纳入核心功能。而一些较旧的功能,包括原始形式的根访问、采样和日志记录功能,正逐步走向废弃,以便被替代功能取代。
对开发者而言,这意味着现有的集成不会因后续的版本更新而突然失效,同时他们也可以有计划地引入新功能,而不必为了跟上规范不断增加的内容而被迫仓促行动。
端到端实现流程
从本地原型到符合当前规范的 EC2 部署的流程如下:
- 本地测试。服务器通过标准输入输出与本地客户端通信,无需网络连接且无需身份验证,因此迭代速度很快。
- 切换为流式 HTTP。服务器被重新配置为使用流式 HTTP 传输协议,并部署到 EC2 实例上。
- 使用 nginx 做前置代理。nginx 负责终止 TLS 加密,检查携带的令牌,确保请求在到达 MCP 进程之前经过验证,之后再将请求反向代理传递。
InputRequiredResult,列出仍需询问的信息。requestState值传回。此次重试不依赖于特定实例,因此nginx背后的任何机器都可以处理。特殊情况
操作缓慢。那些原本依赖在整个过程中保持打开状态的流式连接的场景,应改用 Tasks 扩展。在未支持持久连接的传输层上重新创建持久连接是错误的做法。
永远不会实现远程化的工具。仅在客户端本地运行的服务器最适合使用 stdio,这类场景几乎不存在部署方面的问题。
版本混用的客户端。由于旧版本客户端会自动回退到 2025-11-25 版本,因此一次部署可以在一段时间内同时支持两种协议版本,无需所有客户端都与服务器同步升级。
扩展性与生产环境中的挑战
多个实例。在会话模型下,要让客户端始终绑定到认识它的那个实例,就需要使用粘性会话或共享会话存储。这一需求已不复存在,对于需要管理多个服务器的用户而言,这正是重写代码的主要优势。
覆盖日益增多的服务器的 TLS 加密。在每个 EC2 实例上处理证书很快就会变得难以管理。在服务器群前部署持有 ACM 证书的托管负载均衡器,其工作量远低于在每个服务器上单独配置 nginx TLS。
令牌的发放与轮换。静态承载令牌存在安全上限,而且当客户端数量超过少数几个时,其管理也会变得十分繁琐,因为每次轮换都需要手动协调。基于 OAuth 2.1 的令牌发放机制能够同时解决安全问题与轮换带来的负担。
无需服务器端会话即可跨调用追踪。由于没有服务器端的会话来连接一系列调用,请求载荷中携带的关联 ID 就成了重建单个智能体在多次调用中的操作情况的唯一可靠方式。
在客户端处理恢复功能
nginx 配置与 bearer-check 中间件负责传输层处理及基础身份验证。以下是多往返请求中客户端端的最低限度实现方案。
async def call_tool_with_resume(client, tool_name, params):
result = await client.call_tool(tool_name, params)
if result.get("type") == "InputRequiredResult":
answers = collect_answers(result["questions"])
return await client.call_tool(
tool_name,
{**params, "answers": answers, "requestState": result["requestState"]},
)
return result
关键在于最后一行,此时requestState会完全按照服务器提供的原样返回。
常见误区
- 将“已部署”误认为“可投入生产”。nginx加上令牌仅满足了最低要求,并非全部标准。在广泛共享URL之前,应让MCP端点达到任何公共API的标准,具备适当的身份验证、详细的日志记录以及速率限制机制。
- 过度依赖服务器内存。早期的某些部署默默地假设能够记住上一次请求中的客户端信息。根据当前规范,每次请求都可能连接到不同的实例,因此应做好相应准备。
能在生产环境中持续使用的实践
- 避免设计出无法扩展的架构。所有需要在多次调用之间保持状态的数据都应存储在请求载荷或外部存储中。
- 将每个令牌与其对应的资源绑定,因为那些被所有可信服务器接受的令牌迟早会被用于不当的场景。
- 协商协议版本,不要默认使用某个版本。客户端和服务器很少会在同一天升级,因此需要考虑这种时间差。
- 在边缘层进行身份验证。当 Nginx 拒绝无效请求时,MCP 过程会更简单,暴露的信息也更少。
- 不要改动那些不可见的字段。
requestState及类似字段的存在是为了让服务器能够依赖其返回值,因此客户端不应读取或修改它们。
这对部署意味着什么
从理论上讲,MCP 非常简单。部署过程中需要做出关键决策,而 2026 年 7 月 28 日的规范更新改变了这些决策。Streamable HTTP 更容易在普通基础设施上运行,无状态特性使得添加 EC2 实例成为一种可行的选择,而非必须构建会话管理系统,而且现在的授权模型也更适合面向公网的服务器。这些内容一旦理解起来并不困难,但往往是理论性描述中会忽略的部分。
相关阅读
- 实用笔记:模型上下文协议(MCP)详解:初学者指南 — 该指南提供了可操作的步骤说明,包括合同定义、检查项以及适用于采用该模式的团队的即用型代码模板。
- 实用笔记:RAG系统:从零到精通的完整指南(2026年版) — 该指南同样提供了可操作的步骤说明,包括合同定义、检查项以及适用于采用该模式的团队的即用型代码模板。