Releases: Wicked-Evolutions/abilities-mcp-adapter
Release list
v1.4.9 — error remediation, lean discovery, sibling meta-shape, server-id rename
Adapter v1.4.9. Highlights: distinct error codes for not-found / not-exposed / scope-denied + recovery hint (#140); discover-abilities lean defaults — compact + paginated + categories histogram (#139); 4 sibling mcp-adapter/* abilities brought to correct show_in_rest meta-shape (#137/#138); default MCP server ID renamed to abilities-mcp-adapter-default-server to coexist with the official wordpress/mcp-adapter (#134); McpToolValidator stdClass fix (#125); FluentPlayer OAuth scope (#118); boot-tool allowlist (#87 S3). Pairs with abilities-mcp bridge v1.6.5 (deploy together per site). See CHANGELOG.md for the full [1.4.9] entry.
Abilities MCP Adapter v1.4.8 — Schema Exemption Hotfix
Hotfix completion of the schema-metadata exemption pattern shipped in v1.4.6 — extends the #105 per-ability path to cover the JSON-RPC method-level tools/list and tools/list/all paths.
Closes #113 via #114. Coordinated release wave with abilities-mcp v1.6.3.
See CHANGELOG.md for full technical detail.
What's in this release
Bug — High (cold-AI contract correctness)
#113: tools/list and tools/list/all no longer corrupt per-tool inputSchema / outputSchema via PII redaction.
ResponseRedactionGate now forwards the JSON-RPC method into ResponseRedactor, and the redactor's schema-metadata exemption — previously gated on the per-ability allowlist established by #105 — now also fires for the method-level allowlist SCHEMA_METADATA_METHODS = ['tools/list', 'tools/list/all']. The exempt-key check is also widened symmetrically to recognise both the WordPress-REST shape (input_schema / output_schema, snake_case) and the MCP wire shape (inputSchema / outputSchema, camelCase) so the same schema-metadata subtree is exempted regardless of which projection emits it.
The exemption stays path-aware + schema-aware: only the literal four key names trigger pass-through, and runtime-value redaction on tools/call responses is unchanged (verified by negative-control test). Empirically verified against the full helenawillow.com tools/list payload (789 tools): zero [redacted:bucket_3] sentinels appear in any tool's inputSchema or outputSchema after the fix.
Bug class context: the gap broke AI-client tool catalog loading on any site whose registered abilities include PII-keyword-named properties (email, password, phone, address, ip and prefix/suffix variants) — 40 of 789 tools on the live helenawillow capture (2026-05-10) failed Anthropic API draft 2020-12 validation pre-fix.
Install
Download abilities-mcp-adapter-1.4.8.zip below and upload via WP Admin → Plugins → Add New → Upload Plugin (or the community store). Plugin header Version: and ABILITIES_MCP_ADAPTER_VERSION constant 1.4.7 → 1.4.8.
Artifact integrity
abilities-mcp-adapter-1.4.8.zip— sha2569b4ce878f9f5dc09e31c8e9c86abce5d335541c42fe2631266d1d26bc26bd807
Verify: shasum -a 256 abilities-mcp-adapter-1.4.8.zip
Abilities MCP Adapter v1.4.7 — README rewrite (doc-only patch)
Documentation update — README rewrite for OAuth resource server + Connected Bridges + layered-permissions surface coverage. Code unchanged from v1.4.6.
What's in this release
- README rewritten end-to-end:
- OAuth 2.1 resource server + authorization server documented (RFC 9728 / 8414 discovery, RFC 7591 DCR,
/oauth/authorize,/oauth/token,/oauth/revoke, scope enforcement at every dispatch path, selected-role enforcement). OAuth was invisible in the v1.4.6 README despite being available since v1.4.0. - Connected Bridges admin UI section added — operators manage OAuth client registrations through WP Admin → Settings → MCP Adapter → Connected Bridges
- Settings → Permissions UI section added (the layered-permissions enforcement layer the adapter contributes to)
- PHP version requirement corrected from 8.0+ to 8.2+ (matches
composer.jsonrequire.phpand plugin headerRequires PHP) - Boundary event log emit section added — names the v0.1 events the adapter emits (
boundary.session.init,boundary.session.terminated,boundary.auth.denied,boundary.transport.error,boundary.rate_limit_hit) - Usage with the Abilities MCP bridge rewritten to reflect recommended install paths (
.mcpbfor Claude Desktop +npm install -g @wickedevolutions/abilities-mcpfor terminal MCP clients) - New Notes section: four-layer permissions model with runtime-error-as-teacher pattern, paired ability classes, discovery-vs-authorization distinction (
mcp-adapter-discover-abilitiesis registration manifest;suite/get-statusis authorization gate), selected-consent-role-on-refresh tracked on #94 - Welcome block at top with verbatim "Welcome, Wordpressnaut" spaceship paragraph + 3 URL pointers
- Disclaimer from J at the very top
- Pointer to PRINCIPLES.md as the Official WordPress Compatibility Contract binding all four suite repos
- OAuth 2.1 resource server + authorization server documented (RFC 9728 / 8414 discovery, RFC 7591 DCR,
- Plugin header
Version:andABILITIES_MCP_ADAPTER_VERSIONconstant 1.4.6 → 1.4.7
Install
Download abilities-mcp-adapter-1.4.7.zip below and upload via WP Admin → Plugins → Add New → Upload Plugin. Or, from the community store.
Verification
- sha256:
7a8ac0075a0b7b2a6bb7ec223b318b7cdd8af57779dd72a7587fe084b20c1106 - 180 files, 343 KB packed (same layout as v1.4.6 — only the README + CHANGELOG + version bump differ)
Companion releases
- abilities-mcp v1.6.1 (bridge — README rewrite + npm publish)
- abilities-for-ai v1.9.3 (next: v1.9.4 README rewrite incoming)
- abilities-for-fluent-plugins v1.1.1 (next: v1.1.2 README rewrite incoming)
Abilities MCP Adapter v1.4.6 — PII redaction + schema-printer correctness + OAuth scope coverage
Welcome, Wordpressnaut
Here is the spaceship, now you'll have to learn how to fly and please do remember, humans make mistakes, humans created AI so AI makes mistakes. Learning to fly is your job and to do that you'll need structure, systems, checklists, principles and understanding you stand before a magical leap of a steep and wonderful learning curve. Be patient and do backup things.
→ Knowledge layer (deeper traversal): https://knowledge.wickedevolutions.com
→ https://wickedevolutions.com
→ https://abilitiesforai.io
What changed
Security — High
- #103 — Bucket 3 redaction now covers prefixed email field variants.
ResponseRedactorpreviously matched Bucket 3 keywords by field-name-EQUALS only; prefixed/suffixed email-bearing fields (admin_email,author_email,network_admin_email,to_email,from_email,customer_email,billing_email,contact_email,adminEmail, etc.) slipped through unredacted. Token-based substring matcher added: any field whose tokens (split on_,-, camelCase boundaries) includeemailis redacted as Bucket 3.
Bug — High (cold-AI contract correctness)
- #105 —
mcp-adapter/get-ability-infoschemas no longer corrupted by redaction. The redaction pipeline ran indiscriminately over dispatcher response payloads, returningproperties.username = ["[redacted:bucket_3]"]andproperties.email = ["[redacted:bucket_3]"]instead of the actual JSON Schema. Fix exemptsinput_schemaandoutput_schemasubtrees from redaction when the response originates frommcp-adapter/get-ability-infoormcp-adapter/discover-abilities. Path-aware AND schema-aware — ameta/list-post-metaresponse that happens to contain a key namedinput_schemacannot bypass redaction.
Bug — Medium (operator trust)
- #101 + #102 — OAuth scope coverage now includes every category the live registry surfaces, with a CI drift test pinning the contract. Two missing scope groups added:
sitejoins the read-only umbrella tier;surecart-ecommercejoins the third-party suite tier. NewScopeCoverageDriftTest+ captured live-catalog snapshot (covering wickedevolutions multisite + helenawillow single-site) fails when a registered category has no scope mapping. Per Principle 9 (Scope Coverage Is Derived Or Coverage-Tested), the test is the actual fix; manual scope-list maintenance is the drift it prevents.
Notes
- Paired ability classes are an architecture pattern. Compact-vs-full pairs exist across the API by design — for example
content-list↔content-list-structure(full block markup vs id/title/slug/status/date/link), andcontent-get↔content-get-text(full block markup vs plain text stripped). Pick the pair member that matches the traversal you intend: compact for bulk discovery, full for targeted inspection. Each ability description names its payload tradeoff. mcp-adapter-batch-executeworkflow. Batch-execute works cleanly when batched requests use compact-class abilities. Reach for full-class abilities when targeting specific items. Tracked for compact-mode work post-alpha on #104.- Selected consent role on token refresh. The #88 fix (v1.4.5) persists the operator's selected role on the auth-code → access-token → refresh-token chain for tokens minted from the interactive consent screen. Auto-approve refresh / silent reauth does not currently carry the prior consent's role choice. Mitigation: explicit reauth (
bridge reauth --scope=...on the bridge CLI, or revoke + re-consent) renders a fresh consent screen and resets the chain to the chosen role. Tracked on #94.
Verification
- sha256:
a434af0654343d0fd5271880508cdf42ef59191ea96a63052d56826083e27ab2 - Install: WP Admin → Plugins → Add New → Upload Plugin →
abilities-mcp-adapter-1.4.6.zip
Companion releases
v1.4.4 — Stretch to Stable
Stretch-to-Stable release. Closes the post-OAuth-alpha audit-pass items plus mid-sprint discoveries. Nine issues shipped across nine sequential PRs, all merged behind the orchestrator/dev-chat workflow law established during the OAuth 2.1 sprint.
Companion releases: abilities-mcp v1.5.1 (bridge — coordinated release) and abilities-for-ai v1.9.1 (engine — coordinated release).
Test infra
- #27: Rate-limit burst harness shipped. New CLI tool
bin/rate-limit-burst.phpandRateLimit\BurstHarnesshelper class — session-aware harness that exercises the live/wp-json/mcp/mcp-adapter-default-serverendpoint past its IP and initialize windows and verifies the wire response (429 + Retry-After + boundary log entry) matches the limiter contract. Pairs withRateLimiter's 33 unit cases, which pin the in-memory math; this harness pins the wire behavior. Two cases (threshold-trip,initialize-window) automated; the multi-source per-IP separation and trusted-proxy header trust matrix are documented in the script header for operator-run coverage. 15 unit tests cover header parsing, session merge (defensive vs future per-request token rotation), and result classification. (#27)
UX / tech-debt
- L-3: DCR registration response now includes
sensitive_scopes_requested. Audit (2026-04-27) flagged that the Connected Bridges admin UI conflated "client requested sensitive scopes at DCR" with "client has been consented to sensitive scopes" — a misleading audit signal because sensitive scopes still require explicit interactive consent at/oauth/authorizeper H.3.4. Newsensitive_scopes_requestedfield on the POST/oauth/registerresponse (RFC 7591 extension) lists the subset of valid requested scopes that are sensitive, so bridges and the Connected Bridges UI can show "X sensitive scopes requested — will require explicit consent" without inventing the classification client-side. Storage unchanged (sensitive scopes still survive into the DCR record; gating remains at consent time). NewRegisterEndpoint::classify_scopes()helper makes the valid/sensitive split unit-testable. (#68)
Bug — High
- Fluent suite scopes added to
ScopeRegistry— every Fluent ability under OAuth now grantable. The OAuth scope catalog had per-module entries forpresto-player,surecart,astra,spectra, and the WordPress core categories, but none for the Fluent suite. Every Fluent ability (FluentCRM, Fluent Community, Fluent Forms, Fluent Support, Fluent Boards, FluentBooking, FluentSMTP, FluentAuth, Fluent Snippets, Fluent Messaging, FluentCart, FluentAffiliate, plus cross-modulefluent) failed at execute time withinsufficient_scope, andbridge reauth --scope=...rejected upfront with "Unknown scope(s)". Implemented Option B (per-module scopes, matching the existing third-party suite pattern): 12 Fluent module categories × {read, write, delete} = 36 new scopes, plusabilities:fluent:{read, write, delete}for cross-module abilities (39 scopes total). No abilities-for-fluent-plugins changes required —OAuthScopeEnforcerderivesabilities:<category>:<op>fromWP_Ability::get_category()and the abilities-for-fluent-plugins Registrar already auto-sets per-module categories. (#74)
Tech-debt
- AuthHeaderProbe namespace gate cleaned up (H.2.6 diagnostic now actually fires).
AuthorizationServer::authenticate_bearerwas matching both/wp-json/mcp/and the stale/wp-json/abilities-mcp-adapter/namespace before recording an observation. The latter is left over from a pre-rename branch — no REST routes are registered there, so the probe was silently dead on requests that hit it (and harmlessly recording-on-noise on requests that didn't, which means the rolling counter never reflected real traffic). Dropped the dead OR branch; probe now records only on/wp-json/mcp/traffic, restoring the H.2.6 diagnostic. (#53) - MCP route lifted to a single source of truth. Previously the path
mcp/mcp-adapter-default-serverwas hard-coded acrossAuthorizationServer,AuthorizeEndpoint,DiscoveryEndpoints, andhelpers.php(six callsites across four files). Drift between callsites would silently narrow Bearer auth or break resource validation. NewMcpResourcePathvalue class exposesREST_NAMESPACE,ROUTE,PATH(no leading slash, forrest_url()), andLEADING_SLASH_PATH(for REQUEST_URI / rest_route compares). All production callsites now consume the constants; a future rename touches one file. Behavior-preserving refactor — all 855 existing tests stay green; 4 new tests pin the constant values. (#54)
Performance / tech-debt
- L-2:
TokenStore::touch()no longer issues a synchronousUPDATEper request. Previously every authenticated MCP request blocked on awpdb->updateoflast_used_aton the access-token table — a hot row under burst load.touch()now stamps a per-request in-memory buffer keyed bytoken_hashand registers aregister_shutdown_functionflush on first use. Multiple touches for the same token within one request coalesce into one UPDATE; distinct tokens flush as N UPDATEs after the response is sent. Cross-request batching (transient + cron flush, issue body's Option A) is intentionally not introduced — the simpler shutdown-flush refactor is sufficient at current scale and leaves Option A as a future move without changing thetouch()API. (#67)
Security — Medium
- M-3:
OAuthRequestContext::has_scope()renamed tooauth_has_scope()with strict-false default on non-OAuth requests. Audit (2026-04-27) flagged the previous "non-OAuth → return true so WP caps govern" default as trivially fail-open if a future caller used the function as the sole authorization gate. The function had no production callers insrc/(the actual scope enforcer lives inOAuthScopeEnforcer::check()/check_scope()and consultsis_oauth_request()+granted_scopes()directly), so the rename + semantic flip carries no migration cost. Callers must now handle the non-OAuth path explicitly (typically viacurrent_user_can( ... )afteris_oauth_request()). Directin_arraysemantics retained — sensitive scopes are still NEVER implied by umbrella grants; for umbrella-aware non-sensitive scope expansion, route throughOAuthScopeEnforcer::check_scope(). Spec amendment toDESIGN — OAuth 2.1 in the Adapter 2026-04-27.mdflagged for the CTO during PR review. (#64) - M-8:
LastConsentLookup::timestamp_for()is now explicitly fail-closed. Audit (2026-04-27) flagged the unhandled-throw path: a third-partypre_option_*/option_*filter that throws would bubble to a 500 instead of routing the operator to consent. Verified the existing flow was already implicitly fail-closed (null return →ConsentDecisionEvaluatorbranch 1 →RENDER_FULLwith reasonfirst_authorization). Locked the contract: any\Throwablefrom the option backend now returns null, which is strictly safer than a 500.days_since()inherits the same protection. (#65) - M-9:
AuthorizationCodeStore::store()now checks$wpdb->insertreturn. Previously acode_hashUNIQUE-key collision (probability ~2^-128) or any other DB-side insert failure was silently swallowed; the bridge received the auth code in the redirect URL but/oauth/tokenfailed withinvalid_grantand no log signal.store()now returnsbool, emitsboundary.oauth_code_insert_failedwithclient_id+wpdb->last_erroron failure, andAuthorizeEndpoint::mint_code_and_redirectredirects witherror=server_errorinstead of redirecting with an unredeemable code. (#66)
Internal
- Test count: 893 (+34 since v1.4.3). PHP CI matrix unchanged: 8.2, 8.3.
- Spec:
DESIGN — OAuth 2.1 in the Adapter 2026-04-27.mdAppendix I.7 (oauth_has_scoperename + strict-false default) and H.3.2 v1.1 upgrade contract items 5+6 (routine reauthclearClientId()+ redirect_uri port-match before short-circuit) added during sprint.
Closed in this release
- #27, #53, #54, #64, #65, #66, #67, #68, #74 — milestone v1.4.4 — Stretch to Stable.
- Spec amendments: #73 closed completed (H.3.2 amendment), #16 closed
wontfix(API key path covered by OAuth + App Password fallback).
Upgrade
Drop-in replacement for v1.4.3. No schema migration. No config changes required. App Password fallback semantics unchanged. Existing OAuth grants unchanged — the new Fluent scopes only enroll if you explicitly request them via bridge reauth --scope="abilities:fluent-crm:read ..." or via a fresh add-site flow.
v1.4.3 — OAuth 2.1 Alpha (with security-review-driven hardening)
The Abilities MCP Adapter is now a full OAuth 2.1 authorization server + resource server. This is the first public release of OAuth-capable adapter; it ships the foundation, the consent UI, the Connected Bridges admin surface, and 13 security fixes from an independent external review.
Co-released with abilities-mcp v1.5.0 — the bridge that ships the OAuth 2.1 client.
Existing Application Password setups continue to work unchanged. OAuth is opt-in per site.
⚠️ Breaking change — PHP 8.2 minimum
This release drops PHP 8.0 and 8.1 support. Plugin header Requires PHP: 8.2. The adapter uses native syntax features that require PHP 8.2 (intersection types, readonly classes in places). If you're on a host that ships PHP 8.0/8.1, you must upgrade PHP before installing this release. Most managed WordPress hosts have PHP 8.2 available — check your host's PHP-version selector.
CI matrix: PHP 8.2, 8.3.
What's new
OAuth 2.1 authorization server
The adapter now serves the full OAuth 2.1 surface at the WordPress site root:
| Endpoint | Spec | Behavior |
|---|---|---|
/.well-known/oauth-protected-resource |
RFC 9728 | Resource-server metadata pointing to this adapter's MCP endpoint |
/.well-known/oauth-authorization-server |
RFC 8414 | Authorization-server metadata |
/.well-known/openid-configuration |
OIDC discovery (honesty-only) | Returns the AS metadata without OIDC-specific fields, so OIDC clients fail-fast instead of silently mis-treating us as an OIDC provider |
/oauth/authorize |
RFC 6749 §4.1 | Authorization-code grant with PKCE S256, server-rendered consent screen, no JavaScript |
/wp-json/mcp/oauth/register |
RFC 7591 | Dynamic client registration (DCR), per-IP rate limited |
/wp-json/mcp/oauth/token |
RFC 6749 §3.2 | Token endpoint — auth-code exchange and refresh-token grants |
/wp-json/mcp/oauth/revoke |
RFC 7009 | Token revocation, per-IP rate limited, with full cascade (refresh revoke → access revoke) |
Bearer tokens authenticate REST requests to /wp-json/mcp/mcp-adapter-default-server only — never to other WordPress REST routes (C-1).
Consent screen + per-scope explicit grant
When an operator authorizes a bridge, the adapter renders a server-side consent page (no JavaScript, full CSP). The page lists every scope the bridge requested, grouped by category. Each scope is a checkbox. Sensitive scopes (settings:write, plugins:write, etc.) carry a lock badge and always require explicit interactive consent — even when the bridge has been previously authorized. Previously-granted non-sensitive scopes show a "Granted YYYY-MM-DD" badge and remain checked by default. Operators can uncheck any scope at any consent flow; the resulting token covers exactly what was checked.
Connected Bridges admin tab
New admin surface at Abilities MCP Adapter → Connected Bridges:
- All registered OAuth clients (DCR'd bridges) in a sortable table.
- Per-client revoke action (cascades through tokens + refresh chains).
- Recent OAuth boundary events (token issued, validated, revoked, host rejected, etc.).
- H.2.6 Authorization-header diagnostic — shows whether MCP traffic is arriving with Bearer headers, which header forms have been seen, and whether the trusted-proxy IP allowlist is recovering them correctly.
Admin UI restructure
The two existing admin pages — Settings → MCP Abilities and Settings → MCP Safety — are consolidated into a single top-level Abilities MCP Adapter menu with three tabs:
- Abilities — per-ability checkbox grid (migrated from MCP Abilities), grouped by permission level.
- Safety — Bucket 1/2/3 redaction, ability exemptions, trusted-proxy config (migrated from MCP Safety).
- Connected Bridges — OAuth client management (new).
Every option, nonce, and redirect target is preserved through the migration. Existing bookmarks to /wp-admin/admin.php?page=abilities-mcp-safety redirect to the new tab.
Database
Four new InnoDB tables, all prefixed kl_oauth_*:
kl_oauth_clients— DCR registrationskl_oauth_codes— short-lived auth codes (PKCE-bound)kl_oauth_tokens— access tokens (hashed at rest)kl_oauth_refresh_tokens— refresh tokens withfamily_idchaining
Schema migration runs automatically on first request after upgrade (db_version 1.0.0 → 1.1.0). Idempotent via dbDelta. No backfill required.
Security
This release closes 13 of 26 findings from an independent external security review. Three Critical, six High, three Medium, and one Low have been fixed and verified live on production sites.
Critical (3) — fixed and verified live
- C-1: Bearer auth global-session leak (#52). Pre-fix, a token issued for
/wp-json/mcp/...could authenticate the holder against any other WordPress REST endpoint. Bearer auth now narrows to MCP resource paths only. - C-2: Refresh idempotent retry returned
invalid_grant(#61). Pre-fix, the bridge's network-blip retry path emittedinvalid_grant, evicting operators to reauth on every flaky connection. Redesigned with encrypt-at-rest grace-window retry: rotation stores the new plaintext pair as AES-256-GCM ciphertext under an HKDF-SHA256 key derived from the old refresh token +AUTH_KEY. Retry within 30 seconds returns the original pair; retry outside grace revokes the family. - C-3: 401 with no
WWW-Authenticatechallenge (#56). Bare-formWWW-Authenticate: Bearer realm=..., resource_metadata=...(noerrorparam) now scheduled on every MCP-path 401 with noAuthorizationheader, per RFC 6750 §3.
High (6) — all fixed
- H-1: Indistinguishable error_description for revoked vs expired/missing tokens (#56)
- H-2:
/oauth/revokeaccepted unauthenticated requests (#56) — now requiresclient_idthat hash-equals the stored value (RFC 7009 §2.1), with full cascade from refresh-revoke to access-revoke. - H-3: No cleanup of expired/unused records (#57) — daily
OAuthCleanupcron, BATCH=500 loops on all four tables, 50,000-row alert. - H-4: Non-atomic per-IP rate-limit counter race (#57) —
wp_cache_add+wp_cache_incron object cache,get_site_transientfallback (network-wide on multisite). - H-5: Scope enforcer not wired at every dispatch path (#55) —
OAuthScopeEnforcer::check()now runs atToolsHandler,ResourcesHandler,PromptsHandler, andExecuteAbilityAbilityper-underlying dispatch. - H-6: Token response missing
token_type(#58) — RFC 6749 §5.1 compliance.
Medium (3) — fixed in this release
- M-2:
OAuthRequestContextreset only fired onrest_api_init(#71) — now also wired toinitpriority 0 to prevent PHP-FPM cross-request leak. - M-4:
esc_url_raw()mutatedredirect_uribefore timing-safe compare (#71) — replaced withtrim()to match storage-side normalization exactly. - M-5: Path-style multisite discovery routing (#60) — discovery URLs now correctly subdomain-scoped on path-style multisite.
- M-6: REST routes bypassed
OAuthHostAllowlist(#71) —/wp-json/mcp/oauth/{register,token,revoke}now gated. - M-7:
esc_attr()HTML-encodedWWW-Authenticateheader values (#56) — corrected. - H-9: Loopback redirect_uri compare order-and-encoding-stable (#72) —
?foo=1&bar=2≡?bar=2&foo=1, percent-encoding normalized.
Low (1) — fixed
- L-1: Bearer token now
trim()'d (#71) — proxy whitespace no longer breaks auth silently.
Other security-relevant fixes
family_idrotation chain (#59) — rotation now preservesfamily_id, sorevoke_family()correctly cascades through arbitrary chain depth.strtok('', '?')returns false on PHP 8.2+ (#43) — guard against emptyREQUEST_URIfatal.- OAuth global helpers in correct namespace (#37) — fixed undefined-function fatal on first DCR.
- Initial scope-enforcement integration (#41) — wired
OAuthScopeEnforcer::check()intoToolsHandler(later expanded by H-5).
Findings tracked but not yet fixed (12)
Filed as GitHub issues for transparency:
v1.4.1 — Patch: Text-channel PII leak in tools/call
Patch release for the v1.4.0 launch-blocker found by GPT 5.5 in post-release verification.
What was wrong
tools/call responses correctly redacted structuredContent but emitted raw email values in content[0].text — the JSON-serialized string channel that bridge clients (Claude Desktop, Cursor) actually read.
The redactor traversed the response and matched field names — but content[0].text is a JSON-encoded string, so the email value lived inside a string blob and was never matched. Redaction only mutated the parallel structuredContent object.
What's fixed
ResponseRedactionGate::reconcile_tool_channels() runs after the recursive redaction pass and:
- Dual-channel (the GPT 5.5 case): regenerates
content[i].textfrom the redactedstructuredContentfor every text part. - Single-channel text: JSON-decode → redact → re-encode; non-JSON plain text left alone.
- Image (
content[i].type === 'image'): skipped (no PII in base64 binary). - Non-tools/call paths: no-op.
ToolsHandler's response shape is untouched — the fix is purely a post-redaction reconciliation in the gate.
Verified live
- Direct
users-listtool call on wickedevolutions.com (the exact GPT 5.5 scenario):emailreturns"[redacted:bucket_3]"incontent[0].text. Bug fixed. - 476/476 unit tests passing (10 new
test_text_channel_*cases).
Sharp edge worth knowing
The single-channel JSON fallback (case 2) introduces a subtle behavior change for any tools/call response that emits content[0].text without structuredContent AND where the text happens to be valid JSON containing redactable field names. Today's ToolsHandler always emits both channels for non-image abilities, so this is mostly theoretical — but a third-party handler returning text-only JSON would now have its text rewritten. Documented in the helper's docblock.
Upgrade path
Direct upgrade from v1.4.0 — additive bug fix, no schema or behavior changes outside the redaction surface.
Closes #28.
v1.4.0 — Public Alpha Hardening: Safety Surface Complete
Public alpha hardening sprint — adapter components.
What's new
Three-bucket redaction filter at the /mcp response boundary.
- Bucket 1 (secrets — passwords, API keys, tokens, salts, hashes, card patterns): always-on, cannot be disabled
- Bucket 2 (payment / regulated identifiers — card numbers, SSN, tax IDs): default-on, configurable, but Admin-UI-only weakening
- Bucket 3 (contact PII / access labels — email, phone, address, IP, login, public_key): default-on, configurable via AI or Admin UI
- Type-aware markers preserve response shape (string fields stay strings)
- Per-ability exemptions for Bucket 3 unlock email visibility for legitimate work (CRM analysis, etc.)
WP Admin → Settings → MCP Safety for operator configuration.
Master toggle (with checkbox warning), keyword editor per bucket, per-ability exemption list, trusted-proxy preset.
7 AI-callable settings abilities for chat-driven safety configuration.
settings/get-redaction-list, settings/add-redaction-keyword, settings/remove-custom-keyword, settings/restore-redaction-defaults, settings/remove-default-bucket3-keyword (1/2 confirmation), settings/exempt-ability-from-bucket3 (1/2 confirmation), settings/unexempt-ability-from-bucket3. All require manage_options.
Rate limiter at /mcp boundary — 60/min/IP + 60/min/user, plus 30/min/IP separate window for initialize handshake. Cloudflare preset for trusted-proxy header trust. Auto-detect storage (object cache + transient fallback). REMOTE_ADDR is the trust base; X-Forwarded-For only honored when proxy is explicitly configured.
Origin allowlist + CORS scoped to MCP routes only (no global REST CORS side-effect). Defense-in-depth against DNS rebinding. Browser-mediated MCP clients now work.
Minimal SSE stub — GET /mcp returns text/event-stream with bounded heartbeat. Future server-initiated events slot in cleanly.
Boundary event security:
api_keyis hashed toapi_key_hashbefore any listener fires (no raw API keys to third-party hooks)- Auth-denied events use enum reason codes, IPs truncated to /24 // /48 before listener fires
- Settings audit events recorded in kl_boundary
Companion releases (this is one of three)
- abilities-for-ai v1.9.0 — boundary writer + Activity admin tab
- abilities-mcp v1.4.0 —
.mcpbdistribution bundle for Claude Desktop
Verified
- 466/466 unit tests passing on the canonical integration branch
- 19 critical-path acceptance tests passing on the live wickedevolutions.com multisite (4 subsites)
- GPT 5.5 reviewed and authorised for code-side launch
Closes
#15, #17, #18, #20, #21, #22, #23, #24, #25, #26 (and underlying research issues #14, #16, #19)
Known follow-ups (post-launch, non-blocking)
- #27 scripted rate-limit threshold burst test
- Pii\IpTruncator shared utility extraction
- boundary.transport.error enum vocabulary alignment
- Multisite-prefixed capability keys (wp_2_capabilities etc.) in meta-abilities denylist
- Top-level WP Admin menu (currently lives under Settings)
Abilities MCP Adapter v1.2.0
What's New in v1.2.0
- Paginated discovery —
limitandoffsetparameters ondiscover-abilitiesfor sites with hundreds of abilities - Compact mode —
compact: truereturns only name, category, and tier (reduces ~128KB response to ~8KB) - GitHub Releases auto-update — users who install from GitHub get update notifications in wp-admin
- Branding and documentation updates
Pagination example
{"ability_name": "mcp-adapter/discover-abilities", "parameters": {"limit": 50, "offset": 0}}Returns _pagination.has_more: true when more results are available.
Compact example
{"ability_name": "mcp-adapter/discover-abilities", "parameters": {"compact": true}}Install
Download the zip below and upload via Plugins > Add New > Upload Plugin in WordPress admin.
Or install from the Wicked Evolutions store for automatic updates.
Abilities MCP Adapter v1.1.1
What's New in v1.1.1
- GitHub Releases auto-update — users who install from GitHub now get update notifications in wp-admin without needing a FluentCart license
- Updated product page URLs and install documentation
Install
Download the zip below and upload via Plugins > Add New > Upload Plugin in WordPress admin.
Or install from the Wicked Evolutions store for automatic updates.