# All files / src/tools/trackLegislation index.ts

96% Statements 24/25
87.5% Branches 7/8
100% Functions 3/3
96% Lines 24/25

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
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163

    4x

    4x

    28x
    1x

    27x
    27x
    25x

    2x

    23x
    23x

    23x
    23x

    23x

    20x
    20x
    20x
    20x
    18x

    2x

    20x

    20x

    3x

    3x

    4x

     /**
     * MCP Tool: track_legislation
     *
     * Track legislative procedure progress through European Parliament
     *
     * **Intelligence Perspective:** Enables real-time legislative pipeline monitoring,
     * procedure stage analysis, and trilogue outcome tracking for strategic intelligence
     * on EU regulatory developments and policy trajectory forecasting.
     *
     * **Business Perspective:** Core compliance monitoring product—essential for enterprises
     * tracking regulatory changes, industry associations monitoring sector-specific legislation.
     *
     * **Marketing Perspective:** Demonstrates end-to-end legislative tracking capability—
     * differentiator for RegTech market and EU affairs consultancy customer acquisition.
     *
     * ISMS Policy: SC-002 (Input Validation), AC-003 (Least Privilege)
     */

    import { TrackLegislationSchema } from '../../schemas/europeanParliament.js';
    import { epClient } from '../../clients/europeanParliamentClient.js';
    import { buildLegislativeTracking } from './procedureTracker.js';
    import type { ToolResult } from '../shared/types.js';
    import type { EPEvent } from '../../types/europeanParliament.js';
    import { ToolError } from '../shared/errors.js';
    import { withTimeout, TimeoutError } from '../../utils/timeout.js';
    import { buildTimeoutResponse } from '../shared/errorHandler.js';

    /**
     * Maximum number of procedure events to fetch for timeline enrichment.
     * 20 covers the typical lifecycle milestones for an EP procedure while
     * keeping the API call lightweight.
     */
    const EVENTS_ENRICHMENT_LIMIT = 20;

    /**
     * Maximum wall-clock time (ms) allowed for the full track_legislation operation.
     * Chosen to be comfortably below the integration-test timeout of 120 000 ms
     * so the tool can return a graceful `timedOut: true` response instead of
     * being killed by the test runner.
     */
    const OPERATION_TIMEOUT_MS = 100_000;

    /**
     * Convert a user-supplied procedure reference to the EP API process-id format.
     *
     * The EP API uses process IDs like `2024-0006` (dashes, no type suffix),
     * while users typically supply references like `2024/0006(COD)`.
     *
     * Supported input formats:
     * - Process-id: `"2024-0006"` (returned as-is)
     * - Reference: `"2024/0006(COD)"` → `"2024-0006"`
     * - Bare reference: `"2024/0006"` → `"2024-0006"` (fallback)
     *
     * @param ref - Procedure reference (e.g. `"2024/0006(COD)"` or `"2024-0006"`)
     * @returns Process ID suitable for the EP API (e.g. `"2024-0006"`)
     */
    export function toProcessId(ref: string): string {
      if (/^\d{4}-\d{4}$/.test(ref)) {
        return ref;
      }
      const match = /^(\d{4})\/(\d{4})\(.*\)$/.exec(ref);
      if (match?.[1] !== undefined && match[2] !== undefined) {
        return `${match[1]}-${match[2]}`;
      }
      return ref.replace(/\(.*\)$/, '').replace(/\//g, '-');
    }

    /**
     * Handles the track_legislation MCP tool request.
     *
     * Tracks a specific European Parliament legislative procedure through its full
     * lifecycle — from initial proposal through committee review, plenary vote,
     * trilogue, and final adoption. Accepts both EP API process-id format
     * (`2024-0006`) and human-readable reference format (`2024/0006(COD)`).
     *
     * @param args - Raw tool arguments, validated against {@link TrackLegislationSchema}
     * @returns MCP tool result containing the procedure's current stage, timeline,
     *   committee assignments, voting records, and next-step projections
     * @throws {ZodError} If `args` fails schema validation (e.g., missing required fields or invalid format)
     * @throws {Error} If the European Parliament API is unreachable or returns an error response
     *
     * @example
     * ```typescript
     * const result = await handleTrackLegislation({
     *   procedureId: '2024/0006(COD)'
     * });
     * // Returns legislative tracking with current stage, timeline milestones,
     * // committee assignments, and estimated adoption timeline
     * ```
     *
     * @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 trackLegislationToolMetadata} for MCP schema registration
     * @see {@link handleMonitorLegislativePipeline} for pipeline-wide health and bottleneck analysis
     */
    export async function handleTrackLegislation(
      args: unknown
    ): Promise<ToolResult> {
      const params = TrackLegislationSchema.parse(args);
      const processId = toProcessId(params.procedureId);

      try {
        return await withTimeout(
          (async (): Promise<ToolResult> => {
            const procedure = await epClient.getProcedureById(processId);

            const enrichmentFailures: string[] = [];
            let events: EPEvent[] = [];
            try {
              const eventsResponse = await epClient.getProcedureEvents(processId, { limit: EVENTS_ENRICHMENT_LIMIT });
              events = eventsResponse.data;
            } catch {
              enrichmentFailures.push('events-lookup');
            }

            const tracking = buildLegislativeTracking(procedure, events, enrichmentFailures);

            return {
              content: [{
                type: 'text',
                text: JSON.stringify(tracking, null, 2)
              }]
            };
          })(),
          OPERATION_TIMEOUT_MS,
          'track_legislation operation timed out'
        );
      } catch (error: unknown) {
        Iif (error instanceof TimeoutError) {
          return buildTimeoutResponse('track_legislation', OPERATION_TIMEOUT_MS);
        }
        throw new ToolError({
          toolName: 'track_legislation',
          operation: 'fetchProcedure',
          message: 'Failed to retrieve legislative procedure data',
          isRetryable: true,
          cause: error,
        });
      }
    }

    /**
     * Tool metadata for MCP registration
     */
    export const trackLegislationToolMetadata = {
      name: 'track_legislation',
      description: 'Track European Parliament legislative procedure progress including current status, timeline of stages, committee assignments, amendments, voting records, and next steps. Provides comprehensive overview of legislation journey from proposal to adoption.',
      inputSchema: {
        type: 'object' as const,
        properties: {
          procedureId: {
            type: 'string',
            description: 'Legislative procedure identifier (e.g., "2024/0001(COD)")',
            minLength: 1,
            maxLength: 100
          }
        },
        required: ['procedureId']
      }
    };