Skip to main content
Glama

Google Maps MCP Server

by iceener
formatting.ts6.37 kB
/** * Message formatting utilities for consistent, LLM-friendly output. * * These utilities help create clear, scannable responses that are optimized * for both LLM understanding and human readability. */ /** * Summarize a list of items with preview and details sections. * * @param items - Array of items to summarize * @param formatPreview - Function to format a single-line preview of each item * @param options - Formatting options * @returns Formatted summary with preview list and optional details * * @example * const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]; * const summary = summarizeList(users, (u) => `${u.id}. ${u.name}`); * // Returns: * // ## List (2 items) * // 1. Alice * // 2. Bob */ export function summarizeList<T>( items: T[], formatPreview: (item: T) => string, options: { title?: string; maxPreview?: number; detailsFormatter?: (item: T) => string; maxDetails?: number; } = {}, ): string { const { title = 'List', maxPreview = 100, detailsFormatter, maxDetails = 5, } = options; if (items.length === 0) { return `## ${title} (0 items)\n\nNo items found.`; } const parts: string[] = []; // Preview section const previewItems = items.slice(0, maxPreview); const hasMore = items.length > maxPreview; parts.push(`## ${title} (${items.length} items)`); parts.push(''); parts.push(...previewItems.map(formatPreview)); if (hasMore) { parts.push(`... and ${items.length - maxPreview} more`); } // Details section (optional) if (detailsFormatter && items.length > 0) { parts.push(''); parts.push('## Details'); parts.push(''); const detailItems = items.slice(0, maxDetails); parts.push(...detailItems.map(detailsFormatter)); if (items.length > maxDetails) { parts.push(''); parts.push( `_Showing ${maxDetails} of ${items.length} items. Use pagination to see more._`, ); } } return parts.join('\n'); } /** * Summarize the results of a batch operation. * * @param results - Array of operation results * @param options - Formatting options * @returns Formatted batch summary with success/failure counts * * @example * const results = [ * { success: true, id: '1', message: 'Created user Alice' }, * { success: false, id: '2', message: 'User Bob already exists' }, * ]; * const summary = summarizeBatch(results, { * operationName: 'Create Users', * successFormatter: (r) => `✓ ${r.message}`, * errorFormatter: (r) => `✗ ${r.message}`, * }); */ export function summarizeBatch<T extends { success: boolean }>( results: T[], options: { operationName: string; successFormatter: (result: T) => string; errorFormatter: (result: T) => string; }, ): string { const { operationName, successFormatter, errorFormatter } = options; const successes = results.filter((r) => r.success); const failures = results.filter((r) => !r.success); const parts: string[] = []; parts.push(`## ${operationName} Results`); parts.push(''); parts.push( `**Summary**: ${successes.length} succeeded, ${failures.length} failed (${results.length} total)`, ); if (successes.length > 0) { parts.push(''); parts.push('### Successful Operations'); parts.push(''); parts.push(...successes.map(successFormatter)); } if (failures.length > 0) { parts.push(''); parts.push('### Failed Operations'); parts.push(''); parts.push(...failures.map(errorFormatter)); } return parts.join('\n'); } /** * Format a field change for before/after comparison. * * @param fieldName - Human-readable field name * @param before - Value before change * @param after - Value after change * @returns Formatted change description * * @example * formatFieldChange('Status', 'pending', 'completed') * // Returns: "Status: pending → completed" */ export function formatFieldChange( fieldName: string, before: string | number | boolean | null | undefined, after: string | number | boolean | null | undefined, ): string { const formatValue = (value: string | number | boolean | null | undefined): string => { if (value === null || value === undefined) return '—'; if (typeof value === 'boolean') return value ? 'true' : 'false'; return String(value); }; return `${fieldName}: ${formatValue(before)} → ${formatValue(after)}`; } /** * Create a structured markdown section with optional tags. * * @param content - Content to wrap * @param options - Section options * @returns Formatted section with tags * * @example * createSection('User details: Alice', { tag: 'user_info' }) * // Returns: * // <ove tag="user_info"> * // User details: Alice * // </ove> */ export function createSection( content: string, options: { tag?: string; indent?: number; } = {}, ): string { const { tag, indent = 0 } = options; const indentation = ' '.repeat(indent); if (!tag) { return content .split('\n') .map((line) => indentation + line) .join('\n'); } const lines: string[] = []; lines.push(`${indentation}<ove tag="${tag}">`); lines.push( ...content.split('\n').map((line) => indentation + (line ? ` ${line}` : line)), ); lines.push(`${indentation}</ove>`); return lines.join('\n'); } /** * Truncate text to a maximum length with ellipsis. * * @param text - Text to truncate * @param maxLength - Maximum length (default: 100) * @returns Truncated text */ export function truncate(text: string, maxLength = 100): string { if (text.length <= maxLength) return text; return `${text.slice(0, maxLength - 3)}...`; } /** * Format a list of key-value pairs as markdown. * * @param pairs - Object with key-value pairs * @returns Formatted markdown list * * @example * formatKeyValueList({ name: 'Alice', age: 30, role: 'Admin' }) * // Returns: * // - **Name**: Alice * // - **Age**: 30 * // - **Role**: Admin */ export function formatKeyValueList( pairs: Record<string, string | number | boolean | null | undefined>, ): string { return Object.entries(pairs) .filter(([_key, value]) => value !== null && value !== undefined) .map(([key, value]) => { const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1); return `- **${capitalizedKey}**: ${value}`; }) .join('\n'); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/iceener/maps-streamable-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server