modelcontextprotocol / modelcontextprotocol/python-sdk
A resource template with a non-ASCII or space literal is advertised but can never be read
Personne n'a encore pris cette issue.
- Langage dominant
- Python
- Étoiles
- 24.3k
- Forks
- 4k
- Merge moyen
- 1 j 1 h
- PR mergées (30 j)
- 31
Description
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.
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par mcp.shared.uri_template et le helper _encode existant, puis examinez les tests unitaires et de routage décrits dans le rapport. Vérifiez le vecteur officiel uritemplate-test Literal Encoding et le parcours aller-retour de resource-template ; la tâche est terminée lorsque l’expansion et la mise en correspondance concordent pour les littéraux encodés en pourcentage et que la suite complète passe.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- python
- Domaine
- api
- Type d'issue
- Bug
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Activité
- Active
- Clarté
- Plutôt claire
- Accessibilité débutants
- 45/100