uk-parliament-mcp 1.0.1__py3-none-any.whl → 1.2.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 +1 -1
- uk_parliament_mcp/config.py +21 -0
- uk_parliament_mcp/http_client.py +78 -1
- uk_parliament_mcp/server.py +3 -1
- uk_parliament_mcp/tools/bills.py +1 -2
- uk_parliament_mcp/tools/committees.py +1 -2
- uk_parliament_mcp/tools/commons_votes.py +1 -2
- uk_parliament_mcp/tools/composite.py +9 -8
- uk_parliament_mcp/tools/core.py +5 -5
- uk_parliament_mcp/tools/erskine_may.py +1 -2
- uk_parliament_mcp/tools/hansard.py +134 -2
- uk_parliament_mcp/tools/interests.py +1 -2
- uk_parliament_mcp/tools/lords_votes.py +1 -2
- uk_parliament_mcp/tools/members.py +1 -2
- uk_parliament_mcp/tools/now.py +1 -2
- uk_parliament_mcp/tools/oral_questions.py +1 -2
- uk_parliament_mcp/tools/statutory_instruments.py +1 -2
- uk_parliament_mcp/tools/treaties.py +1 -2
- uk_parliament_mcp/tools/whatson.py +1 -2
- uk_parliament_mcp/tools/written_questions.py +213 -0
- uk_parliament_mcp/validators.py +58 -0
- uk_parliament_mcp-1.2.0.dist-info/METADATA +436 -0
- uk_parliament_mcp-1.2.0.dist-info/RECORD +27 -0
- uk_parliament_mcp-1.0.1.dist-info/METADATA +0 -348
- uk_parliament_mcp-1.0.1.dist-info/RECORD +0 -24
- {uk_parliament_mcp-1.0.1.dist-info → uk_parliament_mcp-1.2.0.dist-info}/WHEEL +0 -0
- {uk_parliament_mcp-1.0.1.dist-info → uk_parliament_mcp-1.2.0.dist-info}/entry_points.txt +0 -0
uk_parliament_mcp/__init__.py
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Centralized configuration for UK Parliament MCP Server."""
|
|
2
|
+
|
|
3
|
+
# API Base URLs
|
|
4
|
+
MEMBERS_API_BASE = "https://members-api.parliament.uk/api"
|
|
5
|
+
BILLS_API_BASE = "https://bills-api.parliament.uk/api/v1"
|
|
6
|
+
COMMONS_VOTES_API_BASE = "http://commonsvotes-api.parliament.uk/data"
|
|
7
|
+
LORDS_VOTES_API_BASE = "https://lordsvotes-api.parliament.uk/data"
|
|
8
|
+
COMMITTEES_API_BASE = "https://committees-api.parliament.uk/api"
|
|
9
|
+
HANSARD_API_BASE = "https://hansard-api.parliament.uk/api/v1"
|
|
10
|
+
INTERESTS_API_BASE = "https://interests-api.parliament.uk/api/v1"
|
|
11
|
+
NOW_API_BASE = "https://now-api.parliament.uk/api"
|
|
12
|
+
WHATSON_API_BASE = "https://whatson-api.parliament.uk/calendar"
|
|
13
|
+
STATUTORY_INSTRUMENTS_API_BASE = "https://statutoryinstruments-api.parliament.uk/api/v2"
|
|
14
|
+
TREATIES_API_BASE = "https://treaties-api.parliament.uk/api"
|
|
15
|
+
ERSKINE_MAY_API_BASE = "https://erskinemay-api.parliament.uk/api"
|
|
16
|
+
ORAL_QUESTIONS_API_BASE = "https://oralquestionsandmotions-api.parliament.uk"
|
|
17
|
+
WRITTEN_QUESTIONS_API_BASE = "https://writtenquestions-api.parliament.uk/api"
|
|
18
|
+
|
|
19
|
+
# Common constants
|
|
20
|
+
HOUSE_COMMONS = 1
|
|
21
|
+
HOUSE_LORDS = 2
|
uk_parliament_mcp/http_client.py
CHANGED
|
@@ -5,13 +5,42 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json
|
|
7
7
|
import logging
|
|
8
|
-
from
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from typing import Any, NotRequired, TypedDict
|
|
9
10
|
from urllib.parse import urlencode
|
|
10
11
|
|
|
11
12
|
import httpx
|
|
12
13
|
|
|
13
14
|
logger = logging.getLogger(__name__)
|
|
14
15
|
|
|
16
|
+
|
|
17
|
+
# Response type definitions
|
|
18
|
+
class SuccessResponse(TypedDict):
|
|
19
|
+
"""Response structure for successful API calls."""
|
|
20
|
+
|
|
21
|
+
url: str
|
|
22
|
+
data: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ErrorResponse(TypedDict):
|
|
26
|
+
"""Response structure for failed API calls."""
|
|
27
|
+
|
|
28
|
+
url: str
|
|
29
|
+
error: str
|
|
30
|
+
statusCode: NotRequired[int]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Union type for API responses
|
|
34
|
+
APIResponse = SuccessResponse | ErrorResponse
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CacheEntry(TypedDict):
|
|
38
|
+
"""Cache entry structure for storing API responses."""
|
|
39
|
+
|
|
40
|
+
data: str
|
|
41
|
+
expires: datetime
|
|
42
|
+
|
|
43
|
+
|
|
15
44
|
# Configuration constants (matching C# implementation)
|
|
16
45
|
HTTP_TIMEOUT = 30.0 # seconds
|
|
17
46
|
MAX_RETRY_ATTEMPTS = 3
|
|
@@ -20,6 +49,10 @@ RETRY_DELAY_BASE = 1.0 # seconds
|
|
|
20
49
|
# HTTP status codes that should trigger a retry
|
|
21
50
|
TRANSIENT_STATUS_CODES = frozenset({408, 429, 500, 502, 503, 504})
|
|
22
51
|
|
|
52
|
+
# Cache configuration
|
|
53
|
+
CACHE_TTL = timedelta(minutes=15)
|
|
54
|
+
_cache: dict[str, CacheEntry] = {}
|
|
55
|
+
|
|
23
56
|
|
|
24
57
|
def build_url(base_url: str, parameters: dict[str, Any]) -> str:
|
|
25
58
|
"""
|
|
@@ -158,3 +191,47 @@ _client = ParliamentHTTPClient()
|
|
|
158
191
|
async def get_result(url: str) -> str:
|
|
159
192
|
"""Convenience function using global client."""
|
|
160
193
|
return await _client.get_result(url)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async def get_result_cached(url: str, cache_key: str | None = None) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Get result with optional caching for reference data.
|
|
199
|
+
|
|
200
|
+
Use this for frequently accessed reference data that rarely changes,
|
|
201
|
+
such as bill types, bill stages, committee types, etc.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
url: API URL to fetch
|
|
205
|
+
cache_key: Optional cache key. If None, no caching is used.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
JSON response string in the same format as get_result()
|
|
209
|
+
"""
|
|
210
|
+
key = cache_key or url
|
|
211
|
+
|
|
212
|
+
# Check cache
|
|
213
|
+
if key in _cache:
|
|
214
|
+
entry = _cache[key]
|
|
215
|
+
if datetime.now() < entry["expires"]:
|
|
216
|
+
logger.debug("Cache hit for %s", key)
|
|
217
|
+
return entry["data"]
|
|
218
|
+
else:
|
|
219
|
+
# Expired entry, remove it
|
|
220
|
+
del _cache[key]
|
|
221
|
+
logger.debug("Cache expired for %s", key)
|
|
222
|
+
|
|
223
|
+
# Fetch fresh data
|
|
224
|
+
result = await get_result(url)
|
|
225
|
+
|
|
226
|
+
# Cache if successful and cache_key provided
|
|
227
|
+
if cache_key and '"error"' not in result:
|
|
228
|
+
_cache[key] = {"data": result, "expires": datetime.now() + CACHE_TTL}
|
|
229
|
+
logger.debug("Cached result for %s", key)
|
|
230
|
+
|
|
231
|
+
return result
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def clear_cache() -> None:
|
|
235
|
+
"""Clear all cached data."""
|
|
236
|
+
_cache.clear()
|
|
237
|
+
logger.debug("Cache cleared")
|
uk_parliament_mcp/server.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server.fastmcp import FastMCP
|
|
4
4
|
|
|
5
|
-
from uk_parliament_mcp.tools.core import SYSTEM_PROMPT
|
|
6
5
|
from uk_parliament_mcp.tools import (
|
|
7
6
|
bills,
|
|
8
7
|
committees,
|
|
@@ -19,7 +18,9 @@ from uk_parliament_mcp.tools import (
|
|
|
19
18
|
statutory_instruments,
|
|
20
19
|
treaties,
|
|
21
20
|
whatson,
|
|
21
|
+
written_questions,
|
|
22
22
|
)
|
|
23
|
+
from uk_parliament_mcp.tools.core import SYSTEM_PROMPT
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def create_server() -> FastMCP:
|
|
@@ -42,6 +43,7 @@ def create_server() -> FastMCP:
|
|
|
42
43
|
statutory_instruments.register_tools(mcp)
|
|
43
44
|
treaties.register_tools(mcp)
|
|
44
45
|
erskine_may.register_tools(mcp)
|
|
46
|
+
written_questions.register_tools(mcp)
|
|
45
47
|
|
|
46
48
|
# Register prompts (agent skills)
|
|
47
49
|
core.register_prompts(mcp)
|
uk_parliament_mcp/tools/bills.py
CHANGED
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import BILLS_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
8
9
|
|
|
9
|
-
BILLS_API_BASE = "https://bills-api.parliament.uk/api/v1"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register bills tools with the MCP server."""
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import COMMITTEES_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
8
9
|
|
|
9
|
-
COMMITTEES_API_BASE = "https://committees-api.parliament.uk/api"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register committees tools with the MCP server."""
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server.fastmcp import FastMCP
|
|
4
4
|
|
|
5
|
+
from uk_parliament_mcp.config import COMMONS_VOTES_API_BASE
|
|
5
6
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
6
7
|
|
|
7
|
-
COMMONS_VOTES_API_BASE = "http://commonsvotes-api.parliament.uk/data"
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
def register_tools(mcp: FastMCP) -> None:
|
|
11
10
|
"""Register Commons votes tools with the MCP server."""
|
|
@@ -8,15 +8,15 @@ from typing import Any
|
|
|
8
8
|
|
|
9
9
|
from mcp.server.fastmcp import FastMCP
|
|
10
10
|
|
|
11
|
+
from uk_parliament_mcp.config import (
|
|
12
|
+
BILLS_API_BASE,
|
|
13
|
+
COMMITTEES_API_BASE,
|
|
14
|
+
COMMONS_VOTES_API_BASE,
|
|
15
|
+
INTERESTS_API_BASE,
|
|
16
|
+
MEMBERS_API_BASE,
|
|
17
|
+
)
|
|
11
18
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
12
19
|
|
|
13
|
-
# API bases
|
|
14
|
-
MEMBERS_API_BASE = "https://members-api.parliament.uk/api"
|
|
15
|
-
BILLS_API_BASE = "https://bills-api.parliament.uk/api/v1"
|
|
16
|
-
COMMONS_VOTES_API_BASE = "http://commonsvotes-api.parliament.uk/data"
|
|
17
|
-
COMMITTEES_API_BASE = "https://committees-api.parliament.uk/api"
|
|
18
|
-
INTERESTS_API_BASE = "https://interests-api.parliament.uk/api/v1"
|
|
19
|
-
|
|
20
20
|
|
|
21
21
|
def _parse_response(response: str) -> dict[str, Any]:
|
|
22
22
|
"""Parse JSON response and extract data."""
|
|
@@ -75,7 +75,8 @@ def register_tools(mcp: FastMCP) -> None:
|
|
|
75
75
|
|
|
76
76
|
# Get basic info from search result
|
|
77
77
|
basic_info = member_data.get("items", [{}])[0].get("value", {})
|
|
78
|
-
|
|
78
|
+
latest_membership = basic_info.get("latestHouseMembership") or {}
|
|
79
|
+
house = latest_membership.get("house", 1)
|
|
79
80
|
|
|
80
81
|
# Step 2: Parallel requests for details
|
|
81
82
|
biography_url = f"{MEMBERS_API_BASE}/Members/{member_id}/Biography"
|
uk_parliament_mcp/tools/core.py
CHANGED
|
@@ -16,7 +16,7 @@ Convert raw data into human-readable summaries while preserving accuracy, but al
|
|
|
16
16
|
|
|
17
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
18
|
|
|
19
|
-
QUICK_REFERENCE = """## Quick Reference: UK Parliament MCP Tools (
|
|
19
|
+
QUICK_REFERENCE = """## Quick Reference: UK Parliament MCP Tools (92 tools)
|
|
20
20
|
|
|
21
21
|
### Composite Tools (Start Here for Common Queries!)
|
|
22
22
|
These tools combine multiple API calls - use them first for efficiency:
|
|
@@ -35,7 +35,7 @@ These tools combine multiple API calls - use them first for efficiency:
|
|
|
35
35
|
| Module | Tools | Start With |
|
|
36
36
|
|--------|-------|------------|
|
|
37
37
|
| composite | 4 | get_mp_profile(name) |
|
|
38
|
-
| members |
|
|
38
|
+
| members | 25 | get_member_by_name(name) |
|
|
39
39
|
| bills | 21 | search_bills(search_term) |
|
|
40
40
|
| committees | 12 | search_committees(search_term) |
|
|
41
41
|
| commons_votes | 5 | search_commons_divisions(search_term) |
|
|
@@ -93,7 +93,7 @@ Use the individual tools (in members, bills, etc.) when you need:
|
|
|
93
93
|
- Pagination through large result sets
|
|
94
94
|
- Access to specific endpoints not covered by composite tools
|
|
95
95
|
- More control over which data is fetched""",
|
|
96
|
-
"members": """## Members Tools (
|
|
96
|
+
"members": """## Members Tools (25 tools)
|
|
97
97
|
|
|
98
98
|
### Primary Search Tools
|
|
99
99
|
- search_members(name, location, party_id, house, is_current_member, skip, take) - Comprehensive member search with filters
|
|
@@ -376,12 +376,12 @@ International agreements requiring parliamentary scrutiny:
|
|
|
376
376
|
5. Third Reading: Final debate
|
|
377
377
|
6. Lords/Commons stages: Mirror process in other House
|
|
378
378
|
7. Royal Assent: Becomes law""",
|
|
379
|
-
"all": """## All UK Parliament MCP Tools (
|
|
379
|
+
"all": """## All UK Parliament MCP Tools (92 tools)
|
|
380
380
|
|
|
381
381
|
### Composite (4 tools) - Use These First!
|
|
382
382
|
get_mp_profile, check_mp_vote, get_bill_overview, get_committee_summary
|
|
383
383
|
|
|
384
|
-
### Members (
|
|
384
|
+
### Members (25 tools)
|
|
385
385
|
Search: search_members, get_member_by_name, search_members_historical
|
|
386
386
|
Details: get_member_by_id, get_members_biography, get_members_contact, get_member_synopsis, get_member_experience, get_member_focus
|
|
387
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
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import ERSKINE_MAY_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import get_result
|
|
8
9
|
|
|
9
|
-
ERSKINE_MAY_API_BASE = "https://erskinemay-api.parliament.uk/api"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register Erskine May tools with the MCP server."""
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server.fastmcp import FastMCP
|
|
4
4
|
|
|
5
|
+
from uk_parliament_mcp.config import HANSARD_API_BASE
|
|
5
6
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
6
7
|
|
|
7
|
-
HANSARD_API_BASE = "https://hansard-api.parliament.uk"
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
def register_tools(mcp: FastMCP) -> None:
|
|
11
10
|
"""Register Hansard tools with the MCP server."""
|
|
@@ -16,6 +15,9 @@ def register_tools(mcp: FastMCP) -> None:
|
|
|
16
15
|
start_date: str,
|
|
17
16
|
end_date: str,
|
|
18
17
|
search_term: str,
|
|
18
|
+
member_id: int | None = None,
|
|
19
|
+
skip: int = 0,
|
|
20
|
+
take: int = 20,
|
|
19
21
|
) -> str:
|
|
20
22
|
"""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.
|
|
21
23
|
|
|
@@ -24,6 +26,9 @@ def register_tools(mcp: FastMCP) -> None:
|
|
|
24
26
|
start_date: Start date in YYYY-MM-DD format.
|
|
25
27
|
end_date: End date in YYYY-MM-DD format.
|
|
26
28
|
search_term: Search term for speeches or debates (e.g. 'climate change', 'NHS').
|
|
29
|
+
member_id: Optional member ID to filter results to a specific MP/Lord.
|
|
30
|
+
skip: Number of results to skip for pagination (default 0).
|
|
31
|
+
take: Number of results to return (default 20, max varies by endpoint).
|
|
27
32
|
|
|
28
33
|
Returns:
|
|
29
34
|
Hansard records matching the search criteria.
|
|
@@ -35,6 +40,133 @@ def register_tools(mcp: FastMCP) -> None:
|
|
|
35
40
|
"queryParameters.startDate": start_date,
|
|
36
41
|
"queryParameters.endDate": end_date,
|
|
37
42
|
"queryParameters.searchTerm": search_term,
|
|
43
|
+
"queryParameters.memberId": member_id,
|
|
44
|
+
"queryParameters.skip": skip,
|
|
45
|
+
"queryParameters.take": take,
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
return await get_result(url)
|
|
49
|
+
|
|
50
|
+
@mcp.tool()
|
|
51
|
+
async def get_debate_by_id(debate_section_id: str) -> str:
|
|
52
|
+
"""Get full debate transcript | Hansard, speeches, contributions |
|
|
53
|
+
Use after search_hansard to get complete debate with all member speeches.
|
|
54
|
+
Returns debate title, date, house, and all contributions.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
debate_section_id: External ID from search_hansard results.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Full debate with all member contributions.
|
|
61
|
+
"""
|
|
62
|
+
url = f"{HANSARD_API_BASE}/debates/debate/{debate_section_id}.json"
|
|
63
|
+
return await get_result(url)
|
|
64
|
+
|
|
65
|
+
@mcp.tool()
|
|
66
|
+
async def get_member_hansard_contributions(
|
|
67
|
+
member_id: int,
|
|
68
|
+
debate_section_id: str,
|
|
69
|
+
) -> str:
|
|
70
|
+
"""Get all speeches by a specific MP/Lord in a debate | Hansard, member speeches |
|
|
71
|
+
Use to extract just one member's contributions from a debate.
|
|
72
|
+
Returns all contributions by that member in the specified debate.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
member_id: Parliament member ID (from members API).
|
|
76
|
+
debate_section_id: External ID of debate section (from search_hansard).
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
All contributions by that member in the debate.
|
|
80
|
+
"""
|
|
81
|
+
url = f"{HANSARD_API_BASE}/debates/memberdebatecontributions/{member_id}.json?debateSectionExtId={debate_section_id}"
|
|
82
|
+
return await get_result(url)
|
|
83
|
+
|
|
84
|
+
@mcp.tool()
|
|
85
|
+
async def get_debate_divisions(debate_section_id: str) -> str:
|
|
86
|
+
"""Get votes that occurred during a debate | Hansard, divisions, voting |
|
|
87
|
+
Use to find divisions (votes) that took place in a specific debate.
|
|
88
|
+
Returns list of divisions with aye/noe counts.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
debate_section_id: External ID of debate section (from search_hansard).
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
List of divisions with vote counts.
|
|
95
|
+
"""
|
|
96
|
+
url = f"{HANSARD_API_BASE}/debates/divisions/{debate_section_id}.json"
|
|
97
|
+
return await get_result(url)
|
|
98
|
+
|
|
99
|
+
@mcp.tool()
|
|
100
|
+
async def get_division_details(
|
|
101
|
+
division_id: str,
|
|
102
|
+
is_evel: bool = False,
|
|
103
|
+
) -> str:
|
|
104
|
+
"""Get full division details including how each member voted | Hansard, division, voting records |
|
|
105
|
+
Use to see individual voting records for a specific division.
|
|
106
|
+
Returns division with debate title, counts, and member voting records.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
division_id: External ID of division (from get_debate_divisions).
|
|
110
|
+
is_evel: Filter to EVEL (English Votes for English Laws) voters only.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Division details with member voting records.
|
|
114
|
+
"""
|
|
115
|
+
url = build_url(
|
|
116
|
+
f"{HANSARD_API_BASE}/debates/division/{division_id}.json",
|
|
117
|
+
{"isEvel": is_evel if is_evel else None},
|
|
118
|
+
)
|
|
119
|
+
return await get_result(url)
|
|
120
|
+
|
|
121
|
+
@mcp.tool()
|
|
122
|
+
async def get_hansard_sitting_day(
|
|
123
|
+
sitting_date: str,
|
|
124
|
+
house: int,
|
|
125
|
+
) -> str:
|
|
126
|
+
"""Get full agenda/sections for a sitting day | Hansard, daily business, agenda |
|
|
127
|
+
Use to see all debates and business for a specific day.
|
|
128
|
+
Returns all debate sections for that day.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
sitting_date: Date in YYYY-MM-DD format.
|
|
132
|
+
house: House number: 1 for Commons, 2 for Lords.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
All debate sections for that day.
|
|
136
|
+
"""
|
|
137
|
+
url = build_url(
|
|
138
|
+
f"{HANSARD_API_BASE}/overview/sectionsforday.json",
|
|
139
|
+
{
|
|
140
|
+
"date": sitting_date,
|
|
141
|
+
"house": house,
|
|
142
|
+
},
|
|
143
|
+
)
|
|
144
|
+
return await get_result(url)
|
|
145
|
+
|
|
146
|
+
@mcp.tool()
|
|
147
|
+
async def get_hansard_calendar(
|
|
148
|
+
year: int,
|
|
149
|
+
month: int,
|
|
150
|
+
house: int,
|
|
151
|
+
) -> str:
|
|
152
|
+
"""Get all sitting dates for a month | Hansard, calendar, sitting days |
|
|
153
|
+
Use to discover which days have Hansard records available.
|
|
154
|
+
Returns list of sitting dates with Hansard available.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
year: Year (e.g. 2024).
|
|
158
|
+
month: Month number (1-12).
|
|
159
|
+
house: House number: 1 for Commons, 2 for Lords.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
List of sitting dates for the month.
|
|
163
|
+
"""
|
|
164
|
+
url = build_url(
|
|
165
|
+
f"{HANSARD_API_BASE}/overview/calendar.json",
|
|
166
|
+
{
|
|
167
|
+
"year": year,
|
|
168
|
+
"month": month,
|
|
169
|
+
"house": house,
|
|
38
170
|
},
|
|
39
171
|
)
|
|
40
172
|
return await get_result(url)
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server.fastmcp import FastMCP
|
|
4
4
|
|
|
5
|
+
from uk_parliament_mcp.config import INTERESTS_API_BASE
|
|
5
6
|
from uk_parliament_mcp.http_client import get_result
|
|
6
7
|
|
|
7
|
-
INTERESTS_API_BASE = "https://interests-api.parliament.uk/api/v1"
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
def register_tools(mcp: FastMCP) -> None:
|
|
11
10
|
"""Register interests tools with the MCP server."""
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import LORDS_VOTES_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
8
9
|
|
|
9
|
-
LORDS_VOTES_API_BASE = "http://lordsvotes-api.parliament.uk/data"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register Lords votes tools with the MCP server."""
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import MEMBERS_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
8
9
|
|
|
9
|
-
MEMBERS_API_BASE = "https://members-api.parliament.uk/api"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register member tools with the MCP server."""
|
uk_parliament_mcp/tools/now.py
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server.fastmcp import FastMCP
|
|
4
4
|
|
|
5
|
+
from uk_parliament_mcp.config import NOW_API_BASE
|
|
5
6
|
from uk_parliament_mcp.http_client import get_result
|
|
6
7
|
|
|
7
|
-
NOW_API_BASE = "https://now-api.parliament.uk/api"
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
def register_tools(mcp: FastMCP) -> None:
|
|
11
10
|
"""Register now tools with the MCP server."""
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import ORAL_QUESTIONS_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import get_result
|
|
8
9
|
|
|
9
|
-
ORAL_QUESTIONS_API_BASE = "https://oralquestionsandmotions-api.parliament.uk"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register oral questions tools with the MCP server."""
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import STATUTORY_INSTRUMENTS_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import get_result
|
|
8
9
|
|
|
9
|
-
STATUTORY_INSTRUMENTS_API_BASE = "https://statutoryinstruments-api.parliament.uk/api/v2"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register statutory instruments tools with the MCP server."""
|
|
@@ -4,10 +4,9 @@ from urllib.parse import quote
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import FastMCP
|
|
6
6
|
|
|
7
|
+
from uk_parliament_mcp.config import TREATIES_API_BASE
|
|
7
8
|
from uk_parliament_mcp.http_client import get_result
|
|
8
9
|
|
|
9
|
-
TREATIES_API_BASE = "https://treaties-api.parliament.uk/api"
|
|
10
|
-
|
|
11
10
|
|
|
12
11
|
def register_tools(mcp: FastMCP) -> None:
|
|
13
12
|
"""Register treaties tools with the MCP server."""
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server.fastmcp import FastMCP
|
|
4
4
|
|
|
5
|
+
from uk_parliament_mcp.config import WHATSON_API_BASE
|
|
5
6
|
from uk_parliament_mcp.http_client import build_url, get_result
|
|
6
7
|
|
|
7
|
-
WHATSON_API_BASE = "https://whatson-api.parliament.uk/calendar"
|
|
8
|
-
|
|
9
8
|
|
|
10
9
|
def register_tools(mcp: FastMCP) -> None:
|
|
11
10
|
"""Register whatson tools with the MCP server."""
|