Skip to content
Open
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a4c6ae3
Add SEP-2640 protocol types and validation
Sep 9, 2026
573fc06
Add SEP-2640 server support
Sep 9, 2026
0cc8dbc
Add SEP-2640 client support
Sep 9, 2026
0729d95
Document SEP-2640 Python SDK support
Sep 9, 2026
3a3725b
Strengthen SEP-2640 validation test coverage
Sep 9, 2026
d31511c
Rename skill resource-URI validator for clarity
Sep 9, 2026
c00a1e7
Extract parallel directory-URI validator in Skills handlers
Sep 9, 2026
b4edd53
Pin the skill-name grammar rejection cases
Sep 9, 2026
b845490
Fix documentation accuracy in the Skills guide
Sep 9, 2026
2f363d5
Add end-to-end dynamic-skill and cursor-resume client tests
Sep 10, 2026
1adba1d
Report Skills handler-output faults as INTERNAL_ERROR, not INVALID_PA…
Sep 10, 2026
70493de
Reject directory-shaped skill resource URIs and dot-segment directory…
Sep 10, 2026
38706c1
Seed the pagination cursor and preserve request _meta across skills p…
Sep 10, 2026
f1d2920
Correct Skills docs on read_skill_uri return type and verify on dynam…
Sep 10, 2026
63254f8
Construct ListSkillsParams _meta via model_validate in the skills cli…
Sep 10, 2026
7e32fc9
Test that request _meta is camelCase on the wire and snake_case to a …
Sep 10, 2026
473804e
Empty commit to re-trigger CI after a transient PyPI network flake
Sep 10, 2026
a2e49d5
empty coomit to re-trigger ci check
Sep 10, 2026
071ad0e
added changes for cache-attributes and its tests
Sep 11, 2026
6972162
Merge branch 'main' into sep-2640-python-sdk-support
vijaydeepsinha Sep 20, 2026
e90e294
Merge branch 'main' into sep-2640-python-sdk-support
vijaydeepsinha Sep 23, 2026
cde5cff
incorporated review comments and defined skills as extensions
Sep 23, 2026
1ddace7
removed restriction to allow only 512 files and 16 mb for each skill,…
Sep 23, 2026
488e706
removed separate skill validations as per review suggestion
Sep 23, 2026
7ca165a
re validate the outbound payload for mutation
Sep 24, 2026
662ce93
modified the docs and added check for de-dup across pages
Sep 24, 2026
083a5dc
relaxed the de-dup failure
Sep 24, 2026
12957be
removed extra logging and extra exports
Sep 24, 2026
03e24f0
Merge branch 'main' into sep-2640-python-sdk-support
vijaydeepsinha Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add end-to-end dynamic-skill and cursor-resume client tests
Two behavioral cases the existing suite left unpinned:

- A "dynamic" skill now round-trips through the real server extension,
  the wire, and the client wrapper (validated on both ends), proving the
  resources union survives intact rather than only in an isolated model
  round-trip.
- list_skills honours a caller-supplied starting cursor, skipping the
  pages before it — the resume-from-a-saved-cursor contract.
  • Loading branch information
vijay
vijay committed Sep 10, 2026
commit 2f363d58790d74b0ab22dbac62decea8a12327d1
29 changes: 29 additions & 0 deletions tests/client/test_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,32 @@ async def test_get_skill_rejects_a_mismatched_uri_from_a_non_conformant_server()
async with Client(server) as client:
with pytest.raises(ValueError, match="returned skill"):
await get_skill(client.session, "skill://other/SKILL.md")


async def test_get_skill_round_trips_a_dynamic_skill() -> None:
"""SEP-2640 Resources: the `"dynamic"` marker survives the full server -> wire -> client
path — validated on both ends — with the union type intact, not coerced to a list or null."""
dynamic = Skill(
uri="skill://generated/SKILL.md",
frontmatter={"name": "generated", "description": "instructions generated on demand"},
resources="dynamic",
)

async def get_dynamic(ctx: ServerRequestContext[Any, Any], params: GetSkillParams) -> GetSkillResult:
# The server's own _handle_get already enforces the requested-uri match; this test only
# ever asks for `dynamic.uri`, so the handler just returns it.
return GetSkillResult(skill=dynamic)

server = MCPServer("catalog", extensions=[Skills(list_skills=_paginated_list_handler(), get_skill=get_dynamic)])
async with Client(server) as client:
skill = await get_skill(client.session, dynamic.uri)
assert skill.resources == "dynamic"
assert skill.frontmatter["name"] == "generated"


async def test_list_skills_starts_from_a_caller_supplied_cursor() -> None:
"""A host resuming from a saved cursor: `list_skills` begins at that cursor rather than the
top, so only the pages after it come back (here, page 1's skill is skipped)."""
async with Client(_server()) as client:
skills = await list_skills(client.session, ListSkillsParams(cursor="page-2"))
assert [s.uri for s in skills] == ["skill://other/SKILL.md"]
Loading