Repository navigation
fix(core-internal): support RFC 6570 prefix modifiers in UriTemplate (#3000) - #3003
MokuLakshithReddy wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: 7f1ca4c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
koriyoshi2041
left a comment
There was a problem hiding this comment.
The valid Unicode path looks good, but malformed prefix specs are now silently reinterpreted as valid variable names. RFC 6570 defines max-length as [1-9][0-9]{0,3} and makes prefix/explode mutually exclusive. At this head I get:
{name:0} -> abcdef
{name:10000} -> abcdef
{name:abc} -> abcdef
{name:3*} -> abc
All four also report variableNames === ['name']. Before this change they did not resolve name, so this broadens invalid templates into successful expansions. Could parseVarSpec accept only :[1-9][0-9]{0,3} and reject (or preserve the old unresolved behavior for) malformed/combined modifiers? Focused regression cases for :0, five digits, non-digits, and :N* would pin the boundary.
…utual exclusivity
|
@koriyoshi2041 Thank you for the detailed review! I have updated the implementation to address this:
|
|
Confirmed on |
| .filter(v => v !== undefined); | ||
| if (values.length === 0) return ''; | ||
| return values.map(v => (Array.isArray(v) ? v[0] : v)).join(','); | ||
| return values.join(','); |
There was a problem hiding this comment.
Since this branch is being reworked anyway: it never goes through encodeValue and ignores the operator, so the prefix gets applied but the result goes out raw. {x,y:2} with y: 'é ü' expands to é unencoded, while {y:2} on its own gives %C3%A9%20. {/x,y} also comes out as 1024,768 instead of /1024/768. Might be out of scope here, but should multi-var expressions go through the same encode/join switch as the single-var path below?
There was a problem hiding this comment.
Great catch! I have unified the multi-variable and single-variable expansion paths so all values pass through encodeValue(..., part.operator) and join according to the operator separator (e.g. / for path, . for label, , for simple/fragment/reserved). Added unit tests for multi-var encoding ({x,y:2}) and operator separators ({/x,y}, {.x,y}, {#x,y}).
| names: string[]; | ||
| exploded: boolean; | ||
| }): Array<{ pattern: string; name: string }> { | ||
| private partToRegExp(part: TemplateExpressionPart): Array<{ pattern: string; name: string }> { |
There was a problem hiding this comment.
match() doesn't use the prefix at all, so /users/{id:3} matches /users/abcdef and returns id: 'abcdef', a URI this template could never expand to. Is that intended (prefix only affects expansion), or should the pattern be bounded for prefix vars? Either way a test pinning the behavior might help, since resource templates use match() to route reads.
There was a problem hiding this comment.
Prefix modifiers in RFC 6570 define variable expansion truncation. Matching in this SDK extracts the segment value without lossy truncation. I have added an explicit regression test to pin this contract (/users/{id:3} extracts { id: "abc" } and { id: "abcdef" }).
Motivation and Context
Fixes #3000
The SDK's
UriTemplatepreviously treated an RFC 6570 prefix modifier (:N) as part of the variable name becausegetNames()stripped*but retained:N. As a result:template.variableNamesreturned["name:3"]instead of["name"].template.expand({ name: "abcdef" })looked forvariables["name:3"]and dropped scalar values from the expanded URI.template.match()looked for an argument named"name:3".Description of Changes
parseVarSpec()inUriTemplateto separate the variable name, explode modifier (*), and prefix modifier (:N).namesandvariableNamesnow preserve the clean variable name (["name"]).applyPrefix()to truncate scalar values to the specified prefix length using code point spreading ([...value].slice(0, prefix).join('')), ensuring correct handling of multi-byte and supplementary Unicode characters before percent-encoding.partToRegExpto use clean variable names when generating matching regexes.packages/core-internal/test/shared/uriTemplate.test.tscovering ASCII, non-ASCII Unicode, supplementary characters, prefixes longer than values, missing variables, array values, and.match()..changeset/uri-template-prefix-modifiers.md.How Has This Been Tested?
packages/core-internal/test/shared/uriTemplate.test.tscovering all prefix modifier behaviors; all 55 tests pass.