最近在折腾MCP服务,想把它部署在自己的Linux服务器上,然后让本地的Cursor和VS Code Agent通过MCP协议调用里面的工具。但搞了两天,一直卡在认证和权限这块。我是直接用HTTP暴露的,但这样肯定不安全,而且Agent每次连接都要手动配置token,感觉很麻烦。有没有大佬分享下比较成熟的方案?比如用SSH隧道或者反向代理加HTTPS?另外MCP的transport层支持WebSocket吗?我看官方文档说支持stdio和HTTP,但没提WebSocket,不知道是不是我理解错了。希望有实际部署经验的朋友指点一下,谢谢!
MCP部署在自建服务器上,怎么跟本地IDE的Agent安全通信?
全部回复
共 156 条MCP官方确实没把WebSocket列为标准transport,但社区有通过SSE或者自定义gateway绕路实现的,你可以看看mcp-proxy这类项目。我自己是直接用Caddy做反向代理,配个mTLS,然后在IDE端用API Key做header认证,比每次手敲token省心太多。SSH隧道其实也够用,就是多客户端时管理麻烦点。
另外你提到自动配置token,其实Cursor和VS Code的MCP client都支持读取环境变量,你可以把密钥存在服务器上的.env里,本地用dotenv加载,这样就不用每次手动输入了。不过最省事的方案还是上Tailscale,把服务器和本地机器组个虚拟局域网,直接走内网IP,认证交给WireGuard,HTTP暴露的问题就彻底没了。
caddy反代加cloudflare tunnel挺稳的,token放header里每次请求带一下就行,ws协议确实没官方支持。
直接上Caddy反代加mTLS,Token用环境变量注入别手填,WebSocket官方没支持但HTTP轮询够用了。
我之前也踩过这个坑,折腾了一圈下来感觉最省心的方案还是用SSH隧道打底,把MCP服务绑在本地回环地址上,然后本地IDE直接连SSH转发过去的端口,认证交给SSH的密钥机制,体验比手动配token好太多了。而且这样连HTTP暴露的风险也一起解决了,不用再去纠结什么IP白名单。关于WebSocket,官方文档没提是正常的,因为MCP目前的HTTP传输就是基于Streamable HTTP,本质上支持双向流,不是传统那种request-response模式,所以不需要额外搞WebSocket——你只要把HTTP服务起在支持长连接的反向代理后面就行。我自己现在用的是Caddy自动签发HTTPS证书,然后在服务器上跑一个轻量认证中间件,校验JWT,但把token的签发逻辑做成了首次扫码绑定,之后自动刷新,基本感觉不到需要手动操作。另外提个醒,如果Agent那边支持自定义header,建议把认证信息放到header里而不是URL参数,不然日志里全是token泄漏风险。还有个小技巧,MCP的stdio transport其实可以配合tmux在远程服务器上跑,本地用SSH的RemoteCommand直接拉起来,这样连HTTP都省了,不过对Cursor这类GUI工具不太友好,VS Code倒是可以试试。
我之前也踩过这个坑,后来直接用tailscale组了虚拟内网,MCP服务只监听内网IP,本地IDE通过tailscale的IP访问,完全不用暴露公网,token都省了。WebSocket的话官方文档确实没提,但社区有人用Caddy反向代理把WebSocket升级成HTTP/2转发,实际效果还行,不过得自己写适配层。另外你要是坚持用token,建议搞个短时有效的JWT,配合nginx的auth_request做动态校验,别用静态token,不然迟早被扫到。
WS确实没进官方标准,但可以用Caddy反代自动签HTTPS证书,再加个tailscale组虚拟内网,比SSH隧道省心多了。
刚把类似架构跑通,建议别直接暴露HTTP,用tailscale或者wireguard组内网,然后本地IDE走内网IP加token,体验比SSH隧道稳很多,SSH隧道断线重连太折磨了。WebSocket确实官方没直接支持,但可以套一层caddy或者nginx做grpc-web转发,或者直接用SSE替代轮询,延迟能接受。另外token自动刷新这块,写个小脚本定时调MCP的auth接口换新token,配合IDE的env变量注入,基本做到无感。
说实话我在生产环境折腾过类似的东西,最后是走了tailscale + Caddy反向代理这条路,比直接暴露HTTP省心太多。你提到的WebSocket其实官方协议层面是支持的,只是文档没细说,MCP的HTTP transport在升级握手后就是WebSocket,所以用ws://或者wss://完全没问题。我现在的方案是服务器上跑一个Caddy,自动配好HTTPS和Basic Auth,然后本地用curl或者写个小脚本动态获取token,配合Cursor的Agent环境变量注入,这样每次连接不用手动改配置。另外如果你不想搞反向代理,SSH隧道其实是最快的——本地直接ssh -L把远程端口映射到localhost,Agent配置里填http://127.0.0.1:端口就行,认证这块完全交给SSH,省了token的烦恼。不过要注意隧道断了Agent会静默重连失败,建议加个autossh守护。还有个小坑,MCP的HTTP transport对header大小有限制,如果你工具返回内容特别大,容易触发413,别忘了在反向代理层调大client_max_body_size。权限这块建议别用全局token,给每个Agent单独建key,然后在MCP服务端做scope限制,比如只允许调用特定工具组,这样即使泄露也能快速回收。
用tailscale组虚拟局域网连MCP最省心,token免了,SSH隧道也行但麻烦点。
我之前也踩过这个坑,HTTP裸奔确实不行,后来直接用Caddy做了个反向代理,自动申请HTTPS证书,然后在MCP服务端加了个简单的API Key校验,配合Cursor的环境变量注入token,基本就够用了,不用每次手填。关于WebSocket,官方文档确实没提,但MCP的HTTP transport其实支持Streamable HTTP,底层可以走WebSocket的升级,你直接用那个就行,不用自己额外实现。另外如果你服务器和本地在一个内网,SSH隧道是最省事的,但跨公网还是建议套个带认证的反向代理。
我最近也踩过这个坑,后来直接用cloudflared tunnel把MCP服务套了层HTTPS,认证用长期token放在header里,配合Cursor的agent配置里填环境变量,体验还行。MCP官方确实没正式支持WebSocket,不过你可以试试在HTTP transport上跑SSE,或者自己封装一层WS转发,也能凑合用。另外建议别用简单token,搞个类似mTLS的方案,虽然配置麻烦点,但一劳永逸,而且本地IDE那边用系统钥匙串存密钥,不用每次手动输。
这问题我踩过一模一样的坑,后来直接上了Caddy反代加Cloudflare Tunnel,mTLS双向认证,本地Agent那边用token换短期凭证,基本不用手动管。MCP的HTTP transport其实支持Streamable HTTP,本质就是WebSocket的封装,但官方文档确实没明说,你在SDK里找找StreamableHTTPTransport类就能看到。另外如果不想搞太复杂,直接走SSH隧道最省事,本地起个端口转发,Agent连localhost就行,认证交给SSH密钥,比裸暴露强一万倍。
之前也踩过这个坑,后来直接用tailscale组了虚拟内网,本地IDE连MCP就走内网IP加个简单token,省了公网暴露的麻烦。WebSocket官方确实没提,但HTTP升级协议理论上能兼容,不过建议别折腾,直接SSH反向隧道最稳,把远端端口映射到本地回环地址,再配个固定header认证就够用了。另外可以看看mcp的streamable-http特性,新版sdk对长连接支持好了不少,token刷新逻辑自己写个中间层就行。
我之前也卡在认证这块,后来直接上了Caddy反代,自动申请HTTPS证书,然后用Basic Auth或者直接挂个Cloudflare Access在前面,比手动搞token省心多了。MCP的transport层确实官方只提了stdio和HTTP,但HTTP那套其实可以跑在WebSocket上,不过没必要,直接HTTPS就够稳了。SSH隧道适合临时调试,长期用还是反代加个身份验证中间件靠谱,Cursor那边配一次token就行。对了,你服务器上如果开了防火墙,记得只放行443端口,别把MCP服务裸奔到公网上。
我也踩过这个坑,HTTP裸奔确实不行,后来用Caddy反代加了个Basic Auth和mTLS,比手动配token省心不少,Agent端只要在环境变量里塞一次凭证就行。SSH隧道其实更省事,本地起个进程把远程的MCP端口映射过来,IDE里配localhost就行,就是每次重连要重新跑一下命令。WebSocket官方确实没明说支持,但我看过有人用SSE做替代,也能实现类似长连接的效果,你可以搜下MCP的streamable HTTP,那是新规范,兼容性更好。
之前折腾过,直接上Caddy反代加Basic Auth,配合tailscale做内网访问,比SSH隧道稳多了。
说实话我最近也刚趟完这坑,你直接用HTTP暴露肯定不行,我试过用Caddy或者Nginx反代加个Client Certificates验证,比token省心不少,而且MCP现在的HTTP transport其实底层就是Streamable HTTP,官方文档没细说但WebSocket的支持在社区PR里已经有人提了,你可以去GitHub搜下mcp-spec的讨论区。我自己最后是用的SSH隧道方案,在本地IDE里配个Remote-SSH把端口转发出来,然后MCP的server地址直接写localhost,这样完全不用处理认证,只要你的服务器SSH密钥够安全就行。不过如果你需要多客户端同时连,隧道就不太方便了,这时候可以考虑装个cloudflared做Tunnel,它自带短时证书比自签HTTPS好配,而且免费额度够用。另外我看到Cursor新版本好像内置了MCP的OAuth回调支持,你可以试试把服务端注册成Dynamic Client,这样每次连接会弹浏览器授权,虽然还是有点烦但比手动填token强。关于权限,建议你在MCP server里做个简单的RBAC,用JWT的scope字段区分不同Agent能调的工具,别把所有工具都暴露给本地调试。对了,如果你最后决定用HTTP,记得把server的监听地址绑到127.0.0.1,再让反代去连,别直接暴露到公网。
- 别用HTTP裸奔了,直接上Caddy或者Nginx反代,自动签个HTTPS证书,然后配个Basic Auth或者mTLS,比你手动token省心多了,而且顺手把WebSocket也代理了。2. 我试过SSH隧道,但本地IDE每次重启都要重新建隧道,烦死,后来直接改走本地Unix socket再通过SSH转发,稳定是稳定,就是调试的时候有点绕。3. 关于WebSocket,MCP官方HTTP transport其实支持流式响应,但确实没明确说WebSocket,不过你用SSE或者WebSocket封一层也能跑,社区有人这么干过。4. 我现在是干脆把MCP服务拆成两个端口,一个只监听localhost给本地代理用,一个走HTTPS给远程,认证全部走API Key+IP白名单,感觉还算靠谱,你可以参考下。5. 顺便问下,你用Cursor的时候Agent连接超时的问题解决了吗?我这边老是遇到空闲连接被断开,得加心跳才行。
试试另一种风格:
说实话我也踩过这坑,后来直接放弃手动token,改用Cloudflare Tunnel套一层,免费版就够用,自动带证书和访问策略,本地IDE连的时候感觉跟内网一样,就是延迟稍微高点。MCP的transport层目前官方确实只有stdio和HTTP,但HTTP可以走SSE,你WebSocket
反向代理套个Caddy自动HTTPS就行,token用Bearer放header里,别直接HTTP裸奔。WebSocket官方没写但能用,我试过stable,记得开heartbeat。
我最近也踩过这坑,直接暴露HTTP肯定不行,建议用Caddy或者Nginx反代加个mTLS,比纯token省心不少。MCP官方确实没提WebSocket,但HTTP的streamable transport其实已经支持全双工了,你本地Agent用SSH隧道把远程端口映射到localhost再走stdio也行,我目前就是这么干的。至于token自动刷新,可以写个脚本用keyring存凭证,或者干脆用OAuth2的device flow,Cursor那边配一次就能自动续期。