uk-parliament-mcp 1.1.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.
- uk_parliament_mcp/__init__.py +3 -0
- uk_parliament_mcp/__main__.py +23 -0
- uk_parliament_mcp/config.py +20 -0
- uk_parliament_mcp/http_client.py +237 -0
- uk_parliament_mcp/server.py +49 -0
- uk_parliament_mcp/tools/__init__.py +1 -0
- uk_parliament_mcp/tools/bills.py +385 -0
- uk_parliament_mcp/tools/committees.py +385 -0
- uk_parliament_mcp/tools/commons_votes.py +138 -0
- uk_parliament_mcp/tools/composite.py +293 -0
- uk_parliament_mcp/tools/core.py +880 -0
- uk_parliament_mcp/tools/erskine_may.py +25 -0
- uk_parliament_mcp/tools/hansard.py +39 -0
- uk_parliament_mcp/tools/interests.py +43 -0
- uk_parliament_mcp/tools/lords_votes.py +149 -0
- uk_parliament_mcp/tools/members.py +439 -0
- uk_parliament_mcp/tools/now.py +30 -0
- uk_parliament_mcp/tools/oral_questions.py +55 -0
- uk_parliament_mcp/tools/statutory_instruments.py +38 -0
- uk_parliament_mcp/tools/treaties.py +25 -0
- uk_parliament_mcp/tools/whatson.py +72 -0
- uk_parliament_mcp/validators.py +58 -0
- uk_parliament_mcp-1.1.0.dist-info/METADATA +408 -0
- uk_parliament_mcp-1.1.0.dist-info/RECORD +26 -0
- uk_parliament_mcp-1.1.0.dist-info/WHEEL +4 -0
- uk_parliament_mcp-1.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""Members API tools for MPs, Lords, constituencies, and parties."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from uk_parliament_mcp.config import MEMBERS_API_BASE
|
|
8
|
+
from uk_parliament_mcp.http_client import build_url, get_result
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
12
|
+
"""Register member tools with the MCP server."""
|
|
13
|
+
|
|
14
|
+
@mcp.tool()
|
|
15
|
+
async def get_member_by_name(name: str) -> str:
|
|
16
|
+
"""Search for MPs and Lords by name with comprehensive member details | find MP, search politician, lookup member, who is, member search, parliamentary representative | Use for identifying members, checking spellings, finding member IDs, or getting basic member information | Returns member profiles with names, parties, constituencies, and current status
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
name: Full or partial name to search for. Examples: 'Boris Johnson', 'Keir Starmer', 'Smith'. Searches current and former members.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Member profiles with names, parties, constituencies, and current status.
|
|
23
|
+
"""
|
|
24
|
+
url = f"{MEMBERS_API_BASE}/Members/Search?Name={quote(name)}"
|
|
25
|
+
return await get_result(url)
|
|
26
|
+
|
|
27
|
+
@mcp.tool()
|
|
28
|
+
async def get_answering_bodies() -> str:
|
|
29
|
+
"""Get government departments and their parliamentary responsibilities | government departments, ministries, answering bodies, policy areas, department structure, who answers questions | Use for understanding government structure, finding responsible departments, or determining who answers questions on specific topics | Returns department names, abbreviations, and policy responsibilities
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Department names, abbreviations, and policy responsibilities.
|
|
33
|
+
"""
|
|
34
|
+
url = f"{MEMBERS_API_BASE}/Reference/AnsweringBodies"
|
|
35
|
+
return await get_result(url)
|
|
36
|
+
|
|
37
|
+
@mcp.tool()
|
|
38
|
+
async def get_member_by_id(member_id: int) -> str:
|
|
39
|
+
"""Get comprehensive member profile by ID with full parliamentary details | member details, MP profile, member information, parliamentary roles, constituency data | Use when you have a member ID and need complete biographical, political, and contact information | Returns detailed member data including roles, constituency, party, and career information
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
member_id: Parliament member ID. Required: get from member search first. Example: 1423
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Detailed member data including roles, constituency, party, and career information.
|
|
46
|
+
"""
|
|
47
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}"
|
|
48
|
+
return await get_result(url)
|
|
49
|
+
|
|
50
|
+
@mcp.tool()
|
|
51
|
+
async def edms_for_member_id(member_id: int) -> str:
|
|
52
|
+
"""Get all Early Day Motions signed by a specific MP. Use when you want to see what issues a particular member has supported or their political priorities through EDM signatures.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
member_id: Parliament member ID to get EDMs for.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
List of Early Day Motions signed by the member.
|
|
59
|
+
"""
|
|
60
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Edms"
|
|
61
|
+
return await get_result(url)
|
|
62
|
+
|
|
63
|
+
@mcp.tool()
|
|
64
|
+
async def parties_list_by_house(house: int) -> str:
|
|
65
|
+
"""Get list of active political parties in either House of Commons (1) or House of Lords (2). Use when you need to know current party representation or party structures in Parliament.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
house: House number: 1 for Commons, 2 for Lords.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
List of active political parties in the specified house.
|
|
72
|
+
"""
|
|
73
|
+
url = f"{MEMBERS_API_BASE}/Parties/GetActive/{house}"
|
|
74
|
+
return await get_result(url)
|
|
75
|
+
|
|
76
|
+
@mcp.tool()
|
|
77
|
+
async def get_departments() -> str:
|
|
78
|
+
"""Get list of all government departments. Use when you need to know the structure of government or which department handles specific policy areas.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
List of all government departments.
|
|
82
|
+
"""
|
|
83
|
+
url = f"{MEMBERS_API_BASE}/Reference/Departments"
|
|
84
|
+
return await get_result(url)
|
|
85
|
+
|
|
86
|
+
@mcp.tool()
|
|
87
|
+
async def get_contributions(member_id: int) -> str:
|
|
88
|
+
"""Get summary of parliamentary contributions (speeches, questions, interventions) made by a specific member. Use when analyzing an MP or Lord's parliamentary activity and participation levels.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
member_id: Parliament member ID to get contribution summary for.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Summary of parliamentary contributions for the member.
|
|
95
|
+
"""
|
|
96
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/ContributionSummary?page=1"
|
|
97
|
+
return await get_result(url)
|
|
98
|
+
|
|
99
|
+
@mcp.tool()
|
|
100
|
+
async def get_constituencies(
|
|
101
|
+
skip: int | None = None,
|
|
102
|
+
take: int | None = None,
|
|
103
|
+
) -> str:
|
|
104
|
+
"""Get list of UK parliamentary constituencies with pagination support. Use when you need constituency information, want to browse all constituencies, or need constituency data for analysis.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
skip: Number of constituencies to skip (for pagination).
|
|
108
|
+
take: Number of constituencies to return (default 20, max 100).
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
List of UK parliamentary constituencies.
|
|
112
|
+
"""
|
|
113
|
+
url = build_url(
|
|
114
|
+
f"{MEMBERS_API_BASE}/Location/Constituency/Search",
|
|
115
|
+
{"skip": skip, "take": take},
|
|
116
|
+
)
|
|
117
|
+
return await get_result(url)
|
|
118
|
+
|
|
119
|
+
@mcp.tool()
|
|
120
|
+
async def get_election_results_for_constituency(constituency_id: int) -> str:
|
|
121
|
+
"""Get historical election results for a specific constituency. Use when researching constituency voting patterns, election history, or past electoral outcomes for a particular area.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
constituency_id: Unique constituency ID number.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Historical election results for the constituency.
|
|
128
|
+
"""
|
|
129
|
+
url = f"{MEMBERS_API_BASE}/Location/Constituency/{constituency_id}/ElectionResults"
|
|
130
|
+
return await get_result(url)
|
|
131
|
+
|
|
132
|
+
@mcp.tool()
|
|
133
|
+
async def get_lords_interests_staff(search_term: str = "richard") -> str:
|
|
134
|
+
"""Search for staff interests declared by Lords. Use when investigating potential conflicts of interest related to Lords' staff or understanding transparency requirements for parliamentary staff.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
search_term: Search term for staff names or interests (default 'richard').
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Staff interests declared by Lords matching the search term.
|
|
141
|
+
"""
|
|
142
|
+
url = f"{MEMBERS_API_BASE}/LordsInterests/Staff?searchTerm={quote(search_term)}"
|
|
143
|
+
return await get_result(url)
|
|
144
|
+
|
|
145
|
+
@mcp.tool()
|
|
146
|
+
async def get_members_biography(member_id: int) -> str:
|
|
147
|
+
"""Get comprehensive member biography and personal history | MP background, life story, career details, education, personal info, political experience | Use for researching member backgrounds, writing profiles, understanding political journey | Returns detailed biographical data including education, career timeline, and political milestones
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
member_id: Parliament member ID. Required: get from member search first. Returns comprehensive biographical information.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Detailed biographical data including education, career timeline, and political milestones.
|
|
154
|
+
"""
|
|
155
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Biography"
|
|
156
|
+
return await get_result(url)
|
|
157
|
+
|
|
158
|
+
@mcp.tool()
|
|
159
|
+
async def get_members_contact(member_id: int) -> str:
|
|
160
|
+
"""Get member contact details and office information | MP contact, phone number, email, office address, constituency office | Use for contacting members, finding office locations, or getting official contact details | Returns phone numbers, addresses, and official contact information
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
member_id: Parliament member ID. Required: get from member search first. Returns official contact details.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Phone numbers, addresses, and official contact information.
|
|
167
|
+
"""
|
|
168
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Contact"
|
|
169
|
+
return await get_result(url)
|
|
170
|
+
|
|
171
|
+
@mcp.tool()
|
|
172
|
+
async def search_members(
|
|
173
|
+
name: str | None = None,
|
|
174
|
+
location: str | None = None,
|
|
175
|
+
post_title: str | None = None,
|
|
176
|
+
party_id: int | None = None,
|
|
177
|
+
house: int | None = None,
|
|
178
|
+
constituency_id: int | None = None,
|
|
179
|
+
name_starts_with: str | None = None,
|
|
180
|
+
gender: str | None = None,
|
|
181
|
+
membership_started_since: str | None = None,
|
|
182
|
+
membership_ended_since: str | None = None,
|
|
183
|
+
was_member_on_or_after: str | None = None,
|
|
184
|
+
was_member_on_or_before: str | None = None,
|
|
185
|
+
was_member_of_house: int | None = None,
|
|
186
|
+
is_eligible: bool | None = None,
|
|
187
|
+
is_current_member: bool | None = None,
|
|
188
|
+
policy_interest_id: int | None = None,
|
|
189
|
+
experience: str | None = None,
|
|
190
|
+
skip: int = 0,
|
|
191
|
+
take: int = 20,
|
|
192
|
+
) -> str:
|
|
193
|
+
"""Search for current MPs and Lords with comprehensive filtering options. Use when you need to find members by name, location, party, constituency, gender, posts held, or policy interests. Supports advanced search criteria including membership dates and eligibility status.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
name: Optional: full or partial name to search for.
|
|
197
|
+
location: Optional: location or constituency name.
|
|
198
|
+
post_title: Optional: post title (e.g. 'Minister', 'Secretary of State').
|
|
199
|
+
party_id: Optional: party ID to filter by.
|
|
200
|
+
house: Optional: house number (1=Commons, 2=Lords).
|
|
201
|
+
constituency_id: Optional: constituency ID to filter by.
|
|
202
|
+
name_starts_with: Optional: filter names starting with specific letter(s).
|
|
203
|
+
gender: Optional: gender filter ('M' or 'F').
|
|
204
|
+
membership_started_since: Optional: membership started since date in YYYY-MM-DD format.
|
|
205
|
+
membership_ended_since: Optional: membership ended since date in YYYY-MM-DD format.
|
|
206
|
+
was_member_on_or_after: Optional: was member on or after date in YYYY-MM-DD format.
|
|
207
|
+
was_member_on_or_before: Optional: was member on or before date in YYYY-MM-DD format.
|
|
208
|
+
was_member_of_house: Optional: was member of house (1=Commons, 2=Lords).
|
|
209
|
+
is_eligible: Optional: filter by eligibility status.
|
|
210
|
+
is_current_member: Optional: filter by current membership status.
|
|
211
|
+
policy_interest_id: Optional: policy interest ID to filter by.
|
|
212
|
+
experience: Optional: search term for professional experience.
|
|
213
|
+
skip: Number of records to skip (for pagination).
|
|
214
|
+
take: Number of records to return (default 20, max 100).
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Matching member profiles with comprehensive details.
|
|
218
|
+
"""
|
|
219
|
+
url = build_url(
|
|
220
|
+
f"{MEMBERS_API_BASE}/Members/Search",
|
|
221
|
+
{
|
|
222
|
+
"Name": name,
|
|
223
|
+
"Location": location,
|
|
224
|
+
"PostTitle": post_title,
|
|
225
|
+
"PartyId": party_id,
|
|
226
|
+
"House": house,
|
|
227
|
+
"ConstituencyId": constituency_id,
|
|
228
|
+
"NameStartsWith": name_starts_with,
|
|
229
|
+
"Gender": gender,
|
|
230
|
+
"MembershipStartedSince": membership_started_since,
|
|
231
|
+
"MembershipEnded.MembershipEndedSince": membership_ended_since,
|
|
232
|
+
"MembershipInDateRange.WasMemberOnOrAfter": was_member_on_or_after,
|
|
233
|
+
"MembershipInDateRange.WasMemberOnOrBefore": was_member_on_or_before,
|
|
234
|
+
"MembershipInDateRange.WasMemberOfHouse": was_member_of_house,
|
|
235
|
+
"IsEligible": is_eligible,
|
|
236
|
+
"IsCurrentMember": is_current_member,
|
|
237
|
+
"PolicyInterestId": policy_interest_id,
|
|
238
|
+
"Experience": experience,
|
|
239
|
+
"skip": skip,
|
|
240
|
+
"take": take,
|
|
241
|
+
},
|
|
242
|
+
)
|
|
243
|
+
return await get_result(url)
|
|
244
|
+
|
|
245
|
+
@mcp.tool()
|
|
246
|
+
async def search_members_historical(
|
|
247
|
+
name: str | None = None,
|
|
248
|
+
date_to_search_for: str | None = None,
|
|
249
|
+
skip: int = 0,
|
|
250
|
+
take: int = 20,
|
|
251
|
+
) -> str:
|
|
252
|
+
"""Search for historical members who were active on a specific date. Use when you need to find MPs or Lords who served during a particular period in parliamentary history.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
name: Optional: full or partial name to search for.
|
|
256
|
+
date_to_search_for: Optional: specific date to search for members active on that date (YYYY-MM-DD format).
|
|
257
|
+
skip: Number of records to skip (for pagination).
|
|
258
|
+
take: Number of records to return (default 20, max 100).
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Historical members matching the search criteria.
|
|
262
|
+
"""
|
|
263
|
+
url = build_url(
|
|
264
|
+
f"{MEMBERS_API_BASE}/Members/SearchHistorical",
|
|
265
|
+
{
|
|
266
|
+
"name": name,
|
|
267
|
+
"dateToSearchFor": date_to_search_for,
|
|
268
|
+
"skip": skip,
|
|
269
|
+
"take": take,
|
|
270
|
+
},
|
|
271
|
+
)
|
|
272
|
+
return await get_result(url)
|
|
273
|
+
|
|
274
|
+
@mcp.tool()
|
|
275
|
+
async def get_member_experience(member_id: int) -> str:
|
|
276
|
+
"""Get professional experience and career background of a member by ID. Use when researching a member's qualifications, previous employment, education, or professional history before entering Parliament.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
member_id: Parliament member ID to get professional experience for.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Professional experience and career background of the member.
|
|
283
|
+
"""
|
|
284
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Experience"
|
|
285
|
+
return await get_result(url)
|
|
286
|
+
|
|
287
|
+
@mcp.tool()
|
|
288
|
+
async def get_member_focus(member_id: int) -> str:
|
|
289
|
+
"""Get areas of focus and policy interests of a member by ID. Use when understanding what issues and policy areas a member prioritizes or specializes in.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
member_id: Parliament member ID to get policy focus areas for.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Areas of focus and policy interests of the member.
|
|
296
|
+
"""
|
|
297
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Focus"
|
|
298
|
+
return await get_result(url)
|
|
299
|
+
|
|
300
|
+
@mcp.tool()
|
|
301
|
+
async def get_member_registered_interests(
|
|
302
|
+
member_id: int,
|
|
303
|
+
house: int | None = None,
|
|
304
|
+
) -> str:
|
|
305
|
+
"""Get registered interests of a member by ID and house. Use when investigating potential conflicts of interest, financial interests, or external roles. Shows declared interests like directorships, consultancies, and gifts.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
member_id: Parliament member ID to get registered interests for.
|
|
309
|
+
house: Optional: house number (1=Commons, 2=Lords).
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
Registered interests of the member.
|
|
313
|
+
"""
|
|
314
|
+
url = build_url(
|
|
315
|
+
f"{MEMBERS_API_BASE}/Members/{member_id}/RegisteredInterests",
|
|
316
|
+
{"house": house},
|
|
317
|
+
)
|
|
318
|
+
return await get_result(url)
|
|
319
|
+
|
|
320
|
+
@mcp.tool()
|
|
321
|
+
async def get_member_staff(member_id: int) -> str:
|
|
322
|
+
"""Get staff members working for a specific MP or Lord by member ID. Use when researching parliamentary office staff, researchers, or support personnel.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
member_id: Parliament member ID to get staff details for.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
Staff members working for the member.
|
|
329
|
+
"""
|
|
330
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Staff"
|
|
331
|
+
return await get_result(url)
|
|
332
|
+
|
|
333
|
+
@mcp.tool()
|
|
334
|
+
async def get_member_synopsis(member_id: int) -> str:
|
|
335
|
+
"""Get a brief synopsis or summary about a member by ID. Use when you need a concise overview of a member's background, role, or key information.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
member_id: Parliament member ID to get synopsis for.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Brief synopsis or summary about the member.
|
|
342
|
+
"""
|
|
343
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/Synopsis"
|
|
344
|
+
return await get_result(url)
|
|
345
|
+
|
|
346
|
+
@mcp.tool()
|
|
347
|
+
async def get_member_voting(
|
|
348
|
+
member_id: int,
|
|
349
|
+
house: int,
|
|
350
|
+
page: int | None = None,
|
|
351
|
+
) -> str:
|
|
352
|
+
"""Get voting records of a member by ID for a specific house. Use when analyzing how a member votes, their voting patterns, or their stance on particular issues through their voting history.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
member_id: Parliament member ID to get voting record for.
|
|
356
|
+
house: House number (1=Commons, 2=Lords).
|
|
357
|
+
page: Optional: page number for pagination.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Voting records of the member.
|
|
361
|
+
"""
|
|
362
|
+
url = build_url(
|
|
363
|
+
f"{MEMBERS_API_BASE}/Members/{member_id}/Voting",
|
|
364
|
+
{"house": house, "page": page},
|
|
365
|
+
)
|
|
366
|
+
return await get_result(url)
|
|
367
|
+
|
|
368
|
+
@mcp.tool()
|
|
369
|
+
async def get_member_written_questions(
|
|
370
|
+
member_id: int,
|
|
371
|
+
page: int | None = None,
|
|
372
|
+
) -> str:
|
|
373
|
+
"""Get written questions submitted by a member by ID. Use when researching what questions a member has asked of government departments or their areas of parliamentary inquiry.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
member_id: Parliament member ID to get written questions for.
|
|
377
|
+
page: Optional: page number for pagination.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Written questions submitted by the member.
|
|
381
|
+
"""
|
|
382
|
+
url = build_url(
|
|
383
|
+
f"{MEMBERS_API_BASE}/Members/{member_id}/WrittenQuestions",
|
|
384
|
+
{"page": page},
|
|
385
|
+
)
|
|
386
|
+
return await get_result(url)
|
|
387
|
+
|
|
388
|
+
@mcp.tool()
|
|
389
|
+
async def get_members_history(member_ids: list[int]) -> str:
|
|
390
|
+
"""Get historical information for multiple members by their IDs. Returns name history, party affiliations, and membership details over time. Use when researching how members' details have changed throughout their careers.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
member_ids: List of Parliament member IDs to get history for.
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Historical information for the specified members.
|
|
397
|
+
"""
|
|
398
|
+
ids_str = ",".join(str(id) for id in member_ids)
|
|
399
|
+
url = build_url(f"{MEMBERS_API_BASE}/Members/History", {"ids": ids_str})
|
|
400
|
+
return await get_result(url)
|
|
401
|
+
|
|
402
|
+
@mcp.tool()
|
|
403
|
+
async def get_member_latest_election_result(member_id: int) -> str:
|
|
404
|
+
"""Get the latest election result for a member by ID. Use when researching how a member was elected, their constituency performance, vote share, or election margin.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
member_id: Parliament member ID to get latest election result for.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Latest election result for the member.
|
|
411
|
+
"""
|
|
412
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/LatestElectionResult"
|
|
413
|
+
return await get_result(url)
|
|
414
|
+
|
|
415
|
+
@mcp.tool()
|
|
416
|
+
async def get_member_portrait_url(member_id: int) -> str:
|
|
417
|
+
"""Get the portrait image URL for a member by ID. Use when you need a link to a member's official parliamentary portrait photograph.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
member_id: Parliament member ID to get portrait URL for.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
Portrait image URL for the member.
|
|
424
|
+
"""
|
|
425
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/PortraitUrl"
|
|
426
|
+
return await get_result(url)
|
|
427
|
+
|
|
428
|
+
@mcp.tool()
|
|
429
|
+
async def get_member_thumbnail_url(member_id: int) -> str:
|
|
430
|
+
"""Get the thumbnail image URL for a member by ID. Use when you need a link to a smaller version of a member's parliamentary photograph for lists or compact displays.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
member_id: Parliament member ID to get thumbnail URL for.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
Thumbnail image URL for the member.
|
|
437
|
+
"""
|
|
438
|
+
url = f"{MEMBERS_API_BASE}/Members/{member_id}/ThumbnailUrl"
|
|
439
|
+
return await get_result(url)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Now API tools for live chamber activity."""
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import FastMCP
|
|
4
|
+
|
|
5
|
+
from uk_parliament_mcp.config import NOW_API_BASE
|
|
6
|
+
from uk_parliament_mcp.http_client import get_result
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
10
|
+
"""Register now tools with the MCP server."""
|
|
11
|
+
|
|
12
|
+
@mcp.tool()
|
|
13
|
+
async def happening_now_in_commons() -> str:
|
|
14
|
+
"""Get live information about what's currently happening in the House of Commons chamber. Use when you want real-time updates on parliamentary business, current debates, or voting activity.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Live information about current Commons chamber activity.
|
|
18
|
+
"""
|
|
19
|
+
url = f"{NOW_API_BASE}/Message/message/CommonsMain/current"
|
|
20
|
+
return await get_result(url)
|
|
21
|
+
|
|
22
|
+
@mcp.tool()
|
|
23
|
+
async def happening_now_in_lords() -> str:
|
|
24
|
+
"""Get live information about what's currently happening in the House of Lords chamber. Use when you want real-time updates on Lords business, current debates, or voting activity.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Live information about current Lords chamber activity.
|
|
28
|
+
"""
|
|
29
|
+
url = f"{NOW_API_BASE}/Message/message/LordsMain/current"
|
|
30
|
+
return await get_result(url)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Oral Questions API tools for EDMs and question times."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from uk_parliament_mcp.config import ORAL_QUESTIONS_API_BASE
|
|
8
|
+
from uk_parliament_mcp.http_client import get_result
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
12
|
+
"""Register oral questions tools with the MCP server."""
|
|
13
|
+
|
|
14
|
+
@mcp.tool()
|
|
15
|
+
async def get_recently_tabled_edms(take: int = 10) -> str:
|
|
16
|
+
"""Get recently tabled Early Day Motions - formal political statements | EDMs, political motions, backbench initiatives, MP opinions, parliamentary statements, political positions, cross-party support | Use for tracking political sentiment, finding MP stances on issues, monitoring backbench activity, or researching political movements | Returns recent EDMs with titles, sponsors, supporters, and tabling dates | Data freshness: updated daily
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
take: Number of EDMs to return. Default: 10, recommended max: 50. Recent motions returned first.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Recent EDMs with titles, sponsors, supporters, and tabling dates.
|
|
23
|
+
"""
|
|
24
|
+
url = f"{ORAL_QUESTIONS_API_BASE}/EarlyDayMotions/list?parameters.orderBy=DateTabledDesc&skip=0&take={take}"
|
|
25
|
+
return await get_result(url)
|
|
26
|
+
|
|
27
|
+
@mcp.tool()
|
|
28
|
+
async def search_early_day_motions(search_term: str) -> str:
|
|
29
|
+
"""Search Early Day Motions by topic or keyword. Use when researching MP opinions on specific issues or finding motions related to particular subjects. EDMs often reflect backbench MP concerns.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
search_term: Search term for EDM topics or content (e.g. 'climate change', 'NHS funding').
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
EDMs matching the search term.
|
|
36
|
+
"""
|
|
37
|
+
url = f"{ORAL_QUESTIONS_API_BASE}/EarlyDayMotions/list?parameters.searchTerm={quote(search_term)}"
|
|
38
|
+
return await get_result(url)
|
|
39
|
+
|
|
40
|
+
@mcp.tool()
|
|
41
|
+
async def search_oral_question_times(
|
|
42
|
+
answering_date_start: str,
|
|
43
|
+
answering_date_end: str,
|
|
44
|
+
) -> str:
|
|
45
|
+
"""Get scheduled oral question times for ministers in Parliament. Use when you want to know when specific departments will answer questions or when particular topics will be discussed.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
answering_date_start: Start date for question times in YYYY-MM-DD format.
|
|
49
|
+
answering_date_end: End date for question times in YYYY-MM-DD format.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Scheduled oral question times in the date range.
|
|
53
|
+
"""
|
|
54
|
+
url = f"{ORAL_QUESTIONS_API_BASE}/oralquestiontimes/list?parameters.answeringDateStart={quote(answering_date_start)}¶meters.answeringDateEnd={quote(answering_date_end)}"
|
|
55
|
+
return await get_result(url)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Statutory Instruments API tools for secondary legislation."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from uk_parliament_mcp.config import STATUTORY_INSTRUMENTS_API_BASE
|
|
8
|
+
from uk_parliament_mcp.http_client import get_result
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
12
|
+
"""Register statutory instruments tools with the MCP server."""
|
|
13
|
+
|
|
14
|
+
@mcp.tool()
|
|
15
|
+
async def search_statutory_instruments(name: str) -> str:
|
|
16
|
+
"""Search for Statutory Instruments (secondary legislation) by name. Use when researching government regulations, rules, or orders made under primary legislation. SIs are used to implement or modify laws.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
name: Name or title of the statutory instrument to search for.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Statutory Instruments matching the search term.
|
|
23
|
+
"""
|
|
24
|
+
url = f"{STATUTORY_INSTRUMENTS_API_BASE}/StatutoryInstrument?Name={quote(name)}"
|
|
25
|
+
return await get_result(url)
|
|
26
|
+
|
|
27
|
+
@mcp.tool()
|
|
28
|
+
async def search_acts_of_parliament(name: str) -> str:
|
|
29
|
+
"""Search for Acts of Parliament (primary legislation) by name or topic. Use when researching existing laws, finding legislation on specific subjects, or understanding the legal framework on particular issues.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
name: Name or title of the Act to search for (e.g. 'Climate Change Act', 'Human Rights Act').
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Acts of Parliament matching the search term.
|
|
36
|
+
"""
|
|
37
|
+
url = f"{STATUTORY_INSTRUMENTS_API_BASE}/ActOfParliament?Name={quote(name)}"
|
|
38
|
+
return await get_result(url)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Treaties API tools for international agreements."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from uk_parliament_mcp.config import TREATIES_API_BASE
|
|
8
|
+
from uk_parliament_mcp.http_client import get_result
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
12
|
+
"""Register treaties tools with the MCP server."""
|
|
13
|
+
|
|
14
|
+
@mcp.tool()
|
|
15
|
+
async def search_treaties(search_text: str) -> str:
|
|
16
|
+
"""Search UK international treaties and agreements under parliamentary scrutiny | treaties, international agreements, trade deals, diplomatic treaties, international law, bilateral agreements | Use for researching international relations, trade agreements, or diplomatic commitments | Returns treaty details including titles, countries involved, and parliamentary scrutiny status
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
search_text: Search term for treaties. Examples: 'trade', 'EU', 'climate', 'Brexit'. Searches titles and content.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Treaty details including titles, countries involved, and parliamentary scrutiny status.
|
|
23
|
+
"""
|
|
24
|
+
url = f"{TREATIES_API_BASE}/Treaty?SearchText={quote(search_text)}"
|
|
25
|
+
return await get_result(url)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""WhatsOn API tools for parliamentary calendar and sessions."""
|
|
2
|
+
|
|
3
|
+
from mcp.server.fastmcp import FastMCP
|
|
4
|
+
|
|
5
|
+
from uk_parliament_mcp.config import WHATSON_API_BASE
|
|
6
|
+
from uk_parliament_mcp.http_client import build_url, get_result
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register_tools(mcp: FastMCP) -> None:
|
|
10
|
+
"""Register whatson tools with the MCP server."""
|
|
11
|
+
|
|
12
|
+
@mcp.tool()
|
|
13
|
+
async def search_calendar(
|
|
14
|
+
house: str,
|
|
15
|
+
start_date: str,
|
|
16
|
+
end_date: str,
|
|
17
|
+
) -> str:
|
|
18
|
+
"""Search parliamentary calendar for upcoming events and business in either chamber. Use when you want to know what's scheduled in Parliament, upcoming debates, or future parliamentary business. House: Commons/Lords.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
house: House name: 'Commons' or 'Lords'.
|
|
22
|
+
start_date: Start date in YYYY-MM-DD format.
|
|
23
|
+
end_date: End date in YYYY-MM-DD format.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Parliamentary calendar events in the date range.
|
|
27
|
+
"""
|
|
28
|
+
url = build_url(
|
|
29
|
+
f"{WHATSON_API_BASE}/events/list.json",
|
|
30
|
+
{
|
|
31
|
+
"queryParameters.house": house,
|
|
32
|
+
"queryParameters.startDate": start_date,
|
|
33
|
+
"queryParameters.endDate": end_date,
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
return await get_result(url)
|
|
37
|
+
|
|
38
|
+
@mcp.tool()
|
|
39
|
+
async def get_sessions() -> str:
|
|
40
|
+
"""Get list of parliamentary sessions. Use when you need to understand parliamentary terms, session dates, or the parliamentary calendar structure.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
List of parliamentary sessions.
|
|
44
|
+
"""
|
|
45
|
+
url = f"{WHATSON_API_BASE}/sessions/list.json"
|
|
46
|
+
return await get_result(url)
|
|
47
|
+
|
|
48
|
+
@mcp.tool()
|
|
49
|
+
async def get_non_sitting_days(
|
|
50
|
+
house: str,
|
|
51
|
+
start_date: str,
|
|
52
|
+
end_date: str,
|
|
53
|
+
) -> str:
|
|
54
|
+
"""Get periods when Parliament is not sitting (recesses, holidays). Use when you need to know when Parliament is on break, recess periods, or when no parliamentary business is scheduled.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
house: House name: 'Commons' or 'Lords'.
|
|
58
|
+
start_date: Start date in YYYY-MM-DD format.
|
|
59
|
+
end_date: End date in YYYY-MM-DD format.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Non-sitting days in the date range.
|
|
63
|
+
"""
|
|
64
|
+
url = build_url(
|
|
65
|
+
f"{WHATSON_API_BASE}/events/nonsitting.json",
|
|
66
|
+
{
|
|
67
|
+
"queryParameters.house": house,
|
|
68
|
+
"queryParameters.startDate": start_date,
|
|
69
|
+
"queryParameters.endDate": end_date,
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
return await get_result(url)
|