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 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | 15x 14x 14x 4x 2x 1x 1x 1x 1x 2x 15x 2x 2x 2x 2x 20x 20x 5x 5x 5x 15x 15x 15x 12x 2x 2x 2x 2x 10x 10x 9x 9x 1x 1x 4x | /**
* MCP Tool: get_committee_info
*
* Retrieve European Parliament committee information
*
* **Intelligence Perspective:** Maps committee power structures, membership networks,
* and policy domain specialization for institutional analysis and influence mapping.
*
* **Business Perspective:** Powers committee monitoring products for industry associations,
* trade groups, and corporate government affairs tracking specific policy areas.
*
* **Marketing Perspective:** Highlights structured access to EP's 20+ standing committees—
* compelling for policy monitoring platforms and civic tech integrations.
*
* ISMS Policy: SC-002 (Input Validation), AC-003 (Least Privilege)
*/
import { GetCommitteeInfoSchema, CommitteeSchema, PaginatedResponseSchema } 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';
import {
loadWeeklyCorporateBodiesCache,
loadWeeklyMEPCache,
} from '../utils/weeklyDataCache.js';
import {
findCachedCommittee,
listCachedCurrentCorporateBodies,
} from '../utils/committeeCache.js';
import { auditLogger } from '../utils/auditLogger.js';
type GetCommitteeInfoParams = ReturnType<typeof GetCommitteeInfoSchema.parse>;
async function getCachedCommitteeResponse(params: GetCommitteeInfoParams): Promise<ToolResult | null> {
if (params.live) return null;
const [cachedBodies, cachedMEPs] = await Promise.all([
loadWeeklyCorporateBodiesCache(),
loadWeeklyMEPCache(),
]);
if (cachedBodies === null) return null;
if (params.showCurrent === true) {
if (cachedBodies.metadata.scope !== 'current') return null;
const bodies = listCachedCurrentCorporateBodies(cachedBodies, cachedMEPs);
const validated = PaginatedResponseSchema(CommitteeSchema).parse({
data: bodies,
total: bodies.length,
limit: bodies.length,
offset: 0,
hasMore: false,
});
auditLogger.logDataAccess('get_committee_info', { showCurrent: true }, bodies.length);
return buildToolResponse(validated);
}
const lookup = params.abbreviation ?? params.id;
Iif (lookup === undefined) return null;
const committee = findCachedCommittee(lookup, cachedBodies, cachedMEPs);
Iif (committee === undefined) return null;
auditLogger.logDataAccess('get_committee_info', { id: params.id, abbreviation: params.abbreviation }, 1);
return buildToolResponse(committee);
}
/**
* Handles the get_committee_info MCP tool request.
*
* Retrieves European Parliament committee (corporate body) information including
* composition, chair, vice-chairs, members, meeting schedules, and policy areas.
* Supports lookup by committee ID, abbreviation, or listing all current active bodies.
*
* @param args - Raw tool arguments, validated against {@link GetCommitteeInfoSchema}
* @returns MCP tool result containing committee details or a list of current active corporate bodies
* @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
* // Lookup by abbreviation
* const result = await handleGetCommitteeInfo({ abbreviation: 'ENVI' });
* // Returns detailed info for the Environment Committee
*
* // List all current active bodies
* const all = await handleGetCommitteeInfo({ showCurrent: true });
* // Returns all currently active EP corporate bodies
* ```
*
* @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 getCommitteeInfoToolMetadata} for MCP schema registration
* @see {@link handleGetCommitteeDocuments} for retrieving documents produced by committees
*/
export async function handleGetCommitteeInfo(
args: unknown
): Promise<ToolResult> {
let params: GetCommitteeInfoParams;
try {
params = GetCommitteeInfoSchema.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_committee_info',
operation: 'validateInput',
message: `Invalid parameters: ${fieldErrors}`,
isRetryable: false,
cause: error,
});
}
throw error;
}
try {
const cachedResponse = await getCachedCommitteeResponse(params);
if (cachedResponse !== null) return cachedResponse;
if (params.showCurrent === true) {
const result = await epClient.getCurrentCorporateBodies({ live: params.live });
const outputSchema = PaginatedResponseSchema(CommitteeSchema);
const validated = outputSchema.parse(result);
return buildToolResponse(validated);
}
const apiParams = {
...buildApiParams(params, [
{ from: 'id', to: 'id' },
{ from: 'abbreviation', to: 'abbreviation' },
]),
live: params.live,
};
const result = await epClient.getCommitteeInfo(apiParams);
const validated = CommitteeSchema.parse(result);
return buildToolResponse(validated);
} catch (error: unknown) {
Iif (error instanceof z.ZodError) {
const fieldErrors = error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join('; ');
throw new ToolError({
toolName: 'get_committee_info',
operation: 'validateOutput',
message: `Unexpected EP API response format: ${fieldErrors}`,
isRetryable: false,
cause: error,
});
}
throw new ToolError({
toolName: 'get_committee_info',
operation: 'fetchData',
message: 'Failed to retrieve committee information',
isRetryable: true,
cause: error,
});
}
}
/**
* Tool metadata for MCP registration
*/
export const getCommitteeInfoToolMetadata = {
name: 'get_committee_info',
description: 'Retrieve detailed information about EP corporate bodies/committees. Query by ID, abbreviation, or set showCurrent=true for all current active bodies. Returns committee metadata plus roster leadership and membership derived from MEP membership relations when available, along with meeting schedules and areas of responsibility.',
inputSchema: {
type: 'object' as const,
properties: {
id: {
type: 'string',
description: 'Committee identifier',
minLength: 1,
maxLength: 100
},
abbreviation: {
type: 'string',
description: 'Committee abbreviation (e.g., "ENVI", "AGRI", "ECON")',
minLength: 1,
maxLength: 20
},
showCurrent: {
type: 'boolean',
description: 'If true, returns all current active corporate bodies',
default: false
},
live: {
type: 'boolean',
description: 'When true, bypasses weekly cache and fetches directly from the live EP API.',
default: false,
}
}
}
};
|