这篇不影响主线。 等你要把 MCP 部署到别人能访问的地方、或者要调 handler 行为时再看。
配置项
NewStreamableHTTPHandler(getServer, opts) 的第二个参数平时大多传 nil,需要调行为时常用这几个:
| 字段 | 默认 | 作用 |
|---|---|---|
JSONResponse |
false | 默认响应走 SSE 流;设为 true 时每次 POST 直接回一个 JSON,方便 curl 调试,但流式能力弱——服务端没法在同一条连接里先推消息再给结果 |
Stateless |
false | 默认有状态,客户端要带 MCP-Session-Id;设为 true 后每次请求都是临时会话、不校验会话头,适合只有工具调用的轻量部署,代价是服务端主动发起的请求无法使用 |
SessionTimeout |
0 | 会话空闲超过该时长自动关闭,防占资源;零值表示不自动关 |
Logger |
nil | 传入 *slog.Logger 让 handler 打结构化日志。注意 nil 是完全不记日志,不是记到 stdout |
EventStore |
nil | 给 SSE 流做事件存储,客户端断线后能带 Last-Event-ID 续传,见 SSE 断点续传 |
DisableLocalhostProtection |
false | 关掉下面的本地保护开关 |
CrossOriginProtection |
默认策略 | 自定义跨源策略 |
完整字段说明以 pkg.go.dev 为准。
两个默认开启的安全开关
这两个开关默认就是开的,一般不用管。但你要部署到特殊环境、发现请求被莫名拒掉时,得知道是它们在拦。
本地保护:防 DNS 重绑定
浏览器有同源策略,页面里的 JS 只能请求同源地址。攻击者利用「域名解析结果可以随时变」这一点:
- 让
evil.com先解析到自己的公网服务器,页面从那儿加载——页面眼里自己的源是evil.com。 - 随后把
evil.com的解析改成127.0.0.1。 - 页面里再发往
http://evil.com:8000的请求,浏览器眼里仍是同源,实际却打到了你的本机服务上。
这其中包括只监听回环地址、又没做鉴权的 MCP 端口——攻击者就这样绕过了「只绑 127.0.0.1 就安全」的假设。
handler 默认开启的校验会拒绝 Host 不是本机形态的请求。一般不用管它,除非你的部署确实需要别的域名访问本地服务。
跨源保护:防「借用登录态」的跨站请求
典型场景:用户开着银行后台的 Cookie,又打开了攻击者页面;页面发个请求到银行接口,浏览器自动带上 Cookie,转账就被「代执行」了。
对 MCP 来说风险类似。默认策略会校验跨源请求,真要自定义可以传 *http.CrossOriginProtection。
部署检查清单
对外提供服务前,这几条挨个过一遍:
- 监听地址是
127.0.0.1还是0.0.0.0?只有确实要对公网提供服务时才用后者。 - 加鉴权了吗?见加一层 Bearer 鉴权。
- 需要多实例吗?需要的话
EventStore得换成 Redis 之类的共享实现,见 SSE 断点续传。 - 配
SessionTimeout了吗?零值表示会话永不自动关闭,长期运行会积压。
安全方面更完整的规范建议见 security best practices。
下一篇:SSE 断点续传——可选,真遇到断线丢消息再看。