fix(skills): accept any URI scheme, and stop rewriting the one we parsed - #1369
Merged
Conversation
`ParseURI` required the scheme be exactly `skill`, and it sits on the live client path (`ReadSkillManifest`, `ReadSkillFile`, `ReadDirectory`). So a server serving a conforming catalog under a domain-native scheme was refused outright. SEP-2640 says the opposite in as many words: Servers SHOULD use the `skill://` URI scheme ... A server MAY serve skills under another scheme native to its domain (for example, `github://owner/repo/skills/refunds/SKILL.md`). No scheme is privileged. and adds that a host MUST NOT conclude a resource is a skill merely because its URI carries a particular scheme, which is the same rule read from the other side. We were enforcing a SHOULD as a MUST, and our own conformance check already grades it a WARNING, so mcpkit's client was stricter than mcpkit's own suite. Relaxing the parse alone would have introduced a worse bug. `String`, `SkillRootURI` and `ManifestURI` rebuilt from the `Scheme` constant, so a parsed `github://` URI came back out as `skill://` — a different resource the server does not serve, and one that would then fail the digest check for the wrong reason. All three now rebuild from the parsed scheme, falling back to the constant for a URIParts assembled by hand. Every structural rule is unchanged and still applies to every scheme: traversal, empty segments, SKILL.md placement, and the name grammar. What is now rejected is a URI with no scheme at all, which is a relative reference and belongs to ResolveRelative. Server-side construction still emits `skill://`, which is the SHOULD. Noticed via the MCP Inspector's SEP-2640 writeup, which grades the scheme as deliberately unconstrained and reaches the same reading independently.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ParseURIrequired the scheme be exactlyskill, and it sits on the live client path(
ReadSkillManifest,ReadSkillFile,ReadDirectory). A server serving a conformingcatalog under a domain-native scheme was refused outright.
SEP-2640 says the opposite in as many words:
and adds that a host MUST NOT conclude a resource is a skill merely because its URI
carries a particular scheme, which is the same rule read from the other side.
So we were enforcing a SHOULD as a MUST. Our own conformance check,
sep-2640-skill-uri-scheme, already grades this a WARNING, which means mcpkit's clientwas stricter than mcpkit's own suite and no scenario could have caught it.
The half that would have been worse
Relaxing the parse alone introduces a subtler bug.
String,SkillRootURIandManifestURIrebuilt from theSchemeconstant rather than from what was parsed, so agithub://URI came back out asskill://. That is a different resource, one theserver does not serve, and the failure would then surface as a digest mismatch or a
missing resource rather than as anything to do with schemes.
All three now rebuild from the parsed scheme, with a fallback to the constant for a
URIPartsassembled by hand rather than parsed.What did not change
Every structural rule still applies, to every scheme: traversal segments, empty
segments,
SKILL.mdplacement, and the Agent Skills name grammar on the finalskill-path segment.
TestParseURI_SchemeAgnostic_StillStructuralpins that, so therelaxation cannot quietly become a laxer parse.
What is newly rejected is a URI carrying no scheme at all. That is a relative reference,
and it belongs to
ResolveRelativerather than here.ErrInvalidSchemekeeps its nameso
errors.Iscallers are unaffected, but its message and doc now say what it actuallymeans.
Server-side construction is untouched and still emits
skill://, which is the SHOULD.Tests
TestParseURI_SchemeAgnostic—skill,github,httpsand a custom scheme allparse, and
String/SkillRootURI/ManifestURIround-trip without rewritingTestParseURI_SchemeAgnostic_StillStructural— traversal, empty segment, manifest-as-directory and a bad name all still fail under
github://TestResolveRelative_PreservesScheme— a relative reference inside agithub://skillstays in that skill
relative reference and a scheme-relative URI, which are the things that should fail
make testand theext/skillssuite are green.Provenance
Noticed while reading the MCP Inspector's SEP-2640 writeup for Inspector 2.6.0, which
grades the scheme as deliberately unconstrained and reaches the same reading
independently.