2024年11月25日,Anthropic推出模型上下文协议(MCP)的时候,几乎所有人的目光都被吸引了过去,包括当时还是Appwrite工程负责人的Christy。那时我刚以“工程实习生”的身份加入团队,对一整个新协议意味着什么、为什么它是件大事,完全没有概念。
从表面看,我的懵懂也不算全错。MCP本质上就是JSON-RPC,外面包了一层模式定义和一次握手。但真正让我们花了十六个月的东西,是包裹在它周围的所有其他部分。
![]()
MCP刚发布时,可流式传输的HTTP(Streamable HTTP)还不存在。这个传输方式是在2025年3月26日的修订版中才取代了原先的HTTP+SSE方案。Christy在2025年2月26日就已经在代码仓库里跑通了一个基于标准输入输出(stdio)的服务器。那时我们已经有API密钥体系,所以接线非常简单,一条命令就能挂上。
claude mcp add appwrite \ --env APPWRITE_PROJECT_ID= \ --env APPWRITE_API_KEY= \ --env APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 \ -- uvx mcp-server-appwrite这条命令干净利落,但它的权限天花板从一开始就写死在凭证里。一个API密钥天生就被限定在一个项目内,换个项目就得去编辑器配置里改参数。创建新项目完全不可能,任何组织层面的操作也都无从谈起。
凭证,正是stdio版本和托管版本之间唯一的本质区别。托管版本所有棘手的地方,都源于把那个项目范围的API密钥,替换成了一个属于用户而非属于项目的令牌。
按照规范,授权在MCP里确实是可选的。规范原文写得很清楚:授权对MCP实现来说是OPTIONAL的,所有基于HTTP传输的实现应当遵循这一规范。但对于一个工具调用就可以删掉整个数据库的服务来说,我们没办法心安理得地把授权当成可有可无的东西。
如果你用的是Auth0或者WorkOS这类身份服务,这里只是一面配置界面的事情。但Appwrite习惯把一切都收在自己家里,于是Matej负责动手构建了授权服务器本身,而我则构建了资源服务器,以及让真正的客户端能够跑起来之前云端还缺失的一堆东西。整个流程中,第二步到第六步,才是让“只需粘贴这个URL”变得名副其实的关键所在,没有任何东西是预先部署好的。
有三份RFC文档撑起了这个流程。其中受保护资源元数据(RFC 9728)是整个授权规范里唯一真正标注为MUST(必须)的部分。返回的元数据长这样:
{ "resource": "https://mcp.appwrite.io/", "authorization_servers": ["https://cloud.appwrite.io/v1/oauth2/console"], "scopes_supported": ["..."], "bearer_methods_supported": ["header"]}资源指示器(RFC 8707)则会把我们的规范URI打入令牌的aud字段,确保一个为其他服务签发的令牌不能被重放到我们这里来。动态客户端注册(RFC 7591)让客户端能够自行注册。再加上基于S256的PKCE防篡改校验,以及RFC 8414的发现机制,整个授权架构的大致轮廓就出来了。
这些RFC都有文档可查,但文档里没写的是,几乎每个客户端的解读方式都不太一样,而你总是在生产环境里才会发现这个问题。还有一点需要提醒任何一个准备动手构建这套东西的人:RFC 7591中的动态客户端注册,从2025年11月25日起已经从SHOULD降级为MAY,到了2026年7月28日已经被正式弃用,取而代之的是客户端……
在构建这个服务器的过程中,我们逐渐意识到,虽然技术上可以释放出跨项目、跨组织级别的大量能力,但过早地暴露这些能力,只会把安全风险和认知负担同时抛给用户。于是我们做了一个看起来反直觉的决定:把大部分已经实现的能力藏起来,只透出一小部分真正经过精细打磨、可以在任何场
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.