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

オープン
#3,526 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
45/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
活発
技術スタック
python
領域
api

調査の方向性

mcp.shared.uri_template と既存の _encode ヘルパーから始め、次にレポートで説明されているユニットテストとルーティングテストを調べます。公式の uritemplate-test Literal Encoding ベクトルと resource-template のラウンドトリップを検証します。パーセントエンコードされたリテラルについて展開とマッチングが一致し、完全なテストスイートが通過すれば完了です。

索引モデルが issue の本文から書いたものです。

説明

v1 v2
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.

主要言語
Python
スター
24.3k
フォーク
4k
平均マージ
1日 19分
マージ済み PR(30日)
29

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

modelcontextprotocol/python-sdk のほかの issue

modelcontextprotocol/python-sdk の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。