# All files / src/tools getSpeeches.ts

93.75% Statements 15/16
75% Branches 3/4
100% Functions 2/2
93.33% Lines 14/15

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

    16x
    16x

    5x
    5x
    5x

    11x
    11x
    1x
    1x

    10x

    10x

    9x

    1x

    4x

     /**
     * MCP Tool: get_speeches
     *
     * Retrieve European Parliament plenary speeches and speech-related activities.
     * Supports single speech lookup by speechId or list with date range filtering.
     *
     * **Intelligence Perspective:** Speech data enables content analysis of MEP positions,
     * rhetorical patterns, and policy priorities across plenary debates.
     *
     * **Business Perspective:** Speech transcripts power NLP-based products, sentiment
     * analysis dashboards, and topic monitoring services.
     *
     * **EP API Endpoints:**
     * - `GET /speeches` (list)
     * - `GET /speeches/{speech-id}` (single)
     *
     * ISMS Policy: SC-002 (Input Validation), AC-003 (Least Privilege)
     */

    import { GetSpeechesSchema } from '../schemas/europeanParliament.js';
    import { epClient } from '../clients/europeanParliamentClient.js';
    import { buildToolResponse } from './shared/responseBuilder.js';
    import { buildApiParams } from './shared/paramBuilder.js';
    import { ToolError } from './shared/errors.js';
    import { z } from 'zod';
    import type { ToolResult } from './shared/types.js';

    /**
     * Handles the get_speeches MCP tool request.
     *
     * Retrieves European Parliament plenary speeches and debate contributions.
     * Supports single speech lookup by speechId or a filtered list by date range.
     *
     * @param args - Raw tool arguments, validated against {@link GetSpeechesSchema}
     * @returns MCP tool result containing either a single speech record or a paginated list of speeches
     * @throws - If `args` fails schema validation (e.g., missing required fields or invalid format)
     * - If the European Parliament API is unreachable or returns an error response
     *
     * @example
     * ```typescript
     * // Single speech lookup
     * const result = await handleGetSpeeches({ speechId: 'SPEECH-2024-001' });
     * // Returns the full record for the specified speech
     *
     * // List speeches with date filter
     * const list = await handleGetSpeeches({ dateFrom: '2024-01-01', dateTo: '2024-03-31', limit: 50 });
     * // Returns up to 50 speeches from Q1 2024
     * ```
     *
     * @security - Input is validated with Zod before any API call.
     * - 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 0.8.0
     * @see {@link getSpeechesToolMetadata} for MCP schema registration
     * @see {@link handleGetMeetingActivities} for retrieving broader meeting-level activities
     */
    export async function handleGetSpeeches(args: unknown): Promise<ToolResult> {
      let params: ReturnType<typeof GetSpeechesSchema.parse>;
      try {
        params = GetSpeechesSchema.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_speeches',
            operation: 'validateInput',
            message: `Invalid parameters: ${fieldErrors}`,
            isRetryable: false,
            cause: error,
          });
        }
        throw error;
      }

      try {
        if (params.speechId !== undefined) {
          const result = await epClient.getSpeechById(params.speechId);
          return buildToolResponse(result);
        }

        const apiParams = {
          limit: params.limit,
          offset: params.offset,
          ...buildApiParams(params, [
            { from: 'dateFrom', to: 'dateFrom' },
            { from: 'dateTo', to: 'dateTo' },
          ]),
        };

        const result = await epClient.getSpeeches(
          apiParams
        );

        return buildToolResponse(result);
      } catch (error: unknown) {
        throw new ToolError({
          toolName: 'get_speeches',
          operation: 'fetchData',
          message: 'Failed to retrieve speeches',
          isRetryable: true,
          cause: error,
        });
      }
    }
    /** Tool metadata for get_speeches */
    export const getSpeechesToolMetadata = {
      name: 'get_speeches',
      description:
        'Get European Parliament plenary speeches and debate contributions. Supports single speech lookup by speechId or list with date range filtering (dateFrom/dateTo mapped to EP API sitting-date/sitting-date-end). Data source: European Parliament Open Data Portal.',
      inputSchema: {
        type: 'object' as const,
        properties: {
          speechId: { type: 'string', description: 'Speech ID for single speech lookup' },
          dateFrom: { type: 'string', description: 'Start date (YYYY-MM-DD). Mapped to EP API sitting-date parameter.' },
          dateTo: { type: 'string', description: 'End date (YYYY-MM-DD). Mapped to EP API sitting-date-end parameter.' },
          limit: { type: 'number', description: 'Maximum results to return (1-100)', default: 50 },
          offset: { type: 'number', description: 'Pagination offset', default: 0 },
        },
      },
    };