Skip to content

Commit 36264c2

Browse files
committed
Revert "Default skill reads to the main resource"
This reverts commit 9e0da68.
1 parent 9e0da68 commit 36264c2

5 files changed

Lines changed: 16 additions & 39 deletions

File tree

‎docs/advanced/skills.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ These three validate the server's response against the SEP-2640 conformance rule
6666
it. A name that doesn't match its URI, a digest in the wrong shape, or an incomplete manifest raises
6767
`ValueError` rather than reaching your code.
6868

69-
`read_skill_resource(skill)` fetches the skill's `SKILL.md` and verifies it against the manifest in
70-
the `Skill` entry you pass. For a supporting file, pass its URI as a second argument. It returns a
71-
`ReadResourceResult` with text or blob content only after the URI, size, and SHA-256 digest match.
72-
Keep that entry while you act on the skill so every file is checked against the same manifest.
69+
`read_skill_resource(skill, uri)` fetches a file and verifies it against the manifest in the `Skill`
70+
entry you pass. It returns a `ReadResourceResult` with text or blob content only after the URI,
71+
size, and SHA-256 digest match. Keep that entry while you act on the skill so every file is checked
72+
against the same manifest.
7373

7474
!!! tip
7575
`read_skill_resource` checks the bytes returned by `resources/read` against the held manifest.

‎docs_src/skills/tutorial001_client.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async def main() -> None:
1212
print(skill.uri, skill.frontmatter["description"])
1313

1414
skill = await catalog.get_skill("skill://git-workflow/SKILL.md")
15-
result = await catalog.read_skill_resource(skill)
15+
result = await catalog.read_skill_resource(skill, skill.uri)
1616
content = result.contents[0]
1717
if isinstance(content, TextResourceContents):
1818
print(content.text)

‎src/mcp/client/skills.py‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,12 @@ async def get_skill(self, uri: str) -> Skill:
138138
raise ValueError(f"server returned skill {result.skill.uri!r} for requested {uri!r}")
139139
return result.skill
140140

141-
async def read_skill_resource(self, skill: Skill, uri: str | None = None) -> ReadResourceResult:
141+
async def read_skill_resource(self, skill: Skill, uri: str) -> ReadResourceResult:
142142
"""Read and verify one file against a held `Skill` entry.
143143
144-
Pass the entry obtained from `list_skills` or `get_skill`. Omit `uri`
145-
to read the skill's `SKILL.md`, or pass a supporting file's URI. A
146-
dynamic skill has no manifest to verify; use `Client.read_resource`
147-
if you choose to read its unverified content.
144+
Pass the entry obtained from `list_skills` or `get_skill`. A dynamic
145+
skill has no manifest to verify; use `Client.read_resource` if you
146+
choose to read its unverified content.
148147
149148
Raises:
150149
ValueError: If the file is not in the manifest, or the server's
@@ -153,7 +152,6 @@ async def read_skill_resource(self, skill: Skill, uri: str | None = None) -> Rea
153152
RuntimeError: If the server returns an `InputRequiredResult`; this
154153
method does not drive the input-required loop.
155154
"""
156-
uri = skill.uri if uri is None else uri
157155
if skill.resources == "dynamic":
158156
raise ValueError(f"skill {skill.uri!r} has dynamic resources and cannot be integrity-verified")
159157
if not any(resource.uri == uri for resource in skill.resources):

‎tests/client/test_skills.py‎

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -209,33 +209,12 @@ async def test_read_skill_resource_returns_verified_text() -> None:
209209
"""SDK-defined: a matching manifest allows a fetched text file."""
210210
async with _skills(_server()) as skills:
211211
skill = await skills.get_skill(_SKILL_URI)
212-
result = await skills.read_skill_resource(skill)
212+
result = await skills.read_skill_resource(skill, _SKILL_URI)
213213
contents = result.contents[0]
214214
assert isinstance(contents, TextResourceContents)
215215
assert contents.text == _SKILL_CONTENT
216216

217217

