examples/MCP-tools Public
import { z } from "zod";import { defineTool } from "./types.ts";// A common estimate for silent reading of English prose.const DEFAULT_WPM = 238;const MAX_LENGTH = 200_000;const graphemes = new Intl.Segmenter("en", { granularity: "grapheme" });const words = new Intl.Segmenter("en", { granularity: "word" });const sentences = new Intl.Segmenter("en", { granularity: "sentence" });function count<T>(items: Iterable<T>, keep: (item: T) => boolean = () => true): number { let n = 0; for (const item of items) if (keep(item)) n++; return n;}function readingTime(seconds: number): string { if (seconds === 0) return "0 min"; if (seconds < 60) return "under a minute"; return `${Math.round(seconds / 60)} min`;}export const textStats = defineTool({ name: "text_stats", title: "Text statistics", description: "Count the characters, words, sentences and paragraphs in a text and estimate its reading time. " + "Use it when the user asks how long a text is or needs to meet a length limit; counting by eye is unreliable. " + "Characters are what a reader sees, so an emoji or an accented letter counts once.", input: z.object({ text: z.string().max(MAX_LENGTH).describe(`The text to measure, up to ${MAX_LENGTH} characters.`), words_per_minute: z.number().int().min(50).max(1000).default(DEFAULT_WPM).describe(`Reading speed for the estimate. Defaults to ${DEFAULT_WPM}.`), }), output: z.object({ characters: z.number().int(), characters_excluding_spaces: z.number().int(), words: z.number().int(), sentences: z.number().int(), paragraphs: z.number().int(), reading_time_seconds: z.number().int(), reading_time: z.string().describe("The estimate rounded for people, such as 4 min."), }), run({ text, words_per_minute }) { const wordCount = count(words.segment(text), (s) => !!s.isWordLike); const seconds = Math.round((wordCount / words_per_minute) * 60); return { characters: count(graphemes.segment(text)), characters_excluding_spaces: count(graphemes.segment(text), (s) => !/^\s+$/.test(s.segment)), words: wordCount, sentences: count(sentences.segment(text), (s) => /\p{L}|\p{N}/u.test(s.segment)), paragraphs: text.split(/\n\s*\n/).filter((p) => p.trim()).length, reading_time_seconds: seconds, reading_time: readingTime(seconds), }; },});