get_case_metadata
Get detailed metadata for a specific Canadian case: CanLII URL, citation, decision date, docket number, keywords, and topics. Use after finding a case to gather complete details before citing.
Instructions
Get detailed metadata for a specific case including its CanLII URL, citation, decision date, docket number, keywords, and topics. The URL field links directly to the full decision text on canlii.org — always provide this to the user for verification. Use after finding a case via search or browse to get complete details before citing it.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| language | No | Language: 'en' for English (default), 'fr' for French | en |
| databaseId | Yes | Court database ID (e.g., 'onsc', 'onca', 'csc-scc') | |
| caseId | Yes | Case unique identifier from search/browse results (e.g., '2021onsc8582') | |
| publishedBefore | No | Date first published on CanLII (YYYY-MM-DD) | |
| publishedAfter | No | Date first published on CanLII (YYYY-MM-DD) | |
| modifiedBefore | No | Date content last modified on CanLII (YYYY-MM-DD) | |
| modifiedAfter | No | Date content last modified on CanLII (YYYY-MM-DD) | |
| changedBefore | No | Date metadata or content last changed on CanLII (YYYY-MM-DD) | |
| changedAfter | No | Date metadata or content last changed on CanLII (YYYY-MM-DD) | |
| decisionDateBefore | No | Decision date upper bound (YYYY-MM-DD) | |
| decisionDateAfter | No | Decision date lower bound (YYYY-MM-DD) |
Implementation Reference
- src/index.ts:271-294 (handler)The handler function for the 'get_case_metadata' tool. It takes language, databaseId, caseId, and optional date parameters, calls the CanLII API endpoint /v1/caseBrowse/{lang}/{dbId}/{caseId}, and parses the response with CaseMetadataSchema.
async (params) => { try { const { language, databaseId, caseId, ...dateParams } = params; const urlParams = new URLSearchParams({ api_key: apiKey }); buildDateParams(urlParams, dateParams); const response = await apiFetch( `https://api.canlii.org/v1/caseBrowse/${language}/${encodeURIComponent(databaseId)}/${encodeURIComponent(caseId)}/?${urlParams.toString()}` ); if (!response.ok) { return errorResponse(`Error: Failed to fetch case metadata (${response.status})`); } const data = await response.json(); const parsed = CaseMetadataSchema.parse(data); return jsonResponse(parsed); } catch (error) { return errorResponse( `Error: ${error instanceof Error ? error.message : "Unknown error"}` ); } } ); - src/index.ts:257-294 (registration)The full tool registration using server.tool() with name 'get_case_metadata', description, input schema (language, databaseId, caseId, date parameters), and the async handler function.
server.tool( "get_case_metadata", "Get detailed metadata for a specific case including its CanLII URL, citation, decision date, docket number, keywords, and topics. " + "The URL field links directly to the full decision text on canlii.org — always provide this to the user for verification. " + "Use after finding a case via search or browse to get complete details before citing it.", { language: z.enum(["en", "fr"]).default("en") .describe("Language: 'en' for English (default), 'fr' for French"), databaseId: pathSegmentSchema .describe("Court database ID (e.g., 'onsc', 'onca', 'csc-scc')"), caseId: pathSegmentSchema .describe("Case unique identifier from search/browse results (e.g., '2021onsc8582')"), ...dateParametersSchema, }, async (params) => { try { const { language, databaseId, caseId, ...dateParams } = params; const urlParams = new URLSearchParams({ api_key: apiKey }); buildDateParams(urlParams, dateParams); const response = await apiFetch( `https://api.canlii.org/v1/caseBrowse/${language}/${encodeURIComponent(databaseId)}/${encodeURIComponent(caseId)}/?${urlParams.toString()}` ); if (!response.ok) { return errorResponse(`Error: Failed to fetch case metadata (${response.status})`); } const data = await response.json(); const parsed = CaseMetadataSchema.parse(data); return jsonResponse(parsed); } catch (error) { return errorResponse( `Error: ${error instanceof Error ? error.message : "Unknown error"}` ); } } ); - src/schema.ts:36-49 (schema)The CaseMetadataSchema Zod schema used to validate the API response for get_case_metadata. Fields include databaseId, caseId, url, title, citation, language, docketNumber, decisionDate, keywords, topics, concatenatedId, and aiContentId.
export const CaseMetadataSchema = z.object({ databaseId: z.string(), caseId: z.string(), url: z.string(), title: z.string(), citation: z.string(), language: z.string().optional(), docketNumber: z.string().optional(), decisionDate: z.string().optional(), keywords: z.string().optional(), topics: z.string().optional(), concatenatedId: z.string().optional(), aiContentId: z.string().optional(), }).passthrough(); - src/index.ts:32-53 (helper)The apiFetch helper function that the handler uses to make rate-limited API calls to CanLII.
async function apiFetch(url: string): Promise<Response> { return new Promise((resolve, reject) => { requestQueue = requestQueue.then(async () => { const today = new Date().toDateString(); if (today !== dailyResetDate) { dailyCount = 0; dailyResetDate = today; } if (dailyCount >= 5000) { throw new Error("Daily API limit reached (5,000 queries). Try again tomorrow."); } const now = Date.now(); const elapsed = now - lastRequestTime; if (elapsed < MIN_INTERVAL_MS) { await new Promise(r => setTimeout(r, MIN_INTERVAL_MS - elapsed)); } lastRequestTime = Date.now(); dailyCount++; return fetch(url); }).then(resolve, reject); }); } - src/index.ts:69-87 (helper)The buildDateParams helper function used to append optional date filter parameters to the API URL.
function buildDateParams(params: URLSearchParams, options: { publishedBefore?: string; publishedAfter?: string; modifiedBefore?: string; modifiedAfter?: string; changedBefore?: string; changedAfter?: string; decisionDateBefore?: string; decisionDateAfter?: string; }) { if (options.publishedBefore) params.append('publishedBefore', options.publishedBefore); if (options.publishedAfter) params.append('publishedAfter', options.publishedAfter); if (options.modifiedBefore) params.append('modifiedBefore', options.modifiedBefore); if (options.modifiedAfter) params.append('modifiedAfter', options.modifiedAfter); if (options.changedBefore) params.append('changedBefore', options.changedBefore); if (options.changedAfter) params.append('changedAfter', options.changedAfter); if (options.decisionDateBefore) params.append('decisionDateBefore', options.decisionDateBefore); if (options.decisionDateAfter) params.append('decisionDateAfter', options.decisionDateAfter); }