Skip to main content
Glama
paulieb89

UK Legal Research MCP Server

Search HMRC Guidance

hmrc_search_guidance
Read-onlyIdempotent

Search 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

TableJSON Schema
NameRequiredDescriptionDefault
paramsYesHMRCGuidanceSearchInput with query (topic to search).

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesThe search query that was run
totalYesNumber of guidance documents returned in this call
resultsNoMatching HMRC guidance pages. Each entry's `summary` is capped per the max_summary_chars input parameter.

Implementation Reference

  • 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,
        )
  • 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)
  • 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")
  • 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."
            ),
        )
  • 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},
    )
  • 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)
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations (readOnlyHint, idempotentHint) already indicate safe, idempotent operations. The description adds value by explaining the data source (GOV.UK API) and the filtered nature (HMRC publications), providing context beyond annotations. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences with no fluff: the first states purpose, the second lists returns, and the third specifies the source. Front-loaded and concise, every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity (1 parameter) and existence of an output schema, the description covers the essential aspects: what it searches, what is returned, and the data source. It could mention result limits or sorting, but this is not critical for basic usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description does not add meaning beyond the schema's description of the 'query' parameter. The schema already includes an example, and the description only states 'search query for HMRC guidance', which is redundant.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it searches GOV.UK for HMRC tax guidance documents, specifies the returned fields (titles, URLs, summaries, last-updated dates), and mentions the use of the official GOV.UK content API filtered to HMRC. This distinguishes it from sibling tools like hmrc_check_mtd_status or hmrc_get_vat_rate which are not search tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for finding HMRC tax guidance but does not explicitly state when to use this tool vs alternatives like bills_search_bills or case_law_search. No when-not or exclusion criteria are provided, though the context of HMRC is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/paulieb89/uk-legal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server