import { WordPressClient } from "@/client/api.js"; import type { MCPToolSchema } from "@/types/mcp.js"; import { WordPressApplicationPassword } from "@/types/wordpress.js"; import { preserveToolError } from "@/utils/error.js"; import { htmlToPlainText } from "@/utils/htmlText.js"; /** * Provides tools for managing general site settings and operations on a WordPress site. * This class encapsulates tool definitions and their corresponding handlers. */ export class SiteTools { /** * Retrieves the list of site management tools. * @returns An array of MCPTool definitions. */ public getTools(): Array<{ name: string; description: string; inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ { name: "wp_get_site_settings", description: "Retrieves the general settings for a WordPress site. Requires administrator role (manage_options capability).", inputSchema: { type: "object", properties: {}, }, handler: this.handleGetSiteSettings.bind(this), }, { name: "wp_update_site_settings", description: "Updates one or more general settings for a WordPress site. Requires administrator role (manage_options capability).", inputSchema: { type: "object", properties: { title: { type: "string", description: "The title of the site.", }, description: { type: "string", description: "The tagline or description of the site.", }, timezone: { type: "string", description: "A city in the same timezone, e.g., 'America/New_York'.", }, }, }, handler: this.handleUpdateSiteSettings.bind(this), }, { name: "wp_search_site", description: "Performs a site-wide search for content across posts, pages, and media with comprehensive results and metadata.\n\n" + "**Usage Examples:**\n" + '• Search everything: `wp_search_site --term="WordPress"`\n' + '• Search posts only: `wp_search_site --term="tutorial" --type="posts"`\n' + '• Search pages: `wp_search_site --term="about" --type="pages"`\n' + '• Search media: `wp_search_site --term="logo" --type="media"`\n' + '• Find specific content: `wp_search_site --term="contact form"`', inputSchema: { type: "object", properties: { term: { type: "string", description: "The search term to look for.", }, type: { type: "string", description: "The type of content to search.", enum: ["posts", "pages", "media"], }, }, required: ["term"], }, handler: this.handleSearchSite.bind(this), }, { name: "wp_get_application_passwords", description: "Lists application passwords for a specific user.", inputSchema: { type: "object", properties: { user_id: { type: "number", description: "The ID of the user to get application passwords for.", }, }, required: ["user_id"], }, handler: this.handleGetApplicationPasswords.bind(this), }, { name: "wp_create_application_password", description: "Creates a new application password for a user.", inputSchema: { type: "object", properties: { user_id: { type: "number", description: "The ID of the user to create the password for.", }, app_name: { type: "string", description: "The name of the application this password is for.", }, }, required: ["user_id", "app_name"], }, handler: this.handleCreateApplicationPassword.bind(this), }, { name: "wp_delete_application_password", description: "Revokes an existing application password.", inputSchema: { type: "object", properties: { user_id: { type: "number", description: "The ID of the user who owns the password.", }, uuid: { type: "string", description: "The UUID of the application password to revoke.", }, }, required: ["user_id", "uuid"], }, handler: this.handleDeleteApplicationPassword.bind(this), }, ]; } public async handleGetSiteSettings(client: WordPressClient, _params: Record): Promise { try { const settings = await client.getSiteSettings(); const siteUrl = client.getSiteUrl(); // Enhanced site settings with comprehensive details const weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; const startOfWeek = settings.start_of_week !== undefined ? weekDays[settings.start_of_week] : "Not set"; // Get additional site information const currentTime = new Date().toLocaleString("en-US", { timeZone: settings.timezone || "UTC", weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZoneName: "short", }); let content = `**🌐 Site Settings for ${siteUrl}**\n\n`; content += `**📋 Basic Information:**\n`; content += `- **Title:** ${settings.title || "Not set"}\n`; content += `- **Description:** ${settings.description || "Not set"}\n`; content += `- **URL:** ${settings.url || siteUrl}\n`; content += `- **Admin Email:** ${settings.email || "Not set"}\n\n`; content += `**🌍 Localization:**\n`; content += `- **Language:** ${settings.language || "English (US)"}\n`; content += `- **Timezone:** ${settings.timezone || "UTC"}\n`; content += `- **Current Time:** ${currentTime}\n\n`; content += `**📅 Date & Time Format:**\n`; content += `- **Date Format:** ${settings.date_format || "Not set"}\n`; content += `- **Time Format:** ${settings.time_format || "Not set"}\n`; content += `- **Start of Week:** ${startOfWeek}\n\n`; content += `**📝 Content Settings:**\n`; content += `- **Posts per Page:** ${settings.posts_per_page || "Not set"}\n`; content += `- **Default Category:** ${settings.default_category || "Not set"}\n`; content += `- **Default Post Format:** ${settings.default_post_format || "Standard"}\n\n`; content += `**💬 Discussion Settings:**\n`; content += `- **Default Comment Status:** ${settings.default_comment_status || "Not set"}\n`; content += `- **Default Ping Status:** ${settings.default_ping_status || "Not set"}\n`; content += `- **Use Smilies:** ${settings.use_smilies ? "Yes" : "No"}\n\n`; content += `**📊 Retrieved:** ${new Date().toLocaleString()}`; return content; } catch (_error) { preserveToolError("Failed to get site settings", _error); } } public async handleUpdateSiteSettings(client: WordPressClient, params: Record): Promise { try { const updatedSettings = await client.updateSiteSettings(params); return `✅ Site settings updated successfully. New title: ${updatedSettings.title}`; } catch (_error) { preserveToolError("Failed to update site settings", _error); } } public async handleSearchSite(client: WordPressClient, params: Record): Promise { try { const { term, type } = params as { term: string; type?: "posts" | "pages" | "media" }; const results = await client.search(term, type ? [type] : undefined); if (results.length === 0) { return `No results found for "${term}".`; } const content = `Found ${results.length} results for "${term}":\n\n` + results .map((r) => { // Titles arrive entity-encoded; an empty one (e.g. a custom post type) must still be identifiable. const title = htmlToPlainText(r.title); const label = title ? `**${title}**` : `**(untitled)** (${r.subtype || r.type}, ID ${r.id})`; return `- [${r.type}] ${label}\n Link: ${r.url}`; }) .join("\n"); return content; } catch (_error) { preserveToolError("Failed to perform search", _error); } } public async handleGetApplicationPasswords( client: WordPressClient, params: Record, ): Promise { try { const { user_id } = params as { user_id: number }; const passwords = await client.getApplicationPasswords(user_id); if (passwords.length === 0) { return `No application passwords found for user ID ${user_id}.`; } const content = `Found ${passwords.length} application passwords for user ID ${user_id}:\n\n` + passwords .map( (p: WordPressApplicationPassword) => `- **${p.name}** (UUID: ${p.uuid})\n Created: ${new Date(p.created).toLocaleDateString()}`, ) .join("\n"); return content; } catch (_error) { preserveToolError("Failed to get application passwords", _error); } } public async handleCreateApplicationPassword( client: WordPressClient, params: Record, ): Promise { try { const { user_id, app_name } = params as { user_id: number; app_name: string }; const result = await client.createApplicationPassword(user_id, app_name); const content = "✅ **Application password created successfully!**\n\n" + `**Name:** ${result.name}\n` + `**Password:** \`${result.password}\`\n\n` + "**IMPORTANT:** This password is shown only once. Please save it securely."; return content; } catch (_error) { preserveToolError("Failed to create application password", _error); } } public async handleDeleteApplicationPassword( client: WordPressClient, params: Record, ): Promise { try { const { user_id, uuid } = params as { user_id: number; uuid: string }; await client.deleteApplicationPassword(user_id, uuid); return `✅ Application password with UUID ${uuid} has been revoked.`; } catch (_error) { preserveToolError("Failed to delete application password", _error); } } } export default SiteTools;