ab1cfc88da
Hermes Agent plugin that drives WordPress sites running the WordVibe plugin over the site MCP server (POST /wp-json/wp-ide/v1/mcp, Bearer key from Settings -> MCP). 8 lean tools (sites, site_info, tools, call, read_file, write_file, edit_file, search) + /wordvibe slash command + bundled skill. The site exposes ~100 tools; the rest are reachable via wordvibe_call to keep the per-turn schema small. Verified against a mock of the plugin JSON-RPC server: 15/15 scenarios (initialize, tools/list, tools/call, blocked tools, unknown tool, 403 when MCP off, connection refused, plain-permalink ?rest_route= fallback).
82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
"""wordvibe-hermes — registration.
|
|
|
|
Connects Hermes to WordPress sites running the WordVibe plugin (which ships an
|
|
MCP server at /wp-json/wp-ide/v1/mcp) and exposes a lean set of tools for
|
|
driving those sites from any Hermes surface — CLI, Telegram, cron, subagents.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from . import schemas, tools
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
TOOLSET = "wordvibe"
|
|
|
|
|
|
def _split_commands(raw: str) -> list:
|
|
return [c for c in (raw or "").split() if c]
|
|
|
|
|
|
def _handle_command(ctx, raw_args: str) -> str:
|
|
"""`/wordvibe [sites|info|tools|call <tool> <json>]` — quick access without a model turn."""
|
|
parts = _split_commands(raw_args)
|
|
sub = (parts[0].lower() if parts else "sites")
|
|
|
|
if sub in ("sites", "list"):
|
|
return tools.sites({"probe": True}, ctx=ctx)
|
|
if sub in ("info", "status"):
|
|
return tools.site_info({"site": parts[1] if len(parts) > 1 else ""}, ctx=ctx)
|
|
if sub in ("tools", "listtools"):
|
|
return tools.tools({"site": parts[1] if len(parts) > 1 else ""}, ctx=ctx)
|
|
if sub in ("call", "run") and len(parts) >= 2:
|
|
import json as _json
|
|
|
|
tool_name = parts[1]
|
|
raw_json = raw_args.split(tool_name, 1)[1].strip() if tool_name in raw_args else ""
|
|
try:
|
|
arguments = _json.loads(raw_json) if raw_json else {}
|
|
except Exception as exc: # noqa: BLE001
|
|
return tools._err(f"arguments must be JSON: {exc}")
|
|
return tools.call({"tool": tool_name, "arguments": arguments}, ctx=ctx)
|
|
|
|
return tools._ok(
|
|
{
|
|
"usage": "/wordvibe sites | info [site] | tools [site] | call <tool> '{json}'",
|
|
"example": "/wordvibe call post_list '{\"post_type\":\"post\",\"limit\":5}'",
|
|
}
|
|
)
|
|
|
|
|
|
def _on_post_tool_call(tool_name, args, result, task_id, **kwargs):
|
|
"""Log wordvibe_* traffic at debug level (visibility without noise)."""
|
|
if str(tool_name).startswith("wordvibe_"):
|
|
logger.debug("wordvibe tool %s (session %s)", tool_name, task_id)
|
|
|
|
|
|
def register(ctx):
|
|
"""Wire the wordvibe tools into the Hermes tool registry."""
|
|
registry = [
|
|
("wordvibe_sites", schemas.SITES, tools.sites),
|
|
("wordvibe_site_info", schemas.SITE_INFO, tools.site_info),
|
|
("wordvibe_tools", schemas.TOOLS, tools.tools),
|
|
("wordvibe_call", schemas.CALL, tools.call),
|
|
("wordvibe_read_file", schemas.READ_FILE, tools.read_file),
|
|
("wordvibe_write_file", schemas.WRITE_FILE, tools.write_file),
|
|
("wordvibe_edit_file", schemas.EDIT_FILE, tools.edit_file),
|
|
("wordvibe_search", schemas.SEARCH, tools.search),
|
|
]
|
|
|
|
for name, schema, handler in registry:
|
|
ctx.register_tool(name=name, toolset=TOOLSET, schema=schema, handler=handler)
|
|
|
|
ctx.register_command(
|
|
"wordvibe",
|
|
lambda raw: _handle_command(ctx, raw),
|
|
description="WordVibe sites: `sites`, `info [site]`, `tools [site]`, `call <tool> '{json}'`",
|
|
)
|
|
|
|
ctx.register_hook("post_tool_call", _on_post_tool_call)
|
|
|
|
logger.info("wordvibe-hermes registered %d tools", len(registry))
|