get_legislation_regulation_metadata
Retrieve metadata for a specific statute or regulation, including its CanLII URL, citation, and table of contents. Provides direct link to full legislation text on canlii.org.
Instructions
Get metadata for a specific statute or regulation including its CanLII URL, citation, and table of contents. The URL links directly to the full legislation text on canlii.org — always provide this to the user.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| language | No | Language: 'en' for English (default), 'fr' for French | en |
| databaseId | Yes | Legislation database ID (e.g., 'ons' for Ontario Statutes) | |
| legislationId | Yes | Specific legislation ID from browse results |
Implementation Reference
- src/index.ts:475-510 (handler)The handler/registration for the get_legislation_regulation_metadata tool. Fetches legislation metadata from the CanLII API (legislationBrowse endpoint) and returns the parsed response.
// TOOL: get_legislation_regulation_metadata // ============================================================ server.tool( "get_legislation_regulation_metadata", "Get metadata for a specific statute or regulation including its CanLII URL, citation, and table of contents. " + "The URL links directly to the full legislation text on canlii.org — always provide this to the user.", { language: z.enum(["en", "fr"]).default("en") .describe("Language: 'en' for English (default), 'fr' for French"), databaseId: pathSegmentSchema .describe("Legislation database ID (e.g., 'ons' for Ontario Statutes)"), legislationId: pathSegmentSchema .describe("Specific legislation ID from browse results"), }, async ({ language, databaseId, legislationId }) => { try { const params = new URLSearchParams({ api_key: apiKey }); const response = await apiFetch( `https://api.canlii.org/v1/legislationBrowse/${language}/${encodeURIComponent(databaseId)}/${encodeURIComponent(legislationId)}/?${params.toString()}` ); if (!response.ok) { return errorResponse(`Error: Failed to fetch legislation metadata (${response.status})`); } const data = await response.json(); const parsed = LegislationMetadataSchema.parse(data); return jsonResponse(parsed); } catch (error) { return errorResponse( `Error: ${error instanceof Error ? error.message : "Unknown error"}` ); } } ); - src/schema.ts:56-67 (schema)Zod validation schema for the legislation metadata response, defining the shape of data returned by the tool.
export const LegislationMetadataSchema = z.object({ legislationId: z.string(), url: z.string(), title: z.string(), citation: z.string(), language: z.string().optional(), dateScheme: z.string().optional(), startDate: z.string().optional(), endDate: z.string().optional(), repealed: z.string().optional(), content: z.array(LegislationMetadataContentSchema).optional(), }).passthrough(); - src/schema.ts:51-54 (schema)Sub-schema for the table of contents (content array) items within the legislation metadata.
export const LegislationMetadataContentSchema = z.object({ partId: z.string(), partName: z.string(), }).passthrough(); - src/index.ts:32-53 (helper)Helper rate-limited API fetch function used by the tool to call the CanLII API.
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:93-103 (helper)Helper response formatters: jsonResponse serializes data to JSON, errorResponse formats error messages (with API key redaction).
function jsonResponse(data: unknown) { return textResponse(JSON.stringify(data, null, 2)); } function sanitizeError(message: string): string { return message.replace(/api_key=[^&\s]*/gi, "api_key=REDACTED"); } function errorResponse(message: string) { return textResponse(sanitizeError(message)); }