feat(mcp): #36 add prompts assertions + per-type content + dual-mode drill-ins#37
Merged
Merged
Conversation
… content + dual-mode drill-ins - Refactor AssertableContent into discriminator + 5 per-type subclasses (AssertableTextContent, AssertableImageContent, AssertableAudioContent, AssertableResourceLinkContent, AssertableResourceContent) with @overload type narrowing on is_<type>(cb=None) - Migrate AssertableToolCall.content() to dual-mode (callback optional) and add typed is_text / is_image / is_audio / is_resource_link / is_resource(idx) shortcuts that scope into the typed subclass - Add AssertablePromptList + AssertablePromptDef for prompts/list assertions (with_count, contains_prompt, does_not_contain_prompt, every_prompt, has_more_pages, documented, accepts, accepts_optional, does_not_accept) - Add AssertablePromptGet + AssertablePromptMessage for prompts/get assertions (with_description, with_message_count, first_message, message, last_message, every_message, is_rejected_with_invalid_params, is_from_user, is_from_assistant, has_text_content, content, is_<type>) - Add is_prompts_list_changed_notification() helper on AssertableMCP - Apply dual-mode scoping (cb=None) across content() and message drill-ins for consistency with json() / html() / arch.module() patterns Closes #36
1c33db2 to
cd000db
Compare
409ab67 to
de1a53a
Compare
…tool shortcuts
BREAKING CHANGE: AssertableTextContent, AssertableImageContent,
AssertableAudioContent, AssertableResourceLinkContent, and
AssertableResourceContent have been removed from the public API.
All type-aware assertions live on AssertableContent now, with
implicit type-checks per method and auto-dispatch across compatible
types (e.g. with_uri works on both resource_link and embedded
resource; with_text works on both text and resource embedded text).
The typed shortcut methods on AssertableToolCall
(is_text/is_image/is_audio/is_resource_link/is_resource at index)
have been removed. Chain via .content(idx) instead.
The text-shortcut methods on AssertablePromptMessage
(has_text, with_text, with_text_containing) and the typed
is_<type> shortcuts have been removed. Chain via .content()
instead.
Behavior changes:
- AssertableContent.is_not_empty() now covers image and audio block
types (was text/resource only). A resource block with blob but
no text field now passes is_not_empty - semantically "has payload,
even if binary".
- named() now accepts embedded resource blocks too, not just
resource_link, per MCP spec 2025-11-25 which permits a name field
inside the resource object.
Migration:
.tool().is_text(0).with_text("hi") -> .tool().content(0).with_text("hi")
.first_message().has_text() -> .first_message().content().is_not_empty()
.first_message().with_text("hi") -> .first_message().content().with_text("hi")
.first_message().is_image()... -> .first_message().content().with_mime_type(...)
Co-changes in this PR:
- AssertablePromptGet gains succeeds() for symmetry with ToolCall.
- AssertableContent constructor takes label: str (was index: int)
so error messages carry caller context (Tool 'X' content[N] vs
Message[N].content).
- AssertableContent gained a class-level docstring with the
method/type compatibility matrix.
- AssertablePromptMessage._content_block distinguishes "missing
content field" from "non-dict content".
- AssertableToolCall._content_at resolves negative indices to
positive in error labels for consistency with PromptMessage.
- Parametrized tests use ids= for readable failure messages.
Closes #36
de1a53a to
0c4be12
Compare
|
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.



Summary
Closes #36. Adds
prompts/listandprompts/getassertions to the MCP module and refactors content drill-in to follow the library-wide dual-mode scoping pattern.AssertableContent— discriminator splits into 5 typed subclasses (AssertableTextContent,AssertableImageContent,AssertableAudioContent,AssertableResourceLinkContent,AssertableResourceContent) with@overloadnarrowing onis_<type>(cb=None).content()onAssertableToolCall(callback now optional) +is_<type>(idx, cb=None)typed shortcuts as siblings.AssertablePromptList+AssertablePromptDeffor catalog assertions mirroring the tools surface.AssertablePromptGet+AssertablePromptMessageforprompts/get, with dual-modefirst_message/message/last_messageand per-typeis_<type>(cb=None)shortcuts inside messages.is_prompts_list_changed_notification()onAssertableMCP.The full design discussion, pattern audit, and decision log lives in #36.
Backward compatibility
AssertableToolCall.content(idx, callback)callback was required, now optional. All existing call sites still work (they pass callbacks).AssertableContent.is_<type>()previously returnedSelf; now returns the typed subclass. Existing chains likec.is_text().text_equals(...)keep working becausetext_equalsnow lives onAssertableTextContent(returned by the newis_text()), not onAssertableContent.Test plan
uv run pytest)uv run mypy --strict src/pyssertive/protocols/mcp/cleanuv run ruff check .anduv run ruff format --check .clean_envelope.resultaccesses cited in feat(mcp): add prompts/list and prompts/get assertions #36 can be rewritten using only the public API