Commit 65043cf
authored
* fix(build): stop shipping the vendored MCP adapter to WordPress.org
WordPress.org rejected the submission over generic and reserved prefixes. Our
own 126 elements were already clean — every one of the flagged wp_mcp and
mcp_adapter names came from includes/lib/wp-mcp/, the vendored WordPress MCP
Adapter, which is 347 of 464 shipped files and about half the zip. It also
carried the only error_log and fwrite calls in the tree and stood up a second,
undeclared endpoint at /wp-json/mcp/mcp-adapter-default-server.
So the .org build no longer ships it. Nothing is lost: Saddle_MCP's own
JSON-RPC transport already served the same /saddle/v1/mcp URL with the same
abilities behind the same tier and approval gate, and a site that installs the
official MCP Adapter plugin gets the adapter path back automatically.
Absence is the switch, the way it already is for the updater. The bundle loader
moved into its own excluded file rather than staying a guarded branch: it
declares the library's WP_MCP_DIR/WP_MCP_VERSION, and a scanner reading source
cannot tell those two defines are unreachable — shipping the file only where the
library ships means the .org build contains no reserved-prefix declaration at
all. The session shim moved with it, having no purpose without the adapter.
Hardening the built-in transport first, since it is now the only one .org
installs get:
- Tool names lose the namespace slash. saddle/list-posts is illegal in MCP and
rejected outright by OpenAI's clients, which drop the whole list over one bad
name — the exact "no callable actions" symptom, waiting to happen again.
call_tool() accepts both forms so a cached name still resolves.
- Tools carry title and behaviour hints. Every ability already declared
readonly/destructive/idempotent via saddle_ability_meta(); the transport was
throwing it away. An agent can now weigh a call before making it.
- A refusal is a tool result with isError, not a JSON-RPC error. MCP reserves
protocol errors for protocol faults, and several clients never show the model
a JSON-RPC error — which meant our carefully written "the site is at the read
access level, do not retry" text was reaching nobody.
Verified on the built .org zip, not the dev tree: 114 files, zero includes/lib,
zero wp_mcp, zero fwrite, zero error_log calls. Then run in WP Playground over
real HTTP — initialize returns tools-only capabilities, a stateless tools/list
returns 61 tools with zero illegal names, a read tool executes, and a write tool
at the default read tier is refused as isError with the reason intact.
Refs #82
* fix(context): keep installed inventory at the admin tier, where it belongs
WordPress.org flagged get-instructions for handing out plugin inventory at the
read tier. The literal claim was wrong — the ability has a permission_callback
— but the point underneath was right, and worse than they could see.
site.php already states the policy: "Reads that expose configuration (option
values, installed inventory) sit at admin too, not read — the inventory itself
is sensitive." saddle/list-plugins and saddle/list-themes are gated on exactly
that. Meanwhile the system context handed every read-tier session a prose copy
of the same active-plugin list, with versions, plus the theme by name.
So the plugin list and the theme name now appear only at the admin tier. What
survives at every tier is the part an agent actually needs to avoid mangling a
page: whether the theme is block-based, and the builder/multilingual detection,
which is behavioural guidance rather than an inventory.
The worse half was the handshake. initialize runs before any ability's
permission_callback — its only check is that someone is logged in — and it
served MORE than get-instructions would (the entire context plus the owner's
own written instructions) with FEWER checks. A paused site still answered it,
and a Subscriber-level key or a saddle:read bearer got the same payload as an
admin. It now honours the pause switch, and its context is tier-aware, so the
handshake can no longer exceed what the equivalent ability would return.
Tests drive the real path rather than restating it: get_plugins() has no filter,
so the plugin case primes the cache group it reads from, and asserts the same
plugin is named at admin tier and absent at read.
* docs: say what transport a site has, and stop describing a bundle we dropped
readme.txt claimed Saddle "bundles the WordPress MCP Adapter library... license
included in includes/lib/wp-mcp/". That stopped being true one commit ago, and a
false statement in the readme is exactly the kind of thing a reviewer checks.
Rewritten as what is now the case, and as the answer to the question the change
raises: Saddle speaks MCP itself, there is nothing else to install, and if the
separate MCP Adapter plugin is active Saddle uses it — same address, same tools,
same access levels either way. Same wording in the Connections HelpTip, which
called the built-in path a fallback, and in docs/connections.md.
Also closes the troubleshooting gap that started this whole thread: the docs
covered "connected but can't do something" and had nothing for "connected but
shows no tools at all" — the exact sentence the customer sent twice. It now
points at Client traffic and explains how to read it, including the case only
that panel can show, where no row appears because the request never reached
WordPress at all.
WPORG-SUBMISSION.md: §3 no longer describes a bundled library, and the argument
in §11 that the flagged notices were "unreachable in the shipped zip, not
patched deliberately" is struck — they are simply not shipped. Records that the
two remaining mcp_adapter_* strings are add_action/add_filter calls against that
plugin's own hooks, which cannot carry our prefix.
POT regenerated (1087 to 1090 msgids), bundle rebuilt.
1 parent 32275f5 commit 65043cf
16 files changed
Lines changed: 710 additions & 143 deletions
File tree
- admin
- build
- src/components
- docs
- includes
- admin
- languages
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
96 | 127 | | |
97 | 128 | | |
98 | 129 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
93 | 112 | | |
94 | 113 | | |
95 | 114 | | |
| |||
228 | 247 | | |
229 | 248 | | |
230 | 249 | | |
231 | | - | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
237 | | - | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
238 | 257 | | |
239 | 258 | | |
240 | 259 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
0 commit comments