@@ -92,10 +92,12 @@ def _require_extension(self, *, directory_read: bool = False) -> None:
9292 async def list_skills (self , params : ListSkillsParams | None = None ) -> list [Skill ]:
9393 """Call `skills/list`, following `nextCursor` to completion, and validate the result.
9494
95+ A skill a changing catalog surfaces on more than one page is returned once, in the order
96+ first seen.
97+
9598 Raises:
96- ValueError: If the server doesn't advertise the Skills extension, its
97- response is not SEP-2640 conformant, it repeats a pagination
98- cursor, or it lists the same skill URI on two pages.
99+ ValueError: If the server doesn't advertise the Skills extension, its response is not
100+ SEP-2640 conformant, or it repeats a pagination cursor.
99101 MCPError: If the server returns an error response.
100102 """
101103 self ._require_extension ()
@@ -113,12 +115,12 @@ async def list_skills(self, params: ListSkillsParams | None = None) -> list[Skil
113115 # A server stuck repeating its cursor is a loop; catch that before the content checks.
114116 if page .next_cursor is not None and page .next_cursor in seen_cursors :
115117 raise ValueError (f"server repeated skills/list pagination cursor { page .next_cursor !r} " )
116- # Per-page validation can't catch a URI repeated *across* pages, so track that here.
118+ # A catalog that changes between page fetches can legitimately repeat a skill across
119+ # pages; keep the first occurrence rather than treating it as an error.
117120 for skill in page .skills :
118- if skill .uri in seen_uris :
119- raise ValueError (f"server listed skill { skill .uri !r} on more than one skills/list page" )
120- seen_uris .add (skill .uri )
121- skills .extend (page .skills )
121+ if skill .uri not in seen_uris :
122+ seen_uris .add (skill .uri )
123+ skills .append (skill )
122124 if page .next_cursor is None :
123125 return skills
124126 seen_cursors .add (page .next_cursor )
@@ -162,10 +164,12 @@ async def read_skill_uri(self, uri: str) -> ReadResourceResult:
162164 async def read_directory (self , uri : str , params : ReadDirectoryParams | None = None ) -> list [Resource ]:
163165 """Call `resources/directory/read` for `uri`, following `nextCursor` to completion.
164166
167+ A child a changing directory surfaces on more than one page is returned once, in the order
168+ first seen.
169+
165170 Raises:
166- ValueError: If the server doesn't advertise the `directoryRead`
167- setting, its response is not a valid child listing of `uri`, it
168- repeats a pagination cursor, or it lists the same child on two pages.
171+ ValueError: If the server doesn't advertise the `directoryRead` setting, its response
172+ is not a valid child listing of `uri`, or it repeats a pagination cursor.
169173 MCPError: If the server returns an error response.
170174 """
171175 self ._require_extension (directory_read = True )
@@ -183,12 +187,11 @@ async def read_directory(self, uri: str, params: ReadDirectoryParams | None = No
183187 if page .next_cursor is not None and page .next_cursor in seen_cursors :
184188 raise ValueError (f"server repeated resources/directory/read pagination cursor { page .next_cursor !r} " )
185189 validate_directory_result (uri , page )
186- # `validate_directory_result` dedupes within a page; catch a child repeated across pages.
190+ # A changing directory can legitimately repeat a child across pages; keep the first .
187191 for resource in page .resources :
188- if resource .uri in seen_uris :
189- raise ValueError (f"server listed child { resource .uri !r} on more than one directory page" )
190- seen_uris .add (resource .uri )
191- resources .extend (page .resources )
192+ if resource .uri not in seen_uris :
193+ seen_uris .add (resource .uri )
194+ resources .append (resource )
192195 if page .next_cursor is None :
193196 return resources
194197 seen_cursors .add (page .next_cursor )
0 commit comments