Search HMRC Guidance
hmrc_search_guidanceSearch HMRC tax guidance on GOV.UK by topic. Retrieve titles, summaries, URLs, and last-updated dates.
Instructions
Search GOV.UK for HMRC tax guidance documents.
Returns matching guidance titles, URLs, summaries, and last-updated dates. Searches the official GOV.UK content API filtered to HMRC publications.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes | HMRCGuidanceSearchInput with query (topic to search). |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | The search query that was run | |
| total | Yes | Number of guidance documents returned in this call | |
| results | No | Matching HMRC guidance pages. Each entry's `summary` is capped per the max_summary_chars input parameter. |
Implementation Reference
- src/modules/hmrc/tools.py:173-211 (handler)The main handler function 'hmrc_search_guidance' decorated with @mcp.tool(name='search_guidance'). It queries the GOV.UK search API filtered to HMRC publications, parses results into HMRCGuidanceResult objects, and returns a HMRCGuidanceSearchResult.
@mcp.tool( name="search_guidance", annotations={"title": "Search HMRC Guidance", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": True}, ) async def hmrc_search_guidance(params: HMRCGuidanceSearchInput, ctx: Context) -> HMRCGuidanceSearchResult: """Search GOV.UK for HMRC tax guidance documents. Returns matching guidance titles, URLs, summaries, and last-updated dates. Searches the official GOV.UK content API filtered to HMRC publications. Args: params: HMRCGuidanceSearchInput with query (topic to search). """ client: httpx.AsyncClient = ctx.lifespan_context["http"] resp = await client.get( GOVUK_SEARCH_BASE, params={"q": params.query, "filter_organisations": "hm-revenue-customs", "fields[]": ["title", "description", "link", "public_timestamp"], "count": 10}, ) resp.raise_for_status() results: list[HMRCGuidanceResult] = [] for item in resp.json().get("results", []): updated = None ts = item.get("public_timestamp") if ts: try: updated = date.fromisoformat(ts[:10]) except ValueError: pass results.append(HMRCGuidanceResult( title=item.get("title", "Unknown"), url=f"https://www.gov.uk{item.get('link', '')}", summary=item.get("description"), updated=updated, )) return HMRCGuidanceSearchResult( query=params.query, total=len(results), results=results, ) - src/modules/hmrc/tools.py:96-99 (schema)Input schema 'HMRCGuidanceSearchInput' (Pydantic BaseModel) with a single required 'query' field (3-300 chars).
class HMRCGuidanceSearchInput(BaseModel): model_config = ConfigDict(str_strip_whitespace=True, extra="forbid") query: str = Field(..., description="Search query for HMRC guidance, e.g. 'VAT digital services', 'R&D tax relief SME'", min_length=3, max_length=300) - src/modules/hmrc/models.py:36-44 (schema)Output sub-model 'HMRCGuidanceResult' representing a single guidance document (title, url, summary, updated).
class HMRCGuidanceResult(BaseModel): """A single HMRC guidance document search result.""" model_config = ConfigDict(str_strip_whitespace=True) title: str = Field(..., description="Guidance document title") url: str = Field(..., description="GOV.UK URL for the guidance") summary: str | None = Field(None, description="Brief summary of the guidance content") updated: date | None = Field(None, description="Date the guidance was last updated") - src/modules/hmrc/models.py:47-65 (schema)Output model 'HMRCGuidanceSearchResult' wrapping results with query and total count.
class HMRCGuidanceSearchResult(BaseModel): """Result of an HMRC guidance search on GOV.UK. Wraps the list of matching guidance documents with search metadata so the LLM client sees a real nested object on the wire rather than a stringified JSON blob. """ model_config = ConfigDict(str_strip_whitespace=True) query: str = Field(..., description="The search query that was run") total: int = Field(..., description="Number of guidance documents returned in this call") results: list[HMRCGuidanceResult] = Field( default_factory=list, description=( "Matching HMRC guidance pages. Each entry's `summary` is capped " "per the max_summary_chars input parameter." ), ) - src/modules/hmrc/tools.py:173-176 (registration)Tool registration via @mcp.tool(name='search_guidance') decorator with annotations (readOnlyHint, idempotentHint, etc.). The actual public-facing name is 'hmrc_search_guidance' — the caller prefix is from the FastMCP server name 'hmrc'.
@mcp.tool( name="search_guidance", annotations={"title": "Search HMRC Guidance", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": True}, ) - src/modules/hmrc/__init__.py:8-20 (registration)Module-level registration: creates FastMCP named 'hmrc', adds caching middleware, and calls register_tools() which decorates the handler.
hmrc_mcp = FastMCP( name="hmrc", instructions=( "Look up UK tax information via HMRC APIs and GOV.UK. " "Use hmrc_get_vat_rate to find the VAT rate for any commodity or service type. " "Use hmrc_check_mtd_status to check Making Tax Digital VAT status (requires HMRC OAuth credentials). " "Use hmrc_search_guidance to find HMRC guidance documents on GOV.UK." ), ) hmrc_mcp.add_middleware(ResponseCachingMiddleware(call_tool_settings=CallToolSettings(ttl=7776000))) register_tools(hmrc_mcp)