Create, review, or update MCP tools inside a plugin using the action+subaction dispatch pattern. Use when the user wants to add a new tool to an existing plugin, audit existing tools for conformance, refactor a flat tool list into action+subaction shape, or generate the parameter schema and dispatch logic for a tool.
From plugin-labnpx claudepluginhub jmagar/claude-homelab --plugin plugin-labThis skill uses the workspace's default tool permissions.
references/canonical-error-shape.mdreferences/dispatch-table-patterns.mdreferences/help-tool-template.mdProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Calculates TAM/SAM/SOM using top-down, bottom-up, and value theory methodologies for market sizing, revenue estimation, and startup validation.
Design and implement MCP tools that follow the canonical action+subaction dispatch pattern.
Every tool exposes a single entry point with two required parameters:
action — the high-level resource being operated on (e.g., message, application, client)subaction — the specific verb within that resource (e.g., create, list, delete)Additional parameters are operation-specific and validated after dispatch.
Note: The action and subaction values in the example below are from the Gotify plugin. Substitute your plugin's actual resource names and verbs.
{
"name": "gotify",
"description": "Manage Gotify notifications",
"inputSchema": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["message", "application", "client"],
"description": "Resource to operate on"
},
"subaction": {
"type": "string",
"enum": ["create", "list", "delete", "get"],
"description": "Operation to perform"
}
},
"required": ["action", "subaction"]
}
}
A companion *_help tool lists all valid action+subaction combinations with parameter details (see references/help-tool-template.md).
Every tool call that fails must return this shape — never throw an unhandled exception:
{
"isError": true,
"content": [
{
"type": "text",
"text": "Error: <action>/<subaction> failed — <reason>"
}
]
}
isError: true signals to the MCP client that the tool call failed (as distinct from a successful call that returns an error message in its content). The MCP client uses this flag to decide whether to surface a tool failure vs. relay a normal response. See references/canonical-error-shape.md for language-specific helper functions and common mistakes.
Map (action, subaction) tuples to handler functions. This is the canonical Python pattern:
DISPATCH = {
("message", "create"): handle_message_create,
("message", "list"): handle_message_list,
("message", "delete"): handle_message_delete,
("application", "create"): handle_application_create,
("application", "list"): handle_application_list,
# add rows as you add handlers
}
async def handle_tool(action: str, subaction: str, params: dict) -> dict:
handler = DISPATCH.get((action, subaction))
if handler is None:
return error_response(f"Unknown action/subaction: {action}/{subaction}")
return await handler(params)
For Rust and TypeScript dispatch patterns, see references/dispatch-table-patterns.md.
Before designing or modifying tools, collect:
If inputs are missing, ask before proceeding.
Produce in order:
Follow the language-specific handler pattern from the appropriate language layer directory:
~/workspace/plugin-templates/py/~/workspace/plugin-templates/rs/~/workspace/plugin-templates/ts/Every plugin must include a <plugin-name>_help tool alongside the main tool. Its inputSchema takes no required parameters (optionally an action filter). Its response is a plain-text table listing every valid action/subaction pair and their parameters. MCP clients can call it to discover capabilities at runtime without reading the JSON schema.
See references/help-tool-template.md for the full structure, response format, and a concrete Gotify example.
Check for:
*_help companion toolisError: false with error text, missing content array wrapper)Produce a findings list with file references before making changes.
When modifying existing tools:
Avoid renaming stable action/subaction pairs — that is a breaking change.
Tests for MCP tools live in two places:
tests/test_live.sh — shell integration tests that hit the real running service. Run these against a real deployment to verify end-to-end behavior.tests/test_tools.pytests/tools_test.rstests/tools.test.tsUnit tests mock the service layer and verify dispatch, parameter validation, and error shape. Live tests hit the real service and verify the full stack. When adding a new action/subaction pair, add both a unit test for the handler and a live test for the round-trip.
At minimum:
*_help conformance and canonical error shape