Skip to main content
Glama
DocumentationGenerator.ts30.4 kB
/** * Comprehensive API Documentation Auto-Generation System * Extracts documentation from tool classes, types, and WordPress mappings */ import * as fs from "fs"; import * as path from "path"; import * as Tools from "@/tools/index.js"; import type { ToolDefinition } from "@/server/ToolRegistry.js"; import { LoggerFactory } from "@/utils/logger.js"; export interface DocumentationConfig { outputDir: string; includeExamples: boolean; includeWordPressMapping: boolean; generateOpenAPI: boolean; generateInteractiveHtml: boolean; validateExamples: boolean; } export interface ToolDocumentation { name: string; category: string; description: string; parameters: ParameterDocumentation[]; examples: ExampleUsage[]; wordpressEndpoint: string | undefined; requiredPermissions: string[] | undefined; returnType: string; errorCodes: ErrorDocumentation[]; relatedTools: string[]; } export interface ParameterDocumentation { name: string; type: string; required: boolean; description: string; defaultValue: unknown; allowedValues: string[] | undefined; examples: string[]; } export interface ExampleUsage { title: string; description: string; command: string; parameters: Record<string, unknown>; expectedResponse: unknown; errorExample?: { scenario: string; error: unknown; }; } export interface ErrorDocumentation { code: string; message: string; description: string; resolution: string; } export interface DocumentationOutput { tools: ToolDocumentation[]; categories: CategoryDocumentation[]; types: TypeDocumentation[]; openApiSpec: OpenAPISpecification | undefined; summary: DocumentationSummary; } export interface CategoryDocumentation { name: string; description: string; toolCount: number; tools: string[]; usagePatterns: string[]; } export interface TypeDocumentation { name: string; description: string; properties: PropertyDocumentation[]; examples: unknown[]; wordpressSource?: string; } export interface PropertyDocumentation { name: string; type: string; required: boolean; description: string; format?: string; } export interface OpenAPISpecification { openapi: string; info: Record<string, unknown>; paths: Record<string, unknown>; components: Record<string, unknown>; } export interface DocumentationSummary { totalTools: number; totalCategories: number; totalTypes: number; lastUpdated: string; version: string; coverage: { toolsWithExamples: number; toolsWithWordPressMapping: number; typesDocumented: number; }; } /** * Main Documentation Generator */ export class DocumentationGenerator { private config: DocumentationConfig; private toolCategories: Map<string, string[]> = new Map(); private wordpressEndpoints: Map<string, string> = new Map(); private logger = LoggerFactory.api(); constructor(config: Partial<DocumentationConfig> = {}) { this.config = { outputDir: "docs/api", includeExamples: true, includeWordPressMapping: true, generateOpenAPI: true, generateInteractiveHtml: true, validateExamples: false, ...config, }; this.initializeWordPressMapping(); this.initializeToolCategories(); } /** * Generate complete documentation for all tools and types */ async generateFullDocumentation(): Promise<DocumentationOutput> { this.logger.info("🚀 Starting API documentation generation..."); const tools = await this.extractAllToolDocumentation(); const categories = this.generateCategoryDocumentation(tools); const types = await this.extractTypeDocumentation(); let openApiSpec: OpenAPISpecification | undefined = undefined; if (this.config.generateOpenAPI) { openApiSpec = this.generateOpenAPISpecification(tools, types); } const summary = this.generateDocumentationSummary(tools, categories, types); const output: DocumentationOutput = { tools, categories, types, openApiSpec, summary, }; // Write documentation to files await this.writeDocumentationFiles(output); this.logger.info(`✅ Documentation generation complete! ${tools.length} tools documented.`); return output; } /** * Extract documentation from all tool classes */ private async extractAllToolDocumentation(): Promise<ToolDocumentation[]> { const toolDocs: ToolDocumentation[] = []; // Iterate through all tool classes for (const [className, ToolClass] of Object.entries(Tools)) { try { // Create tool instance let toolInstance: { getTools(): unknown[] }; if (className === "CacheTools" || className === "PerformanceTools") { // These tools need client map - use empty map for doc generation toolInstance = new ToolClass(new Map()); } else { toolInstance = new (ToolClass as new () => { getTools(): unknown[] })(); } const toolDefinitions = toolInstance.getTools(); const category = this.extractCategoryFromClassName(className); for (const toolDef of toolDefinitions) { const doc = await this.extractToolDocumentation(toolDef as ToolDefinition, category, className); toolDocs.push(doc); } } catch (_error) { this.logger.warn(`⚠️ Failed to extract documentation for ${className}:`, { _error: String(_error) }); } } return toolDocs.sort((a, b) => a.name.localeCompare(b.name)); } /** * Extract documentation for a single tool */ private async extractToolDocumentation( toolDef: ToolDefinition, category: string, className: string, ): Promise<ToolDocumentation> { const parameters = this.extractParameterDocumentation(toolDef.parameters || []); const examples = this.generateToolExamples(toolDef, category); const wordpressEndpoint = this.wordpressEndpoints.get(toolDef.name); const returnType = this.inferReturnType(toolDef.name, category); const errorCodes = this.generateErrorDocumentation(toolDef.name); const relatedTools = this.findRelatedTools(toolDef.name, category); return { name: toolDef.name, category, description: toolDef.description || `${category} tool: ${toolDef.name}`, parameters, examples, wordpressEndpoint, requiredPermissions: this.getRequiredPermissions(toolDef.name), returnType, errorCodes, relatedTools, }; } /** * Extract parameter documentation */ private extractParameterDocumentation( parameters: Array<{ name: string; type?: string; description?: string; required?: boolean; [key: string]: unknown; }>, ): ParameterDocumentation[] { return parameters.map((param) => ({ name: param.name, type: param.type || "string", required: param.required || false, description: param.description || `${param.name} parameter`, defaultValue: this.getDefaultValue(param), allowedValues: this.getAllowedValues(param) || undefined, examples: this.generateParameterExamples(param), })); } /** * Generate usage examples for tools */ private generateToolExamples(toolDef: ToolDefinition, category: string): ExampleUsage[] { const examples: ExampleUsage[] = []; // Basic usage example const basicExample = this.generateBasicExample(toolDef, category); if (basicExample) { examples.push(basicExample); } // Multi-site example (if applicable) if (this.supportsMultiSite(toolDef)) { const multiSiteExample = this.generateMultiSiteExample(toolDef, category); if (multiSiteExample) { examples.push(multiSiteExample); } } // Advanced example with all parameters const advancedExample = this.generateAdvancedExample(toolDef, category); if (advancedExample) { examples.push(advancedExample); } return examples; } /** * Generate basic usage example */ private generateBasicExample(toolDef: ToolDefinition, category: string): ExampleUsage | null { const toolName = toolDef.name; const basicParams: Record<string, unknown> = {}; // Add essential parameters const requiredParams = (toolDef.parameters || []).filter((p) => p.required); for (const param of requiredParams.slice(0, 2)) { // Limit to 2 for basic example basicParams[param.name] = this.generateExampleValue(param); } return { title: `Basic ${category} Usage`, description: `Simple example of using ${toolName}`, command: toolName, parameters: basicParams, expectedResponse: this.generateExpectedResponse(toolName, category, "basic"), errorExample: { scenario: "Authentication failure", error: { error: "Authentication failed", message: "Invalid credentials or insufficient permissions", }, }, }; } /** * Generate multi-site example */ private generateMultiSiteExample(toolDef: ToolDefinition, category: string): ExampleUsage | null { const params = { site: "site1", ...this.getExampleParameters(toolDef, 1) }; return { title: `Multi-Site ${category} Usage`, description: `Using ${toolDef.name} with specific site targeting`, command: toolDef.name, parameters: params, expectedResponse: this.generateExpectedResponse(toolDef.name, category, "multisite"), }; } /** * Generate advanced example with all parameters */ private generateAdvancedExample(toolDef: ToolDefinition, category: string): ExampleUsage | null { const allParams = this.getExampleParameters(toolDef, "all"); if (Object.keys(allParams).length <= 2) { return null; // Skip if not enough parameters for advanced example } return { title: `Advanced ${category} Configuration`, description: "Comprehensive example using all available parameters", command: toolDef.name, parameters: allParams, expectedResponse: this.generateExpectedResponse(toolDef.name, category, "advanced"), }; } /** * Generate category documentation */ private generateCategoryDocumentation(tools: ToolDocumentation[]): CategoryDocumentation[] { const categories = new Map<string, ToolDocumentation[]>(); // Group tools by category for (const tool of tools) { if (!categories.has(tool.category)) { categories.set(tool.category, []); } categories.get(tool.category)!.push(tool); } return Array.from(categories.entries()).map(([categoryName, categoryTools]) => ({ name: categoryName, description: this.getCategoryDescription(categoryName), toolCount: categoryTools.length, tools: categoryTools.map((t) => t.name).sort(), usagePatterns: this.generateUsagePatterns(categoryName, categoryTools), })); } /** * Extract type documentation from TypeScript definitions */ private async extractTypeDocumentation(): Promise<TypeDocumentation[]> { // This would analyze TypeScript files to extract type information // For now, we'll provide key WordPress and MCP types return [ { name: "WordPressPost", description: "WordPress blog post object", properties: [ { name: "id", type: "number", required: true, description: "Unique identifier", }, { name: "title", type: "string", required: true, description: "Post title", }, { name: "content", type: "string", required: true, description: "Post content", }, { name: "status", type: "string", required: true, description: "Publication status", }, ], examples: [this.generateWordPressPostExample()], wordpressSource: "/wp-json/wp/v2/posts", }, // Add more types as needed ]; } /** * Generate OpenAPI specification */ private generateOpenAPISpecification(tools: ToolDocumentation[], types: TypeDocumentation[]): OpenAPISpecification { const paths: Record<string, unknown> = {}; const components: Record<string, unknown> = { schemas: {}, parameters: {}, responses: {}, }; // Convert tools to OpenAPI paths for (const tool of tools) { const path = `/tools/${tool.name}`; paths[path] = { post: { summary: tool.description, description: `Execute ${tool.name} MCP tool`, tags: [tool.category], requestBody: { required: true, content: { "application/json": { schema: this.generateParameterSchema(tool.parameters), }, }, }, responses: { "200": { description: "Successful response", content: { "application/json": { schema: { type: "object" }, }, }, }, "400": { description: "Bad request - invalid parameters", }, "401": { description: "Authentication failed", }, "500": { description: "Internal server error", }, }, }, }; } // Add type schemas to components const schemas = components.schemas as Record<string, unknown>; for (const type of types) { schemas[type.name] = this.convertTypeToJsonSchema(type); } return { openapi: "3.0.3", info: { title: "WordPress MCP Server API", description: "Model Context Protocol server for WordPress management", version: "1.2.0", contact: { name: "MCP WordPress", url: "https://github.com/docdyhr/mcp-wordpress", }, license: { name: "MIT", url: "https://opensource.org/licenses/MIT", }, }, paths, components, }; } /** * Write all documentation files */ private async writeDocumentationFiles(output: DocumentationOutput): Promise<void> { const outputDir = this.config.outputDir; // Ensure output directory exists await fs.promises.mkdir(outputDir, { recursive: true }); await fs.promises.mkdir(path.join(outputDir, "tools"), { recursive: true }); await fs.promises.mkdir(path.join(outputDir, "types"), { recursive: true }); await fs.promises.mkdir(path.join(outputDir, "examples"), { recursive: true, }); await fs.promises.mkdir(path.join(outputDir, "categories"), { recursive: true, }); // Write main API documentation await this.writeApiOverview(output); // Write individual tool documentation for (const tool of output.tools) { await this.writeToolDocumentation(tool); } // Write category documentation for (const category of output.categories) { await this.writeCategoryDocumentation(category); } // Write type documentation for (const type of output.types) { await this.writeTypeDocumentation(type); } // Write OpenAPI specification if (output.openApiSpec) { await fs.promises.writeFile(path.join(outputDir, "openapi.json"), JSON.stringify(output.openApiSpec, null, 2)); } // Write summary await fs.promises.writeFile(path.join(outputDir, "summary.json"), JSON.stringify(output.summary, null, 2)); this.logger.info(`📁 Documentation written to ${outputDir}/`); } // Helper methods for specific documentation tasks... private extractCategoryFromClassName(className: string): string { return className.replace("Tools", "").toLowerCase(); } private initializeWordPressMapping(): void { // Map MCP tools to WordPress REST API endpoints this.wordpressEndpoints.set("wp_list_posts", "/wp-json/wp/v2/posts"); this.wordpressEndpoints.set("wp_get_post", "/wp-json/wp/v2/posts/{id}"); this.wordpressEndpoints.set("wp_create_post", "/wp-json/wp/v2/posts"); this.wordpressEndpoints.set("wp_update_post", "/wp-json/wp/v2/posts/{id}"); this.wordpressEndpoints.set("wp_delete_post", "/wp-json/wp/v2/posts/{id}"); // Add more mappings... } private initializeToolCategories(): void { this.toolCategories.set("Posts", [ "wp_list_posts", "wp_get_post", "wp_create_post", "wp_update_post", "wp_delete_post", "wp_search_posts", ]); this.toolCategories.set("Pages", [ "wp_list_pages", "wp_get_page", "wp_create_page", "wp_update_page", "wp_delete_page", "wp_search_pages", ]); // Add more categories... } private generateDocumentationSummary( tools: ToolDocumentation[], categories: CategoryDocumentation[], types: TypeDocumentation[], ): DocumentationSummary { return { totalTools: tools.length, totalCategories: categories.length, totalTypes: types.length, lastUpdated: new Date().toISOString(), version: "1.2.0", coverage: { toolsWithExamples: tools.filter((t) => t.examples.length > 0).length, toolsWithWordPressMapping: tools.filter((t) => t.wordpressEndpoint).length, typesDocumented: types.length, }, }; } /** * Helper methods for documentation generation */ private getDefaultValue(param: { [key: string]: unknown }): unknown { const defaults: Record<string, unknown> = { per_page: 10, page: 1, order: "desc", orderby: "date", status: "publish", format: "summary", category: "all", includeExamples: true, includeTrends: true, }; return defaults[String(param.name)]; } private getAllowedValues(param: { [key: string]: unknown }): string[] | undefined { const allowedValues: Record<string, string[]> = { status: ["publish", "draft", "private", "pending", "future"], order: ["asc", "desc"], orderby: ["date", "title", "author", "modified"], format: ["summary", "detailed", "raw"], category: ["overview", "requests", "cache", "system", "tools", "all"], timeframe: ["1h", "6h", "12h", "24h", "7d", "30d"], priority: ["quick_wins", "medium_term", "long_term", "all"], focus: ["speed", "reliability", "efficiency", "scaling"], }; return allowedValues[String(param.name)]; } private generateParameterExamples(param: { name: string; type?: string; [key: string]: unknown }): string[] { const examples: Record<string, string[]> = { id: ["123", "456"], title: ["My Blog Post", "Hello World"], content: ["<p>Post content here</p>", "This is my post content"], site: ["site1", "production", "staging"], per_page: ["10", "20", "50"], search: ["wordpress", "tutorial"], author: ["1", "2"], email: ["user@example.com", "admin@site.com"], username: ["john_doe", "admin"], limit: ["10", "20", "50"], timeframe: ["24h", "7d", "1h"], }; return examples[param.name] || ["example"]; } private supportsMultiSite(toolDef: ToolDefinition): boolean { // All tools support multi-site via the site parameter return toolDef.parameters?.some((p) => p.name === "site") ?? true; } private generateExampleValue(param: { name: string; type?: string; [key: string]: unknown }): unknown { const exampleValues: Record<string, unknown> = { id: 123, title: "Example Post Title", content: "This is example content for the post.", site: "site1", per_page: 10, page: 1, search: "wordpress", author: 1, email: "user@example.com", username: "john_doe", status: "publish", order: "desc", orderby: "date", limit: 20, timeframe: "24h", format: "summary", category: "overview", }; return exampleValues[param.name] || "example_value"; } private generateExpectedResponse(toolName: string, category: string, type: string): unknown { if (toolName.includes("list")) { return { success: true, data: [ { id: 1, title: `Example ${category} 1`, status: "publish" }, { id: 2, title: `Example ${category} 2`, status: "draft" }, ], total: 2, pages: 1, }; } if (toolName.includes("get")) { return { success: true, data: { id: 123, title: `Example ${category}`, content: "Example content", status: "publish", date: "2024-01-01T00:00:00Z", }, }; } if (toolName.includes("create") || toolName.includes("update")) { return { success: true, data: { id: 123, title: "Created/Updated successfully", status: "publish", }, }; } if (toolName.includes("delete")) { return { success: true, data: { deleted: true, id: 123, }, }; } if (toolName.includes("performance")) { return { success: true, data: { overview: { overallHealth: "Good", performanceScore: 85, averageResponseTime: "245ms", cacheHitRate: "87.5%", }, }, }; } return { success: true, data: {}, message: `${toolName} executed successfully`, }; } private getExampleParameters(toolDef: ToolDefinition, type: string | number): Record<string, unknown> { const params: Record<string, unknown> = {}; const parameters = toolDef.parameters || []; if (type === "all") { // Include all parameters for (const param of parameters) { params[param.name] = this.generateExampleValue(param); } } else if (typeof type === "number") { // Include limited number of parameters for (const param of parameters.slice(0, type)) { params[param.name] = this.generateExampleValue(param); } } return params; } private getCategoryDescription(categoryName: string): string { const descriptions: Record<string, string> = { posts: "Blog post creation, editing, and management tools", pages: "Static page creation and management tools", media: "File upload, management, and media library tools", users: "User account management and authentication tools", comments: "Comment moderation and management tools", taxonomies: "Category and tag management tools", site: "Site settings and configuration tools", auth: "Authentication testing and management tools", cache: "Performance caching and optimization tools", performance: "Performance monitoring and analytics tools", }; return descriptions[categoryName.toLowerCase()] || `${categoryName} management tools`; } private generateUsagePatterns(categoryName: string, tools: ToolDocumentation[]): string[] { const patterns: Record<string, string[]> = { posts: [ "Create and publish blog posts", "Bulk edit multiple posts", "Search and filter posts by criteria", "Schedule posts for future publication", ], media: [ "Upload images and files", "Organize media library", "Generate thumbnails and variants", "Bulk media operations", ], users: [ "Manage user accounts and roles", "User authentication and permissions", "Bulk user operations", "User profile management", ], performance: [ "Monitor real-time performance metrics", "Analyze historical performance trends", "Generate optimization recommendations", "Export performance reports", ], }; return ( patterns[categoryName.toLowerCase()] || [ `Manage ${categoryName.toLowerCase()} efficiently`, `Bulk ${categoryName.toLowerCase()} operations`, `Search and filter ${categoryName.toLowerCase()}`, ] ); } private generateWordPressPostExample(): Record<string, unknown> { return { id: 123, title: "Welcome to WordPress", content: "<p>This is your first post. Edit or delete it to get started!</p>", status: "publish", date: "2024-01-01T00:00:00Z", author: 1, categories: [1], tags: [1, 2], featured_media: 0, excerpt: "A sample WordPress post", slug: "welcome-to-wordpress", }; } private generateParameterSchema(parameters: ParameterDocumentation[]): Record<string, unknown> { const properties: Record<string, unknown> = {}; const required: string[] = []; for (const param of parameters) { properties[param.name] = { type: param.type, description: param.description, }; if (param.allowedValues) { (properties[param.name] as Record<string, unknown>).enum = param.allowedValues; } if (param.defaultValue !== undefined) { (properties[param.name] as Record<string, unknown>).default = param.defaultValue; } if (param.required) { required.push(param.name); } } return { type: "object", properties, required: required.length > 0 ? required : undefined, }; } private convertTypeToJsonSchema(type: TypeDocumentation): Record<string, unknown> { const properties: Record<string, unknown> = {}; const required: string[] = []; for (const prop of type.properties) { properties[prop.name] = { type: prop.type, description: prop.description, }; if (prop.format) { (properties[prop.name] as Record<string, unknown>).format = prop.format; } if (prop.required) { required.push(prop.name); } } return { type: "object", description: type.description, properties, required: required.length > 0 ? required : undefined, }; } private inferReturnType(toolName: string, category: string): string { if (toolName.includes("list")) return `${category}[]`; if (toolName.includes("get")) return category; if (toolName.includes("create")) return category; if (toolName.includes("update")) return category; if (toolName.includes("delete")) return "DeleteResult"; if (toolName.includes("search")) return `${category}[]`; if (toolName.includes("performance")) return "PerformanceMetrics"; if (toolName.includes("cache")) return "CacheStats"; return "object"; } private generateErrorDocumentation(toolName: string): ErrorDocumentation[] { return [ { code: "AUTHENTICATION_FAILED", message: "Authentication failed", description: "Invalid credentials or insufficient permissions", resolution: "Check your authentication credentials and user permissions", }, { code: "VALIDATION_ERROR", message: "Parameter validation failed", description: "One or more required parameters are missing or invalid", resolution: "Review the required parameters and their formats", }, { code: "NOT_FOUND", message: "Resource not found", description: "The requested resource does not exist", resolution: "Verify the resource ID and ensure it exists", }, { code: "PERMISSION_DENIED", message: "Insufficient permissions", description: "The user does not have permission to perform this action", resolution: "Contact an administrator to grant the necessary permissions", }, ]; } private findRelatedTools(toolName: string, category: string): string[] { // Find tools in the same category const categoryTools = this.toolCategories.get(category) || []; return categoryTools.filter((tool) => tool !== toolName).slice(0, 3); } private getRequiredPermissions(toolName: string): string[] | undefined { const permissions: Record<string, string[]> = { wp_create_post: ["publish_posts", "edit_posts"], wp_update_post: ["edit_posts"], wp_delete_post: ["delete_posts"], wp_create_page: ["publish_pages", "edit_pages"], wp_update_page: ["edit_pages"], wp_delete_page: ["delete_pages"], wp_upload_media: ["upload_files"], wp_delete_media: ["delete_files"], wp_create_user: ["create_users"], wp_update_user: ["edit_users"], wp_delete_user: ["delete_users"], wp_moderate_comment: ["moderate_comments"], wp_get_site_settings: ["manage_options"], }; return permissions[toolName]; } /** * File writing implementations */ private async writeApiOverview(output: DocumentationOutput): Promise<void> { const { MarkdownFormatter } = await import("./MarkdownFormatter.js"); const formatter = new MarkdownFormatter(); const content = formatter.generateApiOverview(output); await fs.promises.writeFile(path.join(this.config.outputDir, "README.md"), content); } private async writeToolDocumentation(tool: ToolDocumentation): Promise<void> { const { MarkdownFormatter } = await import("./MarkdownFormatter.js"); const formatter = new MarkdownFormatter(); const content = formatter.generateToolDocumentation(tool); await fs.promises.writeFile(path.join(this.config.outputDir, "tools", `${tool.name}.md`), content); } private async writeCategoryDocumentation(category: CategoryDocumentation): Promise<void> { const { MarkdownFormatter } = await import("./MarkdownFormatter.js"); const formatter = new MarkdownFormatter(); const content = formatter.generateCategoryDocumentation(category); await fs.promises.writeFile( path.join(this.config.outputDir, "categories", `${category.name.toLowerCase()}.md`), content, ); } private async writeTypeDocumentation(type: TypeDocumentation): Promise<void> { const { MarkdownFormatter } = await import("./MarkdownFormatter.js"); const formatter = new MarkdownFormatter(); const content = formatter.generateTypeDocumentation(type); await fs.promises.writeFile(path.join(this.config.outputDir, "types", `${type.name}.md`), content); } }

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/docdyhr/mcp-wordpress'

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