All files / scripts/mcp-client/methods votes.ts

100% Statements 19/19
95.83% Branches 23/24
100% Functions 5/5
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100                                              6x             8x       8x 3x 8x     8x 5x 5x 5x 5x       5x     5x 1x                                   8x 8x                 8x 3x                         3x       3x    
/**
 * @module mcp-client/methods/votes
 * @description Voting record / votering domain methods for the MCP client.
 *
 * @author Hack23 AB
 * @license Apache-2.0
 */
 
import type { MCPTransportClient } from '../transport/jsonrpc.js';
import type {
  FetchVotingFilters,
  FetchVotingGroupFilters,
  MCPSearchResult,
  MCPStructuredSignal,
  MCPProvenance,
} from '../../types/mcp.js';
import { attachCoverageMetadata, buildMcpProvenance } from '../coverage.js';
import { previousRiksmote } from '../riksmote/helpers.js';
 
export async function fetchVotingRecords(
  transport: MCPTransportClient,
  filters: FetchVotingFilters,
): Promise<unknown[]> {
  return (await fetchVotingRecordsWithDiagnostics(transport, filters)).items;
}
 
export async function fetchVotingRecordsWithDiagnostics(
  transport: MCPTransportClient,
  filters: FetchVotingFilters,
): Promise<MCPSearchResult<Record<string, unknown>> & { signal?: MCPStructuredSignal }> {
  const response = await transport.request(
    'search_voteringar',
    filters as unknown as Record<string, unknown>,
  );
  const items = ((response['votes'] ?? response['voteringar'] ?? []) as Record<string, unknown>[])
    .map((vote) => ({ ...vote }));
  const resultCount = items.length;
  let signal: MCPStructuredSignal | undefined;
 
  if (resultCount === 0 && typeof filters.rm === 'string') {
    const comparisonRm = previousRiksmote(filters.rm);
    Eif (comparisonRm) {
      try {
        const comparisonResponse = await transport.request(
          'search_voteringar',
          { ...(filters as Record<string, unknown>), rm: comparisonRm },
        );
        const comparisonCount = (
          (comparisonResponse['votes'] ?? comparisonResponse['voteringar'] ?? []) as unknown[]
        ).length;
        if (comparisonCount > 0) {
          signal = {
            code: 'MCP_INDEXING_LAG',
            severity: 'warning',
            message: `search_voteringar returned 0 rows for ${filters.rm} while ${comparisonRm} still returns ${comparisonCount}; this may indicate indexing lag or pending vote availability, so queue an exact-query retry for the next run.`,
            tool: 'search_voteringar',
            query: { ...(filters as Record<string, unknown>) },
            observedResultCount: resultCount,
            comparisonRm,
            comparisonResultCount: comparisonCount,
            action: 'retry_queue',
          };
        }
      } catch {
        // Best-effort comparison only; the primary zero-result response still stands.
      }
    }
  }
 
  const coverageState = resultCount === 0 ? 'search_empty' : 'metadata_only';
  const provenance: MCPProvenance = buildMcpProvenance({
    endpoint: transport.baseURL,
    tool: 'search_voteringar',
    query: filters as Record<string, unknown>,
    resultCount,
    coverageState,
    signals: signal ? [signal] : undefined,
  });
 
  return {
    items: items.map((vote) => attachCoverageMetadata(vote, provenance)),
    query: { ...(filters as Record<string, unknown>) },
    resultCount,
    coverageState,
    provenance,
    ...(signal ? { signal } : {}),
  };
}
 
export async function fetchVotingGroup(
  transport: MCPTransportClient,
  params: FetchVotingGroupFilters = {},
): Promise<unknown[]> {
  const response = await transport.request(
    'get_voting_group',
    params as unknown as Record<string, unknown>,
  );
  return (response['groups'] ?? response['votes'] ?? []) as unknown[];
}