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.
Describe the bug
expand()percent-encodes variable values, butmatch()returns them still encoded, so the two are not inverses: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:expandmatchreturnsfile:///{path}a bfile:///a%20ba%20bfile:///{path}a/bfile:///a%2Fba%2Fbfile:///{path}üfile:///%C3%BC%C3%BCx://h/{a}/{b}a#bx://h/a%23b/a%23ba%23bThere is no
decodeURIComponentcall anywhere inpackages/core-internal/src/shared/uriTemplate.ts.Impact
ResourceTemplateroutes resource URIs throughmatch(), 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
uriTemplatePRs — #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/sdk1.30.0 and confirmed present onmain.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.