UNPKG

rhombus-node-mcp

Version:
60 lines (59 loc) 2.45 kB
import { parse } from "chrono-node"; import { DateTime } from "luxon"; import { z } from "zod"; import { logger } from "../logger.js"; function nullToUndefined(value) { return value === null ? undefined : value; } export function createTool(server) { server.tool("time-tool", "Tool for converting a natural language time description into a timestamp in milliseconds.", { time_description: z .string() .describe("A natural language description of the time (e.g., '2pm today', 'tomorrow at noon')."), timezone: z .string() .optional() .describe("Optional IANA timezone string (e.g., 'America/Los_Angeles', 'UTC'). Will default to system timezone if not provided."), }, async ({ time_description, timezone }, extra) => { logger.info("EXTRA", extra); // console.error(`🕛 handling tool call for time ${time_description} using timezone ${timezone}`); const now = DateTime.now() .setZone(timezone || undefined) .toJSDate(); const parsed = parse(time_description, now, { forwardDate: true }); if (!parsed || parsed.length === 0) { throw new Error(`Could not parse time description: ${time_description}`); } 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 { content: [ { type: "text", text: JSON.stringify({ timestamp, iso: dt.toISO(), timezone: dt.zoneName, }), }, ], }; }); }