uk-parliament-mcp 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,385 @@
1
+ """Committees API tools for committee info, meetings, and evidence."""
2
+ from urllib.parse import quote
3
+
4
+ from mcp.server.fastmcp import FastMCP
5
+
6
+ from uk_parliament_mcp.http_client import build_url, get_result
7
+
8
+ COMMITTEES_API_BASE = "https://committees-api.parliament.uk/api"
9
+
10
+
11
+ def register_tools(mcp: FastMCP) -> None:
12
+ """Register committees tools with the MCP server."""
13
+
14
+ @mcp.tool()
15
+ async def get_committee_meetings(from_date: str, to_date: str) -> str:
16
+ """Find committee meetings and hearings by date range | committee meetings, parliamentary hearings, committee schedule, committee calendar, when committees meet | Use for finding committee schedules, planning attendance, or researching past committee activity | Returns meeting details including committees, dates, times, and topics | Covers both Commons and Lords committees
17
+
18
+ Args:
19
+ from_date: Start date. Format: YYYY-MM-DD. Example: 2024-01-15
20
+ to_date: End date. Format: YYYY-MM-DD. Must be after start date.
21
+
22
+ Returns:
23
+ Meeting details including committees, dates, times, and topics.
24
+ """
25
+ url = f"{COMMITTEES_API_BASE}/Broadcast/Meetings?FromDate={quote(from_date)}&ToDate={quote(to_date)}"
26
+ return await get_result(url)
27
+
28
+ @mcp.tool()
29
+ async def search_committees(search_term: str) -> str:
30
+ """Search for parliamentary committees by name or subject area. Use when you need to find which committee covers a specific policy area or when researching committee work.
31
+
32
+ Args:
33
+ search_term: Search term for committee names or subject areas (e.g. 'Treasury', 'Health', 'Defence').
34
+
35
+ Returns:
36
+ Matching committees with details.
37
+ """
38
+ url = f"{COMMITTEES_API_BASE}/Committees?SearchTerm={quote(search_term)}"
39
+ return await get_result(url)
40
+
41
+ @mcp.tool()
42
+ async def get_committee_types() -> str:
43
+ """Get all types of parliamentary committees (e.g., Select Committee, Public Bill Committee, Delegated Legislation Committee). Use when understanding committee structures or finding the right committee type.
44
+
45
+ Returns:
46
+ All committee types with descriptions.
47
+ """
48
+ url = f"{COMMITTEES_API_BASE}/CommitteeType"
49
+ return await get_result(url)
50
+
51
+ @mcp.tool()
52
+ async def get_committee_by_id(
53
+ committee_id: int,
54
+ include_banners: bool = False,
55
+ show_on_website_only: bool = True,
56
+ ) -> str:
57
+ """Get comprehensive committee profile and membership details | committee information, committee members, committee purpose, parliamentary committee, scrutiny committee | Use for understanding committee roles, finding committee members, or researching committee activities | Returns full committee details including purpose, members, departments scrutinized, and contact information
58
+
59
+ Args:
60
+ committee_id: Committee ID. Required: get from committee search first. Example: 739
61
+ include_banners: Include banner images. Default: false. May increase response size.
62
+ show_on_website_only: Show only public committees. Default: true. Recommended for general use.
63
+
64
+ Returns:
65
+ Full committee details including purpose, members, and contact information.
66
+ """
67
+ url = build_url(
68
+ f"{COMMITTEES_API_BASE}/Committees/{committee_id}",
69
+ {
70
+ "includeBanners": include_banners,
71
+ "showOnWebsiteOnly": show_on_website_only,
72
+ },
73
+ )
74
+ return await get_result(url)
75
+
76
+ @mcp.tool()
77
+ async def get_events(
78
+ committee_id: int | None = None,
79
+ committee_business_id: int | None = None,
80
+ search_term: str | None = None,
81
+ start_date_from: str | None = None,
82
+ start_date_to: str | None = None,
83
+ end_date_from: str | None = None,
84
+ location_id: int | None = None,
85
+ exclude_cancelled_events: bool | None = None,
86
+ sort_ascending: bool | None = None,
87
+ event_type_id: int | None = None,
88
+ include_event_attendees: bool = False,
89
+ show_on_website_only: bool = True,
90
+ skip: int = 0,
91
+ take: int = 30,
92
+ ) -> str:
93
+ """Search for committee events with flexible filtering options. Use when you need to find meetings, hearings, or other committee activities by date, location, or committee. Supports filtering by start/end dates, house, event type, and location.
94
+
95
+ Args:
96
+ committee_id: Optional: filter by specific committee ID.
97
+ committee_business_id: Optional: filter by committee business ID.
98
+ search_term: Optional: search term for event titles or content.
99
+ start_date_from: Optional: start date from in YYYY-MM-DD format.
100
+ start_date_to: Optional: start date to in YYYY-MM-DD format.
101
+ end_date_from: Optional: end date from in YYYY-MM-DD format.
102
+ location_id: Optional: location ID to filter events.
103
+ exclude_cancelled_events: Optional: exclude cancelled events.
104
+ sort_ascending: Optional: sort ascending by date.
105
+ event_type_id: Optional: filter by event type ID.
106
+ include_event_attendees: Include event attendees in response.
107
+ show_on_website_only: Show only events visible on website.
108
+ skip: Number of records to skip (for pagination).
109
+ take: Number of records to return (default 30, max 100).
110
+
111
+ Returns:
112
+ Committee events matching the criteria.
113
+ """
114
+ url = build_url(
115
+ f"{COMMITTEES_API_BASE}/Events",
116
+ {
117
+ "CommitteeId": committee_id,
118
+ "CommitteeBusinessId": committee_business_id,
119
+ "SearchTerm": search_term,
120
+ "StartDateFrom": start_date_from,
121
+ "StartDateTo": start_date_to,
122
+ "EndDateFrom": end_date_from,
123
+ "LocationId": location_id,
124
+ "ExcludeCancelledEvents": exclude_cancelled_events,
125
+ "SortAscending": sort_ascending,
126
+ "EventTypeId": event_type_id,
127
+ "IncludeEventAttendees": include_event_attendees,
128
+ "ShowOnWebsiteOnly": show_on_website_only,
129
+ "Skip": skip,
130
+ "Take": take,
131
+ },
132
+ )
133
+ return await get_result(url)
134
+
135
+ @mcp.tool()
136
+ async def get_event_by_id(
137
+ event_id: int,
138
+ show_on_website_only: bool = True,
139
+ ) -> str:
140
+ """Get detailed information about a specific committee event by ID. Use when you need complete event details including activities, attendees, committees involved, and related business.
141
+
142
+ Args:
143
+ event_id: Unique event ID number.
144
+ show_on_website_only: Show only events visible on website.
145
+
146
+ Returns:
147
+ Detailed information about the event.
148
+ """
149
+ url = build_url(
150
+ f"{COMMITTEES_API_BASE}/Events/{event_id}",
151
+ {"showOnWebsiteOnly": show_on_website_only},
152
+ )
153
+ return await get_result(url)
154
+
155
+ @mcp.tool()
156
+ async def get_committee_events(
157
+ committee_id: int,
158
+ committee_business_id: int | None = None,
159
+ search_term: str | None = None,
160
+ start_date_from: str | None = None,
161
+ start_date_to: str | None = None,
162
+ end_date_from: str | None = None,
163
+ location_id: int | None = None,
164
+ exclude_cancelled_events: bool | None = None,
165
+ sort_ascending: bool | None = None,
166
+ event_type_id: int | None = None,
167
+ include_event_attendees: bool = False,
168
+ show_on_website_only: bool = True,
169
+ skip: int = 0,
170
+ take: int = 30,
171
+ ) -> str:
172
+ """Get events for a specific committee by committee ID. Use when you want to see all meetings and activities for a particular committee, with options to filter by date range, business, and event type.
173
+
174
+ Args:
175
+ committee_id: Committee ID to get events for.
176
+ committee_business_id: Optional: filter by committee business ID.
177
+ search_term: Optional: search term for event titles or content.
178
+ start_date_from: Optional: start date from in YYYY-MM-DD format.
179
+ start_date_to: Optional: start date to in YYYY-MM-DD format.
180
+ end_date_from: Optional: end date from in YYYY-MM-DD format.
181
+ location_id: Optional: location ID to filter events.
182
+ exclude_cancelled_events: Optional: exclude cancelled events.
183
+ sort_ascending: Optional: sort ascending by date.
184
+ event_type_id: Optional: filter by event type ID.
185
+ include_event_attendees: Include event attendees in response.
186
+ show_on_website_only: Show only events visible on website.
187
+ skip: Number of records to skip (for pagination).
188
+ take: Number of records to return (default 30, max 100).
189
+
190
+ Returns:
191
+ Events for the specified committee.
192
+ """
193
+ url = build_url(
194
+ f"{COMMITTEES_API_BASE}/Committees/{committee_id}/Events",
195
+ {
196
+ "CommitteeBusinessId": committee_business_id,
197
+ "SearchTerm": search_term,
198
+ "StartDateFrom": start_date_from,
199
+ "StartDateTo": start_date_to,
200
+ "EndDateFrom": end_date_from,
201
+ "LocationId": location_id,
202
+ "ExcludeCancelledEvents": exclude_cancelled_events,
203
+ "SortAscending": sort_ascending,
204
+ "EventTypeId": event_type_id,
205
+ "IncludeEventAttendees": include_event_attendees,
206
+ "ShowOnWebsiteOnly": show_on_website_only,
207
+ "Skip": skip,
208
+ "Take": take,
209
+ },
210
+ )
211
+ return await get_result(url)
212
+
213
+ @mcp.tool()
214
+ async def get_committee_members(
215
+ committee_id: int,
216
+ membership_status: str | None = None,
217
+ show_on_website_only: bool = True,
218
+ skip: int = 0,
219
+ take: int = 30,
220
+ ) -> str:
221
+ """Get members and staff of a specific committee by committee ID. Use when you need to know who serves on a committee, their roles, and membership status (current/former). Returns both elected members and lay members.
222
+
223
+ Args:
224
+ committee_id: Committee ID to get members for.
225
+ membership_status: Optional: filter by membership status (e.g. 'Current', 'Former').
226
+ show_on_website_only: Show only members visible on website.
227
+ skip: Number of records to skip (for pagination).
228
+ take: Number of records to return (default 30, max 100).
229
+
230
+ Returns:
231
+ Members and staff of the committee.
232
+ """
233
+ url = build_url(
234
+ f"{COMMITTEES_API_BASE}/Committees/{committee_id}/Members",
235
+ {
236
+ "MembershipStatus": membership_status,
237
+ "ShowOnWebsiteOnly": show_on_website_only,
238
+ "Skip": skip,
239
+ "Take": take,
240
+ },
241
+ )
242
+ return await get_result(url)
243
+
244
+ @mcp.tool()
245
+ async def get_publications(
246
+ search_term: str | None = None,
247
+ start_date: str | None = None,
248
+ end_date: str | None = None,
249
+ committee_business_id: int | None = None,
250
+ committee_id: int | None = None,
251
+ show_on_website_only: bool = True,
252
+ skip: int = 0,
253
+ take: int = 30,
254
+ ) -> str:
255
+ """Search for committee publications including reports, government responses, and other documents. Use when researching committee outputs, finding reports on specific topics, or tracking publication dates and paper numbers.
256
+
257
+ Args:
258
+ search_term: Optional: search term for publication titles or content.
259
+ start_date: Optional: start date in YYYY-MM-DD format.
260
+ end_date: Optional: end date in YYYY-MM-DD format.
261
+ committee_business_id: Optional: committee business ID to filter by.
262
+ committee_id: Optional: committee ID to filter by.
263
+ show_on_website_only: Show only publications visible on website.
264
+ skip: Number of records to skip (for pagination).
265
+ take: Number of records to return (default 30, max 100).
266
+
267
+ Returns:
268
+ Committee publications matching the criteria.
269
+ """
270
+ url = build_url(
271
+ f"{COMMITTEES_API_BASE}/Publications",
272
+ {
273
+ "SearchTerm": search_term,
274
+ "StartDate": start_date,
275
+ "EndDate": end_date,
276
+ "CommitteeBusinessId": committee_business_id,
277
+ "CommitteeId": committee_id,
278
+ "ShowOnWebsiteOnly": show_on_website_only,
279
+ "Skip": skip,
280
+ "Take": take,
281
+ },
282
+ )
283
+ return await get_result(url)
284
+
285
+ @mcp.tool()
286
+ async def get_publication_by_id(
287
+ publication_id: int,
288
+ show_on_website_only: bool = True,
289
+ ) -> str:
290
+ """Get detailed information about a specific committee publication by ID. Use when you need complete publication details including documents, HC numbers, government responses, and associated committee business.
291
+
292
+ Args:
293
+ publication_id: Unique publication ID number.
294
+ show_on_website_only: Show only publications visible on website.
295
+
296
+ Returns:
297
+ Detailed information about the publication.
298
+ """
299
+ url = build_url(
300
+ f"{COMMITTEES_API_BASE}/Publications/{publication_id}",
301
+ {"showOnWebsiteOnly": show_on_website_only},
302
+ )
303
+ return await get_result(url)
304
+
305
+ @mcp.tool()
306
+ async def get_written_evidence(
307
+ committee_business_id: int | None = None,
308
+ committee_id: int | None = None,
309
+ search_term: str | None = None,
310
+ start_date: str | None = None,
311
+ end_date: str | None = None,
312
+ show_on_website_only: bool = True,
313
+ skip: int = 0,
314
+ take: int = 30,
315
+ ) -> str:
316
+ """Search for written evidence submissions to committees. Use when researching stakeholder submissions, witness statements, or public input to committee inquiries. Can filter by committee, business, witness names, or publication dates.
317
+
318
+ Args:
319
+ committee_business_id: Optional: committee business ID to filter by.
320
+ committee_id: Optional: committee ID to filter by.
321
+ search_term: Optional: search term for evidence content or witness names.
322
+ start_date: Optional: start date in YYYY-MM-DD format.
323
+ end_date: Optional: end date in YYYY-MM-DD format.
324
+ show_on_website_only: Show only evidence visible on website.
325
+ skip: Number of records to skip (for pagination).
326
+ take: Number of records to return (default 30, max 100).
327
+
328
+ Returns:
329
+ Written evidence submissions matching the criteria.
330
+ """
331
+ url = build_url(
332
+ f"{COMMITTEES_API_BASE}/WrittenEvidence",
333
+ {
334
+ "CommitteeBusinessId": committee_business_id,
335
+ "CommitteeId": committee_id,
336
+ "SearchTerm": search_term,
337
+ "StartDate": start_date,
338
+ "EndDate": end_date,
339
+ "ShowOnWebsiteOnly": show_on_website_only,
340
+ "Skip": skip,
341
+ "Take": take,
342
+ },
343
+ )
344
+ return await get_result(url)
345
+
346
+ @mcp.tool()
347
+ async def get_oral_evidence(
348
+ committee_business_id: int | None = None,
349
+ committee_id: int | None = None,
350
+ search_term: str | None = None,
351
+ start_date: str | None = None,
352
+ end_date: str | None = None,
353
+ show_on_website_only: bool = True,
354
+ skip: int = 0,
355
+ take: int = 30,
356
+ ) -> str:
357
+ """Search for oral evidence sessions from committee hearings. Use when researching witness testimonies, committee hearings, or transcripts from evidence sessions. Can filter by committee, business, witness names, or meeting dates.
358
+
359
+ Args:
360
+ committee_business_id: Optional: committee business ID to filter by.
361
+ committee_id: Optional: committee ID to filter by.
362
+ search_term: Optional: search term for evidence content or witness names.
363
+ start_date: Optional: start date in YYYY-MM-DD format.
364
+ end_date: Optional: end date in YYYY-MM-DD format.
365
+ show_on_website_only: Show only evidence visible on website.
366
+ skip: Number of records to skip (for pagination).
367
+ take: Number of records to return (default 30, max 100).
368
+
369
+ Returns:
370
+ Oral evidence sessions matching the criteria.
371
+ """
372
+ url = build_url(
373
+ f"{COMMITTEES_API_BASE}/OralEvidence",
374
+ {
375
+ "CommitteeBusinessId": committee_business_id,
376
+ "CommitteeId": committee_id,
377
+ "SearchTerm": search_term,
378
+ "StartDate": start_date,
379
+ "EndDate": end_date,
380
+ "ShowOnWebsiteOnly": show_on_website_only,
381
+ "Skip": skip,
382
+ "Take": take,
383
+ },
384
+ )
385
+ return await get_result(url)
@@ -0,0 +1,138 @@
1
+ """Commons Votes API tools for House of Commons divisions and voting records."""
2
+ from mcp.server.fastmcp import FastMCP
3
+
4
+ from uk_parliament_mcp.http_client import build_url, get_result
5
+
6
+ COMMONS_VOTES_API_BASE = "http://commonsvotes-api.parliament.uk/data"
7
+
8
+
9
+ def register_tools(mcp: FastMCP) -> None:
10
+ """Register Commons votes tools with the MCP server."""
11
+
12
+ @mcp.tool()
13
+ async def search_commons_divisions(
14
+ search_term: str,
15
+ member_id: int | None = None,
16
+ start_date: str | None = None,
17
+ end_date: str | None = None,
18
+ division_number: int | None = None,
19
+ ) -> str:
20
+ """Search House of Commons voting records (divisions). Use when you want to find how MPs voted on specific issues, bills, or amendments. Can filter by member, date range, or division number.
21
+
22
+ Args:
23
+ search_term: Search term for division topics (e.g. 'brexit', 'climate', 'NHS').
24
+ member_id: Optional: specific member ID to filter votes.
25
+ start_date: Optional: start date in YYYY-MM-DD format.
26
+ end_date: Optional: end date in YYYY-MM-DD format.
27
+ division_number: Optional: specific division number.
28
+
29
+ Returns:
30
+ Commons divisions matching the search criteria.
31
+ """
32
+ url = build_url(
33
+ f"{COMMONS_VOTES_API_BASE}/divisions.json/search",
34
+ {
35
+ "queryParameters.searchTerm": search_term,
36
+ "memberId": member_id,
37
+ "queryParameters.startDate": start_date,
38
+ "queryParameters.endDate": end_date,
39
+ "queryParameters.divisionNumber": division_number,
40
+ },
41
+ )
42
+ return await get_result(url)
43
+
44
+ @mcp.tool()
45
+ async def get_commons_voting_record_for_member(member_id: int) -> str:
46
+ """Get complete voting record of an MP in House of Commons divisions. Use when analyzing how a specific MP votes, their voting patterns, or their stance on particular issues through their voting history.
47
+
48
+ Args:
49
+ member_id: Parliament member ID to get Commons voting record for.
50
+
51
+ Returns:
52
+ Complete Commons voting record for the member.
53
+ """
54
+ url = f"{COMMONS_VOTES_API_BASE}/divisions.json/membervoting?queryParameters.memberId={member_id}"
55
+ return await get_result(url)
56
+
57
+ @mcp.tool()
58
+ async def get_commons_division_by_id(division_id: int) -> str:
59
+ """Get detailed information about a specific House of Commons division by ID. Use when you need complete details about a particular vote including who voted for/against, tellers, and voting totals.
60
+
61
+ Args:
62
+ division_id: Unique Commons division ID number.
63
+
64
+ Returns:
65
+ Detailed information about the Commons division.
66
+ """
67
+ url = f"{COMMONS_VOTES_API_BASE}/division/{division_id}.json"
68
+ return await get_result(url)
69
+
70
+ @mcp.tool()
71
+ async def get_commons_divisions_grouped_by_party(
72
+ search_term: str | None = None,
73
+ member_id: int | None = None,
74
+ start_date: str | None = None,
75
+ end_date: str | None = None,
76
+ division_number: int | None = None,
77
+ include_when_member_was_teller: bool | None = None,
78
+ ) -> str:
79
+ """Get House of Commons divisions grouped by party voting patterns. Use when analyzing how different parties voted on issues or understanding party-line voting behavior. Shows vote counts by party rather than individual MPs.
80
+
81
+ Args:
82
+ search_term: Optional: search term to filter divisions.
83
+ member_id: Optional: member ID to filter divisions.
84
+ start_date: Optional: start date in YYYY-MM-DD format.
85
+ end_date: Optional: end date in YYYY-MM-DD format.
86
+ division_number: Optional: specific division number.
87
+ include_when_member_was_teller: Optional: include when member was a teller.
88
+
89
+ Returns:
90
+ Commons divisions grouped by party.
91
+ """
92
+ url = build_url(
93
+ f"{COMMONS_VOTES_API_BASE}/divisions.json/groupedbyparty",
94
+ {
95
+ "queryParameters.searchTerm": search_term,
96
+ "queryParameters.memberId": member_id,
97
+ "queryParameters.startDate": start_date,
98
+ "queryParameters.endDate": end_date,
99
+ "queryParameters.divisionNumber": division_number,
100
+ "queryParameters.includeWhenMemberWasTeller": include_when_member_was_teller,
101
+ },
102
+ )
103
+ return await get_result(url)
104
+
105
+ @mcp.tool()
106
+ async def get_commons_divisions_search_count(
107
+ search_term: str | None = None,
108
+ member_id: int | None = None,
109
+ start_date: str | None = None,
110
+ end_date: str | None = None,
111
+ division_number: int | None = None,
112
+ include_when_member_was_teller: bool | None = None,
113
+ ) -> str:
114
+ """Get total count of House of Commons divisions matching search criteria. Use when you need to know how many divisions match your search parameters before retrieving the actual results.
115
+
116
+ Args:
117
+ search_term: Optional: search term to filter divisions.
118
+ member_id: Optional: member ID to filter divisions.
119
+ start_date: Optional: start date in YYYY-MM-DD format.
120
+ end_date: Optional: end date in YYYY-MM-DD format.
121
+ division_number: Optional: specific division number.
122
+ include_when_member_was_teller: Optional: include when member was a teller.
123
+
124
+ Returns:
125
+ Total count of matching Commons divisions.
126
+ """
127
+ url = build_url(
128
+ f"{COMMONS_VOTES_API_BASE}/divisions.json/searchTotalResults",
129
+ {
130
+ "queryParameters.searchTerm": search_term,
131
+ "queryParameters.memberId": member_id,
132
+ "queryParameters.startDate": start_date,
133
+ "queryParameters.endDate": end_date,
134
+ "queryParameters.divisionNumber": division_number,
135
+ "queryParameters.includeWhenMemberWasTeller": include_when_member_was_teller,
136
+ },
137
+ )
138
+ return await get_result(url)
@@ -0,0 +1,28 @@
1
+ """Core tools for Parliament data assistant session management."""
2
+ from mcp.server.fastmcp import FastMCP
3
+
4
+ SYSTEM_PROMPT = """You are a helpful assistant that answers questions using only data from UK Parliament MCP servers.
5
+ When the session begins, introduce yourself with a brief message such as:
6
+ "Hello! I'm a parliamentary data assistant. I can help answer questions using official data from the UK Parliament MCP APIs. Just ask me something, and I'll fetch what I can - and I'll always show you which sources I used."
7
+ When responding to user queries, you must:
8
+ Only retrieve and use data from the MCP API endpoints this server provides.
9
+ Avoid using any external sources or inferred knowledge.
10
+ After every response, append a list of all MCP API URLs used to generate the answer.
11
+ If no relevant data is available via the MCP API, state that clearly and do not attempt to fabricate a response.
12
+ Convert raw data into human-readable summaries while preserving accuracy, but always list the raw URLs used."""
13
+
14
+ GOODBYE_PROMPT = """You are now interacting as a normal assistant. There are no special restrictions or requirements for using UK Parliament MCP data. You may answer questions using any available data or knowledge, and you do not need to append MCP API URLs or limit yourself to MCP sources. Resume normal assistant behavior."""
15
+
16
+
17
+ def register_tools(mcp: FastMCP) -> None:
18
+ """Register core tools with the MCP server."""
19
+
20
+ @mcp.tool()
21
+ async def hello_parliament() -> str:
22
+ """Initialize Parliament data assistant with system prompt | setup, configuration, start session, getting started, how to use, instructions | Use FIRST when beginning parliamentary research to get proper assistant behavior and data handling guidelines | Returns system prompt for optimal parliamentary data interaction"""
23
+ return SYSTEM_PROMPT
24
+
25
+ @mcp.tool()
26
+ async def goodbye_parliament() -> str:
27
+ """End Parliament session and restore normal assistant behavior | exit, quit, finish session, reset, normal mode, end parliamentary mode | Use when finished with parliamentary research to return to standard assistant behavior | Removes parliamentary data restrictions and requirements"""
28
+ return GOODBYE_PROMPT
@@ -0,0 +1,25 @@
1
+ """Erskine May API tools for parliamentary procedure."""
2
+ from urllib.parse import quote
3
+
4
+ from mcp.server.fastmcp import FastMCP
5
+
6
+ from uk_parliament_mcp.http_client import get_result
7
+
8
+ ERSKINE_MAY_API_BASE = "https://erskinemay-api.parliament.uk/api"
9
+
10
+
11
+ def register_tools(mcp: FastMCP) -> None:
12
+ """Register Erskine May tools with the MCP server."""
13
+
14
+ @mcp.tool()
15
+ async def search_erskine_may(search_term: str) -> str:
16
+ """Search Erskine May parliamentary procedure manual. Use when you need to understand parliamentary rules, procedures, or precedents. Erskine May is the authoritative guide to parliamentary procedure.
17
+
18
+ Args:
19
+ search_term: Search term for parliamentary procedure rules (e.g. 'Speaker', 'amendment', 'division').
20
+
21
+ Returns:
22
+ Erskine May paragraphs matching the search term.
23
+ """
24
+ url = f"{ERSKINE_MAY_API_BASE}/Search/ParagraphSearchResults/{quote(search_term)}"
25
+ return await get_result(url)
@@ -0,0 +1,39 @@
1
+ """Hansard API tools for searching the official parliamentary record."""
2
+ from mcp.server.fastmcp import FastMCP
3
+
4
+ from uk_parliament_mcp.http_client import build_url, get_result
5
+
6
+ HANSARD_API_BASE = "https://hansard-api.parliament.uk"
7
+
8
+
9
+ def register_tools(mcp: FastMCP) -> None:
10
+ """Register Hansard tools with the MCP server."""
11
+
12
+ @mcp.tool()
13
+ async def search_hansard(
14
+ house: int,
15
+ start_date: str,
16
+ end_date: str,
17
+ search_term: str,
18
+ ) -> str:
19
+ """Search Hansard (official parliamentary record) for speeches and debates. Use when researching what was said in Parliament on specific topics, by specific members, or in specific time periods. House: 1=Commons, 2=Lords.
20
+
21
+ Args:
22
+ house: House number: 1 for Commons, 2 for Lords.
23
+ start_date: Start date in YYYY-MM-DD format.
24
+ end_date: End date in YYYY-MM-DD format.
25
+ search_term: Search term for speeches or debates (e.g. 'climate change', 'NHS').
26
+
27
+ Returns:
28
+ Hansard records matching the search criteria.
29
+ """
30
+ url = build_url(
31
+ f"{HANSARD_API_BASE}/search.json",
32
+ {
33
+ "queryParameters.house": house,
34
+ "queryParameters.startDate": start_date,
35
+ "queryParameters.endDate": end_date,
36
+ "queryParameters.searchTerm": search_term,
37
+ },
38
+ )
39
+ return await get_result(url)
@@ -0,0 +1,43 @@
1
+ """Interests API tools for Register of Interests."""
2
+ from mcp.server.fastmcp import FastMCP
3
+
4
+ from uk_parliament_mcp.http_client import get_result
5
+
6
+ INTERESTS_API_BASE = "https://interests-api.parliament.uk/api/v1"
7
+
8
+
9
+ def register_tools(mcp: FastMCP) -> None:
10
+ """Register interests tools with the MCP server."""
11
+
12
+ @mcp.tool()
13
+ async def search_roi(member_id: int) -> str:
14
+ """Search member's Register of Interests for financial and business declarations | register of interests, ROI, financial interests, conflicts of interest, directorships, consultancies, gifts, external roles, transparency | Use for investigating potential conflicts, researching member finances, or checking declared interests | Returns declared interests including directorships, consultancies, gifts, and other financial interests
15
+
16
+ Args:
17
+ member_id: Parliament member ID. Required: get from member search first. Returns all declared interests.
18
+
19
+ Returns:
20
+ Declared interests including directorships, consultancies, gifts, and other financial interests.
21
+ """
22
+ url = f"{INTERESTS_API_BASE}/Interests/?MemberId={member_id}"
23
+ return await get_result(url)
24
+
25
+ @mcp.tool()
26
+ async def interests_categories() -> str:
27
+ """Get categories of interests that MPs and Lords must declare in the Register of Interests. Use when you need to understand what types of financial or other interests parliamentarians must declare.
28
+
29
+ Returns:
30
+ Categories of interests that must be declared.
31
+ """
32
+ url = f"{INTERESTS_API_BASE}/Categories"
33
+ return await get_result(url)
34
+
35
+ @mcp.tool()
36
+ async def get_registers_of_interests() -> str:
37
+ """Get list of published Registers of Interests. Use when you need to see all available interest registers or understand the transparency framework for parliamentary interests.
38
+
39
+ Returns:
40
+ List of published Registers of Interests.
41
+ """
42
+ url = f"{INTERESTS_API_BASE}/Registers"
43
+ return await get_result(url)