218-
async def test_read_skill_resource_accepts_a_supporting_file_uri() -> None:
219-
"""SEP-2640: a supporting file is checked against the same held manifest."""
220-
uri = "skill://git-workflow/references/GUIDE.md"
221-
content = "# Guide\n"
222-
server = _server()
223-
server.add_resource(TextResource(uri=uri, name="GUIDE.md", text=content))
224-
skill = Skill(
225-
uri=_SKILL_URI,
226-
frontmatter={"name": "git-workflow", "description": "d"},
227-
resources=[
228-
SkillResource(uri=_SKILL_URI, digest=_SKILL_DIGEST, size=len(_SKILL_CONTENT)),
229-
SkillResource(uri=uri, digest=f"sha256:{hashlib.sha256(content.encode()).hexdigest()}", size=len(content)),
230-
],
231-
)
232-
async with _skills(server) as skills:
233-
result = await skills.read_skill_resource(skill, uri)
234-
contents = result.contents[0]
235-
assert isinstance(contents, TextResourceContents)
236-
assert contents.text == content
237-
238-
239218
async def test_read_skill_resource_rejects_a_changed_file() -> None:
240219
"""SEP-2640: a host rejects content that differs from the held manifest."""
241220
stale = Skill(
@@ -245,7 +224,7 @@ async def test_read_skill_resource_rejects_a_changed_file() -> None:
245224
)
246225
async with _skills(_server()) as skills:
247226
with pytest.raises(ValueError) as exc_info:
248-
await skills.read_skill_resource(stale)
227+
await skills.read_skill_resource(stale, _SKILL_URI)
249228
assert str(exc_info.value) == snapshot(
250229
"resource 'skill://git-workflow/SKILL.md' has digest "
251230
"'sha256:7187b725da92d7447b6f50e21d0bf4f3b9db3387acb39239c4a4edc193ddcd4b', "
@@ -268,7 +247,7 @@ async def test_read_skill_resource_rejects_a_dynamic_skill() -> None:
268247
dynamic = Skill(uri=_SKILL_URI, frontmatter={"name": "git-workflow", "description": "d"}, resources="dynamic")
269248
async with _skills(_server()) as skills:
270249
with pytest.raises(ValueError) as exc_info:
271-
await skills.read_skill_resource(dynamic)
250+
await skills.read_skill_resource(dynamic, _SKILL_URI)
272251
assert str(exc_info.value) == snapshot(
273252
"skill 'skill://git-workflow/SKILL.md' has dynamic resources and cannot be integrity-verified"
274253
)
@@ -285,7 +264,7 @@ async def test_read_skill_resource_returns_verified_binary_content() -> None:
285264
resources=[SkillResource(uri=_SKILL_URI, digest=f"sha256:{hashlib.sha256(data).hexdigest()}", size=len(data))],
286265
)
287266
async with _skills(server) as skills:
288-
result = await skills.read_skill_resource(skill)
267+
result = await skills.read_skill_resource(skill, _SKILL_URI)
289268
contents = result.contents[0]
290269
assert isinstance(contents, BlobResourceContents)
291270
assert contents.blob == base64.b64encode(data).decode("ascii")
@@ -315,7 +294,7 @@ async def handler(ctx: ServerRequestContext[Any, Any], params: ReadResourceReque
315294
server.add_request_handler("resources/read", ReadResourceRequestParams, handler)
316295
async with Client(server, extensions=[ClientSkills()]) as client:
317296
with pytest.raises(ValueError) as exc_info:
318-
await client.extension(ClientSkills).read_skill_resource(_skill())
297+
await client.extension(ClientSkills).read_skill_resource(_skill(), _SKILL_URI)
319298
assert str(exc_info.value) == snapshot(
320299
"resource 'skill://git-workflow/SKILL.md' did not return exactly one matching content item"
321300
)
@@ -335,7 +314,7 @@ async def handler(ctx: ServerRequestContext[Any, Any], params: ReadResourceReque
335314
server.add_request_handler("resources/read", ReadResourceRequestParams, handler)
336315
async with Client(server, extensions=[ClientSkills()]) as client:
337316
with pytest.raises(ValueError) as exc_info:
338-
await client.extension(ClientSkills).read_skill_resource(_skill())
317+
await client.extension(ClientSkills).read_skill_resource(_skill(), _SKILL_URI)
339318
assert str(exc_info.value) == snapshot("resource 'skill://git-workflow/SKILL.md' returned invalid base64 content")
340319

341320

‎tests/docs_src/test_skills.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async def test_read_skill_resource_returns_verified_content() -> None:
5353
async with Client(tutorial001.mcp, extensions=[skills]) as client:
5454
catalog = client.extension(Skills)
5555
skill = await catalog.get_skill(tutorial001.SKILL_URI)
56-
result = await catalog.read_skill_resource(skill)
56+
result = await catalog.read_skill_resource(skill, skill.uri)
5757
contents = result.contents[0]
5858
assert isinstance(contents, TextResourceContents)
5959
assert contents.text == tutorial001.SKILL_MD

0 commit comments

Comments
 (0)