Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 29 additions & 75 deletions src/tools/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,42 @@ export class CacheTools {
{
name: "wp_cache_stats",
description: "Get cache statistics for a WordPress site.",
parameters: [
{
name: "site",
type: "string",
description:
"Site ID to get cache stats for. If not provided, uses default site or fails if multiple sites configured.",
},
],
inputSchema: {
type: "object" as const,
properties: {},
},
handler: this.handleGetCacheStats.bind(this),
},
{
name: "wp_cache_clear",
description: "Clear cache for a WordPress site.",
parameters: [
{
name: "site",
type: "string",
description: "Site ID to clear cache for.",
inputSchema: {
type: "object" as const,
properties: {
pattern: {
type: "string",
description: 'Optional pattern to clear specific cache entries (e.g., "posts", "categories").',
},
},
{
name: "pattern",
type: "string",
description: 'Optional pattern to clear specific cache entries (e.g., "posts", "categories").',
},
],
},
handler: this.handleClearCache.bind(this),
},
{
name: "wp_cache_warm",
description: "Pre-warm cache with essential WordPress data.",
parameters: [
{
name: "site",
type: "string",
description: "Site ID to warm cache for.",
},
],
inputSchema: {
type: "object" as const,
properties: {},
},
handler: this.handleWarmCache.bind(this),
},
{
name: "wp_cache_info",
description: "Get detailed cache configuration and status information.",
parameters: [
{
name: "site",
type: "string",
description: "Site ID to get cache info for.",
},
],
inputSchema: {
type: "object" as const,
properties: {},
},
handler: this.handleGetCacheInfo.bind(this),
},
];
Expand All @@ -78,10 +65,8 @@ export class CacheTools {
/**
* Get cache statistics
*/
async handleGetCacheStats(params: { site?: string }) {
async handleGetCacheStats(client: WordPressClient, _params: Record<string, unknown>) {
return toolWrapper(async () => {
const client = this.resolveClient(params.site);

if (!(client instanceof CachedWordPressClient)) {
return {
caching_enabled: false,
Expand Down Expand Up @@ -112,10 +97,8 @@ export class CacheTools {
/**
* Clear cache
*/
async handleClearCache(params: { site?: string; pattern?: string }) {
async handleClearCache(client: WordPressClient, params: Record<string, unknown>) {
return toolWrapper(async () => {
const client = this.resolveClient(params.site);

if (!(client instanceof CachedWordPressClient)) {
return {
success: false,
Expand All @@ -124,14 +107,15 @@ export class CacheTools {
}

let cleared: number;
const pattern = params.pattern as string | undefined;

if (params.pattern) {
cleared = client.clearCachePattern(params.pattern);
if (pattern) {
cleared = client.clearCachePattern(pattern);
return {
success: true,
message: `Cleared ${cleared} cache entries matching pattern "${params.pattern}".`,
message: `Cleared ${cleared} cache entries matching pattern "${pattern}".`,
cleared_entries: cleared,
pattern: params.pattern,
pattern,
};
} else {
cleared = client.clearCache();
Expand All @@ -147,10 +131,8 @@ export class CacheTools {
/**
* Warm cache with essential data
*/
async handleWarmCache(params: { site?: string }) {
async handleWarmCache(client: WordPressClient, _params: Record<string, unknown>) {
return toolWrapper(async () => {
const client = this.resolveClient(params.site);

if (!(client instanceof CachedWordPressClient)) {
return {
success: false,
Expand All @@ -174,10 +156,8 @@ export class CacheTools {
/**
* Get detailed cache information
*/
async handleGetCacheInfo(params: { site?: string }) {
async handleGetCacheInfo(client: WordPressClient, _params: Record<string, unknown>) {
return toolWrapper(async () => {
const client = this.resolveClient(params.site);

if (!(client instanceof CachedWordPressClient)) {
return {
caching_enabled: false,
Expand Down Expand Up @@ -224,32 +204,6 @@ export class CacheTools {
};
});
}

/**
* Resolve client from site parameter
*/
private resolveClient(siteId?: string): WordPressClient {
if (!siteId) {
if (this.clients.size === 1) {
return Array.from(this.clients.values())[0];
} else if (this.clients.size === 0) {
throw new Error("No WordPress sites configured.");
} else {
throw new Error(
`Multiple sites configured. Please specify --site parameter. Available sites: ${Array.from(
this.clients.keys(),
).join(", ")}`,
);
}
}

const client = this.clients.get(siteId);
if (!client) {
throw new Error(`Site "${siteId}" not found. Available sites: ${Array.from(this.clients.keys()).join(", ")}`);
}

return client;
}
}

export default CacheTools;
17 changes: 15 additions & 2 deletions src/tools/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,21 @@ export class PageTools {
public async handleDeletePage(client: WordPressClient, params: Record<string, unknown>): Promise<unknown> {
const { id, force } = params as { id: number; force?: boolean };
try {
await client.deletePage(id, force);
const action = params.force ? "permanently deleted" : "moved to trash";
const result = await client.deletePage(id, force);
const action = force ? "permanently deleted" : "moved to trash";

if (result?.deleted === false) {
throw new Error(
`WordPress refused to delete page ${id}. The page may be protected or the operation was rejected.`,
);
}

if (result?.deleted) {
const title = result.previous?.title?.rendered;
return title ? `✅ Page "${title}" has been ${action}.` : `✅ Page ${id} has been ${action}.`;
}

// Some WordPress installations return empty/null responses on successful deletion
Comment on lines +224 to +229
return `✅ Page ${id} has been ${action}.`;
} catch (_error) {
throw new Error(`Failed to delete page: ${getErrorMessage(_error)}`);
Expand Down
10 changes: 8 additions & 2 deletions src/tools/performance/PerformanceTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ export default class PerformanceTools {
return [
{
name: "wp_performance_stats",
description: "Get real-time performance statistics and metrics",
description:
"Get real-time performance statistics and metrics. " +
"Note: Top-level metrics (totalRequests, averageResponseTime, errorRate) are session-wide aggregates across all sites. " +
"Per-site cache and client stats are shown in the siteSpecific section when a site parameter is provided.",
parameters: [
{
name: "site",
Expand Down Expand Up @@ -156,7 +159,9 @@ export default class PerformanceTools {
},
{
name: "wp_performance_benchmark",
description: "Compare current performance against industry benchmarks",
description:
"Compare current performance against industry benchmarks. " +
"Note: Benchmarks are based on session-wide aggregated metrics across all sites, not per-site metrics.",
parameters: [
{
name: "site",
Expand Down Expand Up @@ -318,6 +323,7 @@ export default class PerformanceTools {

if (category === "overview" || category === "all") {
result.overview = {
scope: site ? "session-wide (all sites combined)" : "session-wide",
overallHealth: calculateHealthStatus(metrics),
performanceScore: calculatePerformanceScore(metrics),
totalRequests: metrics.requests.total,
Expand Down
19 changes: 16 additions & 3 deletions src/tools/seo/auditors/SiteAuditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ export class SiteAuditor {
const posts = await this.client.getPosts({ per_page: this.config.maxPagesForContentAudit, status: ["publish"] });
const pages = await this.client.getPages({ per_page: this.config.maxPagesForContentAudit, status: ["publish"] });

// Get site info (mock for now)
const siteUrl = "https://example.com"; // Would come from WordPress REST API
// Get site URL from the WordPress client configuration
const siteUrl = this.client.getSiteUrl();

return {
siteUrl,
Expand Down Expand Up @@ -599,9 +599,22 @@ export class SiteAuditor {
}

// Check for external dependencies (basic analysis)
let siteHostname: string;
try {
siteHostname = new URL(siteData.siteUrl).hostname;
} catch {
siteHostname = siteData.siteUrl.replace(/^https?:\/\//, "").replace(/[/:].*/g, "");
}
const externalDependencies = [...siteData.posts, ...siteData.pages].reduce((count, item) => {
const content = item.content?.rendered || "";
const externalLinks = content.match(/https?:\/\/(?!example\.com)[^"'\s>]*/gi) || [];
const externalLinks =
content.match(/https?:\/\/[^"'\s>]*/gi)?.filter((url) => {
try {
return new URL(url).hostname !== siteHostname;
} catch {
return true;
}
}) || [];
return count + externalLinks.length;
}, 0);

Expand Down
6 changes: 4 additions & 2 deletions src/tools/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export class SiteTools {
return [
{
name: "wp_get_site_settings",
description: "Retrieves the general settings for a WordPress site.",
description:
"Retrieves the general settings for a WordPress site. Requires administrator role (manage_options capability).",
inputSchema: {
type: "object",
properties: {},
Expand All @@ -30,7 +31,8 @@ export class SiteTools {
},
{
name: "wp_update_site_settings",
description: "Updates one or more general settings for a WordPress site.",
description:
"Updates one or more general settings for a WordPress site. Requires administrator role (manage_options capability).",
inputSchema: {
type: "object",
properties: {
Expand Down
8 changes: 5 additions & 3 deletions src/tools/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class UserTools {
name: "wp_list_users",
description:
"Lists users from a WordPress site with comprehensive filtering and detailed user information including roles, registration dates, and activity status.\n\n" +
"**Note:** Role, email, and registration date fields require **administrator** privileges. " +
"Non-admin users will see limited metadata due to WordPress REST API restrictions.\n\n" +
"**Usage Examples:**\n" +
"• List all users: `wp_list_users`\n" +
'• Search users: `wp_list_users --search="john"`\n' +
Expand Down Expand Up @@ -206,16 +208,16 @@ export class UserTools {
month: "short",
day: "numeric",
})
: "Unknown";
: "Restricted (requires admin)";

const roles = u.roles?.join(", ") || "No roles";
const roles = u.roles?.length ? u.roles.join(", ") : "Restricted (requires admin)";
const description = u.description || "No description";
const displayName = u.name || "No display name";
const userUrl = u.url || "No URL";

return (
`- **ID ${u.id}**: ${displayName} (@${u.slug})\n` +
` 📧 Email: ${u.email || "No email"}\n` +
` 📧 Email: ${u.email || "Restricted (requires admin)"}\n` +
` 🎭 Roles: ${roles}\n` +
` 📅 Registered: ${registrationDate}\n` +
` 🔗 URL: ${userUrl}\n` +
Expand Down
Loading
Loading