examples/MCP-tools Public
import { z } from "zod";import { ToolError } from "./errors.ts";import { defineTool } from "./types.ts";const QUANTITIES = ["length", "mass", "volume", "temperature"] as const;type Quantity = (typeof QUANTITIES)[number];// Each unit converts to and from its quantity's base unit: metres, kilograms, litres, kelvin.type Unit = { symbol: string; quantity: Quantity; toBase: (v: number) => number; fromBase: (v: number) => number;};function linear(quantity: Quantity, symbol: string, factor: number): Unit { return { symbol, quantity, toBase: (v) => v * factor, fromBase: (v) => v / factor };}// Imperial volumes are US customary; the description says so.const UNITS: Unit[] = [ linear("length", "mm", 0.001), linear("length", "cm", 0.01), linear("length", "m", 1), linear("length", "km", 1000), linear("length", "in", 0.0254), linear("length", "ft", 0.3048), linear("length", "yd", 0.9144), linear("length", "mi", 1609.344), linear("length", "nmi", 1852), linear("mass", "mg", 1e-6), linear("mass", "g", 0.001), linear("mass", "kg", 1), linear("mass", "t", 1000), linear("mass", "oz", 0.028349523125), linear("mass", "lb", 0.45359237), linear("mass", "st", 6.35029318), linear("volume", "ml", 0.001), linear("volume", "l", 1), linear("volume", "m3", 1000), linear("volume", "tsp", 0.00492892159375), linear("volume", "tbsp", 0.01478676478125), linear("volume", "fl-oz", 0.0295735295625), linear("volume", "cup", 0.2365882365), linear("volume", "pt", 0.473176473), linear("volume", "qt", 0.946352946), linear("volume", "gal", 3.785411784), { symbol: "C", quantity: "temperature", toBase: (v) => v + 273.15, fromBase: (v) => v - 273.15 }, { symbol: "F", quantity: "temperature", toBase: (v) => ((v - 32) * 5) / 9 + 273.15, fromBase: (v) => ((v - 273.15) * 9) / 5 + 32 }, { symbol: "K", quantity: "temperature", toBase: (v) => v, fromBase: (v) => v },];const BY_KEY = new Map(UNITS.map((u) => [u.symbol.toLowerCase(), u]));export function findUnit(text: string): Unit | undefined { return BY_KEY.get(text.trim().toLowerCase());}function unitOrThrow(text: string): Unit { const unit = findUnit(text); if (!unit) throw new ToolError(`Unknown unit "${text}". Known units, by quantity: ${knownUnits()}.`); return unit;}function knownUnits(): string { return QUANTITIES.map((q) => `${q}: ${UNITS.filter((u) => u.quantity === q).map((u) => u.symbol).join(", ")}`).join("; ");}// Twelve significant digits hides float noise (0.30000000000000004) without losing anything a// person would read.function tidy(v: number): number { return Number(v.toPrecision(12));}export const convertUnits = defineTool({ name: "convert_units", title: "Convert units", description: "Convert a value between units of length, mass, volume or temperature. " + "Use it instead of doing the arithmetic yourself whenever the user needs an exact figure. " + "Units are short symbols: length mm, cm, m, km, in, ft, yd, mi, nmi; mass mg, g, kg, t, oz, lb, st; " + "volume ml, l, m3, tsp, tbsp, fl-oz, cup, pt, qt, gal (US customary); temperature C, F, K. " + "Both units must measure the same quantity.", input: z.object({ value: z.number().describe("The number to convert, in the `from` unit."), from: z.string().min(1).describe("The unit the value is in, for example km or F."), to: z.string().min(1).describe("The unit to convert to, for example mi or C."), }), output: z.object({ quantity: z.enum(QUANTITIES), value: z.number(), from: z.string(), result: z.number(), to: z.string(), }), run({ value, from, to }) { const a = unitOrThrow(from); const b = unitOrThrow(to); if (a.quantity !== b.quantity) { throw new ToolError(`Cannot convert ${a.symbol} (${a.quantity}) to ${b.symbol} (${b.quantity}). Pick two units of the same quantity.`); } const base = a.toBase(value); if (a.quantity === "temperature" && base < 0) { throw new ToolError(`${value} ${a.symbol} is below absolute zero (${tidy(a.fromBase(0))} ${a.symbol}).`); } return { quantity: a.quantity, value, from: a.symbol, result: tidy(b.fromBase(base)), to: b.symbol }; },});