Implement workflow-first legislative UX tools#70
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces workflow-oriented public tools, such as claim verification, issue briefing, and bill timelines, along with helper utilities, updated documentation, and new unit tests. The review feedback identifies several potential TypeError bugs where service calls could return None instead of lists, and suggests converting numeric values to floats during comparison to prevent false mismatch reports.
| ) | ||
| data = to_public_data(details) | ||
| else: | ||
| matches = await service.get_bill_info(age=age, bill_name=value, limit=limit) |
There was a problem hiding this comment.
If get_bill_info returns None (e.g., due to an API error or empty response), attempting to iterate over it in the list comprehension on the next line will raise a TypeError: 'NoneType' object is not iterable. Defaulting to an empty list [] prevents this potential crash.
| matches = await service.get_bill_info(age=age, bill_name=value, limit=limit) | |
| matches = await service.get_bill_info(age=age, bill_name=value, limit=limit) or [] |
| details={"claim": claim}, | ||
| ) | ||
|
|
||
| matches = await service.get_member_info(value) |
There was a problem hiding this comment.
If get_member_info returns None, attempting to iterate over it in the list comprehension on the next line will raise a TypeError: 'NoneType' object is not iterable. Defaulting to an empty list [] prevents this potential crash.
| matches = await service.get_member_info(value) | |
| matches = await service.get_member_info(value) or [] |
| details={"claim": claim}, | ||
| ) | ||
|
|
||
| matches = await service.get_committee_list(value) |
There was a problem hiding this comment.
If get_committee_list returns None, attempting to iterate over it in the list comprehension on the next line will raise a TypeError: 'NoneType' object is not iterable. Defaulting to an empty list [] prevents this potential crash.
| matches = await service.get_committee_list(value) | |
| matches = await service.get_committee_list(value) or [] |
|
|
||
| warnings: list[dict[str, Any]] = [] | ||
| try: | ||
| meetings = await meeting_svc.get_meeting_records(bill_data.get("BILL_ID") or bill_id) |
There was a problem hiding this comment.
If get_meeting_records returns None instead of raising an exception, the meetings variable will be None. This will cause a TypeError: 'NoneType' object is not iterable when iterating over meetings on line 555. Defaulting to an empty list [] prevents this potential crash.
| meetings = await meeting_svc.get_meeting_records(bill_data.get("BILL_ID") or bill_id) | |
| meetings = await meeting_svc.get_meeting_records(bill_data.get("BILL_ID") or bill_id) or [] |
| if isinstance(expected_value, int | float) or isinstance(actual, int | float): | ||
| if actual != expected_value: | ||
| mismatches.append({"field": key, "expected": expected_value, "actual": actual}) | ||
| continue |
There was a problem hiding this comment.
When comparing numeric values, comparing an integer/float directly with a string (e.g., 180 != "180") will evaluate to True in Python, leading to false mismatch reports. Converting both values to floats before comparison ensures robust numeric validation regardless of whether they are represented as strings or numbers in the source data.
| if isinstance(expected_value, int | float) or isinstance(actual, int | float): | |
| if actual != expected_value: | |
| mismatches.append({"field": key, "expected": expected_value, "actual": actual}) | |
| continue | |
| try: | |
| if float(actual) == float(expected_value): | |
| continue | |
| except (ValueError, TypeError): | |
| pass | |
| if isinstance(expected_value, int | float) or isinstance(actual, int | float): | |
| if actual != expected_value: | |
| mismatches.append({"field": key, "expected": expected_value, "actual": actual}) | |
| continue |
CheckDeployment readiness check before merge: uv build produced sdist/wheel, stdio MCP integration script passed, HTTP MCP server smoke passed, Docker image build passed, Docker container HTTP MCP smoke passed, local ruff/pytest passed, and GitHub CI is green. Local ASSEMBLY_API_KEY is loaded but direct Assembly API calls return ERROR-990/empty rows, so live data-row verification is blocked by the local key/API authorization state rather than server startup, MCP protocol, packaging, or tool-schema behavior. Context:
|
Summary
Implements the workflow-first AssemblyMCP UX roadmap across issues #62-#69.
Changes
get_legislative_research_kitas the first-call public tool surface contract.[NOT_FOUND],[AMBIGUOUS],[VERIFY_FAILED],[API_FAILED],[PARTIAL].verify_legislative_claims.issue_brief,bill_timeline,legislative_impact_map, andwatch_action_plan.Verification
uv run ruff check .uv run pytest -q-> 80 passed127.0.0.1:8765/mcpuv run python scripts/test_streamable_http.py http://127.0.0.1:8765/mcp-> initialize/tools-list/ping passed, 26 tools listedget_legislative_research_kitover MCP HTTP ->isError=false, 6 workflow tools returned,[VERIFY_FAILED]marker presentCloses #62
Closes #63
Closes #64
Closes #65
Closes #66
Closes #67
Closes #68
Closes #69