diff --git a/nextcloud_mcp_server/app.py b/nextcloud_mcp_server/app.py index af30d55c..feace0ee 100644 --- a/nextcloud_mcp_server/app.py +++ b/nextcloud_mcp_server/app.py @@ -1790,8 +1790,8 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None = # falls back to the procrastinate job counts there. await tg.start( vector_sync_metrics_task, - task_producer, - receive_stream, + ingest_transport.producer, + ingest_transport.receive_stream, shutdown_event, ) @@ -2011,8 +2011,8 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None = # falls back to the procrastinate job counts there. await tg.start( vector_sync_metrics_task, - task_producer, - receive_stream, + ingest_transport.producer, + ingest_transport.receive_stream, shutdown_event, ) diff --git a/nextcloud_mcp_server/search/context.py b/nextcloud_mcp_server/search/context.py index 42eb8813..d8d898fd 100644 --- a/nextcloud_mcp_server/search/context.py +++ b/nextcloud_mcp_server/search/context.py @@ -6,12 +6,14 @@ position markers for better visualization and understanding of search results. import logging from dataclasses import dataclass +from typing import cast from httpx import HTTPStatusError from qdrant_client.models import FieldCondition, Filter, MatchValue from nextcloud_mcp_server.client import NextcloudClient from nextcloud_mcp_server.config import get_settings +from nextcloud_mcp_server.models.deck import DeckCard from nextcloud_mcp_server.search.access_filter import build_ownership_filter from nextcloud_mcp_server.utils.validation import is_valid_nextcloud_doc_id from nextcloud_mcp_server.vector.html_processor import html_to_markdown @@ -792,7 +794,10 @@ async def _fetch_document_text( if card_found: break if stack.cards: - for c in stack.cards: + # get_stacks() always yields full DeckCard objects; + # the DeckCardSummary projection only happens in the + # tool layer, never on freshly-fetched stacks. + for c in cast(list[DeckCard], stack.cards): if c.id == int(doc_id): card = c card_found = True diff --git a/nextcloud_mcp_server/vector/processor.py b/nextcloud_mcp_server/vector/processor.py index a25d919f..4cc207bc 100644 --- a/nextcloud_mcp_server/vector/processor.py +++ b/nextcloud_mcp_server/vector/processor.py @@ -18,6 +18,7 @@ from nextcloud_mcp_server.client import NextcloudClient from nextcloud_mcp_server.config import get_settings from nextcloud_mcp_server.document_processors import get_registry from nextcloud_mcp_server.embedding import get_bm25_service, get_embedding_service +from nextcloud_mcp_server.models.deck import DeckCard from nextcloud_mcp_server.observability.metrics import ( record_document_chunks, record_embedding, @@ -421,7 +422,10 @@ async def _index_document( if card_found: break if s.cards: - for c in s.cards: + # get_stacks() always yields full DeckCard objects; + # the DeckCardSummary projection only happens in the + # tool layer, never on freshly-fetched stacks. + for c in cast(list[DeckCard], s.cards): if c.id == int(doc_task.doc_id): card = c board = b diff --git a/nextcloud_mcp_server/vector/scanner.py b/nextcloud_mcp_server/vector/scanner.py index 29311346..7108bffa 100644 --- a/nextcloud_mcp_server/vector/scanner.py +++ b/nextcloud_mcp_server/vector/scanner.py @@ -19,6 +19,7 @@ from qdrant_client.models import FieldCondition, Filter, MatchValue, Record from nextcloud_mcp_server.client import NextcloudClient from nextcloud_mcp_server.client.news import NewsItemType from nextcloud_mcp_server.config import get_settings +from nextcloud_mcp_server.models.deck import DeckCard from nextcloud_mcp_server.observability.metrics import record_vector_sync_scan from nextcloud_mcp_server.observability.tracing import trace_operation from nextcloud_mcp_server.server.tag_exclusion import ( @@ -1097,8 +1098,10 @@ async def scan_deck_cards( if not stack.cards: continue - # Iterate through cards in stack - for card in stack.cards: + # Iterate through cards in stack. get_stacks() always yields full + # DeckCard objects; the DeckCardSummary projection only happens in + # the tool layer (server/deck.py), never on freshly-fetched stacks. + for card in cast(list[DeckCard], stack.cards): # Skip archived cards if card.archived: continue