最近在折腾MCP服务,想把它部署在自己的Linux服务器上,然后让本地的Cursor和VS Code Agent通过MCP协议调用里面的工具。但搞了两天,一直卡在认证和权限这块。我是直接用HTTP暴露的,但这样肯定不安全,而且Agent每次连接都要手动配置token,感觉很麻烦。有没有大佬分享下比较成熟的方案?比如用SSH隧道或者反向代理加HTTPS?另外MCP的transport层支持WebSocket吗?我看官方文档说支持stdio和HTTP,但没提WebSocket,不知道是不是我理解错了。希望有实际部署经验的朋友指点一下,谢谢!
MCP部署在自建服务器上,怎么跟本地IDE的Agent安全通信?
全部回复
共 155 条我最近刚把MCP服务从裸HTTP迁到Caddy反代+Cloudflare Tunnel,认证这块直接用Caddy的forward_auth接了个OIDC,Cursor那边用Authorization头带token,一次配置好就再没手动碰过。你提到的WebSocket,MCP官方spec确实没把它列为正式transport,但有个社区PR在搞WS支持,不过稳定性存疑,不如直接HTTP+SSE靠谱。SSH隧道其实是最快的方案,但有个坑是Cursor的Agent进程如果重启,隧道断了不会自动重连,得用autossh或者systemd定时检查。你要是追求零配置,可以试试把MCP服务包装成systemd socket unit,然后通过ssh localhost转发,配合免密登录,体验很接近原生。另外权限这块别只看transport,工具粒度一定要做白名单,我之前就是图省事全放开了,结果Agent自己调了个危险函数,差点把数据库删了。
我之前也是直接HTTP暴露,后来换成了Caddy自动HTTPS加Basic Auth,配合MCP的header里带token,比手动配省事多了。WebSocket官方确实没提,但MCP的HTTP transport其实支持Streamable HTTP,底层可以走WebSocket,只是文档写得比较隐晦。SSH隧道对单机开发够用,但多设备同步还是得靠反向代理,建议用cloudflared tunnel,免费且不用暴露端口。另外token刷新可以用MCP的OAuth授权流程,或者干脆用API key加IP白名单,虽然麻烦点但最稳。
我之前也踩过这个坑,HTTP裸奔确实不行,后来我是用Caddy反代加了个Basic Auth,然后让Agent走HTTPS,虽然每次要填密码但至少比明文强。MCP的transport层目前官方确实只支持stdio和HTTP,WebSocket还没进规范,不过你可以自己写个适配器把WS包一层,社区里有人这么干过。SSH隧道其实最简单,本地IDE直接连localhost转发端口,认证走SSH密钥,省心很多,就是每次重启隧道有点烦。
其实我后来发现一个取巧的办法,用tailscale把服务器和本地组个内网,然后MCP直接绑在内网IP上,这样既不用暴露公网,也省了配置token的功夫,Agent连起来就当成局域网服务用。不过你要是想多端共享,还是得搞个正经的认证方案,比如OAuth2代理,但那个配置起来又是一天。
对了你提到的token手动配置,可以试试在MCP server端写个动态token脚本,每次启动时生成短期凭证,然后Agent这边用环境变量读取,这样至少不用频繁手改。反正这玩意儿官方文档确实有点简陋,好多细节都得自己摸。
之前折腾过类似场景,后来直接放弃了HTTP暴露,改用Cloudflare Tunnel加Access策略,Agent那边用short-lived token自动换,省心很多。MCP官方确实没提WebSocket,但社区有个unofficial的ws transport实现,不过我试下来稳定性一般,不如直接走SSH反向隧道实在。其实你如果限定内网用,最简单是wireguard组网,然后IDE里直连内网IP+固定token,比公网暴露安全一个量级,配置也就一次的事。
其实我最近刚好踩过这个坑,最后是用Caddy反代加mTLS解决的,比纯token省心不少,Agent那边配一次证书就完事。WebSocket的话MCP官方确实没直接支持,但你可以用websocat包一层,或者干脆让Agent走SSE,效果差不多。另外token麻烦的话,可以试试短期token配合Agent的密钥链存储,虽然还是得配一次,但至少不用每次手动敲。
直接用SSH隧道吧,最省事也最稳,本地IDE连远程MCP服务就跟连本地一样,根本不用暴露HTTP端口,token都能省了。反向代理加HTTPS我也试过,但证书和token轮换烦得要死,除非你有现成的网关设施,否则别碰。WebSocket其实官方没明说支持,但有人用SSE或者自定义transport绕过,不过稳定性看运气,我建议还是老老实实走stdio+SSH,Cursor里配置个远程命令就行,体验挺顺的。
说实话我最近也在搞这个,最后直接上了Caddy反代+Cloudflare Tunnel,mTLS校验客户端证书,Agent那边配一次就完事,比手动token省心太多。WebSocket官方确实没写,但MCP的HTTP transport其实底层就是streamable HTTP,你可以用nginx把Upgrade头转发一下,实测Cursor是能走的。另外建议别直接用HTTP暴露公网,哪怕内网穿透也套个VPN,不然哪天扫描器给你塞个挖矿脚本就哭了。
刚好上个月折腾完这套东西,说点实际踩坑经验。别直接用HTTP暴露,哪怕套了token也容易被扫,我后来是拿Caddy反向代理加自动HTTPS,然后在MCP服务前面挂了个简单的Basic Auth或者API Key校验,配合防火墙只允许服务器IP访问,这样至少把攻击面缩到最小。关于WS,官方文档确实只写了stdio和HTTP,但MCP的HTTP transport其实支持Streamable HTTP,底层可以走WebSocket升级,你如果用的是官方SDK,把endpoint配成/ws或者直接走/streamable就可以,具体看服务端实现,Node和Python的SDK行为还不完全一样。至于Agent每次都要配token,这个没法完全省,但我建议用环境变量或者Cursor的.cursor/mcp.json里引用本地文件,别硬编码在配置里,这样换机器也方便。SSH隧道也是个办法,但如果你想让多个Agent同时连,维护隧道反而麻烦,不如直接用Tailscale或者WireGuard组虚拟内网,把MCP服务绑在虚拟网卡的IP上,这样既安全又不用每次搞端口转发。还有个细节,MCP的权限粒度挺粗的,工具级别要么全放要么全禁,你可以自己在服务端包一层参数白名单,比如只允许读文件不允许执行命令,否则Agent一旦被诱导调用危险工具就GG了。
折腾过同样的问题,最后我是用cloudflared tunnel解决的,内网穿透加自动HTTPS,认证走Cloudflare Access的零信任策略,比反向代理省事不少。MCP的transport层确实只定义了stdio和HTTP,WebSocket没进官方规范,但有些社区实现自己扩展了,不过不建议依赖这个,毕竟要和Cursor这类IDE的客户端兼容,老老实实用HTTP+SSE更稳。你提到token手动配置麻烦,可以试试在本地起一个轻量的代理进程,把MCP的服务地址伪装成localhost,代理层处理好认证,这样IDE那边就感知不到远程细节了,类似Docker的socket转发思路。另外,如果你不想维护隧道,也可以考虑Tailscale的MagicDNS,直接在内网里用机器名互相访问,再套一层mTLS,虽然配置也有点繁琐,但胜在稳定。目前我自己是HTTP暴露在非标准端口+防火墙只允许VPN网段IP访问,配合MCP的Authorization header做动态token,每次会话前用脚本刷新,算是勉强能用,但确实不够优雅,同求更成熟的方案。
直接上Caddy反代+Cloudflare Tunnel,tls和认证全包了,token用header传一次搞定,别折腾SSH隧道。
WebSocket官方没支持就别硬等了,HTTP+Streamable够用,等社区轮子吧。
MCP官方确实没把WebSocket列进标准transport,但你可以在HTTP上叠一层SSE或者自己封装WS,社区里有人这么干过,稳定性还行。我自己的方案是用Caddy反代加mTLS,证书直接塞进Agent的配置里,比token省心,而且Caddy的自动HTTPS能省掉不少麻烦。认证这块别自己造轮子,直接上OAuth2的client credentials流程,Cursor那边写个小脚本定期刷新token就行,手动配置一次后面就全自动了。
对了,你提到SSH隧道,其实如果只是单机用,直接ssh -L把远程端口映射到本地localhost,然后Agent连本地地址,连HTTPS都省了,就是注意别把隧道挂到公网上。
我之前也踩过这个坑,后来直接用Caddy反代加个Basic Auth或者mTLS,体感比裸HTTP强太多,而且配置起来比Nginx省心。MCP官方确实只提了stdio和HTTP,但HTTP transport在1.0里已经能支持流式响应了,WebSocket其实不是必须的,除非你要做双向长连接。SSH隧道适合单机临时用,但Agent每次重连都要重新建隧道,不如干脆搞个内网穿透或者VPN,一劳永逸。令牌这块可以试试让IDE侧用环境变量注入,别写死在配置里,配合密钥管理服务定期轮换,能省掉不少手动操作。
试过用cloudflared隧道套HTTPS,token放请求头里,比裸HTTP省心多了,但WebSocket确实没官方支持,得自己包一层。
我直接ssh -L把远程MCP端口映射到本地,IDE连localhost就行,token走ssh验证,简单粗暴不折腾。
折腾过同样的问题,说下我的方案吧:别直接用HTTP暴露,套个Caddy或者Nginx反代做TLS termination,然后用mTLS或者简单的Bearer token加IP白名单双保险,这样比纯token靠谱多了。关于WebSocket,官方确实没写,但MCP的HTTP transport其实底层就是走的Streamable HTTP,你完全可以用WebSocket网关把请求包一层,我试过用websocat做转发,能跑通但没必要,直接HTTP+SSH隧道更省事。另外你提到每次手动配token烦,可以用Cursor的env文件或者VS Code的tasks.json里预置环境变量,让Agent启动时自动读取,或者写个小脚本动态生成短期token配到IDE配置里,虽然不算全自动但至少不用每次敲。还有个小坑,自建服务器记得把MCP服务绑到内网IP或者Unix socket上,别监听0.0.0.0,然后SSH隧道用本地端口转发,这样IDE连localhost就能走加密通道,权限控制也能靠系统用户隔离。最后,如果Agent支持自定义headers,用Authorization头传JWT比token更灵活,能设过期时间,配合refresh机制能省掉很多手动更新的事。总之别追求一步到位,先用隧道跑通,再慢慢加认证层,比一上来搞全套要稳。
我最近也在搞这个,最后用的是cloudflared tunnel,直接把MCP服务暴露成HTTPS,然后在Cursor里配一次token就行,不用每次手动搞。WebSocket的话,MCP的HTTP transport其实已经支持streamable HTTP了,底层就是类似WebSocket的长连接,你直接用这个就行,没必要纠结协议名。认证这块建议上个简单的OAuth2代理,比如oauth2-proxy,比手动配token省心多了。
之前也踩过这个坑,HTTP裸奔肯定不行,我现在是直接用cloudflared tunnel把MCP服务包一层,本地Agent配个cloudflared token,比SSH隧道省心多了,而且自带鉴权。至于WebSocket,官方确实没直接支持,但可以用websocat这类工具在传输层转一下,把WS包成HTTP的升级请求,实测能跑通。另外token自动刷新这块,建议在服务端做个短期token+refresh机制,或者直接用mcp的session header自定义认证,能省掉每次手动配的麻烦。
我跟你情况差不多,折腾MCP部署的时候也在认证这块卡了很久。我最后是用Caddy做的反向代理,直接自动签HTTPS证书,然后在Caddy层用forward_auth接了个简单的OAuth2代理,这样Cursor那边的Agent只需要在登录时拿一次token,后面Caddy会自动刷新,省得手动配了。关于WebSocket,官方文档确实只提了stdio和HTTP,但HTTP transport其实支持Streamable HTTP,底层是可以走WebSocket升级的,只是没有单独列出,你可以在MCP SDK里看下transport的握手逻辑,有Upgrade头处理。另外推荐试试cloudflared tunnel,它能把本地服务暴露成HTTPS,还能加Access策略做设备认证,比纯SSH隧道省心,SSH隧道在Agent重连时经常断,体验不太好。权限这块建议别用全局API key,给每个工具单独配个role,用JWT带scope去控制,虽然前期配置麻烦点,但后面维护会舒服很多。
我之前折腾过一阵这个,最后是走Cloudflare Tunnel加Access策略搞定的,比直接暴露端口省心很多,而且不用自己维护证书。你说的WebSocket,MCP官方规范里其实预留了,但主要面向浏览器场景,目前主流SDK确实只把HTTP和stdio作为一等公民,建议别在WebSocket上花时间。认证这块,如果你是单机自己用,其实可以试试在服务器上跑个轻量的OAuth2代理,比如oauth2-proxy,跟IDE的Agent配合时用device flow,一次性授权后token自动刷新,体验比手动配token好太多。另外如果你用的是Cursor,它的Agent对自定义MCP的认证支持其实还不完善,我后来干脆把敏感工具拆成独立的小服务,用mTLS互相认证,虽然配置麻烦点但一劳永逸。还有个小技巧,可以把MCP的stdio模式包装成SSH子命令,本地IDE直接通过SSH执行远程命令来调用,这样连网络端口都省了,完全复用系统认证,不过延迟会高一点,看你能不能接受。
说实话你踩的坑我刚入坑时也全踩了一遍,HTTP裸奔那肯定不行,稍微扫一下端口就被机器人打爆了。我现在的方案是nginx反代加client certificate,只允许我这台机器的证书访问,然后在MCP server层用API key做二次校验,双保险。token管理麻烦的问题,建议你用mcp的header template功能(如果SDK支持的话)动态从系统钥匙串里读token,写个小脚本就行,不用每次手动敲。至于WebSocket,官方明确没实现,别期待了,但HTTP其实够用,关键是别把MCP服务绑到公网网卡上,只监听本地或内网,然后用WireGuard组个虚拟局域网,这样IDE和服务器之间就走加密隧道了,配置一次永久生效,比SSH隧道稳定多了,而且还能顺便访问服务器上的其他内网服务。
我最近也在搞这个,试下来最省心的方案是直接上Caddy反代加Basic Auth或mTLS,比手动配SSH隧道省事多了,而且Caddy自动续HTTPS证书。关于WebSocket,官方确实没明说,但MCP的HTTP transport其实底层支持WebSocket升级,你可以在网关层把WS请求代理到后端stdio服务,实测Cursor是能正常识别的。token麻烦的话可以写个脚本用MCP的OAuth流程自动刷新,或者干脆用系统keychain存凭据,别每次手敲。
我之前踩过的坑是权限隔离,建议在服务器上用Docker跑MCP服务,每个工具单独容器,这样即使Agent被攻破也拿不到宿主机权限。另外如果你不想暴露公网,Tailscale或者WireGuard组虚拟内网也挺好用,直接让IDE连内网IP,省掉一堆HTTPS配置。你那边Agent连接超时问题遇到过没?我调了好久才发现是反向代理的keep-alive设置太短。
我之前也踩过这坑,最后用cloudflared隧道解决的,配个Access policy比搞token省心多了。