feat(deck): compact card/comment retrieval (summaries, filters, board overview)
Deck read tools returned too many tokens to be usable as boards grow — even deck_get_stacks(description_max_length=1) exceeded the MCP token limit because every card was fully serialized in list views. - Add compact projection models (DeckCardSummary, DeckCommentSummary, StackOverview, BoardOverviewResponse) and a uniform detail="summary"|"full" knob (summary default) on deck_get_cards / get_stacks / get_stack / get_archived_stacks. - Add pre-serialization filtering: status (open/done/archived/all), label, assigned_to. - Add deck_get_board_overview: board title + label legend + stacks with compact card rows + counts in a single call. - Compact comments: detail / message_max_length / newest-first order on deck_get_card_comments. - Docs + unit/integration tests. BREAKING CHANGE: deck list tools now default to detail="summary" and status="open". The include_archived_cards parameter is replaced by status (use status="all" to include archived cards); pass detail="full" to restore the previous per-card shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c662d57c4f
commit
b11103064c
@@ -129,6 +129,44 @@ class DeckCard(BaseModel):
|
||||
return validated_users
|
||||
|
||||
|
||||
class DeckCardSummary(BaseModel):
|
||||
"""Compact projection of a :class:`DeckCard` for list/overview views.
|
||||
|
||||
Drops the heavy fields that dominate token cost when many cards are
|
||||
returned at once — the full ``description`` (kept only as a short
|
||||
``descriptionPreview``), the nested ``labels``/``assignedUsers``/
|
||||
``attachments`` objects (kept as flat title/uid lists + counts), and the
|
||||
``etag``/``order``/``*Modified``/``createdAt``/``deletedAt``/``overdue``/
|
||||
``type``/``owner`` bookkeeping fields. Fetch a single card with
|
||||
``deck_get_card`` (or ``detail="full"``) when the full body is needed.
|
||||
"""
|
||||
|
||||
id: int
|
||||
title: str
|
||||
stackId: int
|
||||
archived: bool = False
|
||||
duedate: Optional[datetime] = None
|
||||
done: Optional[datetime] = None
|
||||
labels: List[str] = Field(
|
||||
default_factory=list, description="Label titles assigned to the card"
|
||||
)
|
||||
assignedUsers: List[str] = Field(
|
||||
default_factory=list, description="UIDs of users assigned to the card"
|
||||
)
|
||||
attachmentCount: Optional[int] = Field(
|
||||
default=None, description="Number of attachments on the card"
|
||||
)
|
||||
commentsUnread: Optional[int] = Field(
|
||||
default=None, description="Number of unread comments (Deck exposes no total)"
|
||||
)
|
||||
hasDescription: bool = Field(
|
||||
default=False, description="Whether the card has a non-empty description"
|
||||
)
|
||||
descriptionPreview: Optional[str] = Field(
|
||||
default=None, description="Truncated preview of the card description"
|
||||
)
|
||||
|
||||
|
||||
class DeckStack(BaseModel):
|
||||
id: int
|
||||
title: str
|
||||
@@ -136,7 +174,9 @@ class DeckStack(BaseModel):
|
||||
order: int
|
||||
deletedAt: int
|
||||
lastModified: Optional[int] = None
|
||||
cards: Optional[List[DeckCard]] = None
|
||||
# Cards may be projected to DeckCardSummary when a tool is called with
|
||||
# detail="summary" (the default for list tools).
|
||||
cards: Optional[List[Union[DeckCard, DeckCardSummary]]] = None
|
||||
etag: Optional[str] = Field(default=None, alias="ETag")
|
||||
|
||||
|
||||
@@ -175,6 +215,20 @@ class DeckComment(BaseModel):
|
||||
replyTo: Optional[Any] = None # Self-referencing, handle later if needed
|
||||
|
||||
|
||||
class DeckCommentSummary(BaseModel):
|
||||
"""Compact projection of a :class:`DeckComment` for list views.
|
||||
|
||||
Drops ``mentions``, ``actorType``, ``actorDisplayName`` and ``replyTo``,
|
||||
keeping only the fields needed to read the conversation. Long messages are
|
||||
truncated by the tool layer via ``message_max_length``.
|
||||
"""
|
||||
|
||||
id: int
|
||||
actorId: str
|
||||
message: str
|
||||
creationDateTime: datetime
|
||||
|
||||
|
||||
class DeckSession(BaseModel):
|
||||
token: str
|
||||
|
||||
@@ -220,6 +274,36 @@ class ListStacksResponse(BaseResponse):
|
||||
total: int = Field(description="Total number of stacks")
|
||||
|
||||
|
||||
class StackOverview(BaseModel):
|
||||
"""A stack plus its compact card rows, for the board-overview tool."""
|
||||
|
||||
id: int = Field(description="Stack ID")
|
||||
title: str = Field(description="Stack title")
|
||||
order: Optional[int] = Field(default=None, description="Stack sort order")
|
||||
card_count: int = Field(description="Number of cards returned for this stack")
|
||||
cards: List[DeckCardSummary] = Field(
|
||||
default_factory=list, description="Compact card rows in this stack"
|
||||
)
|
||||
|
||||
|
||||
class BoardOverviewResponse(BaseResponse):
|
||||
"""Compact whole-board snapshot: board → stacks → summary card rows.
|
||||
|
||||
A single-call replacement for deck_get_board + deck_get_stacks that keeps
|
||||
the response small enough to fit the token budget on large boards.
|
||||
"""
|
||||
|
||||
board_id: int = Field(description="Board ID")
|
||||
title: str = Field(description="Board title")
|
||||
labels: List[str] = Field(
|
||||
default_factory=list, description="Board label titles (legend)"
|
||||
)
|
||||
stacks: List[StackOverview] = Field(
|
||||
default_factory=list, description="Stacks with compact card rows"
|
||||
)
|
||||
total_cards: int = Field(description="Total cards across all returned stacks")
|
||||
|
||||
|
||||
class CreateStackResponse(BaseResponse):
|
||||
"""Response model for stack creation."""
|
||||
|
||||
@@ -269,7 +353,9 @@ class CreateLabelResponse(BaseResponse):
|
||||
class ListCardsResponse(BaseResponse):
|
||||
"""Response model for listing deck cards."""
|
||||
|
||||
cards: list[DeckCard] = Field(description="List of deck cards")
|
||||
cards: list[Union[DeckCard, DeckCardSummary]] = Field(
|
||||
description="List of deck cards (summaries unless detail='full')"
|
||||
)
|
||||
total: int = Field(description="Total number of cards")
|
||||
|
||||
|
||||
@@ -293,7 +379,9 @@ class LabelOperationResponse(StatusResponse):
|
||||
class ListCardCommentsResponse(BaseResponse):
|
||||
"""Response model for listing card comments."""
|
||||
|
||||
results: list[DeckComment] = Field(description="Card comments in this page")
|
||||
results: list[Union[DeckComment, DeckCommentSummary]] = Field(
|
||||
description="Card comments in this page (summaries unless detail='full')"
|
||||
)
|
||||
count: int = Field(
|
||||
description=(
|
||||
"Number of comments returned in this page (page size, not the "
|
||||
|
||||
Reference in New Issue
Block a user