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.
@@ -0,0 +1,880 @@
1
+ """Core tools for Parliament data assistant session management and guidance."""
2
+
3
+ from typing import Any
4
+
5
+ from mcp.server.fastmcp import FastMCP
6
+
7
+ SYSTEM_PROMPT = """You are a helpful assistant that answers questions using only data from UK Parliament MCP servers.
8
+ When the session begins, introduce yourself with a brief message such as:
9
+ "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."
10
+ When responding to user queries, you must:
11
+ Only retrieve and use data from the MCP API endpoints this server provides.
12
+ Avoid using any external sources or inferred knowledge.
13
+ After every response, append a list of all MCP API URLs used to generate the answer.
14
+ If no relevant data is available via the MCP API, state that clearly and do not attempt to fabricate a response.
15
+ Convert raw data into human-readable summaries while preserving accuracy, but always list the raw URLs used."""
16
+
17
+ 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."""
18
+
19
+ QUICK_REFERENCE = """## Quick Reference: UK Parliament MCP Tools (92 tools)
20
+
21
+ ### Composite Tools (Start Here for Common Queries!)
22
+ These tools combine multiple API calls - use them first for efficiency:
23
+ - get_mp_profile(name) - Complete MP profile in one call
24
+ - check_mp_vote(mp_name, topic) - Check how an MP voted on a topic
25
+ - get_bill_overview(search_term) - Full bill info with stages
26
+ - get_committee_summary(topic) - Committee with evidence and publications
27
+
28
+ ### Key Conventions
29
+ - House IDs: 1 = Commons, 2 = Lords (for some tools: 'Commons' or 'Lords' as strings)
30
+ - Dates: YYYY-MM-DD format
31
+ - Pagination: skip/take parameters (typical defaults: 20-30)
32
+ - IDs: Use search tools first to get member_id, bill_id, etc.
33
+
34
+ ### Tool Categories & Entry Points
35
+ | Module | Tools | Start With |
36
+ |--------|-------|------------|
37
+ | composite | 4 | get_mp_profile(name) |
38
+ | members | 25 | get_member_by_name(name) |
39
+ | bills | 21 | search_bills(search_term) |
40
+ | committees | 12 | search_committees(search_term) |
41
+ | commons_votes | 5 | search_commons_divisions(search_term) |
42
+ | lords_votes | 5 | search_lords_divisions(search_term) |
43
+ | hansard | 1 | search_hansard(house, start_date, end_date, search_term) |
44
+ | oral_questions | 3 | search_early_day_motions(search_term) |
45
+ | interests | 3 | search_roi(member_id) |
46
+ | now | 2 | happening_now_in_commons() |
47
+ | whatson | 3 | search_calendar(house, start_date, end_date) |
48
+ | statutory_instruments | 2 | search_statutory_instruments() |
49
+ | treaties | 1 | search_treaties(search_text) |
50
+ | erskine_may | 1 | search_erskine_may(search_term) |
51
+
52
+ ### Common Patterns
53
+ 1. Use composite tools first for common queries (saves multiple calls)
54
+ 2. For detailed data: Search by name/term -> Get by ID -> Get related data
55
+ 3. For MP voting: check_mp_vote() OR get_member_by_name() -> get_commons_voting_record_for_member()
56
+ 4. For bill progress: get_bill_overview() OR search_bills() -> get_bill_stages()
57
+
58
+ Use parliament_guide(topic) for detailed tool information.
59
+ Use parliament_workflow(query) for step-by-step research planning."""
60
+
61
+ GUIDANCE_CONTENT = {
62
+ "composite": """## Composite Tools (4 tools)
63
+
64
+ High-level tools that combine multiple API calls for common research tasks.
65
+ Use these FIRST for common queries to reduce tool calls and improve efficiency.
66
+
67
+ ### MP/Lord Research
68
+ - get_mp_profile(name) - Complete profile in one call
69
+ - Combines: member search + details + biography + interests + voting
70
+ - Returns: Basic info, biography, registered interests, recent votes
71
+ - Example: get_mp_profile("Keir Starmer")
72
+
73
+ - check_mp_vote(mp_name, topic) - Check voting stance on a topic
74
+ - Combines: member search + division search with member filter
75
+ - Returns: MP info and divisions on the topic where they voted
76
+ - Example: check_mp_vote("Boris Johnson", "climate")
77
+
78
+ ### Bill Research
79
+ - get_bill_overview(search_term) - Full bill information
80
+ - Combines: bill search + details + stages + publications
81
+ - Returns: Bill details, legislative stages, associated documents
82
+ - Example: get_bill_overview("Online Safety")
83
+
84
+ ### Committee Research
85
+ - get_committee_summary(topic) - Committee with evidence and reports
86
+ - Combines: committee search + details + oral evidence + written evidence + publications
87
+ - Returns: Committee info, witness testimonies, written submissions, reports
88
+ - Example: get_committee_summary("Treasury")
89
+
90
+ ### When to Use Individual Tools Instead
91
+ Use the individual tools (in members, bills, etc.) when you need:
92
+ - Specific filtering options not available in composite tools
93
+ - Pagination through large result sets
94
+ - Access to specific endpoints not covered by composite tools
95
+ - More control over which data is fetched""",
96
+ "members": """## Members Tools (25 tools)
97
+
98
+ ### Primary Search Tools
99
+ - search_members(name, location, party_id, house, is_current_member, skip, take) - Comprehensive member search with filters
100
+ - get_member_by_name(name) - Simple name search for MPs and Lords
101
+ - search_members_historical(name, date_to_search_for) - Find historical members at a point in time
102
+
103
+ ### Member Details (require member_id from search)
104
+ - get_member_by_id(member_id) - Full member profile with party, constituency, status
105
+ - get_members_biography(member_id) - Education, career, background
106
+ - get_members_contact(member_id) - Phone, email, office addresses
107
+ - get_member_synopsis(member_id) - Brief summary
108
+ - get_member_experience(member_id) - Professional background
109
+ - get_member_focus(member_id) - Policy interests and expertise
110
+
111
+ ### Member Activity
112
+ - get_member_voting(member_id, house) - Recent voting records (house: 1=Commons, 2=Lords)
113
+ - get_commons_voting_record_for_member(member_id) - All Commons votes by member
114
+ - get_lords_voting_record_for_member(member_id) - All Lords votes by member
115
+ - get_member_written_questions(member_id, skip, take) - Written questions submitted
116
+ - get_contributions(member_id, skip, take) - Speeches, questions, interventions
117
+ - edms_for_member_id(member_id) - Early Day Motions signed/sponsored
118
+
119
+ ### Interests & Staff
120
+ - get_member_registered_interests(member_id, house) - Declared financial interests
121
+ - get_member_staff(member_id) - Office staff details
122
+
123
+ ### Electoral Data
124
+ - get_member_latest_election_result(member_id) - Last election result
125
+ - get_constituencies(skip, take) - List all constituencies
126
+ - get_election_results_for_constituency(constituency_id, skip, take) - Historical results
127
+
128
+ ### Reference Data
129
+ - parties_list_by_house(house) - Active parties (house: 1=Commons, 2=Lords)
130
+ - get_departments() - Government departments
131
+ - get_answering_bodies() - Which dept answers which questions
132
+
133
+ ### Typical Workflow
134
+ 1. Search: get_member_by_name("Keir Starmer") -> extract member_id
135
+ 2. Profile: get_member_by_id(member_id) -> basic info
136
+ 3. Details: get_members_biography(member_id) -> background
137
+ 4. Activity: get_commons_voting_record_for_member(member_id) -> voting history""",
138
+ "bills": """## Bills Tools (21 tools)
139
+
140
+ ### Search & Discovery
141
+ - search_bills(search_term, skip, take) - Find bills by keyword
142
+ - get_recently_updated_bills(take) - Latest legislative activity
143
+ - get_bill_by_id(bill_id) - Full bill details with current stage
144
+ - bill_types() - Types of bills (Public, Private, Hybrid, etc.)
145
+
146
+ ### Bill Progress & Stages
147
+ - get_bill_stages(bill_id) - Track legislative journey through Parliament
148
+ - bill_stages() - Stage definitions (1st Reading, 2nd Reading, Committee, etc.)
149
+ - get_bill_stage_sittings(bill_id, stage_id) - When stage was debated
150
+
151
+ ### Amendments
152
+ - get_bill_stage_amendments(bill_id, stage_id) - Amendments at specific stage
153
+ - get_amendment_by_id(bill_id, bill_stage_id, amendment_id) - Detailed amendment info
154
+
155
+ ### Publications & News
156
+ - get_bill_publications(bill_id) - Associated documents and papers
157
+ - get_publication_types() - Types of parliamentary publications
158
+ - get_bill_news_articles(bill_id) - News articles about the bill
159
+
160
+ ### RSS Feeds
161
+ - get_all_bills_rss() - RSS feed of all bills
162
+ - get_public_bills_rss() - RSS feed of public bills
163
+ - get_private_bills_rss() - RSS feed of private bills
164
+ - get_bill_rss(bill_id) - RSS feed for a specific bill
165
+
166
+ ### Typical Workflow
167
+ 1. Search: search_bills("climate") -> find bill_id
168
+ 2. Overview: get_bill_by_id(bill_id) -> current status
169
+ 3. Progress: get_bill_stages(bill_id) -> legislative journey
170
+ 4. Details: get_bill_stage_amendments(bill_id, stage_id) -> proposed changes""",
171
+ "votes": """## Voting Tools (10 tools: 5 Commons + 5 Lords)
172
+
173
+ ### Commons Divisions
174
+ - search_commons_divisions(search_term) - Find Commons votes by keyword
175
+ - get_commons_division_by_id(division_id) - Full voting details with lists
176
+ - get_commons_voting_record_for_member(member_id) - All votes by MP
177
+ - get_commons_divisions_grouped_by_party(search_term, member_id) - Votes grouped by party alignment
178
+ - get_commons_divisions_search_count(search_term) - Count of matching divisions
179
+
180
+ ### Lords Divisions
181
+ - search_lords_divisions(search_term) - Find Lords votes by keyword
182
+ - get_lords_division_by_id(division_id) - Full voting details with lists
183
+ - get_lords_voting_record_for_member(member_id) - All votes by Lord
184
+ - get_lords_divisions_grouped_by_party(member_id) - Votes grouped by party alignment
185
+ - get_lords_divisions_search_count(search_term) - Count of matching divisions
186
+
187
+ ### Key Concepts
188
+ - Division: A formal recorded vote in Parliament
189
+ - Aye/Content: Voting yes (Commons uses Aye, Lords uses Content)
190
+ - No/Not Content: Voting no (Commons uses No, Lords uses Not Content)
191
+ - Teller: Member who counts votes
192
+
193
+ ### Typical Workflow
194
+ 1. Find division: search_commons_divisions("Rwanda") -> get division_id
195
+ 2. Full results: get_commons_division_by_id(division_id) -> see all votes
196
+ 3. Member record: get_commons_voting_record_for_member(member_id) -> voting history""",
197
+ "committees": """## Committee Tools (12 tools)
198
+
199
+ ### Search & Discovery
200
+ - search_committees(search_term) - Find committees by topic
201
+ - get_committee_by_id(committee_id) - Full committee details
202
+ - get_committee_types() - Select Committee, Joint Committee, etc.
203
+
204
+ ### Meetings & Events
205
+ - get_committee_meetings(from_date, to_date) - Find meetings by date range (YYYY-MM-DD)
206
+ - get_events(...) - Search events with flexible filtering
207
+ - get_event_by_id(event_id) - Specific event details
208
+ - get_committee_events(committee_id, ...) - Events for a specific committee
209
+
210
+ ### Members
211
+ - get_committee_members(committee_id) - Current committee members
212
+
213
+ ### Evidence & Publications
214
+ - get_oral_evidence(committee_id) - Oral evidence transcripts
215
+ - get_written_evidence(committee_id) - Written submissions
216
+ - get_publications(committee_id) - Reports and documents
217
+ - get_publication_by_id(publication_id) - Specific publication details
218
+
219
+ ### Typical Workflow
220
+ 1. Search: search_committees("health") -> find committee_id
221
+ 2. Details: get_committee_by_id(committee_id) -> scope and membership
222
+ 3. Activity: get_committee_meetings("2024-01-01", "2024-12-31") -> recent meetings
223
+ 4. Evidence: get_oral_evidence(committee_id=committee_id) -> witness testimonies
224
+ 5. Reports: get_publications(committee_id=committee_id) -> published reports""",
225
+ "hansard": """## Hansard Tools (1 tool)
226
+
227
+ ### Search
228
+ - search_hansard(search_term, house, skip, take) - Search parliamentary record
229
+
230
+ ### About Hansard
231
+ Hansard is the official verbatim record of everything said in Parliament:
232
+ - Debates in the Commons and Lords chambers
233
+ - Questions to ministers
234
+ - Statements and announcements
235
+ - Interventions and points of order
236
+
237
+ ### Parameters
238
+ - search_term: Keywords to search for
239
+ - house: 1 = Commons, 2 = Lords (optional)
240
+ - skip/take: Pagination
241
+
242
+ ### Tips
243
+ - Search for specific topics, MP names, or phrases
244
+ - Results include date, speaker, and context
245
+ - Use date ranges to narrow results
246
+ - Combine with member tools to find who said what""",
247
+ "questions": """## Questions & Motions Tools (4 tools)
248
+
249
+ ### Early Day Motions (EDMs)
250
+ - get_recently_tabled_edms(take) - Get recently tabled EDMs
251
+ - search_early_day_motions(search_term) - Find EDMs by topic
252
+ - edms_for_member_id(member_id) - EDMs signed/sponsored by a member
253
+
254
+ ### Oral Questions
255
+ - search_oral_question_times(answering_date_start, answering_date_end) - Find question sessions
256
+
257
+ ### About EDMs
258
+ Early Day Motions are formal notices of a motion:
259
+ - Used to draw attention to issues
260
+ - MPs can sign EDMs to show support
261
+ - Rarely debated but show parliamentary sentiment
262
+
263
+ ### About Oral Questions
264
+ Question Time sessions where MPs/Lords ask ministers:
265
+ - Prime Minister's Questions (PMQs) - Wednesdays
266
+ - Departmental questions - rotating schedule
267
+ - Urgent Questions - same-day topical issues""",
268
+ "interests": """## Register of Interests Tools (3 tools)
269
+
270
+ ### Search & Browse
271
+ - search_roi(member_id) - Get member's registered interests
272
+ - interests_categories() - List interest categories
273
+ - get_registers_of_interests() - Available registers
274
+
275
+ ### About Register of Interests
276
+ MPs and Lords must declare:
277
+ - Employment and earnings
278
+ - Donations and gifts
279
+ - Shareholdings
280
+ - Property
281
+ - Family members' interests
282
+
283
+ ### Interest Categories
284
+ 1. Employment and earnings
285
+ 2. Donations (property, goods, services)
286
+ 3. Gifts, benefits, hospitality
287
+ 4. Visits outside UK
288
+ 5. Gifts and benefits from UK sources
289
+ 6. Land and property
290
+ 7. Shareholdings
291
+ 8. Miscellaneous
292
+ 9. Family members employed
293
+ 10. Family members with interests
294
+
295
+ ### Typical Workflow
296
+ 1. Find member: get_member_by_name("Name") -> member_id
297
+ 2. Get interests: search_roi(member_id) -> declared interests
298
+ 3. Understand categories: interests_categories() -> what each means""",
299
+ "live": """## Live Activity & Calendar Tools (5 tools)
300
+
301
+ ### What's Happening Now
302
+ - happening_now_in_commons() - Current Commons chamber activity
303
+ - happening_now_in_lords() - Current Lords chamber activity
304
+
305
+ ### Calendar & Schedule
306
+ - search_calendar(house, start_date, end_date) - Find scheduled events (house: 'Commons' or 'Lords')
307
+ - get_sessions() - List of parliamentary sessions
308
+ - get_non_sitting_days(house, start_date, end_date) - Recesses, bank holidays, etc.
309
+
310
+ ### Parameters
311
+ - Dates: YYYY-MM-DD format
312
+ - house: 1 = Commons, 2 = Lords (optional)
313
+
314
+ ### What "Now" Tools Return
315
+ - Current business being debated
316
+ - Speaker/chair information
317
+ - Estimated timing
318
+ - Link to live stream
319
+
320
+ ### Tips
321
+ - "Now" tools only work when Parliament is sitting
322
+ - Check get_non_sitting_days() for recess periods
323
+ - Calendar includes debates, questions, legislation""",
324
+ "legislation": """## Legislation Tools (3 tools)
325
+
326
+ ### Statutory Instruments
327
+ - search_statutory_instruments(search_term, skip, take) - Find SIs
328
+ - get_statutory_instruments_business_items(statutory_instrument_id) - SI progress
329
+
330
+ ### Treaties
331
+ - search_treaties(search_text, skip, take) - Find international treaties
332
+
333
+ ### About Statutory Instruments
334
+ SIs are secondary/delegated legislation:
335
+ - Made under powers in primary Acts
336
+ - Subject to affirmative or negative procedure
337
+ - Cover detailed regulations
338
+
339
+ ### About Treaties
340
+ International agreements requiring parliamentary scrutiny:
341
+ - Trade agreements
342
+ - Extradition treaties
343
+ - Mutual legal assistance
344
+ - Environmental agreements
345
+
346
+ ### Typical Workflow
347
+ 1. Search: search_statutory_instruments("environment")
348
+ 2. Details: get_statutory_instruments_business_items(si_id) -> parliamentary progress""",
349
+ "procedures": """## Procedure Tools (3 tools)
350
+
351
+ ### Erskine May
352
+ - search_erskine_may(search_term) - Search parliamentary procedure manual
353
+
354
+ ### Bill Reference
355
+ - bill_types() - Types of bills (from bills module)
356
+ - bill_stages() - Stage definitions (from bills module)
357
+
358
+ ### About Erskine May
359
+ "Parliamentary Practice" - the authoritative guide to:
360
+ - House procedures and rules
361
+ - Precedents and conventions
362
+ - Powers and privileges
363
+ - Committee procedures
364
+
365
+ ### Bill Types
366
+ - Public Bills: Change general law
367
+ - Private Bills: Affect specific individuals/organizations
368
+ - Hybrid Bills: Mix of public and private
369
+ - Private Members' Bills: Introduced by backbenchers
370
+
371
+ ### Bill Stages
372
+ 1. First Reading: Formal introduction
373
+ 2. Second Reading: Debate on principles
374
+ 3. Committee Stage: Line-by-line scrutiny
375
+ 4. Report Stage: House considers amendments
376
+ 5. Third Reading: Final debate
377
+ 6. Lords/Commons stages: Mirror process in other House
378
+ 7. Royal Assent: Becomes law""",
379
+ "all": """## All UK Parliament MCP Tools (92 tools)
380
+
381
+ ### Composite (4 tools) - Use These First!
382
+ get_mp_profile, check_mp_vote, get_bill_overview, get_committee_summary
383
+
384
+ ### Members (25 tools)
385
+ Search: search_members, get_member_by_name, search_members_historical
386
+ Details: get_member_by_id, get_members_biography, get_members_contact, get_member_synopsis, get_member_experience, get_member_focus
387
+ Activity: get_member_voting, get_commons_voting_record_for_member, get_lords_voting_record_for_member, get_member_written_questions, get_contributions, edms_for_member_id
388
+ Interests: get_member_registered_interests, get_member_staff, get_lords_interests_staff
389
+ Electoral: get_member_latest_election_result, get_constituencies, get_election_results_for_constituency
390
+ Reference: parties_list_by_house, get_departments, get_answering_bodies
391
+ History: get_members_history
392
+ Images: get_member_portrait_url, get_member_thumbnail_url
393
+
394
+ ### Bills (21 tools)
395
+ Search: search_bills, get_recently_updated_bills, get_bill_by_id, bill_types
396
+ Stages: get_bill_stages, bill_stages, get_bill_stage_details, get_sittings
397
+ Amendments: get_bill_stage_amendments, get_amendment_by_id
398
+ Ping-pong: get_bill_stage_ping_pong_items, get_ping_pong_item_by_id
399
+ Publications: get_bill_publications, get_bill_stage_publications, get_publication_document, get_publication_types, get_bill_news_articles
400
+ RSS: get_all_bills_rss, get_public_bills_rss, get_private_bills_rss, get_bill_rss
401
+
402
+ ### Votes (10 tools)
403
+ Commons: search_commons_divisions, get_commons_division_by_id, get_commons_voting_record_for_member, get_commons_divisions_grouped_by_party, get_commons_divisions_search_count
404
+ Lords: search_lords_divisions, get_lords_division_by_id, get_lords_voting_record_for_member, get_lords_divisions_grouped_by_party, get_lords_divisions_search_count
405
+
406
+ ### Committees (12 tools)
407
+ Search: search_committees, get_committee_by_id, get_committee_types
408
+ Meetings: get_committee_meetings, get_events, get_event_by_id, get_committee_events
409
+ Members: get_committee_members
410
+ Evidence: get_oral_evidence, get_written_evidence
411
+ Publications: get_publications, get_publication_by_id
412
+
413
+ ### Other Tools (22 tools)
414
+ Hansard: search_hansard
415
+ Questions: get_recently_tabled_edms, search_early_day_motions, search_oral_question_times
416
+ Interests: search_roi, interests_categories, get_registers_of_interests
417
+ Live: happening_now_in_commons, happening_now_in_lords
418
+ Calendar: search_calendar, get_sessions, get_non_sitting_days
419
+ Legislation: search_statutory_instruments, get_statutory_instruments_business_items, search_treaties
420
+ Procedures: search_erskine_may
421
+ Session: hello_parliament, goodbye_parliament, parliament_guide, parliament_workflow""",
422
+ "conventions": """## UK Parliament MCP Conventions
423
+
424
+ ### House Identification
425
+ - House 1 = House of Commons (MPs)
426
+ - House 2 = House of Lords (Lords/Peers)
427
+ - Some tools accept house as integer, others as string ("Commons"/"Lords")
428
+
429
+ ### Date Format
430
+ - Always use YYYY-MM-DD (e.g., "2024-03-15")
431
+ - Date ranges: from_date/start_date and to_date/end_date
432
+
433
+ ### Pagination
434
+ - skip: Number of records to skip (default: 0)
435
+ - take: Number of records to return (typical: 20-30, max: 100)
436
+ - Use skip/take for paging through large result sets
437
+
438
+ ### IDs
439
+ - member_id: Unique identifier for MPs and Lords
440
+ - bill_id: Unique identifier for legislation
441
+ - division_id: Unique identifier for votes
442
+ - committee_id: Unique identifier for committees
443
+ - Always get IDs from search results first, then use in detail tools
444
+
445
+ ### Response Format
446
+ - All tools return JSON from Parliament APIs
447
+ - Successful: {"url": "...", "data": {...}}
448
+ - Error: {"url": "...", "error": "...", "statusCode": N}
449
+
450
+ ### Naming Conventions
451
+ - search_* : Search/discovery tools
452
+ - get_* : Retrieve specific item by ID or parameters
453
+ - *_by_id : Requires specific ID parameter
454
+ - *_for_member : Requires member_id parameter""",
455
+ "workflows": """## Common Research Workflows
456
+
457
+ ### MP Voting Research
458
+ Goal: Find how an MP voted on a topic
459
+ 1. get_member_by_name(name) -> member_id
460
+ 2. search_commons_divisions(topic) -> find relevant division_id
461
+ 3. get_commons_division_by_id(division_id) -> check Ayes/Noes lists
462
+
463
+ ### Bill Tracking
464
+ Goal: Track a bill's progress through Parliament
465
+ 1. search_bills(topic) -> bill_id
466
+ 2. get_bill_by_id(bill_id) -> current status
467
+ 3. get_bill_stages(bill_id) -> full journey
468
+ 4. get_bill_amendments(bill_id) -> proposed changes
469
+
470
+ ### Committee Investigation
471
+ Goal: Find what a committee examined
472
+ 1. search_committees(topic) -> committee_id
473
+ 2. get_committee_by_id(committee_id) -> scope
474
+ 3. get_oral_evidence(committee_id) -> witness testimony
475
+ 4. get_committee_publications(committee_id) -> reports
476
+
477
+ ### Conflict of Interest Research
478
+ Goal: Check MP's financial interests
479
+ 1. get_member_by_name(name) -> member_id
480
+ 2. search_roi(member_id) -> declared interests
481
+ 3. interests_categories() -> understand categories
482
+
483
+ ### Live Parliament Activity
484
+ Goal: See what's happening now
485
+ 1. happening_now_in_commons() or happening_now_in_lords()
486
+ 2. search_calendar(today, today) -> today's schedule
487
+
488
+ ### Historical Voting
489
+ Goal: Research past votes on topic
490
+ 1. search_commons_divisions(topic) -> list of votes
491
+ 2. get_commons_division_by_id(division_id) -> full results
492
+ 3. Repeat for key divisions
493
+
494
+ ### MP Background Research
495
+ Goal: Full profile of an MP
496
+ 1. get_member_by_name(name) -> member_id
497
+ 2. get_member_by_id(member_id) -> basic info
498
+ 3. get_members_biography(member_id) -> background
499
+ 4. get_member_registered_interests(member_id, 1) -> interests
500
+ 5. get_commons_voting_record_for_member(member_id) -> votes
501
+
502
+ ### Hansard Search
503
+ Goal: Find what was said in Parliament
504
+ 1. search_hansard(topic, house) -> speeches and debates
505
+ 2. Filter by date, speaker, or house
506
+
507
+ ### Electoral Research
508
+ Goal: Constituency election history
509
+ 1. get_constituencies() -> find constituency_id
510
+ 2. get_election_results_for_constituency(constituency_id) -> all results
511
+
512
+ ### Legislation Search
513
+ Goal: Find regulations and treaties
514
+ 1. search_statutory_instruments(topic) -> SIs
515
+ 2. search_treaties(topic) -> international agreements""",
516
+ }
517
+
518
+ WORKFLOW_PATTERNS: list[dict[str, Any]] = [
519
+ {
520
+ "keywords": ["vote", "voted", "voting", "division", "aye", "noes"],
521
+ "name": "MP Voting Research",
522
+ "description": "Find how an MP voted on a specific topic or bill",
523
+ "steps": [
524
+ {
525
+ "step": 1,
526
+ "tool": "get_member_by_name(name)",
527
+ "purpose": "Get the member_id for the MP",
528
+ "output": "member_id (integer)",
529
+ },
530
+ {
531
+ "step": 2,
532
+ "tool": "search_commons_divisions(search_term)",
533
+ "purpose": "Find divisions (votes) on the topic",
534
+ "output": "List of divisions with division_id",
535
+ },
536
+ {
537
+ "step": 3,
538
+ "tool": "get_commons_division_by_id(division_id)",
539
+ "purpose": "Get full voting list including how members voted",
540
+ "output": "Aye and No lists with member names",
541
+ },
542
+ ],
543
+ "alternative": "Use get_commons_voting_record_for_member(member_id) for all votes by an MP",
544
+ },
545
+ {
546
+ "keywords": ["bill", "legislation", "law", "act", "progress", "stage"],
547
+ "name": "Bill Tracking",
548
+ "description": "Track a bill's progress through Parliament",
549
+ "steps": [
550
+ {
551
+ "step": 1,
552
+ "tool": "search_bills(search_term)",
553
+ "purpose": "Find the bill by keyword",
554
+ "output": "bill_id and current status",
555
+ },
556
+ {
557
+ "step": 2,
558
+ "tool": "get_bill_by_id(bill_id)",
559
+ "purpose": "Get full bill details",
560
+ "output": "Title, summary, sponsors, current stage",
561
+ },
562
+ {
563
+ "step": 3,
564
+ "tool": "get_bill_stages(bill_id)",
565
+ "purpose": "See the legislative journey",
566
+ "output": "All stages with dates and houses",
567
+ },
568
+ {
569
+ "step": 4,
570
+ "tool": "get_bill_amendments(bill_id)",
571
+ "purpose": "See proposed changes",
572
+ "output": "List of amendments with sponsors",
573
+ },
574
+ ],
575
+ },
576
+ {
577
+ "keywords": ["committee", "inquiry", "evidence", "witness", "hearing"],
578
+ "name": "Committee Research",
579
+ "description": "Find what a committee examined on a topic",
580
+ "steps": [
581
+ {
582
+ "step": 1,
583
+ "tool": "search_committees(search_term)",
584
+ "purpose": "Find relevant committee",
585
+ "output": "committee_id",
586
+ },
587
+ {
588
+ "step": 2,
589
+ "tool": "get_committee_by_id(committee_id)",
590
+ "purpose": "Get committee details and scope",
591
+ "output": "Committee info and current inquiries",
592
+ },
593
+ {
594
+ "step": 3,
595
+ "tool": "get_oral_evidence(committee_id)",
596
+ "purpose": "Find witness testimonies",
597
+ "output": "Oral evidence transcripts",
598
+ },
599
+ {
600
+ "step": 4,
601
+ "tool": "get_committee_publications(committee_id)",
602
+ "purpose": "Find reports and conclusions",
603
+ "output": "Published reports",
604
+ },
605
+ ],
606
+ },
607
+ {
608
+ "keywords": ["interest", "conflict", "financial", "donation", "declare"],
609
+ "name": "Interests/Conflicts Research",
610
+ "description": "Check an MP's declared financial interests",
611
+ "steps": [
612
+ {
613
+ "step": 1,
614
+ "tool": "get_member_by_name(name)",
615
+ "purpose": "Get the member_id",
616
+ "output": "member_id",
617
+ },
618
+ {
619
+ "step": 2,
620
+ "tool": "search_roi(member_id)",
621
+ "purpose": "Get all declared interests",
622
+ "output": "List of registered interests",
623
+ },
624
+ {
625
+ "step": 3,
626
+ "tool": "interests_categories()",
627
+ "purpose": "Understand what each category means",
628
+ "output": "Category definitions",
629
+ },
630
+ ],
631
+ },
632
+ {
633
+ "keywords": ["now", "today", "happening", "live", "current", "sitting"],
634
+ "name": "Live Parliament Activity",
635
+ "description": "See what's happening in Parliament right now",
636
+ "steps": [
637
+ {
638
+ "step": 1,
639
+ "tool": "happening_now_in_commons() or happening_now_in_lords()",
640
+ "purpose": "Get current chamber activity",
641
+ "output": "Current business, speaker, timing",
642
+ },
643
+ {
644
+ "step": 2,
645
+ "tool": "search_calendar(today_date, today_date)",
646
+ "purpose": "See full day's schedule",
647
+ "output": "All scheduled events for today",
648
+ },
649
+ ],
650
+ "note": "Only works when Parliament is sitting - check get_non_sitting_days() for recesses",
651
+ },
652
+ {
653
+ "keywords": ["background", "biography", "profile", "who is", "about"],
654
+ "name": "MP Background Research",
655
+ "description": "Get comprehensive information about an MP",
656
+ "steps": [
657
+ {
658
+ "step": 1,
659
+ "tool": "get_member_by_name(name)",
660
+ "purpose": "Find the member",
661
+ "output": "member_id and basic info",
662
+ },
663
+ {
664
+ "step": 2,
665
+ "tool": "get_member_by_id(member_id)",
666
+ "purpose": "Get full profile",
667
+ "output": "Party, constituency, status",
668
+ },
669
+ {
670
+ "step": 3,
671
+ "tool": "get_members_biography(member_id)",
672
+ "purpose": "Get background",
673
+ "output": "Education, career, personal",
674
+ },
675
+ {
676
+ "step": 4,
677
+ "tool": "get_member_registered_interests(member_id, house=1)",
678
+ "purpose": "Get declared interests",
679
+ "output": "Financial interests",
680
+ },
681
+ ],
682
+ },
683
+ {
684
+ "keywords": ["said", "speech", "debate", "hansard", "parliament said"],
685
+ "name": "Hansard Search",
686
+ "description": "Find what was said in Parliament about a topic",
687
+ "steps": [
688
+ {
689
+ "step": 1,
690
+ "tool": "search_hansard(search_term, house)",
691
+ "purpose": "Search parliamentary record",
692
+ "output": "Speeches and debates matching topic",
693
+ },
694
+ ],
695
+ "tips": [
696
+ "Use specific phrases for better results",
697
+ "Filter by house (1=Commons, 2=Lords) if needed",
698
+ "Results include speaker, date, and context",
699
+ ],
700
+ },
701
+ {
702
+ "keywords": ["election", "constituency", "result", "majority", "swing"],
703
+ "name": "Electoral Research",
704
+ "description": "Find election results for a constituency",
705
+ "steps": [
706
+ {
707
+ "step": 1,
708
+ "tool": "get_constituencies()",
709
+ "purpose": "Find constituency ID",
710
+ "output": "List of constituencies with IDs",
711
+ },
712
+ {
713
+ "step": 2,
714
+ "tool": "get_election_results_for_constituency(constituency_id)",
715
+ "purpose": "Get historical results",
716
+ "output": "All election results with votes and majorities",
717
+ },
718
+ ],
719
+ },
720
+ {
721
+ "keywords": ["edm", "early day motion", "support", "signed"],
722
+ "name": "Early Day Motion Research",
723
+ "description": "Find EDMs on a topic or signed by an MP",
724
+ "steps": [
725
+ {
726
+ "step": 1,
727
+ "tool": "search_early_day_motions(search_term)",
728
+ "purpose": "Find EDMs by topic",
729
+ "output": "List of EDMs with signatories",
730
+ },
731
+ ],
732
+ "alternative": "Use edms_for_member_id(member_id) to find EDMs signed by a specific MP",
733
+ },
734
+ {
735
+ "keywords": ["treaty", "international", "agreement", "trade deal"],
736
+ "name": "Treaty Research",
737
+ "description": "Find international treaties and agreements",
738
+ "steps": [
739
+ {
740
+ "step": 1,
741
+ "tool": "search_treaties(search_text)",
742
+ "purpose": "Find treaties by keyword",
743
+ "output": "Treaty details and status",
744
+ },
745
+ ],
746
+ },
747
+ ]
748
+
749
+
750
+ def _format_workflow(pattern: dict[str, Any]) -> str:
751
+ """Format a workflow pattern into readable text."""
752
+ lines = [
753
+ f"## Workflow: {pattern['name']}",
754
+ "",
755
+ pattern["description"],
756
+ "",
757
+ "### Steps",
758
+ ]
759
+
760
+ for step in pattern["steps"]:
761
+ lines.append(f"**Step {step['step']}**: `{step['tool']}`")
762
+ lines.append(f"- Purpose: {step['purpose']}")
763
+ lines.append(f"- Output: {step['output']}")
764
+ lines.append("")
765
+
766
+ if "alternative" in pattern:
767
+ lines.append(f"**Alternative**: {pattern['alternative']}")
768
+ lines.append("")
769
+
770
+ if "note" in pattern:
771
+ lines.append(f"**Note**: {pattern['note']}")
772
+ lines.append("")
773
+
774
+ if "tips" in pattern:
775
+ lines.append("**Tips**:")
776
+ for tip in pattern["tips"]:
777
+ lines.append(f"- {tip}")
778
+ lines.append("")
779
+
780
+ return "\n".join(lines)
781
+
782
+
783
+ def _suggest_general_approach(query: str) -> str:
784
+ """Suggest a general approach when no specific workflow matches."""
785
+ return f"""## Research Approach for: "{query}"
786
+
787
+ No specific workflow pattern matched. Here's a general approach:
788
+
789
+ ### Step 1: Identify What You Need
790
+ - **People**: Use get_member_by_name() to find MPs/Lords
791
+ - **Legislation**: Use search_bills() to find bills
792
+ - **Votes**: Use search_commons_divisions() or search_lords_divisions()
793
+ - **Committees**: Use search_committees()
794
+ - **Debates**: Use search_hansard()
795
+
796
+ ### Step 2: Get IDs from Search Results
797
+ Most detail tools require an ID (member_id, bill_id, etc.) from search results.
798
+
799
+ ### Step 3: Drill Down
800
+ Use specific tools like get_member_by_id(), get_bill_stages(), etc.
801
+
802
+ ### Available Topics for Detailed Guidance
803
+ Use parliament_guide(topic) with one of:
804
+ - members, bills, votes, committees, hansard
805
+ - questions, interests, live, legislation, procedures
806
+ - all, conventions, workflows"""
807
+
808
+
809
+ def register_tools(mcp: FastMCP) -> None:
810
+ """Register core tools with the MCP server."""
811
+
812
+ @mcp.tool()
813
+ async def hello_parliament() -> str:
814
+ """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"""
815
+ return f"{SYSTEM_PROMPT}\n\n---\n\n{QUICK_REFERENCE}"
816
+
817
+ @mcp.tool()
818
+ async def goodbye_parliament() -> str:
819
+ """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"""
820
+ return GOODBYE_PROMPT
821
+
822
+ @mcp.tool()
823
+ async def parliament_guide(topic: str) -> str:
824
+ """Get detailed guidance for a specific Parliament data domain | tool help, API reference, how to use tools, available tools | Use when you need detailed information about tools in a specific area | Returns comprehensive guidance including tool names, parameters, and typical workflows
825
+
826
+ Args:
827
+ topic: Domain to get guidance for. Options: members, bills, votes, committees, hansard, questions, interests, live, legislation, procedures, all, conventions, workflows
828
+ """
829
+ topic_lower = topic.lower().strip()
830
+
831
+ if topic_lower not in GUIDANCE_CONTENT:
832
+ available = ", ".join(sorted(GUIDANCE_CONTENT.keys()))
833
+ return f"Topic '{topic}' not recognized.\n\nAvailable topics: {available}"
834
+
835
+ return GUIDANCE_CONTENT[topic_lower]
836
+
837
+ @mcp.tool()
838
+ async def parliament_workflow(query: str) -> str:
839
+ """Get step-by-step workflow guidance for a parliamentary research task | workflow planning, multi-step tasks, research planning, how to find | Use when you have a specific research question and need to plan which tools to use in sequence | Returns recommended sequence of tools with parameters and expected data flow
840
+
841
+ Args:
842
+ query: Research question or task description. Examples: "How did my MP vote on climate?", "Track a bill's progress", "Find committee evidence on NHS"
843
+ """
844
+ query_lower = query.lower()
845
+
846
+ # Find matching workflow pattern
847
+ for pattern in WORKFLOW_PATTERNS:
848
+ if any(keyword in query_lower for keyword in pattern["keywords"]):
849
+ return _format_workflow(pattern)
850
+
851
+ # No match - provide general guidance
852
+ return _suggest_general_approach(query)
853
+
854
+
855
+ def register_prompts(mcp: FastMCP) -> None:
856
+ """Register MCP prompts (agent skills) with the server."""
857
+
858
+ @mcp.prompt()
859
+ async def parliament(topic: str | None = None) -> str:
860
+ """Initialize UK Parliament research session with guidance on 86 available tools.
861
+
862
+ Provides system instructions for parliamentary data queries, quick reference
863
+ of tool categories, and guidance on common research workflows.
864
+
865
+ Use this prompt to start a parliamentary research session. Optionally specify
866
+ a topic to get detailed guidance for that domain.
867
+
868
+ Args:
869
+ topic: Optional topic for detailed guidance (members, bills, votes,
870
+ committees, hansard, questions, interests, live, legislation,
871
+ procedures, all, conventions, workflows)
872
+ """
873
+ base_content = f"{SYSTEM_PROMPT}\n\n---\n\n{QUICK_REFERENCE}"
874
+
875
+ if topic:
876
+ topic_lower = topic.lower().strip()
877
+ if topic_lower in GUIDANCE_CONTENT:
878
+ return f"{base_content}\n\n---\n\n{GUIDANCE_CONTENT[topic_lower]}"
879
+
880
+ return base_content