Skip to main content
Glama
markdown-formatter.ts7.33 kB
/** * Markdown Formatter for CLI Output * * Generates markdown documentation for WP Navigator MCP tools. * * @package WP_Navigator_MCP * @since 2.3.0 */ import { Tool } from '@modelcontextprotocol/sdk/types.js'; // ============================================================================= // Types // ============================================================================= export interface ParameterInfo { name: string; type: string; required: boolean; description: string; enum?: string[]; default?: unknown; } export interface ToolDocumentation { name: string; description: string; category: string; parameters: ParameterInfo[]; } export interface FormatOptions { includeExamples?: boolean; includeTableOfContents?: boolean; } // ============================================================================= // Parameter Extraction // ============================================================================= /** * Extract parameter information from a tool's inputSchema */ export function extractParameters(tool: Tool): ParameterInfo[] { const schema = tool.inputSchema as { type?: string; properties?: Record< string, { type?: string; description?: string; enum?: string[]; default?: unknown; } >; required?: string[]; }; if (!schema || schema.type !== 'object' || !schema.properties) { return []; } const required = schema.required || []; const params: ParameterInfo[] = []; for (const [name, prop] of Object.entries(schema.properties)) { params.push({ name, type: prop.type || 'unknown', required: required.includes(name), description: prop.description || '', enum: prop.enum, default: prop.default, }); } // Sort: required params first, then alphabetically return params.sort((a, b) => { if (a.required !== b.required) { return a.required ? -1 : 1; } return a.name.localeCompare(b.name); }); } // ============================================================================= // Markdown Generation // ============================================================================= /** * Escape pipe characters for markdown tables */ function escapeTableCell(text: string): string { return text.replace(/\|/g, '\\|').replace(/\n/g, ' '); } /** * Format a parameter type with enum values if present */ function formatType(param: ParameterInfo): string { let type = param.type; if (param.enum && param.enum.length > 0) { type = param.enum.map((v) => `"${v}"`).join(' \\| '); } return type; } /** * Format description with default value if present */ function formatDescription(param: ParameterInfo): string { let desc = param.description; if (param.default !== undefined) { const defaultStr = typeof param.default === 'string' ? `"${param.default}"` : String(param.default); desc += ` (default: ${defaultStr})`; } return escapeTableCell(desc); } /** * Generate a parameter table for a tool */ export function formatParameterTable(params: ParameterInfo[]): string { if (params.length === 0) { return '*No parameters*\n'; } const lines: string[] = [ '| Name | Type | Required | Description |', '|------|------|----------|-------------|', ]; for (const param of params) { lines.push( `| ${param.name} | ${formatType(param)} | ${param.required ? 'Yes' : 'No'} | ${formatDescription(param)} |` ); } return lines.join('\n') + '\n'; } /** * Generate an example JSON for a tool */ export function generateExample(params: ParameterInfo[]): string { if (params.length === 0) { return '```json\n{}\n```\n'; } const example: Record<string, unknown> = {}; for (const param of params) { if (param.required) { // Always include required params example[param.name] = getExampleValue(param); } else if (params.filter((p) => p.required).length === 0) { // If no required params, show first 2 optional ones if (Object.keys(example).length < 2) { example[param.name] = getExampleValue(param); } } } return '```json\n' + JSON.stringify(example, null, 2) + '\n```\n'; } /** * Get an example value for a parameter */ function getExampleValue(param: ParameterInfo): unknown { // Use default if available if (param.default !== undefined) { return param.default; } // Use first enum value if available if (param.enum && param.enum.length > 0) { return param.enum[0]; } // Generate based on type switch (param.type) { case 'string': return `example_${param.name}`; case 'number': case 'integer': return 10; case 'boolean': return true; case 'array': return []; case 'object': return {}; default: return null; } } /** * Format a category name for display */ function formatCategoryName(category: string): string { return category.charAt(0).toUpperCase() + category.slice(1) + ' Tools'; } /** * Format a single tool as markdown */ export function formatToolMarkdown(tool: ToolDocumentation, options: FormatOptions = {}): string { const lines: string[] = []; // Tool heading lines.push(`### ${tool.name}\n`); // Description lines.push(`${tool.description}\n`); // Parameters section lines.push('**Parameters:**\n'); lines.push(formatParameterTable(tool.parameters)); // Example section (if requested and has parameters) if (options.includeExamples && tool.parameters.length > 0) { lines.push('**Example:**\n'); lines.push(generateExample(tool.parameters)); } return lines.join('\n'); } /** * Format all tools as markdown documentation */ export function formatToolsAsMarkdown( toolsByCategory: Record<string, ToolDocumentation[]>, options: FormatOptions = {} ): string { const lines: string[] = []; // Title lines.push('# WP Navigator MCP Tools\n'); // Table of contents (if requested) if (options.includeTableOfContents) { lines.push('## Table of Contents\n'); for (const category of Object.keys(toolsByCategory).sort()) { const anchor = category.toLowerCase().replace(/\s+/g, '-'); lines.push(`- [${formatCategoryName(category)}](#${anchor}-tools)`); } lines.push(''); } // Tools by category const sortedCategories = Object.keys(toolsByCategory).sort(); for (const category of sortedCategories) { const tools = toolsByCategory[category]; // Category heading lines.push(`## ${formatCategoryName(category)}\n`); // Sort tools alphabetically within category const sortedTools = [...tools].sort((a, b) => a.name.localeCompare(b.name)); for (const tool of sortedTools) { lines.push(formatToolMarkdown(tool, options)); lines.push('---\n'); } } return lines.join('\n'); } // ============================================================================= // Conversion Helper // ============================================================================= /** * Convert a Tool from the registry to ToolDocumentation */ export function toolToDocumentation(tool: Tool, category: string): ToolDocumentation { return { name: tool.name, description: tool.description || '', category, parameters: extractParameters(tool), }; }

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/littlebearapps/wp-navigator-mcp'

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