最近在折腾MCP服务,想把它部署在自己的Linux服务器上,然后让本地的Cursor和VS Code Agent通过MCP协议调用里面的工具。但搞了两天,一直卡在认证和权限这块。我是直接用HTTP暴露的,但这样肯定不安全,而且Agent每次连接都要手动配置token,感觉很麻烦。有没有大佬分享下比较成熟的方案?比如用SSH隧道或者反向代理加HTTPS?另外MCP的transport层支持WebSocket吗?我看官方文档说支持stdio和HTTP,但没提WebSocket,不知道是不是我理解错了。希望有实际部署经验的朋友指点一下,谢谢!
MCP部署在自建服务器上,怎么跟本地IDE的Agent安全通信?
全部回复
共 155 条折腾过类似的东西,我觉得你直接在HTTP上做token是绕不开的,但可以换个思路:用Caddy或者Nginx反代,前面挂一层Cloudflare Access或者Authelia做OIDC,这样IDE那边只需要配一次token,后面由反代帮你刷新,Agent连接的时候其实不用手动管。MCP的transport层官方文档确实只写了stdio和HTTP,但HTTP那套其实可以承载Streamable HTTP,底层走长连接,效果上接近WebSocket,Cursor和VS Code的MCP客户端都认这个,你直接从HTTP升级到SSE也行,不用非得等官方支持WebSocket。至于SSH隧道,如果你服务器和本地都是固定IP或者走内网穿透,那确实最省事,直接把MCP端口绑到127.0.0.1,然后本地用ssh -L转发,但这样每次重连都要起隧道,不如做个systemd服务自动维护。我自己的做法是Caddy反代加mTLS,客户端证书放本地,服务端只认那个证书,这样既不用每次配token,也比纯HTTP裸奔安全得多,你可以试试看。另外权限控制别只依赖MCP层,工具内部最好也做一层角色校验,不然一旦隧道打通,所有工具都是全量暴露。
说实话我也踩过这个坑,后来直接用tailscale组虚拟内网,本地IDE走私有IP访问MCP服务,比SSH隧道省心多了,token都省了。WebSocket官方确实没提,但你别纠结,HTTP+SSE就够用,关键是反向代理层把TLS和鉴权做好。我现在的做法是nginx前面挂一层authelia做OIDC,然后MCP那边只监听localhost,这样Agent配置一次token就能长期用,不用反复折腾。
我前几天刚折腾完这个,用的就是Caddy反代加Cloudflare Tunnel,MCP服务本身只绑内网端口,外面完全暴露不出去,Agent那边配个一次性token就够了,比裸HTTP省心太多。关于WebSocket,官方确实只写了stdio和HTTP,但HTTP transport本来就可以走WS升级,实际用下来没遇到问题,不过得确认你用的MCP SDK版本支持。你现在的Agent是每次启动都要重新填token,还是能存到本地keychain里?我这边VS Code配好后重启都不用再输了。
我最近也踩过这个坑,最后用cloudflared tunnel做的反向代理,配合Cloudflare Access做零信任认证,体验比SSH隧道省心不少。MCP的HTTP transport其实已经够用了,WebSocket官方确实没提,但如果你用SSE的话,本质上就是走HTTP长连接,没必要纠结WS。另外token这块我建议搞个短期JWT,配合Cursor的environment variable自动注入,基本能实现免手动配置。你试试把MCP服务挂在nginx后面,加个basic auth先跑通,再往上叠方案。
我前段时间刚好折腾过这个,最后是用tailscale把服务器和本地组了个虚拟内网,MCP直接走内网IP加自定义端口,省掉了公网暴露的麻烦。认证这块我用的是MCP官方的OAuth授权流程,虽然配置起来麻烦点,但配完以后Cursor那边基本无感,不用每次手动填token。关于WebSocket,你理解没错,官方transport确实只定义了stdio和HTTP,但HTTP那套其实可以自己封装成WS用,只是社区里没多少现成案例,我试过用cloudflared隧道把HTTP包成WS,稳定性一般,后来还是老实退回纯HTTP了。另外建议你反向代理前面加个类似authelia的轻量认证层,这样即使MCP本身没做细粒度权限控制,至少能挡住大部分扫描流量。还有个坑是Agent连接超时设置,默认值经常不够用,尤其是工具执行时间长的时候,记得调一下服务器端的keep-alive参数。
我之前也卡在这块,后来直接上了Caddy反代加mTLS,证书双向校验比token省心多了,配合MCP的HTTP transport基本够用。WebSocket官方确实没提,但MCP的HTTP支持streamable HTTP,本质上是半双工,你那些长任务用SSE轮询也行。另外如果只在自己电脑上用,试试ssh -L把远程的MCP端口映射到本地localhost,Agent配置里直接写127.0.0.1,连令牌都省了,就是每次开机得手动起隧道,稍微有点烦。
折腾过类似方案,说下我的实践。MCP官方确实只把HTTP和stdio列为正式transport,但WebSocket其实可以走HTTP的升级握手,自己封装一层就行,不过没必要,因为SSH隧道完全够用。我现在的做法是服务器上跑一个systemd服务,只监听127.0.0.1,然后本地IDE通过ssh -L 8765:localhost:8080 user@server建隧道,MCP配置里直接写http://localhost:8765,这样token都不用带,因为SSH本身就带认证了。至于权限,我建议你在MCP服务端做个简单的API key中间件,但别每次手敲,存到IDE的env文件里,Cursor和VS Code都支持从环境变量读取。如果你非要暴露公网,那反向代理加HTTPS是必须的,但还要做好IP白名单和rate limiting,否则分分钟被打爆。我目前这个方案跑了两个月,体验很顺,就是每次换网络重连SSH有点烦,后来写了个小脚本自动重连。
看到你卡在认证这块,我太有同感了,之前也折腾过一轮。HTTP暴露确实别想了,哪怕加了token也容易被扫到,我最后是直接放弃了公网直连。我现在的做法是直接用SSH反向隧道,在本地IDE那边跑一条ssh -R命令把远程的MCP端口映射到本地,然后Agent里就配localhost的地址,这样等于完全走SSH加密通道,认证也省了,因为SSH本身就有密钥验证,比你自己维护token省心太多。至于WebSocket,官方的规范文档里确实没把它列为一等transport,但社区里有人用websocket-client做了适配层,不过跟Cursor的兼容性不太好,说实话没必要硬上,我试过之后又退回stdio了。还有个思路是你可以在服务器上套个Caddy,自动申请HTTPS,然后用Basic Auth或者加个Cloudflare Access做前置认证,这样Agent那边配一次token就能缓存住,比每次手动填强。不过注意Cursor的Agent有时候会强制校验MCP协议头,反向代理的时候别改Content-Type,不然会报schema错误,这个坑我踩过。
WebSocket确实不在官方标准transport里,但MCP的HTTP transport本身支持STREAMING模式,走的是HTTP/2的长连接,效果上跟WebSocket差不多。你如果不想折腾自定义协议,直接用这个就行。认证这块我建议别用token,自建服务器可以搞个mTLS,证书双向校验,比token安全一个量级,而且IDE那边配置一次后面就不用管了。反向代理的话Caddy最省事,自动HTTPS还能帮你做客户端证书校验。不过有个坑,Cursor的Agent有时候会自己重连,如果走代理注意别把keep-alive超时设太短,不然频繁握手会卡。SSH隧道其实最省心,本地起个ssh -L把远端端口映射到localhost,然后MCP server只监听127.0.0.1,这样IDE配置localhost地址就行,token都可以省了,就是得保证服务器ssh服务够稳。你可以先试试这个方案,我用了半年没出过问题。
我最近刚好踩完这个坑,建议别折腾HTTP了,直接上SSH隧道最省心,本地起个端口转发到服务器的MCP服务,认证走系统用户,比token管理简单太多。MCP官方文档确实只提了stdio和HTTP,但实际HTTP transport可以跑在WebSocket之上,直接用支持WS的反代比如Caddy就能搞定。另外如果你用Cursor,它其实原生支持SSH remote,直接把MCP当远程命令跑都行,省掉网络层那堆破事。关于token麻烦的问题,可以本地写个小脚本从keychain读环境变量注入给Agent,不用每次手敲。最后提醒下,反向代理记得开mTLS,光靠HTTPS还是容易被扫到端口。
直接上cloudflared隧道吧,免费还带TLS,token丢环境变量里就行,比SSH反向代理省心多了。
WebSocket确实不在官方transport标准里,可以试试Cloudflare Tunnel加Access策略,省去手动token的麻烦。
SSH反向隧道最省心,配个autossh开机自启,IDE连localhost就行,token也能省了。
我试过用cloudflared tunnel把MCP服务套一层,本地IDE直接连内网地址,认证用短时token挺稳的。
Caddy反代加Cloudflare Tunnel最省心,token用短期JWT自动刷新,别手动配。WebSocket其实MCP新版协议已经支持了,文档更新慢而已。
WebSocket确实不在官方transport标准里,社区有第三方实现但维护状态参差不齐,建议别踩这个坑。我现在的做法是直接SSH隧道转发MCP端口到本地,配合密钥登录,IDE那边连localhost就行,省去token管理的麻烦。不过要注意隧道断线重连的问题,可以用autossh保活。反向代理加HTTPS也可以,但要在服务端做客户端证书校验,比单纯token安全得多,配置量也大一些。
我前阵子刚把MCP从裸HTTP迁到Caddy反代加Cloudflare Tunnel,基本思路是让Agent连本地的localhost,然后用Caddy把远程请求转发到本地,同时用mTLS做双向认证。你问的WebSocket,其实MCP的HTTP transport底层就是基于Streamable HTTP,它内部支持WebSocket升级,但官方文档确实没明说,你只要在Caddy配置里开启websocket支持就行。token这块别用静态的,我试过用JWT加短期过期时间,Agent那边写个插件自动刷新,虽然麻烦点但比手动配强多了。另外如果你用的Cursor,它其实支持自定义MCP server的header,可以在启动脚本里动态注入token,这样每次连接都自动带上,不用手动改配置。还有个坑是CORS,如果你IDE在浏览器里跑(比如VS Code Web),记得在反向代理层加上CORS头,不然工具调用会被浏览器拦。最后建议别直接用SSH隧道,虽然简单但断线重连很痛苦,我踩过这坑,还是systemd管理Caddy加自动重启靠谱。
我之前也是直接HTTP暴露然后被各种扫描器问候,后来用Caddy反代加了个简单的Basic Auth和IP白名单,配合Cloudflare Tunnel,比裸奔强多了。MCP官方确实只提了stdio和HTTP,但HTTP transport层其实可以跑在WebSocket上(就是HTTP Upgrade),我试过用ws替代轮询,延迟更低,不过客户端支持得看Agent的实现。Token这块我也烦,现在写了个小脚本用系统keychain存token,Agent启动时自动读取,省得每次手打。要是你有内网穿透需求,可以考虑frp开个加密通道,比纯SSH隧道稳。
试过用cloudflared tunnel套一层,配个零信任策略,比反向代理省事多了,token就用环境变量传。
MCP transport层目前确实只支持stdio和HTTP,WebSocket得自己封装一层,不过用Caddy反代加个Cloudflare Tunnel挺香的,token放header里也不难搞。
我这边是直接tailscale组网,本地agent走内网IP访问MCP服务,省掉一堆证书和鉴权的事。
试过用cloudflared隧道套一层认证,比裸HTTP省心不少,token用短期凭证定期换就行。