Rebasely Beta: data may be purged

examples/MCP-tools Public

TypeScript · 57 lines · 2.3 KiB · at dae155a

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),    };  },});