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 件 担当者 0 名 GitHub で見る

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

v1 v2
主要言語
Python
スター
24.3k
フォーク
4k
平均マージ
1日 1時間
マージ済み PR(30日)
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. リポジトリをフォークし、ブランチを切って変更します。
  4. 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 を短くまとめたダイジェスト。