#
tokens: 20952/50000 36/36 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── dist
│   ├── index.js
│   └── index.js.map
├── package-lock.json
├── package.json
├── README.md
├── rollup.config.js
├── src
│   ├── helpers.ts
│   ├── index.ts
│   ├── tools
│   │   ├── build-block.ts
│   │   ├── filesystem
│   │   │   ├── edit-block-file.ts
│   │   │   └── edit-block-json-file.ts
│   │   ├── list-available-plugins-in-site-plugins-path.ts
│   │   ├── list-plugin-files.ts
│   │   ├── scaffold-block.ts
│   │   ├── scaffold-plugin.ts
│   │   ├── wp-api
│   │   │   ├── activate-plugin.ts
│   │   │   ├── create-post.ts
│   │   │   ├── deactivate-plugin.ts
│   │   │   ├── delete-post.ts
│   │   │   ├── get-block-types.ts
│   │   │   ├── get-plugins.ts
│   │   │   ├── get-post-types.ts
│   │   │   ├── get-post.ts
│   │   │   ├── get-posts.ts
│   │   │   ├── get-rest-base-for-post-types.ts
│   │   │   ├── get-templates.ts
│   │   │   ├── post
│   │   │   │   ├── get-post-content-before-changes.ts
│   │   │   │   └── get-post-preview-link.ts
│   │   │   ├── site-settings
│   │   │   │   ├── get-site-settings.ts
│   │   │   │   ├── update-integer-site-setting.ts
│   │   │   │   └── update-string-site-setting.ts
│   │   │   ├── update-post-content.ts
│   │   │   ├── update-post-status.ts
│   │   │   └── update-post.ts
│   │   └── wp-cli
│   │       ├── check_and_instal_wp_cli.ts
│   │       └── instal-and-activate-plugin.ts
│   └── types
│       └── wp-sites.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
/node_modules/
/.idea/
wp-sites.json
/wp-sites.json.bak

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# mcp-wordpress-gutenberg

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "allowJs": true,
    "declaration": false,
    "moduleDetection": "force",
    "paths": {
      "*": ["./src/*"]
    },
    "baseUrl": ".",
    "removeComments": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

```

--------------------------------------------------------------------------------
/src/types/wp-sites.ts:
--------------------------------------------------------------------------------

```typescript
// src/types/wp-sites.ts
export interface WordPressSite {
    cli?: string;
    localWpSshEntryFile?: string;
    name: string;
    path: string;           // Ścieżka do instalacji WordPress
    pluginsPath: string;           // Ścieżka do instalacji WordPress
    apiUrl: string;         // URL do WP REST API
    apiCredentials?: {
        username: string;
        password: string;
    };
    database?: {
        host: string;
        port: number;
        user: string;
        password: string;
        name: string;
    };
}

export interface WPSitesConfig {
    sites: Record<string, WordPressSite>;
}
```

--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------

```javascript
import typescript from "rollup-plugin-typescript2";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";

export default {
    input: "src/index.ts",
    output: {
        file: "dist/index.js",
        format: "esm", // Używamy ESM, aby działało z "type": "module"
        sourcemap: true
    },
    plugins: [
        resolve(),
        commonjs(),
        json(),
        typescript({
            tsconfig: "./tsconfig.json",
        }),
    ],
    external: ["fs", "path", "process"], // Nie bundlujemy wbudowanych modułów Node.js
};

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "wordpress-mcp",
  "version": "1.0.0",
  "type": "module",
  "description": "WordPress MCP Server",
  "main": "dist/index.js",
  "scripts": {
    "clean": "rimraf dist",
    "build": "npm run clean && rollup -c",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.5.0",
    "node-fetch": "^3.3.2"
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^28.0.2",
    "@rollup/plugin-json": "^6.1.0",
    "@rollup/plugin-node-resolve": "^16.0.0",
    "@types/node": "^18.15.11",
    "rimraf": "^5.0.0",
    "rollup": "^4.34.8",
    "rollup-plugin-typescript2": "^0.36.0",
    "typescript": "^4.9.5"
  }
}

```

--------------------------------------------------------------------------------
/src/tools/scaffold-plugin.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/scaffold-plugin.ts
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
import { execSync } from 'child_process';

export const scaffoldPluginTool = {
    name: "wp_scaffold_plugin",
    description: "Creates a new WordPress plugin using wp-cli scaffold plugin",
    inputSchema: {
        type: "object",
        properties: {
            site: { type: "string", description: "Site alias from configuration" },
            name: { type: "string", description: "Plugin name" },
            directory: { type: "string", description: "Optional: Custom directory path" }
        },
        required: ["name"]
    },
    execute: async (args: { name: string; directory: string }) => {
        try {
            const command = `wp scaffold plugin ${args.name} --dir=${args.directory}`;
            const output = execSync(command, {
                stdio: ['pipe', 'pipe', 'pipe'],
                encoding: 'utf-8'
            });

            return {
                content: [{
                    type: "text",
                    text: `Plugin "${args.name}" created successfully in ${args.directory}\n\nOutput:\n${output}`
                }]
            };
        } catch (error) {
            console.error('Plugin scaffolding error:', error);
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `Failed to create plugin: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'Unknown error occurred while creating plugin');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/post/get-post-content-before-changes.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetPost} from "tools/wp-api/get-post";

