Skip to content

UriTemplate.match() does not percent-decode extracted values #2728

Description

@MahathirMohammadShuvo

Describe the bug

expand() percent-encodes variable values, but match() returns them still encoded, so the two are not inverses:

const t = new UriTemplate("file:///{path}");
t.expand({ path: "My File.txt" }); // "file:///My%20File.txt"
t.match("file:///My%20File.txt");  // { path: "My%20File.txt" }  <- expected "My File.txt"

Round-tripping 10 templates against 20 values, 147 of 200 fail — every value containing a space, /, ?, #, &, =, %, or a non-ASCII character. A few from that sweep:

template value expand match returns
file:///{path} a b file:///a%20b a%20b
file:///{path} a/b file:///a%2Fb a%2Fb
file:///{path} ü file:///%C3%BC %C3%BC
x://h/{a}/{b} a#b x://h/a%23b/a%23b a%23b

There is no decodeURIComponent call anywhere in packages/core-internal/src/shared/uriTemplate.ts.

Impact

ResourceTemplate routes resource URIs through match(), so a handler receives the encoded string rather than the value the client asked for. Any resource whose template variable contains a space or a non-ASCII character is handed the wrong value, silently.

Prior art I checked

The open uriTemplate PRs — #2633, #2170, #2429, #2216, #2218 — all cover multi-variable or optional matching, and none of them adds a decode call. #1785's title mentioned encoded query parameters, but it closed unmerged and the decoding does not appear in its successors, so this axis looks genuinely open.

Environment

Reproduced on @modelcontextprotocol/sdk 1.30.0 and confirmed present on main.


Happy to open a PR if you assign this to me.

Disclosure, per the org AI policy: I used AI assistance for this investigation and write-up — the differential harness that surfaced it and the drafting of this issue. The finding is reproduced and verified, and I can discuss any part of it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions