Resolve Single OSCOLA Citation
citations_resolveParse and resolve a single OSCOLA citation to its canonical URL. Supports neutral citations, SIs, and legislation sections.
Instructions
Parse and resolve a single OSCOLA citation to its canonical URL.
Supports: neutral citations, SIs, legislation sections, retained EU law. Returns parsed fields and resolved_url if resolvable. Raises ValueError if no recognised citation is found in the input.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes | CitationsResolveInput with a single citation string. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| raw | Yes | Original citation text as found in the source | |
| type | Yes | Classification of the citation type | |
| year | No | Year component of the citation | |
| court | No | Court code: UKSC, UKPC, EWCA Civ, EWCA Crim, EWHC (KB), EWHC (Ch), EWHC (Comm), EWHC (Fam), EWHC (Pat), EWHC (IPEC), UKUT (IAC), UKUT (TCC), UKUT (AAC), UKUT (LC), EAT, UKFTT (TC), UKFTT (GRC) | |
| number | No | Judgment number within the year | |
| report_series | No | Law report series abbreviation: WLR, AC, QB, KB, Ch, All ER, EWCA Civ, etc. | |
| volume | No | Report volume number (for law reports) | |
| page | No | Starting page in the law report | |
| legislation_title | No | Title of legislation (for s.NN Act YYYY citations) | |
| section | No | Section number referenced | |
| si_year | No | SI year (for SI YYYY/NNN citations) | |
| si_number | No | SI number | |
| resolved_url | No | TNA Find Case Law or legislation.gov.uk URL if successfully resolved | |
| confidence | Yes | Parse confidence 0.0–1.0. Citations below 0.7 are ambiguous and may have been sent for LLM disambiguation. |
Implementation Reference
- src/modules/citations/tools.py:211-233 (handler)The `citations_resolve` handler function: parses and resolves a single OSCOLA citation to canonical URL. Registered as an MCP tool with name 'resolve'. Delegates to `_compile_patterns()` and `_extract_all_citations()` for parsing, returning the first matched `ParsedCitation` or raising `ValueError` if none found.
@mcp.tool( name="resolve", annotations={"title": "Resolve Single OSCOLA Citation", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": False}, ) async def citations_resolve(params: CitationsResolveInput) -> ParsedCitation: """Parse and resolve a single OSCOLA citation to its canonical URL. Supports: neutral citations, SIs, legislation sections, retained EU law. Returns parsed fields and resolved_url if resolvable. Raises ValueError if no recognised citation is found in the input. Args: params: CitationsResolveInput with a single citation string. """ patterns = _compile_patterns() confident, ambiguous = _extract_all_citations(params.citation.strip(), patterns) all_found = confident + ambiguous if not all_found: raise ValueError( f"No recognised OSCOLA citation found in '{params.citation}'. " f"Supported: [YYYY] COURT N, [YYYY] N SERIES PAGE, s.N Act YYYY, SI YYYY/N, Regulation (EU) YYYY/N" ) return all_found[0] - src/modules/citations/tools.py:49-57 (schema)`CitationsResolveInput` Pydantic model: input schema for the citations_resolve tool. Accepts a single `citation` string (3-500 chars) as the sole required field.
class CitationsResolveInput(BaseModel): model_config = ConfigDict(str_strip_whitespace=True, extra="forbid") citation: str = Field( ..., description="A single OSCOLA citation to parse and resolve. E.g. '[2024] UKSC 12', 'SI 2018/1234', 's.47 Companies Act 2006'", min_length=3, max_length=500, ) - src/modules/citations/tools.py:211-233 (registration)Registration of `citations_resolve` as an MCP tool via `@mcp.tool(name='resolve', ...)` decorator inside the `register_tools()` function. The tool is decorated with annotations: readOnlyHint=True, idempotentHint=True.
@mcp.tool( name="resolve", annotations={"title": "Resolve Single OSCOLA Citation", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": False}, ) async def citations_resolve(params: CitationsResolveInput) -> ParsedCitation: """Parse and resolve a single OSCOLA citation to its canonical URL. Supports: neutral citations, SIs, legislation sections, retained EU law. Returns parsed fields and resolved_url if resolvable. Raises ValueError if no recognised citation is found in the input. Args: params: CitationsResolveInput with a single citation string. """ patterns = _compile_patterns() confident, ambiguous = _extract_all_citations(params.citation.strip(), patterns) all_found = confident + ambiguous if not all_found: raise ValueError( f"No recognised OSCOLA citation found in '{params.citation}'. " f"Supported: [YYYY] COURT N, [YYYY] N SERIES PAGE, s.N Act YYYY, SI YYYY/N, Regulation (EU) YYYY/N" ) return all_found[0] - src/modules/citations/__init__.py:12-24 (registration)The citations MCP server is created and `register_tools(citations_mcp)` is called, which wires up all tools including citations_resolve. The instructions reference citations_resolve as a user-facing tool.
citations_mcp = FastMCP( name="citations", instructions=( "Parse and resolve OSCOLA legal citations from free text. " "Use citations_parse to extract all citations from a judgment, memo, or article. " "Use citations_resolve to resolve a single known citation to its canonical URL. " "Use citations_network to map all cases and legislation cited within a judgment. " "Supports: neutral citations, law reports, legislation sections, SIs, retained EU law. " "No external API dependency — fully self-contained." ), ) register_tools(citations_mcp) - `ParsedCitation` model — the return type of `citations_resolve`. Contains parsed fields like `raw`, `type`, `year`, `court`, `number`, `resolved_url`, `confidence`, etc.
class ParsedCitation(BaseModel): """A single parsed OSCOLA citation with optional resolution.""" model_config = ConfigDict(str_strip_whitespace=True) raw: str = Field(..., description="Original citation text as found in the source") type: CitationType = Field(..., description="Classification of the citation type") year: int | None = Field(None, description="Year component of the citation") court: str | None = Field( None, description=( "Court code: UKSC, UKPC, EWCA Civ, EWCA Crim, EWHC (KB), EWHC (Ch), " "EWHC (Comm), EWHC (Fam), EWHC (Pat), EWHC (IPEC), UKUT (IAC), UKUT (TCC), " "UKUT (AAC), UKUT (LC), EAT, UKFTT (TC), UKFTT (GRC)" ), ) number: int | None = Field(None, description="Judgment number within the year") report_series: str | None = Field( None, description="Law report series abbreviation: WLR, AC, QB, KB, Ch, All ER, EWCA Civ, etc.", ) volume: int | None = Field(None, description="Report volume number (for law reports)") page: int | None = Field(None, description="Starting page in the law report") legislation_title: str | None = Field(None, description="Title of legislation (for s.NN Act YYYY citations)") section: str | None = Field(None, description="Section number referenced") si_year: int | None = Field(None, description="SI year (for SI YYYY/NNN citations)") si_number: int | None = Field(None, description="SI number") resolved_url: str | None = Field( None, description="TNA Find Case Law or legislation.gov.uk URL if successfully resolved", ) confidence: float = Field( ..., description="Parse confidence 0.0–1.0. Citations below 0.7 are ambiguous and may have been sent for LLM disambiguation.", ge=0.0, le=1.0, )