export const apiGetPostContentBeforeChanges = {
    name: "wp_api_get_post_content_before_changes",
    description: "Retrieve a single post, page, or custom post type CONTENT using REST API. Run it always before doing changes in content.",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postType: {type: "string", description: "Post type (default: posts)"},
            postId: {type: "integer", description: "Post ID to retrieve"}, // New property to specify post ID
        },
        required: ["siteKey", "postType", "postId"]
    },

    async execute(args: { siteKey: string, postType: string, postId: number }, site: WordPressSite) {
        try {

            const {post} = await apiGetPost.execute(args, site)

            return {
                post,
                content: [{
                    type: "text",
                    text: `Post retrieved successfully.\n\nCurrent ${args.postType} #${args.postId} content is:\n\n${post.content.raw}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_post_content_before_changes failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_post_content_before_changes failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-cli/instal-and-activate-plugin.ts:
--------------------------------------------------------------------------------

```typescript
import {execSync} from "child_process";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {WordPressSite} from "types/wp-sites";

export const cliInstallAndActivatePlugin = {
    name: "wp_cli_install_and_activate_plugin",
    description: "Installs and activates a specified WordPress plugin via WP-CLI.",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            pluginSlug: {type: "string", description: "Plugin slug to install and activate."}
        },
        required: ["pluginSlug"]
    },

    async execute(args: { siteKey: string, pluginSlug: string }, site: WordPressSite, cliWrapper: string) {
        try {
            const pluginSlug = args.pluginSlug;

            const cmdInstallAndActivate = cliWrapper.replace('{{cmd}}', `wp plugin install ${pluginSlug} --activate`)
            const outputInstallAndActivate = execSync(cmdInstallAndActivate, {
                stdio: "pipe",
                cwd: site.path
            });

            return {
                content: [{
                    type: "text",
                    text: `✅ Plugin '${pluginSlug}' was already installed and has been activated. Output from WP-CLI:\n\n${outputInstallAndActivate}`
                }]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_cli_install_and_activate_plugin failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_cli_install_and_activate_plugin failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/post/get-post-preview-link.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetPost} from "tools/wp-api/get-post";

export const apiGetPostPreviewLink = {
    name: "wp_api_get_post_preview_link",
    description: "Retrieve a preview link for single post, page, or custom post type item using REST API. Especially for draft status.",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postType: {type: "string", description: "Post type (default: posts)"},
            postId: {type: "integer", description: "Post ID to retrieve"},
        },
        required: ["siteKey", "postType", "postId"]
    },

    async execute(args: { siteKey: string, postType: string, postId: number }, site: WordPressSite) {
        try {

            const {post} = await apiGetPost.execute(args, site)

            const postPreviewUrl = `${post?.guid?.rendered}&preview=true`;

            return {
                post: post,
                previewLink: postPreviewUrl,
                content: [{
                    type: "text",
                    text: `Post preview link retrieved successfully. Remember it is only for logged in user with proper permissions.\nURL: ${postPreviewUrl}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_post_preview_link failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_post_preview_link failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/list-available-plugins-in-site-plugins-path.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/edit-file.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import fs from 'fs';
import {WordPressSite} from "../types/wp-sites";


export const listAvailablePluginsInSitePluginsPath = {
    name: "wp_list_available_plugins_in_site_plugins_path",
    description: "List a plugins directories which is equivalent of listing available plugins for site",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"}
        },
        required: ["siteKey"]
    },

    async execute(args: { siteKey: string }, site: WordPressSite) {

        const pluginsPath = site.pluginsPath

        if (!fs.existsSync(pluginsPath)) {
            throw new Error(`wp_list_available_plugins_in_site_plugins_path failed: directory ${pluginsPath} not exists. Please review a plugin directory and site configuration.`);
        }

        try {
            const files = fs.readdirSync(pluginsPath, {withFileTypes: true});
            const directories = files
                .filter(dirent => dirent.isDirectory())
                .map(dirent => dirent.name);

            return {
                content: [{
                    type: "text",
                    text: `Available plugins directories in ${pluginsPath}:\n\n${directories.join('\n')}`,
                    directories
                }]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, error.message);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_list_available_plugins_in_site_plugins_path: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/site-settings/get-site-settings.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

export const apiGetSiteSettings = {
    name: "wp_api_get_site_settings",
    description: "Retrieves all WordPress site settings using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
        },
        required: ["siteKey"]
    },
    async execute(args: any, site: WordPressSite) {
        try {
            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/settings`;

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to retrieve site settings: ${errorData.message || response.statusText}`);
            }

            const settings = await response.json();
            return {
                settings,
                content: [{
                    type: "text",
                    text: `Site settings retrieved successfully.\n\nSettings: ${JSON.stringify(settings, null, 2)}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_site_settings failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_site_settings failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-block-types.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

export const apiGetGutenbergBlocks = {
    name: "wp_api_get_gutenberg_blocks",
    description: "Get available Gutenberg blocks for the WordPress site",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"}
        },
        required: ["siteKey"]
    },

    async execute(args: { siteKey: string }, site: WordPressSite) {
        try {
            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/block-types`;

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to get Gutenberg blocks: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const blocks = await response.json();

            return {
                blocks: blocks,
                content: [{
                    type: "text",
                    text: `Available Gutenberg blocks:\n${JSON.stringify(blocks, null, 2)}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_gutenberg_blocks failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_gutenberg_blocks failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-post-types.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

export const apiGetPostTypes = {
    name: "wp_api_get_post_types",
    description: "Get a WordPress post types using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
        },
        required: ["siteKey"]
    },

    async execute(args: { siteKey: string }, site: WordPressSite) {
        try {
            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/types`

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to get post types: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();

            const list = Object.values(data).map((postType: any, i: number) => {
                return (i + 1) + '. ' + postType.name + ' (slug: ' + postType.slug + ')';
            })

            return {
                postTypes: data,
                postTypesList: list,
                content: [{
                    type: "text",
                    text: `Available post types:\n${JSON.stringify(data)}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_post_types failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_post_types failed: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-templates.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

export const apiGetTemplates = {
    name: "wp_api_get_templates",
    description: "Get available templates for a specific post type in WordPress",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postType: {type: "string", description: "The post type to fetch templates for"},
        },
        required: ["siteKey", "postType"]
    },

    async execute(args: { siteKey: string, postType: string }, site: WordPressSite) {
        try {

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/templates?post_type=${args.postType}`;

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to get templates: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();

            const list = data.map((template: any, i: number) => {
                return (i + 1) + '. ' + template.title.raw + ' (slug: ' + template.slug + ')';
            })

            return {
                templates: data,
                templatesList: list,
                content: [{
                    type: "text",
                    text: `Available templates for post type "${args.postType}":\n\n${list.join('\n')}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_templates failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_templates failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-rest-base-for-post-types.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetPostTypes} from "./get-post-types.js";

export const apiGetRestBaseForPostType = {
    name: "wp_api_get_rest_base_for_post_type",
    description: "Get a WordPress REST API base for post type to use in REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postType: {type: "string", description: "Post type (default: page)"},
        },
        required: ["siteKey", "postType"]
    },

    async execute(args: { siteKey: string, postType: string }, site: WordPressSite) {
        try {
            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/types/${args.postType}`

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                const {postTypesList} = await apiGetPostTypes.execute(args, site)
                throw new Error(`Failed to get post types: ${errorData.message || response.statusText}.\nRequested URL: ${url}. Please try again with one of the following post type:\n${postTypesList.join('\n')}`);
            }

            const data = await response.json();

            return {
                postType: data,
                restBase: data.rest_base,
                content: [{
                    type: "text",
                    text: `API REST base for post type: ${args.postType} is a: \n${data.rest_base}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_rest_base_for_post_types failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_rest_base_for_post_types failed: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-post.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetRestBaseForPostType} from "./get-rest-base-for-post-types.js";

export const apiGetPost = {
    name: "wp_api_get_post",
    description: "Retrieve a single post, page, or custom post type item using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postType: {type: "string", description: "Post type (default: posts)"},
            postId: {type: "integer", description: "Post ID to retrieve"}, // New property to specify post ID
        },
        required: ["siteKey", "postType", "postId"]
    },

    async execute(args: { siteKey: string, postType: string, postId: number }, site: WordPressSite) {
        try {
            const {restBase} = await apiGetRestBaseForPostType.execute(args, site);

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}/${args.postId}`; // Modified URL to fetch single post by ID

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to retrieve post: ${errorData.message || response.statusText}. Requested URL: ${url}`);
            }

            const data = await response.json();
            return {
                post: data,
                content: [{
                    type: "text",
                    text: `Post retrieved successfully.\n\nPost: ${JSON.stringify(data)}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_post failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_post failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/delete-post.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetRestBaseForPostType} from "tools/wp-api/get-rest-base-for-post-types";

export const apiDeletePost = {
    name: "wp_api_delete_post",
    description: "Deletes a WordPress post, page, or any custom post type using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postId: {type: "integer", description: "ID of the post to delete"},
            postType: {type: "string", description: "Type of the post (e.g., post, page, custom type)"},
            force: {type: "boolean", description: "Whether to force delete the post"}
        },
        required: ["siteKey", "postId", "postType"]
    },

    async execute(args: { siteKey: string; postId: number; postType: string; force?: boolean }, site: WordPressSite) {
        try {

            const {restBase} = await apiGetRestBaseForPostType.execute(args, site)

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}/${args.postId}?force=${args.force ?? false}`;

            const response = await fetch(url, {
                method: 'DELETE',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to delete post: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();

            return {
                postData: data,
                content: [{
                    type: "text",
                    text: `Post ${args.postId} (${args.postType}) deleted successfully.`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_delete_post failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_delete_post failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-posts.ts:
--------------------------------------------------------------------------------

```typescript
import { WordPressSite } from "types/wp-sites";
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
import {apiGetRestBaseForPostType} from "./get-rest-base-for-post-types.js";

export const apiGetPosts = {
    name: "wp_api_get_posts",
    description: "Retrieve a list of posts, pages, or custom post type items using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: { type: "string", description: "Site key" },
            postType: { type: "string", description: "Post type (default: posts)" },
            perPage: { type: "integer", description: "Number of posts to retrieve (default: 10)" },
            page: { type: "integer", description: "Page number for pagination (default: 1)" }
        },
        required: ["siteKey", "postType"]
    },

    async execute(args: { siteKey: string, postType: string, perPage?: number, page?: number }, site: WordPressSite) {
        try {
            const {restBase} = await apiGetRestBaseForPostType.execute(args, site)

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}?per_page=${args.perPage || 10}&page=${args.page || 1}`;

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to retrieve posts: ${errorData.message || response.statusText}.
Requested URL: ${url}`);
            }

            const data = await response.json();
            return {
                pluginData: data,
                content: [{
                    type: "text",
                    text: `Posts retrieved successfully.\n\nPosts: ${JSON.stringify(data)}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_posts failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_posts failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/get-plugins.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

export const apiGetPlugins = {
    name: "wp_api_get_plugins",
    description: "Get WordPress plugins (active, inactive, or all) using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            status: {
                type: "string",
                enum: ["active", "inactive", "all"],
                description: "Filter plugins by status. Default is 'active'."
            },
        },
        required: ["siteKey"],
        additionalProperties: false
    },

    async execute(args: { siteKey: string, status?: string }, site: WordPressSite) {
        try {
            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/plugins`;

            // Status wtyczek domyślnie to "active", chyba że użytkownik określi inaczej
            const filterStatus = args.status || "active";

            const response = await fetch(url, {
                method: 'GET',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to get plugins: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();

            // Filtrujemy wtyczki zgodnie z wybranym statusem
            let filteredPlugins = data;
            if (filterStatus !== "all") {
                filteredPlugins = data.filter((plugin: { status: string }) => plugin.status === filterStatus);
            }

            const pluginsList = filteredPlugins.map((plugin: any) => plugin.name + ' (' + plugin.plugin + ')')

            return {
                plugins: filteredPlugins,
                pluginsList,
                content: [{
                    type: "text",
                    text: `${filterStatus.charAt(0).toUpperCase() + filterStatus.slice(1)} plugins:\n${pluginsList.join('\n')}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_get_plugins failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_get_plugins failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/list-plugin-files.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/edit-file.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import fs from 'fs';
import path from 'path';
import {WordPressSite} from "../types/wp-sites";
import {listAvailablePluginsInSitePluginsPath} from "./list-available-plugins-in-site-plugins-path.js";


const listFiles = (dir: any, fileList: any[] = []) => {
    if (path.basename(dir) === 'node_modules') {
        return fileList; // Pomijamy katalog node_modules
    }

    const files = fs.readdirSync(dir);
    for (const file of files) {
        const filePath = path.join(dir, file);
        const stat = fs.statSync(filePath);

        if (stat.isDirectory()) {
            listFiles(filePath, fileList);
        } else {
            fileList.push(filePath);
        }
    }
    return fileList;
}


export const listPluginFiles = {
    name: "wp_list_plugin_files",
    description: "List a files for a specified plugin directory",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            pluginDirName: {type: "string", description: "Plugin directory name"}
        },
        required: ["pluginDirName", "siteKey"]
    },

    async execute(args: { pluginDirName: string, siteKey: string }, site: WordPressSite) {

        if (args.pluginDirName === '.' || args.pluginDirName === '') {
            throw new McpError(ErrorCode.InvalidParams, 'pluginDirName cannot be "." or empty (self directory).');
        }

        const blockDir = path.join(site.pluginsPath, args.pluginDirName)

        if (!fs.existsSync(blockDir)) {
            const {content: [{directories}]} = await listAvailablePluginsInSitePluginsPath.execute({siteKey: args.siteKey}, site)
            return {
                isError: true,
                directories,
                content: [{
                    type: "text",
                    text: `Directory ${blockDir} not exists. Please review a plugin directory. Available plugins in ${site.pluginsPath}:\n\n${directories.join('\n')}`
                }]
            };
        }

        try {

            const files = listFiles(blockDir)

            return {
                files,
                content: [{
                    type: "text",
                    text: `Files at ${blockDir}:\n\n` + files.map((f) => f.replace(blockDir, '')).join('\n')
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, error.message);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_list_plugin_files: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/activate-plugin.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetPlugins} from "tools/wp-api/get-plugins";

export const apiActivatePlugin = {
    name: "wp_api_activate_plugin",
    description: "Activates a WordPress plugin using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            pluginSlug: {type: "string", description: "Plugin slug to activate"}
        },
        required: ["pluginSlug", "siteKey"]
    },

    async execute(args: { siteKey: string; pluginSlug: string }, site: WordPressSite) {
        try {
            const pluginSlug = args.pluginSlug.replace('.php', '')

            const allPlugins = await apiGetPlugins.execute({...args, status: 'all'}, site)
            if (!allPlugins.plugins.find((p: any) => p.plugin === pluginSlug)) {
                return {
                    isError: true,
                    content: [{
                        type: "text",
                        text: `Plugin ${args.pluginSlug} is invalid. Available plugins:\n\n${allPlugins.pluginsList.join('\n')}. Please try again with correct plugin slug.`
                    }]
                };
            }

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/plugins/${pluginSlug}`

            const response = await fetch(url, {
                method: 'PUT',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    status: 'active'
                })
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to activate plugin: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();


            return {
                pluginData: data,
                content: [{
                    type: "text",
                    text: `Plugin ${args.pluginSlug} activated successfully.\n\nPlugin: ${data}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_activate_plugin failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_activate_plugin failed: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/deactivate-plugin.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetPlugins} from "tools/wp-api/get-plugins";

export const apiDeactivatePlugin = {
    name: "wp_api_deactivate_plugin",
    description: "Deactivates a WordPress plugin using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            pluginSlug: {type: "string", description: "Plugin slug to deactivate"}
        },
        required: ["pluginSlug", "siteKey"]
    },

    async execute(args: { siteKey: string; pluginSlug: string }, site: WordPressSite) {
        try {
            const pluginSlug = args.pluginSlug.replace('.php', '')

            const allPlugins = await apiGetPlugins.execute({...args, status: 'active'}, site)
            if (!allPlugins.plugins.find((p: any) => p.plugin === pluginSlug)) {
                return {
                    isError: true,
                    content: [{
                        type: "text",
                        text: `Plugin ${args.pluginSlug} is not active or invalid. Active plugins:\n${allPlugins.pluginsList.join('\n')}. Please try again with a correct plugin slug.`
                    }]
                };
            }

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/plugins/${pluginSlug}`

            const response = await fetch(url, {
                method: 'PUT',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    status: 'inactive'
                })
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to deactivate plugin: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();

            return {
                pluginData: data,
                content: [{
                    type: "text",
                    text: `Plugin ${args.pluginSlug} deactivated successfully.\n\nPlugin: ${data}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_deactivate_plugin failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_deactivate_plugin failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/site-settings/update-integer-site-setting.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

// Lista dozwolonych kluczy ustawień zgodnie z dokumentacją WordPress REST API
const VALID_SETTING_KEYS = [
    "post_per_page", "page_on_front", "page_for_posts", "site_logo", "site_icon", "default_category", "start_of_week"
] as const;

type SettingKey = (typeof VALID_SETTING_KEYS)[number];

export const apiUpdateIntegerSiteSetting = {
    name: "wp_api_update_integer_site_setting",
    description: "Updates a single WordPress site setting in integer format using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            settingKey: {
                type: "string",
                enum: VALID_SETTING_KEYS, // Walidacja poprawnych kluczy
                description: "The key of the setting to update"
            },
            settingValue: {type: "integer", description: "The new value for the setting"}
        },
        required: ["siteKey", "settingKey", "settingValue"]
    },

    async execute(args: { siteKey: string; settingKey: SettingKey; settingValue: any }, site: WordPressSite) {
        try {

            if (!VALID_SETTING_KEYS.includes(args.settingKey)) {
                throw new Error(`Invalid setting key: ${args.settingKey}. Allowed values: ${VALID_SETTING_KEYS.join(", ")}`);
            }

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/settings`;

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({[args.settingKey]: args.settingValue})
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to update setting: ${errorData.message || response.statusText}`);
            }

            const data = await response.json();

            return {
                settings: data,
                content: [{
                    type: "text",
                    text: `Site settings updated successfully.\n\nNew settings object: ${JSON.stringify(data, null, 2)}`
                }]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_update_integer_site_setting failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_update_integer_site_setting failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/site-settings/update-string-site-setting.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";

// Lista dozwolonych kluczy ustawień zgodnie z dokumentacją WordPress REST API
const VALID_SETTING_KEYS = [
    "title", "description", "url", "email", "timezone", "date_format", "time_format",
    "language", "default_post_format", "show_on_front",
    "default_ping_status", "default_comment_status"
] as const;

type SettingKey = (typeof VALID_SETTING_KEYS)[number];

export const apiUpdateStringSiteSetting = {
    name: "wp_api_update_string_site_setting",
    description: "Updates a single WordPress site setting in string format using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            settingKey: {
                type: "string",
                enum: VALID_SETTING_KEYS, // Walidacja poprawnych kluczy
                description: "The key of the setting to update"
            },
            settingValue: {type: "string", description: "The new value for the setting"}
        },
        required: ["siteKey", "settingKey", "settingValue"]
    },

    async execute(args: { siteKey: string; settingKey: SettingKey; settingValue: any }, site: WordPressSite) {
        try {

            if (!VALID_SETTING_KEYS.includes(args.settingKey)) {
                throw new Error(`Invalid setting key: ${args.settingKey}. Allowed values: ${VALID_SETTING_KEYS.join(", ")}`);
            }

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/settings`;

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({[args.settingKey]: args.settingValue})
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to update setting: ${errorData.message || response.statusText}`);
            }

            const data = await response.json();

            return {
                settings: data,
                content: [{
                    type: "text",
                    text: `Site settings updated successfully.\n\nNew settings object: ${JSON.stringify(data, null, 2)}`
                }]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_update_string_site_setting failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_update_string_site_setting failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/update-post-status.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetRestBaseForPostType} from "./get-rest-base-for-post-types.js";

export const apiUpdatePostStatus = {
    name: "wp_api_update_post_status",
    description: "Update the status of an existing WordPress post, page, or custom post type item using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postId: {type: "integer", description: "ID of the post to update"},
            postType: {type: "string", description: "Post type (default: posts)"},
            status: {type: "string", enum: ["publish", "draft"], description: "Status to set (publish or draft)"},
            publishDate: {
                type: "string",
                format: "date-time",
                description: "Optional scheduled publication date in ISO 8601 format"
            }
        },
        required: ["siteKey", "postId", "postType", "status"]
    },

    async execute(args: {
        siteKey: string,
        postId: number,
        postType: string,
        status: string,
        publishDate?: string
    }, site: WordPressSite) {
        try {
            const {restBase} = await apiGetRestBaseForPostType.execute(args, site)

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}/${args.postId}`;

            const bodyData: any = {status: args.status};
            if (args.status === "publish" && args.publishDate) {
                bodyData.date = args.publishDate;
            }

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(bodyData)
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to update post status: ${errorData.message || response.statusText}.
Requested URL: ${url}`);
            }

            const data = await response.json();
            return {
                pluginData: data,
                content: [{
                    type: "text",
                    text: `Post status updated successfully.\nPost #ID: ${data.id}\nPost URL: ${data.link}\nPost status: ${data.status}\nPost type: ${data.type}\nPost title: ${data.title?.rendered}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_update_post_status failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_update_post_status failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/update-post.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetRestBaseForPostType} from "./get-rest-base-for-post-types.js";

export const apiUpdatePost = {
    name: "wp_api_update_post",
    description: "Update the settings (i.e. template, title, excerpt) of a WordPress post with post type using REST API. THIS TOOL IS NOT DESIGNED TO UPDATE POST CONTENT.",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postId: {type: "number", description: "Post ID to update"},
            postType: {type: "string", description: "Type of the post (e.g., posts, pages)"},
            template: {type: "string", description: "Optional: Post template"},
            title: {type: "string", description: "Optional: Post title"},
            excerpt: {type: "string", description: "Optional: Post excerpt"},
        },
        required: ["siteKey", "postId", "postType"]
    },

    async execute(args: {
        siteKey: string,
        postId: number,
        postType: string,
        title?: string,
        template?: string
        excerpt?: string
    }, site: WordPressSite) {
        try {

            const {restBase} = await apiGetRestBaseForPostType.execute(args, site)

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}/${args.postId}`;

            const bodyData: any = {}
            if (args.title) {
                bodyData.title = args.title
            }
            if (args.template) {
                bodyData.template = args.template
            }
            if (args.excerpt) {
                bodyData.excerpt = args.excerpt
            }
            // @ts-ignore
            if (args.content) {
                throw new Error(`Failed. To update post content use special tool: wp_api_update_post_content`);
            }

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(bodyData)
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to update post: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const updatedPost = await response.json();

            return {
                updatedPost,
                content: [{
                    type: "text",
                    text: `Post settings updated successfully! Preview URL: ${updatedPost.link}.`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_update_post failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_update_post failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/wp-api/update-post-content.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetRestBaseForPostType} from "./get-rest-base-for-post-types.js";
import {apiGetPost} from "tools/wp-api/get-post";

export const apiUpdatePostContent = {
    name: "wp_api_update_post_content",
    description: "Update the content of a WordPress post with post type using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postId: {type: "number", description: "Post ID to update"},
            postType: {type: "string", description: "Type of the post (e.g., posts, pages)"},
            content: {type: "string", description: "Optional: New content for the post (only if content changed)"},
            readCurrentContent: {
                type: "boolean",
                description: "Determine, you need a current version of post content to update context or just update content."
            },
        },
        required: ["siteKey", "postId", "postType", "readCurrentContent"]
    },

    async execute(args: {
        siteKey: string,
        postId: number,
        postType: string,
        content: string,
        readCurrentContent: boolean
    }, site: WordPressSite) {
        try {

            if (args.readCurrentContent) {
                const {post} = await apiGetPost.execute(args, site)
                return {
                    isError: true,
                    content: [{
                        type: "text",
                        text: `Please generate a new post content from current content revision:\n\n${post?.content?.rendered}`
                    }]
                };
            }

            const {restBase} = await apiGetRestBaseForPostType.execute(args, site)

            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}/${args.postId}`;

            const bodyData: any = {
                content: args.content
            }

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(bodyData)
            });

            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(`Failed to update post content: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const updatedPost = await response.json();

            return {
                updatedPost,
                content: [{
                    type: "text",
                    text: `Post content updated successfully! Preview URL: ${updatedPost.link}.`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_update_post_content failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_update_post_content failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/build-block.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/build-block.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {execSync} from 'child_process';
import {isGutenbergBlock} from "../helpers.js";
import {WordPressSite} from "../types/wp-sites";
import path from "path";

export interface BuildBlockArgs {
    blockPluginDirname: string;
    siteKey: string;
}

export const buildBlock = {
    name: "wp_build_block",
    description: "Builds a Gutenberg block using npm run build",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            blockPluginDirname: {
                type: "string",
                description: "Block plugin directory name."
            }
        },
        required: ["blockPluginDirname", "siteKey"]
    },

    execute: async (args: BuildBlockArgs, site: WordPressSite) => {

        const blockDir = path.join(site.pluginsPath, args.blockPluginDirname);

        if (!(await isGutenbergBlock(blockDir))) {
            throw new Error(`wp_build_block failed: directory ${blockDir} does not contain a Gutenberg block. Are you sure ${blockDir} is valid?`);
        }

        try {

            try {
                execSync('npm install --no-audit --no-fund --silent', {
                    cwd: blockDir,
                    stdio: ['pipe', 'pipe', 'pipe'],
                    shell: '/bin/bash'
                });
            } catch (error) {
                throw new Error(`wp_build_block: npm install failed with error - ${error instanceof Error ? error.message : String(error)} in ${blockDir}`);
            }

            try {

                const buildOutput = execSync('npm run build', {
                    cwd: blockDir,
                    stdio: ['pipe', 'pipe', 'pipe'],
                    shell: '/bin/bash',
                    encoding: 'utf-8'
                });

                const relevantOutput = buildOutput
                    .split('\n')
                    .filter(line =>
                        line.includes('webpack') ||
                        line.includes('compiled') ||
                        line.includes('error') ||
                        line.includes('warning')
                    )
                    .join('\n');

                return {
                    content: [{
                        type: "text",
                        text: `✅ Block "${args.blockPluginDirname}" built successfully!\n\nBuild summary:\n${relevantOutput}`
                    }]
                };
            } catch (error) {
                const errorOutput = error instanceof Error ? error.message : String(error);
                const formattedError = errorOutput
                    .split('\n')
                    .filter(line =>
                        line.includes('error') ||
                        line.includes('failed') ||
                        line.includes('webpack')
                    )
                    .join('\n');

                throw new Error(`wp_build_block failed:\n${formattedError}\n\nTry to fix a problem, but do not create a new structure on your own.`);
            }
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, error.message);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_build_block failed: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-cli/check_and_instal_wp_cli.ts:
--------------------------------------------------------------------------------

```typescript
import {execSync} from "child_process";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import path from "path";
import {WordPressSite} from "types/wp-sites";

export const checkAndInstallWPCLI = {
    name: "check_and_install_wp_cli",
    description: "Checks if WP-CLI is installed. If not, installs it locally.",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"}
        },
        required: []
    },

    async execute(args: any, site: WordPressSite) {
        try {
            // Sprawdzenie, czy PHP jest zainstalowane
            let isPhpInstalled;
            try {
                execSync("php -v", {stdio: "pipe"}); // Sprawdzenie wersji PHP
                isPhpInstalled = true;
            } catch (error) {
                isPhpInstalled = false;
            }

            if (!isPhpInstalled) {
                // Jeśli PHP nie jest zainstalowane, zwróć instrukcje
                return {
                    content: [{
                        type: "text",
                        text: "❌ PHP is not installed. Please install PHP manually before proceeding. \n\n" +
                            "You can install PHP by running the following commands:\n" +
                            "1. On macOS (using Homebrew): `brew install php`\n" +
                            "2. On Ubuntu/Debian: `sudo apt update && sudo apt install php`\n" +
                            "3. On Windows, download PHP from https://windows.php.net/download/."
                    }]
                };
            }

            // Sprawdzenie, czy WP-CLI jest zainstalowane
            let isInstalled;
            try {
                execSync("wp --info", {stdio: "pipe"}); // Jeśli WP-CLI jest zainstalowane, to nie rzuci wyjątkiem
                isInstalled = true;
            } catch (error) {
                isInstalled = false;
            }

            if (isInstalled) {
                return {
                    content: [{type: "text", text: "✅ WP-CLI is already installed."}]
                };
            }

            // Jeśli WP-CLI nie jest zainstalowane, pobierz i zainstaluj lokalnie
            const wpCliPath = path.resolve(site.path, "wp-cli.phar");

            // Pobierz WP-CLI i ustaw odpowiednie uprawnienia
            execSync(`
                curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar &&
                chmod +x wp-cli.phar
            `, {stdio: "inherit", cwd: site.path}); // stdio: "inherit" pozwala na wyświetlanie wyników w terminalu

            // Sprawdzenie, czy WP-CLI działa poprawnie
            let isNowInstalled;
            try {
                execSync(`php "${wpCliPath}" --info`, {stdio: "pipe"});
                isNowInstalled = true;
            } catch (error) {
                isNowInstalled = false;
            }

            if (!isNowInstalled) {
                throw new Error(`WP-CLI installation failed. CLI Path: ${wpCliPath}`);
            }

            return {
                content: [{type: "text", text: "✅ WP-CLI was successfully installed locally."}]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `check_and_install_wp_cli failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'check_and_install_wp_cli failed: Unknown error occurred');
        }
    }
};

```

--------------------------------------------------------------------------------
/src/tools/scaffold-block.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/scaffold-block.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {execSync} from 'child_process';
import path from "path";
import fs from 'fs';
import {WordPressSite} from "../types/wp-sites";
import {listPluginFiles} from "./list-plugin-files.js";
import {buildBlock} from "./build-block.js";

interface ScaffoldArgs {
    name: string;
    siteKey: string;
    variant: string;
    namespace: string;
}

export const scaffoldBlockTool = {
    name: "wp_scaffold_block",
    description: "Creates and builds a new Gutenberg block using @wordpress/create-block",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            name: {type: "string", description: "Block name"},
            variant: {
                type: "string",
                enum: ["static", "dynamic"],
                description: "Gutenberg block template variant (static or dynamic). Default: static"
            },
            namespace: {type: "string", description: "Gutenberg block namespace"}
        },
        required: ["name", "siteKey", "variant", "namespace"]
    },
    execute: async (args: ScaffoldArgs, site: WordPressSite) => {

        if (typeof args.name !== 'string') {
            throw new McpError(ErrorCode.InvalidParams, 'Block name must be a string');
        }

        const blockDirname = args.name.toLowerCase().replace(/ /g, '-')
        const namespace = args.namespace.toLowerCase().replace(/ /g, '-')
        const blockDir = path.join(site.pluginsPath, blockDirname)
        const pluginDir = site.pluginsPath

        if (fs.existsSync(blockDir)) {
            throw new McpError(ErrorCode.InvalidRequest, `wp_scaffold_block failed: directory ${blockDir} already exists. Please change a block name or remove existing plugin!`);
        }

        try {

            const variant = args.variant === 'dynamic' ? 'dynamic' : 'static'
            const createBlockCommand = `npx @wordpress/create-block ${args.name} --variant ${variant} --namespace ${namespace}`

            execSync(createBlockCommand, {
                stdio: ['pipe', 'pipe', 'pipe'],
                encoding: 'utf-8',
                cwd: pluginDir
            });

            const buildResult = await buildBlock.execute({
                blockPluginDirname: blockDirname,
                siteKey: args.siteKey
            }, site);

            const listedPluginFiles = await listPluginFiles.execute({
                pluginDirName: path.basename(blockDir),
                siteKey: args.siteKey
            }, site);

            if (listedPluginFiles.isError || !listedPluginFiles?.files) {
                throw new McpError(ErrorCode.InternalError, `wp_scaffold_block failed: directory ${path.basename(blockDir)} is unreachable or files not created.`);
            }

            return {
                content: [{
                    type: "text",
                    text: `Block "${args.name}" created and built successfully.\n\nCreated files: ${listedPluginFiles?.files?.join('\n')}.\n\nDo you want to do any changes in this block or activate WordPress plugin and create a test page in WordPress with this block embedded?`
                }]
            };
        } catch (error) {
            console.error('Block scaffolding error:', error);
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `Failed to create/build block: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'Unknown error occurred while creating/building block');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/tools/wp-api/create-post.ts:
--------------------------------------------------------------------------------

```typescript
import {WordPressSite} from "types/wp-sites";
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {apiGetPostTypes} from "./get-post-types.js";
import {apiGetRestBaseForPostType} from "./get-rest-base-for-post-types.js";
import {apiGetTemplates} from "./get-templates.js";

export const apiCreatePost = {
    name: "wp_api_create_post",
    description: "Create a WordPress post, page or custom post type item using REST API",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            postType: {type: "string", description: "Post type (default: pages)"},
            title: {type: "string", description: "Post title"},
            content: {type: "string", description: "Post content"},
            parent: {type: "integer", description: "Post parent ID"},
            template: {type: "string", description: "Optional: Post template"},
        },
        required: ["postType", "siteKey", "title"]
    },

    async execute(args: {
        siteKey: string,
        postType: string,
        title: string,
        content: string,
        parent: Number,
        template?: string
    }, site: WordPressSite) {
        try {

            const {restBase} = await apiGetRestBaseForPostType.execute(args, site)


            const credentials = Buffer.from(`${site.apiCredentials?.username}:${site.apiCredentials?.password}`).toString('base64');
            const url = `${site.apiUrl}/wp/v2/${restBase}`

            const bodyData: any = {
                title: args.title,
                status: 'draft',
                content: args.content,
                parent: args.parent,
            }

            if (args.template) {
                const {templates, templatesList} = await apiGetTemplates.execute(args, site)
                if (!templates.find((template: any) => template.slug === args.template)) {
                    throw new Error(`Invalid template.\nUse one of the following:\n${templatesList.join('\n')}`);
                }
                bodyData.template = args.template
            }

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': `Basic ${credentials}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(bodyData)
            });

            if (!response.ok) {
                const errorData = await response.json();

                if (errorData.message.includes('No route was found matching the URL and request method')) {
                    const postTypes = await apiGetPostTypes.execute({
                        siteKey: args.siteKey
                    }, site)

                    return {
                        isError: true,
                        content: [
                            {
                                type: "text",
                                text: `Probably ${args.postType} is invalid.\nUsed REST API URL: ${url}.\nPlease select a proper post type from a list: ${Object.keys(postTypes.postTypes).join(', ')}.`
                            }
                        ]
                    }
                }
                throw new Error(`Failed to create post: ${errorData.message || response.statusText}.\nRequested URL: ${url}`);
            }

            const data = await response.json();

            return {
                pluginData: data,
                content: [{
                    type: "text",
                    text: `Post created successfully.\n\nPost: ${JSON.stringify(data, null, 2)}`
                }]
            };
        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, `wp_api_create_post failed: ${error.message}`);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_api_create_post failed: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/helpers.ts:
--------------------------------------------------------------------------------

```typescript
// src/helpers.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import fs from 'fs/promises';
import path from 'path';
import {execSync} from "child_process";

export interface WordPressSite {
    localWpSshEntryFile: string;
    cli?: string;
    name: string;
    path: string;
    pluginsPath: string;
    aliases?: string[];
    apiUrl: string;
    apiCredentials?: {
        username: string;
        password: string;
    };
}

export interface WPSitesConfig {
    sites: {
        [key: string]: WordPressSite;
    };
}

export interface ToolArguments {
    siteKey: string;
}

export function similarity(s1: string, s2: string): number {
    s1 = s1.toLowerCase();
    s2 = s2.toLowerCase();

    if (s1.includes(s2) || s2.includes(s1)) return 0.8;

    let longer = s1;
    let shorter = s2;
    if (s1.length < s2.length) {
        longer = s2;
        shorter = s1;
    }
    const longerLength = longer.length;
    if (longerLength === 0) return 1.0;

    const distance = longer.split('')
        .filter(char => !shorter.includes(char)).length;
    return (longerLength - distance) / longerLength;
}

export function findMatchingSites(config: WPSitesConfig, searchTerm?: string): Array<[string, WordPressSite, number]> {
    if (!searchTerm) {
        return Object.entries(config.sites)
            .map(([key, site]) => [key, site, 1] as [string, WordPressSite, number]);
    }

    return Object.entries(config.sites)
        .map(([key, site]) => {
            const keyMatch = similarity(key, searchTerm);
            const nameMatch = similarity(site.name, searchTerm);
            const aliasMatches = (site.aliases || [])
                .map(alias => similarity(alias, searchTerm));

            const maxScore = Math.max(keyMatch, nameMatch, ...aliasMatches);
            return [key, site, maxScore] as [string, WordPressSite, number];
        })
        .filter(([, , score]) => score > 0.3)
        .sort((a, b) => b[2] - a[2]);
}

export function formatSitesList(sites: Array<[string, WordPressSite, number]>): string {
    return sites
        .map(([key, site], index) =>
            `${index + 1}. ${key} (${site.name})`)
        .join('\n');
}

export async function validateAndGetSite(config: WPSitesConfig, siteArg?: string): Promise<WordPressSite> {
    const matches = findMatchingSites(config, siteArg);

    if (matches.length === 0) {
        const availableSites = formatSitesList(findMatchingSites(config));
        throw new McpError(
            ErrorCode.InvalidParams,
            `No matching sites found. Available sites:\n${availableSites}`
        );
    }

    if (matches.length === 1) {
        const [key, site] = matches[0];
        return site;
    }

    const sitesList = formatSitesList(matches);
    throw new McpError(
        ErrorCode.InvalidParams,
        `Multiple matching sites found. Please choose which one to use and try again:\n${sitesList}`
    );
}

export function validateSiteToolArguments(args: unknown): asserts args is ToolArguments {
    if (!args || typeof args !== 'object') {
        throw new McpError(ErrorCode.InvalidParams, 'Arguments must be an object');
    }

    const typedArgs = args as Record<string, unknown>;

    if (typedArgs.siteKey !== undefined && typeof typedArgs.siteKey !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'siteKey must be a string if provided');
    }
}


export async function isGutenbergBlock(directory: string): Promise<boolean> {
    try {
        // Sprawdź czy istnieje package.json
        const packageJsonPath = path.join(directory, 'package.json');
        const packageJsonExists = await fs.access(packageJsonPath)
            .then(() => true)
            .catch(() => false);

        if (!packageJsonExists) return false;

        // Sprawdź zawartość package.json
        const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));

        // Sprawdź charakterystyczne zależności dla bloków Gutenberga
        const dependencies = {
            ...packageJson.dependencies,
            ...packageJson.devDependencies
        };

        return (
            '@wordpress/scripts' in dependencies
        );
    } catch {
        return false;
    }
}

export async function shouldRebuildBlock(directory: string): Promise<boolean> {
    return isGutenbergBlock(directory);
}


// Funkcja sprawdzająca, czy WP-CLI jest zainstalowane lokalnie
export const checkLocalWPCLI = (cwd: string) => {
    try {
        execSync(`php "${path.resolve(cwd, 'wp-cli.phar')}" --info`, {stdio: "pipe"});
        return true; // WP-CLI jest zainstalowane lokalnie
    } catch (error) {
        return false; // WP-CLI nie jest zainstalowane lokalnie
    }
};

export const checkGlobalWPCLI = () => {
    try {
        execSync("wp --info", {stdio: "pipe"});
        return true; // WP-CLI jest zainstalowane globalnie
    } catch (error) {
        return false; // WP-CLI nie jest zainstalowane globalnie
    }
};
```

--------------------------------------------------------------------------------
/src/tools/filesystem/edit-block-json-file.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/edit-file.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {promises as fs} from 'fs';
import path from 'path';
import {isGutenbergBlock} from '../../helpers.js';
import {WordPressSite} from "../../types/wp-sites.js";
import {buildBlock} from "./../build-block.js";
import {listPluginFiles} from "tools/list-plugin-files";


interface EditFileArgs {
    filePath: string;
    content: object;
    blockPluginDirname: string;
    siteKey: string;
    didUserConfirmChanges: boolean;
}

export const editBlockJsonFile = {
    name: "wp_edit_block_json_file",
    description: "Edits a file: block.json in WordPress Gutenberg Block Plugin with automatic rebuild detection and block.json file validation.",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            blockPluginDirname: {
                type: "string",
                description: "Block plugin directory name."
            },
            filePath: {
                type: "string",
                description: "Path to the block.json file relative to the plugin root."
            },
            content: {
                type: "object",
                description: "New content for the block.json file in JSON format"
            },
            didUserConfirmChanges: {
                type: "boolean",
                description: "Are you sure about this changes from new content?"
            }
        },
        required: ["filePath", "blockPluginDirname", "siteKey", "content", "didUserConfirmChanges"]
    },

    async execute(args: EditFileArgs, site: WordPressSite) {

        const blockDir = path.join(site.pluginsPath, args.blockPluginDirname)
        const fullPath = path.join(blockDir, args.filePath);

        if (!(await isGutenbergBlock(blockDir))) {
            throw new Error(`wp_edit_block_json_file failed: directory ${blockDir} does not contain a Gutenberg block. Are you sure ${blockDir} is valid?`);
        }

        if (!args.filePath.endsWith('block.json')) {
            throw new Error(`wp_edit_block_json_file failed: this tool can only edit block.json file. Are you sure ${fullPath} is valid for this action?`);
        }

        try {
            try {
                await fs.access(fullPath);
            } catch {
                const availableFiles = await listPluginFiles.execute({
                    ...args,
                    pluginDirName: args.blockPluginDirname
                }, site)

                throw new Error(`File block.json not found: ${fullPath}. Available files within dir ${blockDir}:\n${availableFiles?.files?.join('\n')}`);
            }

            let originalContent: string;
            try {
                originalContent = await fs.readFile(fullPath, 'utf-8');
            } catch (error) {
                const errorMessage = error instanceof Error ? error.message : 'Unknown read error';
                throw new Error(`Failed to read current block.json file: ${errorMessage}`);
            }

            let warnings = [];
            try {
                let newContent: any = args.content;

                if (newContent.style !== 'file:./style-index.css') {
                    warnings.push('Property "style" in block.json is not default. Default value should be: "file:./style-index.css". If you have changed in intentionally it is ok.')
                }
                if (newContent.editorStyle !== "file:./index.css") {
                    warnings.push('Property "editorStyle" in block.json is not default. Default value should be: "file:./index.css". If you have changed in intentionally it is ok.')
                }
                if (newContent.render) {
                    warnings.push('Property "render" in block.json exists, so it is dynamic block. In this case there should not be save.js file for block, but render file is needed.')
                }

                if (args.didUserConfirmChanges) {
                    await fs.writeFile(fullPath, JSON.stringify(newContent), 'utf-8');
                } else {
                    return {
                        isError: true,
                        content: [{
                            type: "text",
                            text: `Are you sure to make changes to block.json file within ${fullPath}\nNew content:\n${JSON.stringify(newContent, null, 2)}.\n\nWarnings:\n${warnings.join('\n')}\n\nPlease ask user what he think! Let user confirm or not your changes.`
                        }]
                    };
                }
            } catch (error) {
                throw new Error(`New content cannot be parsed: ${args.content}`);
            }

            const buildResult = await buildBlock.execute({
                blockPluginDirname: args.blockPluginDirname,
                siteKey: args.siteKey
            }, site);

            return {
                content: [{
                    type: "text",
                    text: `block.json file: ${fullPath} edited successfully.\n\nBuild output:\n${buildResult.content[0].text}\n\nSome warnings about block.json file found:\n${warnings.join('\n')}`
                }]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, error.message);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_edit_block_json_file: Unknown error occurred');
        }
    }
};
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
import fs from 'fs';
import { validateAndGetSite, validateSiteToolArguments, WPSitesConfig } from './helpers.js';
import { scaffoldBlockTool } from 'tools/scaffold-block.js';
import { listPluginFiles } from "tools/list-plugin-files.js";
import { listAvailablePluginsInSitePluginsPath } from "tools/list-available-plugins-in-site-plugins-path.js";
import { buildBlock } from "tools/build-block.js";
import { editBlockFile } from "tools/filesystem/edit-block-file.js";
import { apiActivatePlugin } from "tools/wp-api/activate-plugin.js";
import { apiCreatePost } from "tools/wp-api/create-post.js";
import { apiUpdatePostStatus } from "tools/wp-api/update-post-status.js";
import { apiGetPosts } from "tools/wp-api/get-posts.js";
import { apiGetPostTypes } from "tools/wp-api/get-post-types.js";
import { apiGetPlugins } from "tools/wp-api/get-plugins.js";
import { apiUpdatePost } from "tools/wp-api/update-post.js";
import { apiGetGutenbergBlocks } from "tools/wp-api/get-block-types.js";
import { apiGetTemplates } from "tools/wp-api/get-templates.js";
import { apiGetRestBaseForPostType } from "tools/wp-api/get-rest-base-for-post-types.js";
import { apiUpdatePostContent } from "tools/wp-api/update-post-content.js";
import { apiGetPost } from "tools/wp-api/get-post.js";
import { cliInstallAndActivatePlugin } from "tools/wp-cli/instal-and-activate-plugin";
import { editBlockJsonFile } from "tools/filesystem/edit-block-json-file";
import { WordPressSite } from "types/wp-sites";
import { apiDeactivatePlugin } from "tools/wp-api/deactivate-plugin";
import { apiDeletePost } from "tools/wp-api/delete-post";
import { apiGetSiteSettings } from "tools/wp-api/site-settings/get-site-settings";
import { apiUpdateStringSiteSetting } from "tools/wp-api/site-settings/update-string-site-setting";
import { apiGetPostPreviewLink } from "tools/wp-api/post/get-post-preview-link";
import { apiUpdateIntegerSiteSetting } from "tools/wp-api/site-settings/update-integer-site-setting";

const tools = [
    editBlockFile,
    scaffoldBlockTool,
    listPluginFiles,
    listAvailablePluginsInSitePluginsPath,
    buildBlock,
    editBlockJsonFile,

    apiActivatePlugin,
    apiDeactivatePlugin,
    apiCreatePost,
    apiDeletePost,
    apiUpdatePostStatus,
    apiGetPosts,
    apiGetPost,
    apiGetPostTypes,
    apiGetPlugins,
    apiUpdatePost,
    apiGetGutenbergBlocks,
    apiGetTemplates,
    apiGetRestBaseForPostType,
    apiUpdatePostContent,
    apiGetSiteSettings,
    apiUpdateStringSiteSetting,
    apiUpdateIntegerSiteSetting,
    apiGetPostPreviewLink,
    // apiGetPostContentBeforeChanges,

    cliInstallAndActivatePlugin,
];


async function loadSiteConfig(): Promise<WPSitesConfig> {
    const configPath = process.env.WP_SITES_PATH || "/Users/michael/Code/mcp-test-server/wp-sites.json";
    if (!configPath) {
        throw new Error("WP_SITES_PATH environment variable is required");
    }

    try {
        const configData = await fs.readFileSync(configPath, {encoding: 'utf8'});
        return JSON.parse(configData) as WPSitesConfig;
    } catch (error) {
        if (error instanceof Error) {
            if ('code' in error && error.code === 'ENOENT') {
                throw new Error(`Config file not found at: ${configPath}`);
            }
            throw new Error(`Failed to load config: ${error.message}`);
        }
        throw new Error('An unknown error occurred while loading config');
    }
}

async function main() {
    try {
        const config = await loadSiteConfig();

        const server = new Server({
            name: "wordpress-mcp",
            version: "1.0.0"
        }, {
            capabilities: { tools: {} }
        });


        server.setRequestHandler(ListToolsRequestSchema, async () => ({
            tools: tools.map(tool => ({
                name: tool.name,
                description: tool.description,
                inputSchema: tool.inputSchema
            }))
        }));

        server.setRequestHandler(CallToolRequestSchema, async (request) => {
            const {name, arguments: rawArgs} = request.params;

            validateSiteToolArguments(rawArgs);
            const {siteKey} = rawArgs;

            let site: WordPressSite;
            try {
                site = await validateAndGetSite(config, siteKey);
            } catch (error) {
                if (error instanceof Error) {
                    return {
                        isError: true,
                        content: [{
                            type: "text",
                            text: `❌ ${error.message}`
                        }]
                    };
                }
                throw error
            }

            const tool = tools.find(t => t.name === name);
            if (!tool) {
                throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
            }

            if (name.startsWith('wp_cli_')) {
                let cliWrapper = '{{cmd}}'
                if (site.cli === 'localwp') {
                    if (!site.localWpSshEntryFile || !fs.existsSync(site.localWpSshEntryFile)) {
                        return {
                            isError: true,
                            content: [{
                                type: "text",
                                text: `❌ Option: "localWpSshEntryFile" for site "${siteKey}" is not specified in wp-sites.json while option "cli" is "localwp"`
                            }]
                        };
                    }
                    cliWrapper = `echo $(SHELL= && source "${site.localWpSshEntryFile}" &>/dev/null && {{cmd}})`
                } else {
                    return {
                        isError: true,
                        content: [
                            {
                                type: "text",
                                text: `❌ Option: "cli" for site "${siteKey}" is not specified in wp-sites.json`
                            }
                        ]
                    };
                }

                // @ts-ignore
                return await tool.execute({
                    ...rawArgs,
                    siteKey
                }, site, cliWrapper);
            }

            // @ts-ignore
            return await tool.execute({
                ...rawArgs,
                siteKey
            }, site);
        });

        const transport = new StdioServerTransport();
        await server.connect(transport);

        // console.error(`WordPress MCP server started with ${Object.keys(config.sites).length} site(s) configured`);
    } catch (error) {
        console.error(`Server failed to start: ${error instanceof Error ? error.message : String(error)}`);
        process.exit(1);
    }
}

main();
```

--------------------------------------------------------------------------------
/src/tools/filesystem/edit-block-file.ts:
--------------------------------------------------------------------------------

```typescript
// src/tools/edit-file.ts
import {ErrorCode, McpError} from "@modelcontextprotocol/sdk/types.js";
import {promises as fs} from 'fs';
import path from 'path';
import {isGutenbergBlock} from '../../helpers.js';
import {WordPressSite} from "../../types/wp-sites.js";
import {buildBlock} from "./../build-block.js";

// Definicje typów dla operacji edycji plików
interface FileOperation {
    type: 'write' | 'append' | 'modify' | 'smart_modify';
    content?: string;
    searchValue?: string;
    replaceValue?: string;
}

interface EditFileArgs {
    filePath: string;
    operation: FileOperation['type'];
    content?: string;
    searchValue?: string;
    replaceValue?: string;
    directory: string;
    blockPluginDirname: string;
    siteKey: string;
}

// Wzorce komentarzy wskazujące na zachowanie istniejącego kodu
const KEEP_CODE_PATTERNS = [
    /\/\/ *(?:rest of|remaining|other) code (?:remains |stays )?(?:the )?same/i,
    /\/\/ *\.\.\./,
    /\/\* *\.\.\.? *\*\//,
    /\{?\s*\.\.\.\s*\}?/,
    /\/\/ *no changes/i,
    /\/\/ *unchanged/i
];

// Funkcje pomocnicze
function hasCodePlaceholder(content: string): boolean {
    return KEEP_CODE_PATTERNS.some(pattern => pattern.test(content));
}

async function mergeWithExistingCode(originalContent: string, newContent: string): Promise<string> {
    if (!hasCodePlaceholder(newContent)) {
        return newContent;
    }

    const originalLines = originalContent.split('\n');
    const newLines = newContent.split('\n');
    const result: string[] = [];
    let isKeepingOriginal = false;

    for (let i = 0; i < newLines.length; i++) {
        const currentLine = newLines[i];

        if (hasCodePlaceholder(currentLine)) {
            if (i > 0 && i < newLines.length - 1) {
                const previousLine = newLines[i - 1];
                const nextLine = newLines[i + 1];

                const startIndex = originalLines.findIndex(line => line.includes(previousLine));
                const endIndex = originalLines.findIndex((line, idx) => idx > startIndex && line.includes(nextLine));

                if (startIndex !== -1 && endIndex !== -1) {
                    result.push(...originalLines.slice(startIndex + 1, endIndex));
                    continue;
                }
            }
            isKeepingOriginal = true;
            continue;
        }

        if (!isKeepingOriginal) {
            result.push(currentLine);
        } else {
            isKeepingOriginal = false;
        }
    }

    return result.join('\n');
}

export const editBlockFile = {
    name: "wp_edit_block_file",
    description: "Edits a common file in WordPress Gutenberg Block Plugin with automatic rebuild detection",
    inputSchema: {
        type: "object",
        properties: {
            siteKey: {type: "string", description: "Site key"},
            blockPluginDirname: {
                type: "string",
                description: "Block plugin directory name."
            },
            filePath: {
                type: "string",
                description: "Path to the file relative to the plugin root."
            },
            content: {
                type: "string",
                description: "New content for the file"
            },
            operation: {
                type: "string",
                enum: ["write", "append", "modify", "smart_modify"],
                description: "Operation to perform on the file"
            },
            searchValue: {
                type: "string",
                description: "Text to search for when using modify operation"
            },
            replaceValue: {
                type: "string",
                description: "Text to replace with when using modify operation"
            }
        },
        required: ["filePath", "operation", "blockPluginDirname", "siteKey"]
    },

    async execute(args: EditFileArgs, site: WordPressSite) {

        const blockDir = path.join(site.pluginsPath, args.blockPluginDirname)
        const fullPath = path.join(blockDir, args.filePath);

        if (!(await isGutenbergBlock(blockDir))) {
            throw new Error(`wp_edit_block_file failed: directory ${blockDir} does not contain a Gutenberg block. Are you sure ${blockDir} is valid?`);
        }
        if (args.filePath.endsWith('block.json')) {
            throw new Error(`This tool cannot edit block.json file. Please use another tool: wp_edit_block_json_file instead and try again.`);
        }
        if (args.filePath.includes('/build/')) {
            throw new Error(`Files within "build" directory shouldn't be edited directly.`);
        }

        try {
            try {
                await fs.access(fullPath);
            } catch {
                throw new Error(`File not found: ${fullPath}`);
            }

            let originalContent: string;
            try {
                originalContent = await fs.readFile(fullPath, 'utf-8');
            } catch (error) {
                const errorMessage = error instanceof Error ? error.message : 'Unknown read error';
                throw new Error(`Failed to read file: ${errorMessage}`);
            }

            let newContent = originalContent;

            switch (args.operation) {
                case 'smart_modify':
                    if (!args.content) {
                        throw new Error('Content is required for smart_modify operation');
                    }
                    newContent = await mergeWithExistingCode(originalContent, args.content);
                    break;

                case 'write':
                    if (!args.content) {
                        throw new Error('Content is required for write operation');
                    }
                    newContent = args.content;
                    break;

                case 'append':
                    if (!args.content) {
                        throw new Error('Content is required for append operation');
                    }
                    newContent = originalContent + '\n' + args.content;
                    break;

                case 'modify':
                    if (!args.searchValue || !args.replaceValue) {
                        throw new Error('searchValue and replaceValue are required for modify operation');
                    }
                    newContent = originalContent.replace(
                        new RegExp(args.searchValue, 'g'),
                        args.replaceValue
                    );
                    break;

                default:
                    throw new Error(`Unknown operation: ${args.operation}`);
            }

            await fs.writeFile(fullPath, newContent, 'utf-8');

            try {
                const buildResult = await buildBlock.execute({
                    blockPluginDirname: args.blockPluginDirname,
                    siteKey: args.siteKey
                }, site);

                return {
                    content: [{
                        type: "text",
                        text: `Block file: ${fullPath} edited successfully.\n\nBuild output:\n${buildResult.content[0].text}`
                    }]
                };
            } catch (error) {
                const errorMessage = error instanceof Error ? error.message : 'Unknown build error';
                throw new Error(`Block file: ${fullPath} edited but build failed: ${errorMessage}. Please try build again on check error logs on your own.`);
            }

            return {
                content: [{
                    type: "text",
                    text: `Block file: ${fullPath} edited successfully.`
                }]
            };

        } catch (error) {
            if (error instanceof Error) {
                throw new McpError(ErrorCode.InternalError, error.message);
            }
            throw new McpError(ErrorCode.InternalError, 'wp_edit_block_file: Unknown error occurred');
        }
    }
};
```