Effect MCP 资源模板参数名解析修复解析:从 param0/param1 到真实参数名

Effect MCP 资源模板参数名解析修复解析:从 param0/param1 到真实参数名 Effect MCP 资源模板参数名解析修复解析从 param0/param1 到真实参数名【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect本篇文章围绕 effect 仓库中的一条 changeset 补丁fix-mcp-param-name-resolution.md展开剖析 effect 4.0 的effect/unstable/aiMCP 服务器中资源模板Resource Template的 URI 参数名为何会退化为param0、param1以及官方如何通过isParam守卫修复该问题。读完本文你将掌握McpSchema.param与isParam的底层机制、compileUriTemplate的完整工作流程以及如何在源码与测试中验证这一修复。变更记录一条 patch 修复了什么.changeset/pre/fix-mcp-param-name-resolution.md是 changeset 格式的预发布变更记录其完整内容如下--- effect: patch --- Fix MCP resource template parameter names resolving as param0, param1 instead of actual names by checking isParam on the original schema before toCodecStringTree transformation.这段记录声明了两个事实变更级别effect: patch即对 effect 主包的补丁修复不影响公开 API 形状只修正运行期行为。修复目标MCP 资源模板resource template的 URI 参数名此前会被解析成占位名param0、param1按参数索引回退而非开发者通过McpSchema.param(name, ...)声明的真实名称修复方式是在toCodecStringTree转换之前先对原始 schema调用isParam进行判定。问题背景MCP 资源模板与 URI 参数在 MCPModel Context Protocol中服务器可以通过资源模板向客户端描述一类可参数化访问的资源。effect 的McpServer.resource提供两种注册形式见 McpServer.ts静态 URIMcpServer.resource({ uri: file:///static, ... })URI 模板使用标签模板字符串配合McpSchema.param声明路径参数例如import { McpSchema } from effect/unstable/ai import { Schema } from effect const templatePath McpSchema.param(path, Schema.String) const TestResourceTemplate McpServer.resourcefile:///template/${templatePath}({ name: TestResourceTemplate, description: A test resource template, mimeType: text/plain, completion: { path: (value) Effect.succeed(value ? [beta, alpha] : [alpha, beta]) }, content: (uri, path) Effect.succeed(${uri}:${path}) })该示例取自仓库的一致性测试夹具 McpConformanceFixtures.ts。生成的ResourceTemplate描述中uriTemplate是一个符合 RFC 6570 规范的 URI 模板见 McpSchema.ts即file:///template/{path}。模板暴露给客户端的参数名必须与开发者声明的path一致——客户端将用它构造资源 URI、请求补全completion建议。根因分析compileUriTemplate 如何丢失参数名模板形式的resource在运行时调用内部函数compileUriTemplate完成三件事编译路由匹配路径、生成对外暴露的 URI 模板、组装路径参数 schema。其核心实现位于 McpServer.tsconst compileUriTemplate (segments: TemplateStringsArray, ...schemas: ReadonlyArraySchema.Constraint) { let routerPath segments[0].replace(:, ::) let uriPath segments[0] const params: Recordstring, Schema.Top Object.create(null) let pathSchema Schema.Tuple([]) as Schema.Top if (schemas.length 0) { const arr: ArraySchema.Top [] for (let i 0; i schemas.length; i) { const toCodecStringTree Schema.toCodecStringTree(schemas[i]) const segment segments[i 1] const key String(i) arr.push(toCodecStringTree) routerPath :${key}${segment.replace(:, ::)} const schema schemas[i] const paramName isParam(schema) ? (schema as Paramstring, Schema.Top).name : param${key} params[paramName] toCodecStringTree uriPath {${paramName}}${segment} } pathSchema Schema.Tuple(arr) } return { routerPath, uriPath, schema: pathSchema, params } }逐行拆解可以看出问题与修复的关键所在toCodecStringTree转换第 2448 行对每个参数 schema 调用Schema.toCodecStringTree得到用于路由匹配的编码树。toCodecStringTree是 effect Schema 模块暴露的工具在仓库中同样被 CLI 的Primitive用于将参数 schema 序列化为字符串形态见 Primitive.ts。问题在于该转换产物通常只承载如何解析/编码的信息不保证保留param包装时附加的参数名元数据。isParam判定必须作用于原始 schema第 2454 行是修复的核心。代码在循环内保留了对原始 schema 的引用const schema schemas[i]随后以isParam(schema)判定它是否为param创建的参数包装命中则取schema.name否则回退到param${key}即param0、param1。回退逻辑为何会出现 param0/param1若isParam的判定对象是被toCodecStringTree转换后的结果而该结果丢失了参数名标记判定必然失败代码只能走param${key}回退分支——这正是 changeset 所描述的param0、param1现象。因此修复要求在toCodecStringTree转换之前对原始 schema 检查isParam即先完成命名判定再使用转换后的编码树。修复机制param 包装与 isParam 守卫如何协同理解修复需要看param包装与isParam守卫的定义二者都位于 McpSchema.tsconst ParamSchemaTypeId ~effect/ai/McpSchema/ParamSchema export function isParam(schema: Schema.Constraint): schema is Paramstring, Schema.Top { return Predicate.hasProperty(schema, ParamSchemaTypeId) } export function paramconst Name extends string, S extends Schema.Constraint( name: Name, schema: S ): ParamName, S { return Schema.make(schema.ast, { [ParamSchemaTypeId]: ParamSchemaTypeId, name, schema }) }机制链条如下param注入标记与名称param(path, Schema.String)通过Schema.make基于原 schema 的 AST 重新构造一个 schema并附加两个成员——字符串常量ParamSchemaTypeId作为 Symbol 键标记和name真实参数名。Param接口McpSchema.ts因此在类型层面也携带readonly name与readonly schema而行为上与包裹的 schema 一致即解码、编码完全委托给Schema.String。isParam判定isParam是一个类型守卫仅检查对象是否持有ParamSchemaTypeId属性。判定成本极低、且不依赖 schema AST 的内容细节所以它可以安全地作用在原始 schema 上即使该 schema 随后被toCodecStringTree改写。命名结果的下游消费compileUriTemplate返回的params记录paramName - toCodecStringTree直接影响三处行为见 McpServer.ts对外 URI 模板uriPath中的{path}片段将写入ResourceTemplate.uriTemplate即客户端看到的模板补全处理器注册completion的键如path会注册到resource/${template.uriTemplate}/${param}路径下客户端以argument.name请求补全时据此分派解码后的参数注入content(uri, ...params)中参数按位置传入配合pathSchema解码。修复前若参数名退化为param0、param1客户端按{path}构造 URI 并请求path的补全时将与服务器端param0的注册键不匹配导致资源读取与补全双双失效。匹配与读取链路参数名修复后的完整闭环修复后的参数名贯穿整条请求链路。在addResourceTemplate中McpServer.ts模板注册时会用makeUriMatcher构建一个基于 FindMyWay 的路由器const makeUriMatcher A() { const router FindMyWay.makeA({ ignoreTrailingSlash: true, ignoreDuplicateSlashes: true, caseSensitive: true }) const add (uri: string, value: A) { router.on(GET, /${uri}, value) } const find (uri: string) router.find(GET, /${uri}) return { add, find } as const }compileUriTemplate生成的routerPath如file:///template/:0索引占位仅供内部路由使用被注册进该路由器当客户端请求形如file:///template/foo的资源时match回调把路由参数按数字索引重排为数组再交给handle(uri, params)解码后调用contentMcpServer.ts。也就是说param0/param1是内部路由层的索引命名而isParam修复保证对外层uriTemplate、completion 键始终使用真实参数名两层各司其职、不再混用。测试验证补全与模板一致性如何被守护仓库的 MCP 一致性测试直接覆盖了参数名解析行为BaseProtocolTest.ts 注册McpServer.resource\file:///metadata/${McpSchema.param(name, Schema.String)}随后通过 HTTP 发送completion/complete请求其params.argument.name为name并断言补全处理器能通过McpSchema.McpRequestContext读到请求元数据——若参数名被错误解析为param0该请求将无法命中resource/file:///metadata/{name}/name 注册的补全处理器。McpConformanceFixtures.ts 同时使用param(path, Schema.String)与param(id, Schema.FiniteFromString)注册两个模板其中id使用了带字符串编解码转换的 schemaFiniteFromString用于验证参数 schema 可被转换、而参数名仍被保留这一修复核心配套的ResourcesTestResourcesTest.ts在resources/templates/list结果中按uriTemplate查找模板并断言其描述字段从端到端守护模板注册的正确性。使用建议与版本说明该 API 位于effect/unstable/ai命名空间源码中相关导出标注since 4.0.0见 McpSchema.ts说明这是 effect 4.x 引入的 unstable 能力API 形态可能随主版本演进调整声明资源模板参数时始终使用McpSchema.param(真实名称, schema)而非裸 schema否则isParam判定失败参数名将回退为param0、param1影响补全与 URI 模板的对外一致性若需要为参数附加解码转换如Schema.FiniteFromString、Schema.IntFromString将其作为param的第二个参数传入即可toCodecStringTree会负责编码树生成而名称判定不受影响。从一条一行级的 patch 记录出发可以看到 effect 对 unstable MCP 模块的工程化打磨内部路由用索引命名对外协议用语义化参数名二者通过isParam守卫在compileUriTemplate中清晰分界并有完整的 HTTP 一致性测试兜底。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考