modelcontextprotocol / modelcontextprotocol/python-sdk

A resource template with a non-ASCII or space literal is advertised but can never be read

未关闭
#3,526 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

v1 v2
主要语言
Python
星标
24.3k
派生
4k
平均合并
1 天 1 小时
30 天内合并 PR
31

描述

Initial Checks
  • I confirm that I'm using the newest release of my line (main @ 6affe5c)
  • I confirm that I searched for my issue in the issue tracker before opening this issue
Release line

2.x (current stable)

Description

UriTemplate copies literal runs into the expansion verbatim. RFC 6570 §3.1 requires a literal that the URI grammar does not allow (ucschar such as café, or a space) to be pct-encoded as UTF-8 when the template is expanded, so expand() currently returns a string that is not a valid RFC 3986 URI:

UriTemplate.parse("file:///docs/café/{name}").expand({"name": "a.txt"})
# 'file:///docs/café/a.txt'      (uritemplate-test "Literal Encoding" expects caf%C3%A9)

match() is built from the same verbatim literals, so it accepts only that invalid form and rejects the encoded one:

t = UriTemplate.parse("file:///docs/café/{name}")
t.match("file:///docs/caf%C3%A9/a.txt")   # None
t.match("file:///docs/café/a.txt")        # {'name': 'a.txt'}

That second line is the part that bites in practice. A resource URI crosses the wire as a pydantic AnyUrl, which pct-encodes non-ASCII and spaces (AnyUrl('file:///docs/café/a.txt')file:///docs/caf%C3%A9/a.txt). So the encoded form is the only one a server ever sees, and the template can never match it: the resource is listed in resources/templates/list and is permanently unreadable. The same applies to a literal space (file:///my docs/{name}my%20docs).

I hit this while building resource templates whose paths carry Spanish accents, which is ordinary for non-English servers.

Expected: expand() pct-encodes literals per §3.1, and match() accepts the encoded URI a conforming client sends, so an expanded URI round-trips.

Note the direction of the behavior change this implies: after the fix, the raw (unencoded) form stops matching. I believe that is correct, since it is not a valid RFC 3986 URI and it is not what reaches a server over the wire, but it is a visible change for anyone calling UriTemplate.match() directly with a raw IRI, so it seems worth your call rather than mine. Accepting both forms is possible (a fallback scan over the raw atoms) at the cost of a second pass and an ambiguity to document.

I have a patch (the literal encoding applied in both directions, sharing the existing _encode helper) plus unit tests, a routing test, and the official uritemplate-test "Literal Encoding" vector. The full suite passes (5978 passed, 10 skipped, 1 xfailed; baseline 5968), and the new assertions fail on main. Happy to open a PR if you'd like it and can assign this to me.

Example Code
import anyio
from mcp.client.client import Client
from mcp.server.mcpserver import MCPServer

mcp = MCPServer()


@mcp.resource("file:///docs/café/{name}")
def doc(name: str) -> str:
    return f"contents of {name}"


async def main() -> None:
    async with Client(mcp) as client:
        templates = await client.list_resource_templates()
        print(templates.resource_templates[0].uri_template)
        # file:///docs/café/{name}

        # what a conforming client puts on the wire (AnyUrl pct-encodes it)
        await client.read_resource("file:///docs/caf%C3%A9/a.txt")
        # McpError: Unknown resource: file:///docs/caf%C3%A9/a.txt


anyio.run(main)
Python & MCP Python SDK
Python 3.11.3, mcp-python-sdk main @ 6affe5c0d3588fd1705713b3703dc68015cfe3eb

Disclosure: this was found and written with Claude Code. It ran the official uritemplate-test vectors against mcp.shared.uri_template, reduced the failure to this case, and drafted this report and the patch. I reviewed it before posting and can walk through the change.

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从 mcp.shared.uri_template 和现有的 _encode 辅助函数开始,然后检查报告中描述的单元测试和路由测试。验证官方 uritemplate-test Literal Encoding 向量以及 resource-template 的往返;当展开和匹配在百分号编码字面量上达成一致,且完整测试套件通过时,即表示完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
api
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
活跃
描述清晰度
基本清楚
新手友好度
45/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。