examples/MCP-tools Public
import { z } from "zod";import { ToolError } from "./errors.ts";import { defineTool } from "./types.ts";// The canonical IANA names this runtime knows. Intl accepts more than it lists (UTC, links such// as US/Pacific), so this is for matching city names, not for deciding what is valid.const ZONES = Intl.supportedValuesOf("timeZone");// An ISO 8601 instant with an explicit offset. Without one, Date.parse reads the time in the// Worker's own zone, which is UTC in production and something else on a laptop.const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/i;export function resolveZone(text: string): string { const given = text.trim(); const key = given.toLowerCase().replace(/\s+/g, "_"); const listed = ZONES.find((z) => z.toLowerCase() === key); if (listed) return listed; if (isZone(given)) return given.includes("/") ? given : given.toUpperCase(); const cities = ZONES.filter((z) => z.toLowerCase().split("/").pop() === key); if (cities.length === 1) return cities[0]; throw new ToolError(`Unknown time zone "${text}". Use an IANA name such as Europe/Paris, Asia/Tokyo or America/New_York.`);}function isZone(name: string): boolean { try { new Intl.DateTimeFormat("en-US", { timeZone: name }); return true; } catch { return false; }}function parseInstant(at: string | undefined, now: Date): Date { if (at === undefined) return now; const date = INSTANT.test(at.trim()) ? new Date(at.trim()) : null; if (!date || Number.isNaN(date.getTime())) { throw new ToolError(`"${at}" is not an ISO 8601 instant with an offset. Use a form such as 2026-10-16T14:00:00Z or 2026-10-16T09:00:00-05:00.`); } return date;}// "GMT+05:30" -> "+05:30" and 330; "GMT" -> "+00:00" and 0.function offsetOf(date: Date, zone: string): { offset: string; minutes: number } { const name = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "longOffset" }).formatToParts(date).find((p) => p.type === "timeZoneName")?.value ?? "GMT"; const m = /([+-])(\d{2}):(\d{2})/.exec(name); if (!m) return { offset: "+00:00", minutes: 0 }; const minutes = (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])); return { offset: `${m[1]}${m[2]}:${m[3]}`, minutes };}function localParts(date: Date, zone: string): Record<string, string> { const parts = new Intl.DateTimeFormat("en-US", { timeZone: zone, hourCycle: "h23", weekday: "long", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", }).formatToParts(date); return Object.fromEntries(parts.map((p) => [p.type, p.value]));}export const timeInZone = defineTool({ name: "time_in_zone", title: "Time in a time zone", description: "Give the local date, time, weekday and UTC offset in an IANA time zone, for now or for a given instant. " + "Use it for questions like \"what time is it in Tokyo\" or \"when is 15:00 UTC in Chicago\"; " + "your own sense of the current time may be wrong. Daylight saving time is applied.", input: z.object({ zone: z.string().min(1).describe("An IANA time zone such as Europe/Paris or America/New_York. A city that names exactly one zone, such as tokyo, also works."), at: z.string().optional().describe("The instant to convert, in ISO 8601 with Z or an offset, such as 2026-10-16T14:00:00Z. Leave it out for the current time."), }), output: z.object({ zone: z.string(), instant: z.string().describe("The instant in UTC, ISO 8601."), date: z.string().describe("The local date, YYYY-MM-DD."), time: z.string().describe("The local time, HH:MM:SS on a 24-hour clock."), weekday: z.string(), utc_offset: z.string().describe("The offset from UTC at that instant, such as +09:00."), utc_offset_minutes: z.number().int(), formatted: z.string().describe("The local time written out in English."), }), run({ zone, at }, { now }) { const name = resolveZone(zone); const date = parseInstant(at, now); const p = localParts(date, name); const { offset, minutes } = offsetOf(date, name); return { zone: name, instant: date.toISOString(), date: `${p.year}-${p.month}-${p.day}`, time: `${p.hour}:${p.minute}:${p.second}`, weekday: p.weekday, utc_offset: offset, utc_offset_minutes: minutes, formatted: new Intl.DateTimeFormat("en-US", { timeZone: name, dateStyle: "full", timeStyle: "long" }).format(date), }; },});