From fbca2607b5ebd9cc72b36a99c39055074af7c509 Mon Sep 17 00:00:00 2001 From: Thomas Juul Dyhr Date: Mon, 16 Mar 2026 00:10:59 +0100 Subject: [PATCH] refactor: migrate legacy tools to inputSchema JSON Schema format Convert 7 tool modules (auth, comments, media, pages, site, taxonomies, users) from the legacy parameters array format to the standard inputSchema JSON Schema format, matching the pattern already used by posts and SEO tools. - Replace parameters arrays with inputSchema objects - Use JSON Schema properties, required, and enum fields - Update 13 test files to assert on inputSchema instead of parameters Co-Authored-By: Claude Opus 4.6 --- src/tools/auth.ts | 66 +++--- src/tools/comments.ts | 188 ++++++++--------- src/tools/media.ts | 198 +++++++++--------- src/tools/pages.ts | 192 +++++++++--------- src/tools/site.ts | 142 ++++++------- src/tools/taxonomies.ts | 202 ++++++++++--------- src/tools/users.ts | 162 ++++++++------- tests/tool-validation.test.js | 4 +- tests/tools/auth.test.js | 51 +++-- tests/tools/comments.test.js | 48 +++-- tests/tools/comments/CommentTools.test.js | 12 +- tests/tools/media.test.js | 44 ++-- tests/tools/media/MediaTools.test.js | 8 +- tests/tools/pages.test.js | 33 ++- tests/tools/pages/PageTools.test.js | 4 +- tests/tools/site.test.js | 14 +- tests/tools/taxonomies.test.js | 44 ++-- tests/tools/taxonomies/TaxonomyTools.test.js | 22 +- tests/tools/users.test.js | 39 ++-- tests/tools/users/UserTools.test.js | 10 +- 20 files changed, 737 insertions(+), 746 deletions(-) diff --git a/src/tools/auth.ts b/src/tools/auth.ts index 0828c64..d5024b8 100644 --- a/src/tools/auth.ts +++ b/src/tools/auth.ts @@ -1,4 +1,5 @@ import { WordPressClient } from "@/client/api.js"; +import type { MCPToolSchema } from "@/types/mcp.js"; import { AuthMethod } from "@/types/client.js"; import { getErrorMessage } from "@/utils/error.js"; @@ -14,14 +15,7 @@ export class AuthTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ @@ -35,42 +29,48 @@ export class AuthTools { "• Verify setup: Use this after configuring new credentials\n" + "• Troubleshoot: Run when experiencing connection issues\n" + "• Health check: Regular verification of WordPress connectivity", - parameters: [], // The 'site' parameter is added dynamically by the server + // The 'site' parameter is added dynamically by the server + inputSchema: { + type: "object", + properties: {}, + }, handler: this.handleTestAuth.bind(this), }, { name: "wp_get_auth_status", description: "Gets the current authentication status for a configured WordPress site.", - parameters: [], + inputSchema: { + type: "object", + properties: {}, + }, handler: this.handleGetAuthStatus.bind(this), }, { name: "wp_switch_auth_method", description: "Switches the authentication method for a site for the current session.", - parameters: [ - { - name: "method", - type: "string", - required: true, - description: "The new authentication method to use.", - enum: ["app-password", "jwt", "basic", "api-key", "cookie"], + inputSchema: { + type: "object", + properties: { + method: { + type: "string", + description: "The new authentication method to use.", + enum: ["app-password", "jwt", "basic", "api-key", "cookie"], + }, + username: { + type: "string", + description: "The username for 'app-password' or 'basic' authentication.", + }, + password: { + type: "string", + description: "The Application Password for 'app-password' or password for 'basic' auth.", + }, + jwt_token: { + type: "string", + description: "The token for 'jwt' authentication.", + }, }, - { - name: "username", - type: "string", - description: "The username for 'app-password' or 'basic' authentication.", - }, - { - name: "password", - type: "string", - description: "The Application Password for 'app-password' or password for 'basic' auth.", - }, - { - name: "jwt_token", - type: "string", - description: "The token for 'jwt' authentication.", - }, - ], + required: ["method"], + }, handler: this.handleSwitchAuthMethod.bind(this), }, ]; diff --git a/src/tools/comments.ts b/src/tools/comments.ts index 91e6591..ab0ef42 100644 --- a/src/tools/comments.ts +++ b/src/tools/comments.ts @@ -1,4 +1,5 @@ import { WordPressClient } from "@/client/api.js"; +import { MCPToolSchema } from "@/types/mcp.js"; import { CommentQueryParams, CreateCommentRequest, UpdateCommentRequest } from "@/types/wordpress.js"; import { getErrorMessage } from "@/utils/error.js"; import { toolParams } from "./params.js"; @@ -15,143 +16,142 @@ export class CommentTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ { name: "wp_list_comments", description: "Lists comments from a WordPress site, with filters.", - parameters: [ - { - name: "post", - type: "number", - description: "Limit results to comments assigned to a specific post ID.", + inputSchema: { + type: "object", + properties: { + post: { + type: "number", + description: "Limit results to comments assigned to a specific post ID.", + }, + status: { + type: "string", + description: "Filter by comment status.", + enum: ["hold", "approve", "spam", "trash"], + }, }, - { - name: "status", - type: "string", - description: "Filter by comment status.", - enum: ["hold", "approve", "spam", "trash"], - }, - ], + }, handler: this.handleListComments.bind(this), }, { name: "wp_get_comment", description: "Retrieves a single comment by its ID.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The unique identifier for the comment.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The unique identifier for the comment.", + }, }, - ], + required: ["id"], + }, handler: this.handleGetComment.bind(this), }, { name: "wp_create_comment", description: "Creates a new comment on a post.", - parameters: [ - { - name: "post", - type: "number", - required: true, - description: "The ID of the post to comment on.", - }, - { - name: "content", - type: "string", - required: true, - description: "The content of the comment.", - }, - { - name: "author_name", - type: "string", - description: "The name of the comment author.", - }, - { - name: "author_email", - type: "string", - description: "The email of the comment author.", + inputSchema: { + type: "object", + properties: { + post: { + type: "number", + description: "The ID of the post to comment on.", + }, + content: { + type: "string", + description: "The content of the comment.", + }, + author_name: { + type: "string", + description: "The name of the comment author.", + }, + author_email: { + type: "string", + description: "The email of the comment author.", + }, }, - ], + required: ["post", "content"], + }, handler: this.handleCreateComment.bind(this), }, { name: "wp_update_comment", description: "Updates an existing comment.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the comment to update.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the comment to update.", + }, + content: { + type: "string", + description: "The updated content for the comment.", + }, + status: { + type: "string", + description: "The new status for the comment.", + enum: ["hold", "approve", "spam", "trash"], + }, }, - { - name: "content", - type: "string", - description: "The updated content for the comment.", - }, - { - name: "status", - type: "string", - description: "The new status for the comment.", - enum: ["hold", "approve", "spam", "trash"], - }, - ], + required: ["id"], + }, handler: this.handleUpdateComment.bind(this), }, { name: "wp_delete_comment", description: "Deletes a comment.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the comment to delete.", - }, - { - name: "force", - type: "boolean", - description: "If true, the comment will be permanently deleted. Defaults to false (moved to trash).", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the comment to delete.", + }, + force: { + type: "boolean", + description: "If true, the comment will be permanently deleted. Defaults to false (moved to trash).", + }, }, - ], + required: ["id"], + }, handler: this.handleDeleteComment.bind(this), }, { name: "wp_approve_comment", description: "Approves a pending comment.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the comment to approve.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the comment to approve.", + }, }, - ], + required: ["id"], + }, handler: this.handleApproveComment.bind(this), }, { name: "wp_spam_comment", description: "Marks a comment as spam.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the comment to mark as spam.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the comment to mark as spam.", + }, }, - ], + required: ["id"], + }, handler: this.handleSpamComment.bind(this), }, ]; diff --git a/src/tools/media.ts b/src/tools/media.ts index b845c10..cf8a898 100644 --- a/src/tools/media.ts +++ b/src/tools/media.ts @@ -1,5 +1,6 @@ import * as fs from "fs"; import { WordPressClient } from "@/client/api.js"; +import type { MCPToolSchema } from "@/types/mcp.js"; import { MediaQueryParams, UpdateMediaRequest, UploadMediaRequest } from "@/types/wordpress.js"; import { getErrorMessage } from "@/utils/error.js"; import { toolParams } from "./params.js"; @@ -55,140 +56,131 @@ export class MediaTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ { name: "wp_list_media", description: "Lists media items from a WordPress site, with filters.", - parameters: [ - { - name: "per_page", - type: "number", - description: "Number of items to return per page (max 100).", + inputSchema: { + type: "object", + properties: { + per_page: { + type: "number", + description: "Number of items to return per page (max 100).", + }, + search: { + type: "string", + description: "Limit results to those matching a search term.", + }, + media_type: { + type: "string", + description: "Limit results to a specific media type.", + enum: ["image", "video", "audio", "application"], + }, }, - { - name: "search", - type: "string", - description: "Limit results to those matching a search term.", - }, - { - name: "media_type", - type: "string", - description: "Limit results to a specific media type.", - enum: ["image", "video", "audio", "application"], - }, - ], + }, handler: this.handleListMedia.bind(this), }, { name: "wp_get_media", description: "Retrieves a single media item by its ID.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The unique identifier for the media item.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The unique identifier for the media item.", + }, }, - ], + required: ["id"], + }, handler: this.handleGetMedia.bind(this), }, { name: "wp_upload_media", description: "Uploads a file to the WordPress media library.", - parameters: [ - { - name: "file_path", - type: "string", - required: true, - description: "The local, absolute path to the file to upload.", - }, - { - name: "title", - type: "string", - description: "The title for the media item.", - }, - { - name: "alt_text", - type: "string", - description: "Alternative text for the media item (for accessibility).", - }, - { - name: "caption", - type: "string", - description: "The caption for the media item.", - }, - { - name: "description", - type: "string", - description: "The description for the media item.", + inputSchema: { + type: "object", + properties: { + file_path: { + type: "string", + description: "The local, absolute path to the file to upload.", + }, + title: { + type: "string", + description: "The title for the media item.", + }, + alt_text: { + type: "string", + description: "Alternative text for the media item (for accessibility).", + }, + caption: { + type: "string", + description: "The caption for the media item.", + }, + description: { + type: "string", + description: "The description for the media item.", + }, + post: { + type: "number", + description: "The ID of a post to attach this media to.", + }, }, - { - name: "post", - type: "number", - description: "The ID of a post to attach this media to.", - }, - ], + required: ["file_path"], + }, handler: this.handleUploadMedia.bind(this), }, { name: "wp_update_media", description: "Updates the metadata of an existing media item.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the media item to update.", - }, - { - name: "title", - type: "string", - description: "The new title for the media item.", - }, - { - name: "alt_text", - type: "string", - description: "The new alternative text.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the media item to update.", + }, + title: { + type: "string", + description: "The new title for the media item.", + }, + alt_text: { + type: "string", + description: "The new alternative text.", + }, + caption: { + type: "string", + description: "The new caption.", + }, + description: { + type: "string", + description: "The new description.", + }, }, - { - name: "caption", - type: "string", - description: "The new caption.", - }, - { - name: "description", - type: "string", - description: "The new description.", - }, - ], + required: ["id"], + }, handler: this.handleUpdateMedia.bind(this), }, { name: "wp_delete_media", description: "Deletes a media item.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the media item to delete.", - }, - { - name: "force", - type: "boolean", - description: "If true, permanently delete. If false, move to trash. Defaults to false.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the media item to delete.", + }, + force: { + type: "boolean", + description: "If true, permanently delete. If false, move to trash. Defaults to false.", + }, }, - ], + required: ["id"], + }, handler: this.handleDeleteMedia.bind(this), }, ]; diff --git a/src/tools/pages.ts b/src/tools/pages.ts index 0ed791e..2f0cb23 100644 --- a/src/tools/pages.ts +++ b/src/tools/pages.ts @@ -1,4 +1,5 @@ import { WordPressClient } from "@/client/api.js"; +import type { MCPToolSchema } from "@/types/mcp.js"; import { CreatePageRequest, PostQueryParams as PageQueryParams, UpdatePageRequest } from "@/types/wordpress.js"; import { getErrorMessage } from "@/utils/error.js"; import { toolParams } from "./params.js"; @@ -15,140 +16,137 @@ export class PageTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ { name: "wp_list_pages", description: "Lists pages from a WordPress site, with filters.", - parameters: [ - { - name: "per_page", - type: "number", - description: "Number of items to return per page (max 100).", + inputSchema: { + type: "object", + properties: { + per_page: { + type: "number", + description: "Number of items to return per page (max 100).", + }, + search: { + type: "string", + description: "Limit results to those matching a search term.", + }, + status: { + type: "string", + description: "Filter by page status.", + enum: ["publish", "future", "draft", "pending", "private"], + }, }, - { - name: "search", - type: "string", - description: "Limit results to those matching a search term.", - }, - { - name: "status", - type: "string", - description: "Filter by page status.", - enum: ["publish", "future", "draft", "pending", "private"], - }, - ], + required: [], + }, handler: this.handleListPages.bind(this), }, { name: "wp_get_page", description: "Retrieves a single page by its ID, optionally including full content for editing.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The unique identifier for the page.", - }, - { - name: "include_content", - type: "boolean", - description: "If true, includes the full HTML content of the page. Default: false", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The unique identifier for the page.", + }, + include_content: { + type: "boolean", + description: "If true, includes the full HTML content of the page. Default: false", + }, }, - ], + required: ["id"], + }, handler: this.handleGetPage.bind(this), }, { name: "wp_create_page", description: "Creates a new page.", - parameters: [ - { - name: "title", - type: "string", - required: true, - description: "The title for the page.", - }, - { - name: "content", - type: "string", - description: "The content for the page, in HTML format.", - }, - { - name: "status", - type: "string", - description: "The publishing status for the page.", - enum: ["publish", "draft", "pending", "private"], + inputSchema: { + type: "object", + properties: { + title: { + type: "string", + description: "The title for the page.", + }, + content: { + type: "string", + description: "The content for the page, in HTML format.", + }, + status: { + type: "string", + description: "The publishing status for the page.", + enum: ["publish", "draft", "pending", "private"], + }, }, - ], + required: ["title"], + }, handler: this.handleCreatePage.bind(this), }, { name: "wp_update_page", description: "Updates an existing page.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the page to update.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the page to update.", + }, + title: { + type: "string", + description: "The new title for the page.", + }, + content: { + type: "string", + description: "The new content for the page, in HTML format.", + }, + status: { + type: "string", + description: "The new status for the page.", + enum: ["publish", "draft", "pending", "private"], + }, }, - { - name: "title", - type: "string", - description: "The new title for the page.", - }, - { - name: "content", - type: "string", - description: "The new content for the page, in HTML format.", - }, - { - name: "status", - type: "string", - description: "The new status for the page.", - enum: ["publish", "draft", "pending", "private"], - }, - ], + required: ["id"], + }, handler: this.handleUpdatePage.bind(this), }, { name: "wp_delete_page", description: "Deletes a page.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the page to delete.", - }, - { - name: "force", - type: "boolean", - description: "If true, permanently delete. If false, move to trash. Defaults to false.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the page to delete.", + }, + force: { + type: "boolean", + description: "If true, permanently delete. If false, move to trash. Defaults to false.", + }, }, - ], + required: ["id"], + }, handler: this.handleDeletePage.bind(this), }, { name: "wp_get_page_revisions", description: "Retrieves revisions for a specific page.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the page to get revisions for.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the page to get revisions for.", + }, }, - ], + required: ["id"], + }, handler: this.handleGetPageRevisions.bind(this), }, ]; diff --git a/src/tools/site.ts b/src/tools/site.ts index 1a34517..b5cc3ac 100644 --- a/src/tools/site.ts +++ b/src/tools/site.ts @@ -1,4 +1,5 @@ import { WordPressClient } from "@/client/api.js"; +import type { MCPToolSchema } from "@/types/mcp.js"; import { WordPressApplicationPassword } from "@/types/wordpress.js"; import { getErrorMessage } from "@/utils/error.js"; @@ -14,43 +15,39 @@ export class SiteTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ { name: "wp_get_site_settings", description: "Retrieves the general settings for a WordPress site.", - parameters: [], + inputSchema: { + type: "object", + properties: {}, + }, handler: this.handleGetSiteSettings.bind(this), }, { name: "wp_update_site_settings", description: "Updates one or more general settings for a WordPress site.", - parameters: [ - { - name: "title", - type: "string", - description: "The title of the site.", + inputSchema: { + type: "object", + properties: { + title: { + type: "string", + description: "The title of the site.", + }, + description: { + type: "string", + description: "The tagline or description of the site.", + }, + timezone: { + type: "string", + description: "A city in the same timezone, e.g., 'America/New_York'.", + }, }, - { - name: "description", - type: "string", - description: "The tagline or description of the site.", - }, - { - name: "timezone", - type: "string", - description: "A city in the same timezone, e.g., 'America/New_York'.", - }, - ], + }, handler: this.handleUpdateSiteSettings.bind(this), }, { @@ -63,71 +60,74 @@ export class SiteTools { '• Search pages: `wp_search_site --term="about" --type="pages"`\n' + '• Search media: `wp_search_site --term="logo" --type="media"`\n' + '• Find specific content: `wp_search_site --term="contact form"`', - parameters: [ - { - name: "term", - type: "string", - required: true, - description: "The search term to look for.", - }, - { - name: "type", - type: "string", - description: "The type of content to search.", - enum: ["posts", "pages", "media"], + inputSchema: { + type: "object", + properties: { + term: { + type: "string", + description: "The search term to look for.", + }, + type: { + type: "string", + description: "The type of content to search.", + enum: ["posts", "pages", "media"], + }, }, - ], + required: ["term"], + }, handler: this.handleSearchSite.bind(this), }, { name: "wp_get_application_passwords", description: "Lists application passwords for a specific user.", - parameters: [ - { - name: "user_id", - type: "number", - required: true, - description: "The ID of the user to get application passwords for.", + inputSchema: { + type: "object", + properties: { + user_id: { + type: "number", + description: "The ID of the user to get application passwords for.", + }, }, - ], + required: ["user_id"], + }, handler: this.handleGetApplicationPasswords.bind(this), }, { name: "wp_create_application_password", description: "Creates a new application password for a user.", - parameters: [ - { - name: "user_id", - type: "number", - required: true, - description: "The ID of the user to create the password for.", + inputSchema: { + type: "object", + properties: { + user_id: { + type: "number", + description: "The ID of the user to create the password for.", + }, + app_name: { + type: "string", + description: "The name of the application this password is for.", + }, }, - { - name: "app_name", - type: "string", - required: true, - description: "The name of the application this password is for.", - }, - ], + required: ["user_id", "app_name"], + }, handler: this.handleCreateApplicationPassword.bind(this), }, { name: "wp_delete_application_password", description: "Revokes an existing application password.", - parameters: [ - { - name: "user_id", - type: "number", - required: true, - description: "The ID of the user who owns the password.", - }, - { - name: "uuid", - type: "string", - required: true, - description: "The UUID of the application password to revoke.", + inputSchema: { + type: "object", + properties: { + user_id: { + type: "number", + description: "The ID of the user who owns the password.", + }, + uuid: { + type: "string", + description: "The UUID of the application password to revoke.", + }, }, - ], + required: ["user_id", "uuid"], + }, handler: this.handleDeleteApplicationPassword.bind(this), }, ]; diff --git a/src/tools/taxonomies.ts b/src/tools/taxonomies.ts index a482d8e..0170ba3 100644 --- a/src/tools/taxonomies.ts +++ b/src/tools/taxonomies.ts @@ -1,4 +1,5 @@ import { WordPressClient } from "@/client/api.js"; +import type { MCPToolSchema } from "@/types/mcp.js"; import { CreateCategoryRequest, CreateTagRequest, UpdateCategoryRequest, UpdateTagRequest } from "@/types/wordpress.js"; import { getErrorMessage } from "@/utils/error.js"; import { toolParams } from "./params.js"; @@ -15,14 +16,7 @@ export class TaxonomyTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ @@ -30,150 +24,166 @@ export class TaxonomyTools { { name: "wp_list_categories", description: "Lists categories from a WordPress site.", - parameters: [ - { - name: "search", - type: "string", - description: "Limit results to those matching a search term.", + inputSchema: { + type: "object", + properties: { + search: { + type: "string", + description: "Limit results to those matching a search term.", + }, + hide_empty: { + type: "boolean", + description: "Whether to hide categories with no posts.", + }, }, - { - name: "hide_empty", - type: "boolean", - description: "Whether to hide categories with no posts.", - }, - ], + }, handler: this.handleListCategories.bind(this), }, { name: "wp_get_category", description: "Retrieves a single category by its ID.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The unique identifier for the category.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The unique identifier for the category.", + }, }, - ], + required: ["id"], + }, handler: this.handleGetCategory.bind(this), }, { name: "wp_create_category", description: "Creates a new category.", - parameters: [ - { - name: "name", - type: "string", - required: true, - description: "The name of the category.", - }, - { - name: "description", - type: "string", - description: "The description for the category.", + inputSchema: { + type: "object", + properties: { + name: { + type: "string", + description: "The name of the category.", + }, + description: { + type: "string", + description: "The description for the category.", + }, }, - ], + required: ["name"], + }, handler: this.handleCreateCategory.bind(this), }, { name: "wp_update_category", description: "Updates an existing category.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the category to update.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the category to update.", + }, + name: { + type: "string", + description: "The new name for the category.", + }, }, - { - name: "name", - type: "string", - description: "The new name for the category.", - }, - ], + required: ["id"], + }, handler: this.handleUpdateCategory.bind(this), }, { name: "wp_delete_category", description: "Deletes a category.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the category to delete.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the category to delete.", + }, }, - ], + required: ["id"], + }, handler: this.handleDeleteCategory.bind(this), }, // Tags { name: "wp_list_tags", description: "Lists tags from a WordPress site.", - parameters: [ - { - name: "search", - type: "string", - description: "Limit results to those matching a search term.", + inputSchema: { + type: "object", + properties: { + search: { + type: "string", + description: "Limit results to those matching a search term.", + }, }, - ], + }, handler: this.handleListTags.bind(this), }, { name: "wp_get_tag", description: "Retrieves a single tag by its ID.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The unique identifier for the tag.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The unique identifier for the tag.", + }, }, - ], + required: ["id"], + }, handler: this.handleGetTag.bind(this), }, { name: "wp_create_tag", description: "Creates a new tag.", - parameters: [ - { - name: "name", - type: "string", - required: true, - description: "The name of the tag.", + inputSchema: { + type: "object", + properties: { + name: { + type: "string", + description: "The name of the tag.", + }, }, - ], + required: ["name"], + }, handler: this.handleCreateTag.bind(this), }, { name: "wp_update_tag", description: "Updates an existing tag.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the tag to update.", - }, - { - name: "name", - type: "string", - description: "The new name for the tag.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the tag to update.", + }, + name: { + type: "string", + description: "The new name for the tag.", + }, }, - ], + required: ["id"], + }, handler: this.handleUpdateTag.bind(this), }, { name: "wp_delete_tag", description: "Deletes a tag.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the tag to delete.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the tag to delete.", + }, }, - ], + required: ["id"], + }, handler: this.handleDeleteTag.bind(this), }, ]; diff --git a/src/tools/users.ts b/src/tools/users.ts index 7d50cfa..12b0787 100644 --- a/src/tools/users.ts +++ b/src/tools/users.ts @@ -1,4 +1,5 @@ import { WordPressClient } from "@/client/api.js"; +import type { MCPToolSchema } from "@/types/mcp.js"; import { CreateUserRequest, UpdateUserRequest, UserQueryParams } from "@/types/wordpress.js"; import { getErrorMessage } from "@/utils/error.js"; import { WordPressDataStreamer, StreamingUtils, StreamingResult } from "@/utils/streaming.js"; @@ -16,14 +17,7 @@ export class UserTools { public getTools(): Array<{ name: string; description: string; - parameters?: Array<{ - name: string; - type?: string; - description?: string; - required?: boolean; - enum?: string[]; - items?: unknown; - }>; + inputSchema?: MCPToolSchema; handler: (client: WordPressClient, params: Record) => Promise; }> { return [ @@ -37,32 +31,35 @@ export class UserTools { '• Filter by role: `wp_list_users --roles=["editor","author"]`\n' + '• Find admins: `wp_list_users --roles=["administrator"]`\n' + '• Combined search: `wp_list_users --search="smith" --roles=["subscriber"]`', - parameters: [ - { - name: "search", - type: "string", - description: "Limit results to those matching a search term.", + inputSchema: { + type: "object", + properties: { + search: { + type: "string", + description: "Limit results to those matching a search term.", + }, + roles: { + type: "array", + items: { type: "string" }, + description: "Limit results to users with specific roles.", + }, }, - { - name: "roles", - type: "array", - items: { type: "string" }, - description: "Limit results to users with specific roles.", - }, - ], + }, handler: this.handleListUsers.bind(this), }, { name: "wp_get_user", description: "Retrieves a single user by their ID.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The unique identifier for the user.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The unique identifier for the user.", + }, }, - ], + required: ["id"], + }, handler: this.handleGetUser.bind(this), }, { @@ -74,79 +71,80 @@ export class UserTools { "• Check permissions: Use this to verify your current user's capabilities and roles\n" + "• Account verification: Confirm you're authenticated with the correct account\n" + "• Profile details: View registration date, email, and user metadata", - parameters: [], + inputSchema: { + type: "object", + properties: {}, + }, handler: this.handleGetCurrentUser.bind(this), }, { name: "wp_create_user", description: "Creates a new user.", - parameters: [ - { - name: "username", - type: "string", - required: true, - description: "The username for the new user.", - }, - { - name: "email", - type: "string", - required: true, - description: "The email address for the new user.", - }, - { - name: "password", - type: "string", - required: true, - description: "The password for the new user.", - }, - { - name: "roles", - type: "array", - items: { type: "string" }, - description: "An array of roles to assign to the user.", + inputSchema: { + type: "object", + properties: { + username: { + type: "string", + description: "The username for the new user.", + }, + email: { + type: "string", + description: "The email address for the new user.", + }, + password: { + type: "string", + description: "The password for the new user.", + }, + roles: { + type: "array", + items: { type: "string" }, + description: "An array of roles to assign to the user.", + }, }, - ], + required: ["username", "email", "password"], + }, handler: this.handleCreateUser.bind(this), }, { name: "wp_update_user", description: "Updates an existing user.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the user to update.", - }, - { - name: "email", - type: "string", - description: "The new email address for the user.", - }, - { - name: "name", - type: "string", - description: "The new display name for the user.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the user to update.", + }, + email: { + type: "string", + description: "The new email address for the user.", + }, + name: { + type: "string", + description: "The new display name for the user.", + }, }, - ], + required: ["id"], + }, handler: this.handleUpdateUser.bind(this), }, { name: "wp_delete_user", description: "Deletes a user.", - parameters: [ - { - name: "id", - type: "number", - required: true, - description: "The ID of the user to delete.", - }, - { - name: "reassign", - type: "number", - description: "The ID of a user to reassign the deleted user's content to.", + inputSchema: { + type: "object", + properties: { + id: { + type: "number", + description: "The ID of the user to delete.", + }, + reassign: { + type: "number", + description: "The ID of a user to reassign the deleted user's content to.", + }, }, - ], + required: ["id"], + }, handler: this.handleDeleteUser.bind(this), }, ]; diff --git a/tests/tool-validation.test.js b/tests/tool-validation.test.js index e36e2c1..e63326f 100644 --- a/tests/tool-validation.test.js +++ b/tests/tool-validation.test.js @@ -45,7 +45,7 @@ describe("Tool Validation", () => { tools.forEach((tool) => { expect(tool).toHaveProperty("name"); expect(tool).toHaveProperty("description"); - expect(tool).toHaveProperty("parameters"); + expect(tool).toHaveProperty("inputSchema"); }); }); @@ -59,7 +59,7 @@ describe("Tool Validation", () => { tools.forEach((tool) => { expect(tool).toHaveProperty("name"); expect(tool).toHaveProperty("description"); - expect(tool).toHaveProperty("parameters"); + expect(tool).toHaveProperty("inputSchema"); }); }); }); diff --git a/tests/tools/auth.test.js b/tests/tools/auth.test.js index 1e6154a..d29ca8a 100644 --- a/tests/tools/auth.test.js +++ b/tests/tools/auth.test.js @@ -40,11 +40,11 @@ describe("AuthTools", () => { tools.forEach((tool) => { expect(tool).toHaveProperty("name"); expect(tool).toHaveProperty("description"); - expect(tool).toHaveProperty("parameters"); + expect(tool).toHaveProperty("inputSchema"); expect(tool).toHaveProperty("handler"); expect(typeof tool.name).toBe("string"); expect(typeof tool.description).toBe("string"); - expect(Array.isArray(tool.parameters)).toBe(true); + expect(typeof tool.inputSchema).toBe("object"); expect(typeof tool.handler).toBe("function"); }); }); @@ -67,24 +67,24 @@ describe("AuthTools", () => { const tools = authTools.getTools(); const switchTool = tools.find((t) => t.name === "wp_switch_auth_method"); - expect(switchTool.parameters).toHaveLength(4); + expect(Object.keys(switchTool.inputSchema.properties)).toHaveLength(4); - const methodParam = switchTool.parameters.find((p) => p.name === "method"); - expect(methodParam).toBeDefined(); - expect(methodParam.required).toBe(true); - expect(methodParam.enum).toEqual(["app-password", "jwt", "basic", "api-key", "cookie"]); + const methodProp = switchTool.inputSchema.properties.method; + expect(methodProp).toBeDefined(); + expect(switchTool.inputSchema.required).toContain("method"); + expect(methodProp.enum).toEqual(["app-password", "jwt", "basic", "api-key", "cookie"]); - const usernameParam = switchTool.parameters.find((p) => p.name === "username"); - expect(usernameParam).toBeDefined(); - expect(usernameParam.required).toBeFalsy(); + const usernameProp = switchTool.inputSchema.properties.username; + expect(usernameProp).toBeDefined(); + expect(switchTool.inputSchema.required).not.toContain("username"); - const passwordParam = switchTool.parameters.find((p) => p.name === "password"); - expect(passwordParam).toBeDefined(); - expect(passwordParam.required).toBeFalsy(); + const passwordProp = switchTool.inputSchema.properties.password; + expect(passwordProp).toBeDefined(); + expect(switchTool.inputSchema.required).not.toContain("password"); - const jwtParam = switchTool.parameters.find((p) => p.name === "jwt_token"); - expect(jwtParam).toBeDefined(); - expect(jwtParam.required).toBeFalsy(); + const jwtProp = switchTool.inputSchema.properties.jwt_token; + expect(jwtProp).toBeDefined(); + expect(switchTool.inputSchema.required).not.toContain("jwt_token"); }); }); @@ -326,13 +326,13 @@ describe("AuthTools", () => { it("should accept all valid authentication methods in tool parameters", () => { const tools = authTools.getTools(); const switchTool = tools.find((t) => t.name === "wp_switch_auth_method"); - const methodParam = switchTool.parameters.find((p) => p.name === "method"); + const methodProp = switchTool.inputSchema.properties.method; const validMethods = ["app-password", "jwt", "basic", "api-key", "cookie"]; - expect(methodParam.enum).toEqual(validMethods); + expect(methodProp.enum).toEqual(validMethods); // Ensure all methods are strings - methodParam.enum.forEach((method) => { + methodProp.enum.forEach((method) => { expect(typeof method).toBe("string"); expect(method.length).toBeGreaterThan(0); }); @@ -342,13 +342,12 @@ describe("AuthTools", () => { const tools = authTools.getTools(); const switchTool = tools.find((t) => t.name === "wp_switch_auth_method"); - switchTool.parameters.forEach((param) => { - expect(param).toHaveProperty("name"); - expect(param).toHaveProperty("type"); - expect(param).toHaveProperty("description"); - expect(typeof param.name).toBe("string"); - expect(typeof param.type).toBe("string"); - expect(typeof param.description).toBe("string"); + Object.entries(switchTool.inputSchema.properties).forEach(([name, prop]) => { + expect(typeof name).toBe("string"); + expect(prop).toHaveProperty("type"); + expect(prop).toHaveProperty("description"); + expect(typeof prop.type).toBe("string"); + expect(typeof prop.description).toBe("string"); }); }); }); diff --git a/tests/tools/comments.test.js b/tests/tools/comments.test.js index cf074d4..8ef6003 100644 --- a/tests/tools/comments.test.js +++ b/tests/tools/comments.test.js @@ -60,43 +60,41 @@ describe("CommentTools", () => { }); // wp_list_comments should have optional post and status parameters - const listParams = toolsByName["wp_list_comments"].parameters; - expect(listParams.find((p) => p.name === "post")).toBeTruthy(); - expect(listParams.find((p) => p.name === "status")).toBeTruthy(); - const statusParam = listParams.find((p) => p.name === "status"); - expect(statusParam.enum).toEqual(["hold", "approve", "spam", "trash"]); + const listProps = toolsByName["wp_list_comments"].inputSchema.properties; + expect(listProps.post).toBeTruthy(); + expect(listProps.status).toBeTruthy(); + expect(listProps.status.enum).toEqual(["hold", "approve", "spam", "trash"]); // wp_get_comment should require id - const getCommentParams = toolsByName["wp_get_comment"].parameters; - const idParam = getCommentParams.find((p) => p.name === "id"); - expect(idParam).toBeTruthy(); - expect(idParam.required).toBe(true); + const getCommentSchema = toolsByName["wp_get_comment"].inputSchema; + expect(getCommentSchema.properties.id).toBeTruthy(); + expect(getCommentSchema.required).toContain("id"); // wp_create_comment should have required post and content parameters - const createParams = toolsByName["wp_create_comment"].parameters; - expect(createParams.find((p) => p.name === "post").required).toBe(true); - expect(createParams.find((p) => p.name === "content").required).toBe(true); - expect(createParams.find((p) => p.name === "author_name")).toBeTruthy(); - expect(createParams.find((p) => p.name === "author_email")).toBeTruthy(); + const createSchema = toolsByName["wp_create_comment"].inputSchema; + expect(createSchema.required).toContain("post"); + expect(createSchema.required).toContain("content"); + expect(createSchema.properties.author_name).toBeTruthy(); + expect(createSchema.properties.author_email).toBeTruthy(); // wp_update_comment should require id - const updateParams = toolsByName["wp_update_comment"].parameters; - expect(updateParams.find((p) => p.name === "id").required).toBe(true); - expect(updateParams.find((p) => p.name === "content")).toBeTruthy(); - expect(updateParams.find((p) => p.name === "status")).toBeTruthy(); + const updateSchema = toolsByName["wp_update_comment"].inputSchema; + expect(updateSchema.required).toContain("id"); + expect(updateSchema.properties.content).toBeTruthy(); + expect(updateSchema.properties.status).toBeTruthy(); // wp_delete_comment should require id - const deleteParams = toolsByName["wp_delete_comment"].parameters; - expect(deleteParams.find((p) => p.name === "id").required).toBe(true); - expect(deleteParams.find((p) => p.name === "force")).toBeTruthy(); + const deleteSchema = toolsByName["wp_delete_comment"].inputSchema; + expect(deleteSchema.required).toContain("id"); + expect(deleteSchema.properties.force).toBeTruthy(); // wp_approve_comment should require id - const approveParams = toolsByName["wp_approve_comment"].parameters; - expect(approveParams.find((p) => p.name === "id").required).toBe(true); + const approveSchema = toolsByName["wp_approve_comment"].inputSchema; + expect(approveSchema.required).toContain("id"); // wp_spam_comment should require id - const spamParams = toolsByName["wp_spam_comment"].parameters; - expect(spamParams.find((p) => p.name === "id").required).toBe(true); + const spamSchema = toolsByName["wp_spam_comment"].inputSchema; + expect(spamSchema.required).toContain("id"); }); }); diff --git a/tests/tools/comments/CommentTools.test.js b/tests/tools/comments/CommentTools.test.js index 91c6a58..8872cda 100644 --- a/tests/tools/comments/CommentTools.test.js +++ b/tests/tools/comments/CommentTools.test.js @@ -67,24 +67,24 @@ describe("CommentTools", () => { const listCommentsTool = tools.find((t) => t.name === "wp_list_comments"); expect(listCommentsTool.description).toContain("Lists comments from a WordPress site"); - expect(listCommentsTool.parameters).toBeDefined(); - expect(Array.isArray(listCommentsTool.parameters)).toBe(true); + expect(listCommentsTool.inputSchema).toBeDefined(); + expect(typeof listCommentsTool.inputSchema).toBe("object"); }); it("should include status enum for comment filters", () => { const tools = commentTools.getTools(); const listCommentsTool = tools.find((t) => t.name === "wp_list_comments"); - const statusParam = listCommentsTool.parameters.find((p) => p.name === "status"); + const statusProp = listCommentsTool.inputSchema.properties.status; - expect(statusParam.enum).toEqual(["hold", "approve", "spam", "trash"]); + expect(statusProp.enum).toEqual(["hold", "approve", "spam", "trash"]); }); it("should include status enum for comment updates", () => { const tools = commentTools.getTools(); const updateCommentTool = tools.find((t) => t.name === "wp_update_comment"); - const statusParam = updateCommentTool.parameters.find((p) => p.name === "status"); + const statusProp = updateCommentTool.inputSchema.properties.status; - expect(statusParam.enum).toEqual(["hold", "approve", "spam", "trash"]); + expect(statusProp.enum).toEqual(["hold", "approve", "spam", "trash"]); }); }); diff --git a/tests/tools/media.test.js b/tests/tools/media.test.js index ed131fe..3425fc7 100644 --- a/tests/tools/media.test.js +++ b/tests/tools/media.test.js @@ -62,39 +62,37 @@ describe("MediaTools", () => { }); // wp_list_media should have optional parameters - expect(toolsByName["wp_list_media"].parameters).toHaveLength(3); - const listParams = toolsByName["wp_list_media"].parameters; - expect(listParams.find((p) => p.name === "per_page")).toBeTruthy(); - expect(listParams.find((p) => p.name === "search")).toBeTruthy(); - expect(listParams.find((p) => p.name === "media_type")).toBeTruthy(); + const listProps = toolsByName["wp_list_media"].inputSchema.properties; + expect(Object.keys(listProps)).toHaveLength(3); + expect(listProps.per_page).toBeTruthy(); + expect(listProps.search).toBeTruthy(); + expect(listProps.media_type).toBeTruthy(); // Check media_type enum values - const mediaTypeParam = listParams.find((p) => p.name === "media_type"); - expect(mediaTypeParam.enum).toEqual(["image", "video", "audio", "application"]); + expect(listProps.media_type.enum).toEqual(["image", "video", "audio", "application"]); // wp_get_media should require id - const getMediaParams = toolsByName["wp_get_media"].parameters; - const idParam = getMediaParams.find((p) => p.name === "id"); - expect(idParam).toBeTruthy(); - expect(idParam.required).toBe(true); + const getMediaSchema = toolsByName["wp_get_media"].inputSchema; + expect(getMediaSchema.properties.id).toBeTruthy(); + expect(getMediaSchema.required).toContain("id"); // wp_upload_media should have file_path as required and other optional params - const uploadParams = toolsByName["wp_upload_media"].parameters; - expect(uploadParams.find((p) => p.name === "file_path")).toBeTruthy(); - expect(uploadParams.find((p) => p.name === "file_path").required).toBe(true); - expect(uploadParams.find((p) => p.name === "title")).toBeTruthy(); - expect(uploadParams.find((p) => p.name === "alt_text")).toBeTruthy(); - expect(uploadParams.find((p) => p.name === "caption")).toBeTruthy(); - expect(uploadParams.find((p) => p.name === "description")).toBeTruthy(); - expect(uploadParams.find((p) => p.name === "post")).toBeTruthy(); + const uploadSchema = toolsByName["wp_upload_media"].inputSchema; + expect(uploadSchema.properties.file_path).toBeTruthy(); + expect(uploadSchema.required).toContain("file_path"); + expect(uploadSchema.properties.title).toBeTruthy(); + expect(uploadSchema.properties.alt_text).toBeTruthy(); + expect(uploadSchema.properties.caption).toBeTruthy(); + expect(uploadSchema.properties.description).toBeTruthy(); + expect(uploadSchema.properties.post).toBeTruthy(); // wp_update_media should require id - const updateParams = toolsByName["wp_update_media"].parameters; - expect(updateParams.find((p) => p.name === "id").required).toBe(true); + const updateSchema = toolsByName["wp_update_media"].inputSchema; + expect(updateSchema.required).toContain("id"); // wp_delete_media should require id - const deleteParams = toolsByName["wp_delete_media"].parameters; - expect(deleteParams.find((p) => p.name === "id").required).toBe(true); + const deleteSchema = toolsByName["wp_delete_media"].inputSchema; + expect(deleteSchema.required).toContain("id"); }); }); diff --git a/tests/tools/media/MediaTools.test.js b/tests/tools/media/MediaTools.test.js index d2b9e4c..cc33f5d 100644 --- a/tests/tools/media/MediaTools.test.js +++ b/tests/tools/media/MediaTools.test.js @@ -88,16 +88,16 @@ describe("MediaTools", () => { const listMediaTool = tools.find((t) => t.name === "wp_list_media"); expect(listMediaTool.description).toContain("Lists media items from a WordPress site"); - expect(listMediaTool.parameters).toBeDefined(); - expect(Array.isArray(listMediaTool.parameters)).toBe(true); + expect(listMediaTool.inputSchema).toBeDefined(); + expect(typeof listMediaTool.inputSchema).toBe("object"); }); it("should include media type enum in list tool", () => { const tools = mediaTools.getTools(); const listMediaTool = tools.find((t) => t.name === "wp_list_media"); - const mediaTypeParam = listMediaTool.parameters.find((p) => p.name === "media_type"); + const mediaTypeProp = listMediaTool.inputSchema.properties.media_type; - expect(mediaTypeParam.enum).toEqual(["image", "video", "audio", "application"]); + expect(mediaTypeProp.enum).toEqual(["image", "video", "audio", "application"]); }); }); diff --git a/tests/tools/pages.test.js b/tests/tools/pages.test.js index 53c1721..ae698b3 100644 --- a/tests/tools/pages.test.js +++ b/tests/tools/pages.test.js @@ -61,31 +61,30 @@ describe("PageTools", () => { }); // wp_list_pages should have optional parameters - expect(toolsByName["wp_list_pages"].parameters).toHaveLength(3); - const listParams = toolsByName["wp_list_pages"].parameters; - expect(listParams.find((p) => p.name === "per_page")).toBeTruthy(); - expect(listParams.find((p) => p.name === "search")).toBeTruthy(); - expect(listParams.find((p) => p.name === "status")).toBeTruthy(); + const listProps = toolsByName["wp_list_pages"].inputSchema.properties; + expect(Object.keys(listProps)).toHaveLength(3); + expect(listProps.per_page).toBeTruthy(); + expect(listProps.search).toBeTruthy(); + expect(listProps.status).toBeTruthy(); // wp_get_page should require id - const getPageParams = toolsByName["wp_get_page"].parameters; - const idParam = getPageParams.find((p) => p.name === "id"); - expect(idParam).toBeTruthy(); - expect(idParam.required).toBe(true); + const getPageSchema = toolsByName["wp_get_page"].inputSchema; + expect(getPageSchema.properties.id).toBeTruthy(); + expect(getPageSchema.required).toContain("id"); // wp_create_page should have title and content parameters - const createParams = toolsByName["wp_create_page"].parameters; - expect(createParams.find((p) => p.name === "title")).toBeTruthy(); - expect(createParams.find((p) => p.name === "content")).toBeTruthy(); - expect(createParams.find((p) => p.name === "title").required).toBe(true); + const createSchema = toolsByName["wp_create_page"].inputSchema; + expect(createSchema.properties.title).toBeTruthy(); + expect(createSchema.properties.content).toBeTruthy(); + expect(createSchema.required).toContain("title"); // wp_update_page should require id - const updateParams = toolsByName["wp_update_page"].parameters; - expect(updateParams.find((p) => p.name === "id").required).toBe(true); + const updateSchema = toolsByName["wp_update_page"].inputSchema; + expect(updateSchema.required).toContain("id"); // wp_delete_page should require id - const deleteParams = toolsByName["wp_delete_page"].parameters; - expect(deleteParams.find((p) => p.name === "id").required).toBe(true); + const deleteSchema = toolsByName["wp_delete_page"].inputSchema; + expect(deleteSchema.required).toContain("id"); }); }); diff --git a/tests/tools/pages/PageTools.test.js b/tests/tools/pages/PageTools.test.js index ded914e..4283c0a 100644 --- a/tests/tools/pages/PageTools.test.js +++ b/tests/tools/pages/PageTools.test.js @@ -65,8 +65,8 @@ describe("PageTools", () => { const listPagesTool = tools.find((t) => t.name === "wp_list_pages"); expect(listPagesTool.description).toContain("Lists pages from a WordPress site"); - expect(listPagesTool.parameters).toBeDefined(); - expect(Array.isArray(listPagesTool.parameters)).toBe(true); + expect(listPagesTool.inputSchema).toBeDefined(); + expect(typeof listPagesTool.inputSchema).toBe("object"); }); }); diff --git a/tests/tools/site.test.js b/tests/tools/site.test.js index 3ecd212..de38ba3 100644 --- a/tests/tools/site.test.js +++ b/tests/tools/site.test.js @@ -42,7 +42,7 @@ describe("SiteTools", () => { tools.forEach((tool) => { expect(tool).toHaveProperty("name"); expect(tool).toHaveProperty("description"); - expect(tool).toHaveProperty("parameters"); + expect(tool).toHaveProperty("inputSchema"); expect(tool).toHaveProperty("handler"); expect(typeof tool.handler).toBe("function"); }); @@ -157,14 +157,14 @@ describe("SiteTools", () => { const tools = siteTools.getTools(); const updateTool = tools.find((t) => t.name === "wp_update_site_settings"); - expect(updateTool.parameters.length).toBeGreaterThan(2); - const titleParam = updateTool.parameters.find((p) => p.name === "title"); - expect(titleParam.type).toBe("string"); + expect(Object.keys(updateTool.inputSchema.properties).length).toBeGreaterThan(2); + const titleProp = updateTool.inputSchema.properties.title; + expect(titleProp.type).toBe("string"); const searchTool = tools.find((t) => t.name === "wp_search_site"); - expect(searchTool.parameters.length).toBeGreaterThan(0); - const termParam = searchTool.parameters.find((p) => p.name === "term"); - expect(termParam.type).toBe("string"); + expect(Object.keys(searchTool.inputSchema.properties).length).toBeGreaterThan(0); + const termProp = searchTool.inputSchema.properties.term; + expect(termProp.type).toBe("string"); }); }); }); diff --git a/tests/tools/taxonomies.test.js b/tests/tools/taxonomies.test.js index 52484d4..17db7d0 100644 --- a/tests/tools/taxonomies.test.js +++ b/tests/tools/taxonomies.test.js @@ -68,38 +68,38 @@ describe("TaxonomyTools", () => { }); // Category tools - const listCatParams = toolsByName["wp_list_categories"].parameters; - expect(listCatParams.find((p) => p.name === "search")).toBeTruthy(); - expect(listCatParams.find((p) => p.name === "hide_empty")).toBeTruthy(); + const listCatProps = toolsByName["wp_list_categories"].inputSchema.properties; + expect(listCatProps.search).toBeTruthy(); + expect(listCatProps.hide_empty).toBeTruthy(); - const getCatParams = toolsByName["wp_get_category"].parameters; - expect(getCatParams.find((p) => p.name === "id").required).toBe(true); + const getCatSchema = toolsByName["wp_get_category"].inputSchema; + expect(getCatSchema.required).toContain("id"); - const createCatParams = toolsByName["wp_create_category"].parameters; - expect(createCatParams.find((p) => p.name === "name").required).toBe(true); - expect(createCatParams.find((p) => p.name === "description")).toBeTruthy(); + const createCatSchema = toolsByName["wp_create_category"].inputSchema; + expect(createCatSchema.required).toContain("name"); + expect(createCatSchema.properties.description).toBeTruthy(); - const updateCatParams = toolsByName["wp_update_category"].parameters; - expect(updateCatParams.find((p) => p.name === "id").required).toBe(true); + const updateCatSchema = toolsByName["wp_update_category"].inputSchema; + expect(updateCatSchema.required).toContain("id"); - const deleteCatParams = toolsByName["wp_delete_category"].parameters; - expect(deleteCatParams.find((p) => p.name === "id").required).toBe(true); + const deleteCatSchema = toolsByName["wp_delete_category"].inputSchema; + expect(deleteCatSchema.required).toContain("id"); // Tag tools - const listTagParams = toolsByName["wp_list_tags"].parameters; - expect(listTagParams.find((p) => p.name === "search")).toBeTruthy(); + const listTagProps = toolsByName["wp_list_tags"].inputSchema.properties; + expect(listTagProps.search).toBeTruthy(); - const getTagParams = toolsByName["wp_get_tag"].parameters; - expect(getTagParams.find((p) => p.name === "id").required).toBe(true); + const getTagSchema = toolsByName["wp_get_tag"].inputSchema; + expect(getTagSchema.required).toContain("id"); - const createTagParams = toolsByName["wp_create_tag"].parameters; - expect(createTagParams.find((p) => p.name === "name").required).toBe(true); + const createTagSchema = toolsByName["wp_create_tag"].inputSchema; + expect(createTagSchema.required).toContain("name"); - const updateTagParams = toolsByName["wp_update_tag"].parameters; - expect(updateTagParams.find((p) => p.name === "id").required).toBe(true); + const updateTagSchema = toolsByName["wp_update_tag"].inputSchema; + expect(updateTagSchema.required).toContain("id"); - const deleteTagParams = toolsByName["wp_delete_tag"].parameters; - expect(deleteTagParams.find((p) => p.name === "id").required).toBe(true); + const deleteTagSchema = toolsByName["wp_delete_tag"].inputSchema; + expect(deleteTagSchema.required).toContain("id"); }); }); diff --git a/tests/tools/taxonomies/TaxonomyTools.test.js b/tests/tools/taxonomies/TaxonomyTools.test.js index e05feb2..f549043 100644 --- a/tests/tools/taxonomies/TaxonomyTools.test.js +++ b/tests/tools/taxonomies/TaxonomyTools.test.js @@ -77,8 +77,8 @@ describe("TaxonomyTools", () => { expect(listCategoriesTool.description).toContain("Lists categories from a WordPress site"); expect(listTagsTool.description).toContain("Lists tags from a WordPress site"); - expect(listCategoriesTool.parameters).toBeDefined(); - expect(listTagsTool.parameters).toBeDefined(); + expect(listCategoriesTool.inputSchema).toBeDefined(); + expect(listTagsTool.inputSchema).toBeDefined(); }); it("should include hide_empty parameter for categories only", () => { @@ -86,12 +86,12 @@ describe("TaxonomyTools", () => { const listCategoriesTool = tools.find((t) => t.name === "wp_list_categories"); const listTagsTool = tools.find((t) => t.name === "wp_list_tags"); - const categoriesHideEmptyParam = listCategoriesTool.parameters.find((p) => p.name === "hide_empty"); - const tagsHideEmptyParam = listTagsTool.parameters.find((p) => p.name === "hide_empty"); + const categoriesHideEmptyProp = listCategoriesTool.inputSchema.properties.hide_empty; + const tagsHideEmptyProp = listTagsTool.inputSchema.properties.hide_empty; - expect(categoriesHideEmptyParam).toBeDefined(); - expect(categoriesHideEmptyParam.type).toBe("boolean"); - expect(tagsHideEmptyParam).toBeUndefined(); + expect(categoriesHideEmptyProp).toBeDefined(); + expect(categoriesHideEmptyProp.type).toBe("boolean"); + expect(tagsHideEmptyProp).toBeUndefined(); }); it("should include description parameter for categories only", () => { @@ -99,11 +99,11 @@ describe("TaxonomyTools", () => { const createCategoryTool = tools.find((t) => t.name === "wp_create_category"); const createTagTool = tools.find((t) => t.name === "wp_create_tag"); - const categoryDescParam = createCategoryTool.parameters.find((p) => p.name === "description"); - const tagDescParam = createTagTool.parameters.find((p) => p.name === "description"); + const categoryDescProp = createCategoryTool.inputSchema.properties.description; + const tagDescProp = createTagTool.inputSchema.properties.description; - expect(categoryDescParam).toBeDefined(); - expect(tagDescParam).toBeUndefined(); + expect(categoryDescProp).toBeDefined(); + expect(tagDescProp).toBeUndefined(); }); }); diff --git a/tests/tools/users.test.js b/tests/tools/users.test.js index c63ae94..aa7e884 100644 --- a/tests/tools/users.test.js +++ b/tests/tools/users.test.js @@ -60,36 +60,35 @@ describe("UserTools", () => { }); // wp_list_users should have optional search and roles parameters - const listParams = toolsByName["wp_list_users"].parameters; - expect(listParams.find((p) => p.name === "search")).toBeTruthy(); - expect(listParams.find((p) => p.name === "roles")).toBeTruthy(); - expect(listParams.find((p) => p.name === "roles").type).toBe("array"); + const listProps = toolsByName["wp_list_users"].inputSchema.properties; + expect(listProps.search).toBeTruthy(); + expect(listProps.roles).toBeTruthy(); + expect(listProps.roles.type).toBe("array"); // wp_get_user should require id - const getUserParams = toolsByName["wp_get_user"].parameters; - const idParam = getUserParams.find((p) => p.name === "id"); - expect(idParam).toBeTruthy(); - expect(idParam.required).toBe(true); + const getUserSchema = toolsByName["wp_get_user"].inputSchema; + expect(getUserSchema.properties.id).toBeTruthy(); + expect(getUserSchema.required).toContain("id"); // wp_get_current_user should have no parameters - const getCurrentUserParams = toolsByName["wp_get_current_user"].parameters; - expect(getCurrentUserParams).toHaveLength(0); + const getCurrentUserProps = toolsByName["wp_get_current_user"].inputSchema.properties; + expect(Object.keys(getCurrentUserProps)).toHaveLength(0); // wp_create_user should have required username, email, password - const createParams = toolsByName["wp_create_user"].parameters; - expect(createParams.find((p) => p.name === "username").required).toBe(true); - expect(createParams.find((p) => p.name === "email").required).toBe(true); - expect(createParams.find((p) => p.name === "password").required).toBe(true); - expect(createParams.find((p) => p.name === "roles")).toBeTruthy(); + const createSchema = toolsByName["wp_create_user"].inputSchema; + expect(createSchema.required).toContain("username"); + expect(createSchema.required).toContain("email"); + expect(createSchema.required).toContain("password"); + expect(createSchema.properties.roles).toBeTruthy(); // wp_update_user should require id - const updateParams = toolsByName["wp_update_user"].parameters; - expect(updateParams.find((p) => p.name === "id").required).toBe(true); + const updateSchema = toolsByName["wp_update_user"].inputSchema; + expect(updateSchema.required).toContain("id"); // wp_delete_user should require id - const deleteParams = toolsByName["wp_delete_user"].parameters; - expect(deleteParams.find((p) => p.name === "id").required).toBe(true); - expect(deleteParams.find((p) => p.name === "reassign")).toBeTruthy(); + const deleteSchema = toolsByName["wp_delete_user"].inputSchema; + expect(deleteSchema.required).toContain("id"); + expect(deleteSchema.properties.reassign).toBeTruthy(); }); }); diff --git a/tests/tools/users/UserTools.test.js b/tests/tools/users/UserTools.test.js index 734f9d5..54f5a25 100644 --- a/tests/tools/users/UserTools.test.js +++ b/tests/tools/users/UserTools.test.js @@ -111,17 +111,17 @@ describe("UserTools", () => { const listUsersTool = tools.find((t) => t.name === "wp_list_users"); expect(listUsersTool.description).toContain("Lists users from a WordPress site"); - expect(listUsersTool.parameters).toBeDefined(); - expect(Array.isArray(listUsersTool.parameters)).toBe(true); + expect(listUsersTool.inputSchema).toBeDefined(); + expect(typeof listUsersTool.inputSchema).toBe("object"); }); it("should include roles parameter for list users", () => { const tools = userTools.getTools(); const listUsersTool = tools.find((t) => t.name === "wp_list_users"); - const rolesParam = listUsersTool.parameters.find((p) => p.name === "roles"); + const rolesProp = listUsersTool.inputSchema.properties.roles; - expect(rolesParam.type).toBe("array"); - expect(rolesParam.items).toEqual({ type: "string" }); + expect(rolesProp.type).toBe("array"); + expect(rolesProp.items).toEqual({ type: "string" }); }); it("should include usage examples in descriptions", () => {