time-tool
Convert natural language time descriptions (e.g., '2pm today') into precise time values. Optionally specify a timezone for accurate results.
Instructions
This tool is capable of returning the time from a natural language query. If the user asks about the 'current time' use this tool. Try to kee time_description as close to the users initial query as possible. For example if someone says 'was X person seen today?' then time_description should be 'today'.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| time_description | Yes | A natural language description of the time (e.g., '2pm today', 'tomorrow at noon'). | |
| timezone | Yes | Optional IANA timezone string (e.g., 'America/Los_Angeles', 'UTC'). Will default to system timezone if not provided. |
Implementation Reference
- src/tools/time-tool.ts:9-21 (handler)The main handler function for the time-tool that receives time_description and timezone args, calls parseTimeDescription, and returns the result as JSON text content.
const TOOL_HANDLER = async (args: ToolArgs, extra: any) => { const { time_description, timezone } = args; const result = parseTimeDescription(time_description ?? undefined, timezone ?? undefined, extra); return { content: [ { type: "text" as const, text: JSON.stringify(result), }, ], }; }; - src/types/time-tool-types.ts:1-18 (schema)Zod schema definitions for the time-tool arguments: time_description (string) and timezone (nullable string). Exports ToolArgs type.
import { z } from "zod"; export const TOOL_ARGS = { time_description: z .string() .describe( "A natural language description of the time (e.g., '2pm today', 'tomorrow at noon')." ), timezone: z .string() .nullable() .describe( "Optional IANA timezone string (e.g., 'America/Los_Angeles', 'UTC'). Will default to system timezone if not provided." ), }; const TOOL_ARGS_SCHEMA = z.object(TOOL_ARGS); export type ToolArgs = z.infer<typeof TOOL_ARGS_SCHEMA>; - src/tools/time-tool.ts:23-25 (registration)Registers the time-tool on the MCP server using server.tool() with the tool name, description, args schema, and handler.
export function createTool(server: McpServer) { server.tool(TOOL_NAME, TOOL_DESCRIPTION, TOOL_ARGS, TOOL_HANDLER); } - src/api/time-tool-api.ts:66-116 (helper)The parseTimeDescription API function that normalizes the time description using normalizeTimeDescription, parses it with chrono-node, constructs a Luxon DateTime, and returns timestamp, ISO string, and timezone.
export function parseTimeDescription(time_description: string, timezone?: string, extra?: any) { logger.info("EXTRA", extra); const now = new Date(DateTime.now().setZone(timezone || "America/Los_Angeles").toISO({ includeOffset: false })!); const normalizedDescription = normalizeTimeDescription(time_description); logger.info( `TIME TOOL ${timezone}: Normalized "${time_description}" to "${normalizedDescription}"` ); // Use the timezone-adjusted date as the reference date for chrono-node const parsed = parse(normalizedDescription, now); if (!parsed || parsed.length === 0) { throw new Error(`Could not parse time description: ${time_description}`); } logger.info(`TIME TOOLPARSED ${time_description}`, JSON.stringify(parsed)); const dateComponents = parsed[0].start; if (!dateComponents) { throw new Error("Parsed time has no start component"); } const dt = DateTime.fromObject( { year: nullToUndefined(dateComponents.get("year")), month: nullToUndefined(dateComponents.get("month")), day: nullToUndefined(dateComponents.get("day")), hour: nullToUndefined(dateComponents.get("hour")), minute: nullToUndefined(dateComponents.get("minute")), second: nullToUndefined(dateComponents.get("second")), millisecond: 0, }, { zone: timezone || "local", } ); if (!dt.isValid) { throw new Error(`Could not construct valid DateTime: ${dt.invalidReason}`); } const timestamp = dt.toMillis(); return { timestamp, iso: dt.toISO(), timezone: dt.zoneName, }; } - src/api/time-tool-api.ts:9-64 (helper)The normalizeTimeDescription helper function that converts natural language time phrases (e.g., 'current time', 'today', 'this morning') into standardized strings for chrono-node parsing.
function normalizeTimeDescription(description: string): string { const normalized = description.toLowerCase().trim(); if (normalized.includes("current time")) { return "now"; } // Handle plain day references as start of day if (normalized === "today") { return "today at 00:00"; } if (normalized === "yesterday") { return "yesterday at 00:00"; } if (normalized === "tomorrow") { return "tomorrow at 00:00"; } // Handle "this" time periods - always refer to today if (normalized === "this morning") { // For occupancy/access-control investigations, "this morning" should include // all events since local midnight unless a narrower range is explicitly requested. return "today at 00:00"; } if (normalized === "this afternoon") { return "today at 12:00"; } if (normalized === "this evening") { return "today at 18:00"; } if (normalized === "this night" || normalized === "tonight") { return "today at 20:00"; } if (normalized.includes("start of today") || normalized.includes("beginning of today")) { return "today at 00:00"; } if (normalized.includes("start of yesterday") || normalized.includes("beginning of yesterday")) { return "yesterday at 00:00"; } if (normalized.includes("start of tomorrow") || normalized.includes("beginning of tomorrow")) { return "tomorrow at 00:00"; } if (normalized.includes("end of today")) { return "today at 23:59:59"; } if (normalized.includes("end of yesterday")) { return "yesterday at 23:59:59"; } if (normalized.includes("end of tomorrow")) { return "tomorrow at 23:59:59"; } return description; }