All files / src/tools getMeetingPlenarySessionDocumentItems.ts

91.66% Statements 11/12
50% Branches 1/2
100% Functions 2/2
90.9% Lines 10/11

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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                                                                                                                                      16x 16x   7x 7x 7x                     9x 9x         8x   1x                   4x                            
/**
 * MCP Tool: get_meeting_plenary_session_document_items
 *
 * Retrieve plenary session document items linked to a specific EP meeting (plenary sitting).
 *
 * **Intelligence Perspective:** Provides granular access to individual document
 * items within a plenary session, enabling detailed agenda item tracking and
 * legislative analysis.
 *
 * **Business Perspective:** Allows policy teams and compliance officers to
 * enumerate every agenda document item for a plenary sitting, supporting
 * structured review workflows and audit trails.
 *
 * **Marketing Perspective:** Showcases fine-grained EP document access to
 * developers and researchers who need individual agenda-item level data beyond
 * top-level session documents.
 *
 * **EP API Endpoint:** `GET /meetings/{sitting-id}/plenary-session-document-items`
 *
 * ISMS Policy: SC-002 (Input Validation), AC-003 (Least Privilege)
 */
 
import { GetMeetingPlenarySessionDocumentItemsSchema } from '../schemas/europeanParliament.js';
import { epClient } from '../clients/europeanParliamentClient.js';
import { buildToolResponse } from './shared/responseBuilder.js';
import { ToolError } from './shared/errors.js';
import { z } from 'zod';
import type { ToolResult } from './shared/types.js';
 
/**
 * Handle the `get_meeting_plenary_session_document_items` MCP tool request.
 *
 * Retrieves the individual agenda-item-level documents linked to a specific EP
 * plenary sitting by calling
 * `GET /meetings/{sitting-id}/plenary-session-document-items` via {@link epClient}.
 * The raw API response is normalised into a standardised {@link ToolResult}
 * using {@link buildToolResponse}.
 *
 * @param args - Raw tool arguments provided by the MCP client. Must conform to
 *   {@link GetMeetingPlenarySessionDocumentItemsSchema}:
 *   - `sittingId` (string, required): EP plenary sitting identifier.
 *   - `limit` (number, optional): Maximum results to return (1–100, default 20).
 *   - `offset` (number, optional): Pagination offset (default 0).
 * @returns A promise that resolves to an MCP {@link ToolResult} containing the
 *   plenary session document items for the requested sitting.
 * @throws If `args` fails {@link GetMeetingPlenarySessionDocumentItemsSchema} validation
 *   (e.g. missing required `sittingId` or out-of-range `limit`).
 * @throws If `sittingId` contains path-traversal characters (`.`, `\`, `?`, `#`)
 *   — the underlying client throws an `APIError(400)`.
 * @throws If the European Parliament API is unreachable or returns an error response.
 *
 * @example
 * ```typescript
 * const result = await handleGetMeetingPlenarySessionDocumentItems({
 *   sittingId: 'PV-9-2024-04-22',
 *   limit: 20,
 *   offset: 0
 * });
 * // Returns individual agenda-item documents for plenary sitting PV-9-2024-04-22
 * ```
 *
 * @security Input is validated with Zod before any API call.
 *   `sittingId` is checked against path-traversal characters before URL construction.
 *   Personal data in responses is minimised per GDPR Article 5(1)(c).
 *   All requests are rate-limited and audit-logged per ISMS Policy AU-002.
 * @since 1.0.0
 * @see {@link getMeetingPlenarySessionDocumentItemsToolMetadata} for MCP schema registration
 * @see {@link handleGetMeetingPlenarySessionDocuments} for top-level session documents
 * @see {@link handleGetMeetingForeseenActivities} for planned meeting activities
 */
export async function handleGetMeetingPlenarySessionDocumentItems(
  args: unknown
): Promise<ToolResult> {
  // Validate input — ZodErrors here are client mistakes (non-retryable)
  let params: ReturnType<typeof GetMeetingPlenarySessionDocumentItemsSchema.parse>;
  try {
    params = GetMeetingPlenarySessionDocumentItemsSchema.parse(args);
  } catch (error: unknown) {
    Eif (error instanceof z.ZodError) {
      const fieldErrors = error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');
      throw new ToolError({
        toolName: 'get_meeting_plenary_session_document_items',
        operation: 'validateInput',
        message: `Invalid parameters: ${fieldErrors}`,
        isRetryable: false,
        cause: error,
      });
    }
    throw error;
  }
 
  try {
    const result = await epClient.getMeetingPlenarySessionDocumentItems(params.sittingId, {
      limit: params.limit,
      offset: params.offset,
    });
 
    return buildToolResponse(result);
  } catch (error: unknown) {
    throw new ToolError({
      toolName: 'get_meeting_plenary_session_document_items',
      operation: 'fetchData',
      message: 'Failed to retrieve meeting plenary session document items',
      isRetryable: true,
      cause: error,
    });
  }
}
/** Tool metadata for get_meeting_plenary_session_document_items */
export const getMeetingPlenarySessionDocumentItemsToolMetadata = {
  name: 'get_meeting_plenary_session_document_items',
  description:
    'Get plenary session document items for a specific EP meeting/plenary sitting. Returns individual agenda item documents for the meeting. Note: this endpoint can be slower than decisions; use a smaller limit for faster responses. Data source: European Parliament Open Data Portal.',
  inputSchema: {
    type: 'object' as const,
    properties: {
      sittingId: { type: 'string', description: 'Meeting / sitting identifier (required)' },
      limit: { type: 'number', description: 'Maximum results to return (1-100, default 20)', default: 20 },
      offset: { type: 'number', description: 'Pagination offset', default: 0 },
    },
    required: ['sittingId'],
  },
};