Commit Graph
63 Commits
Author SHA1 Message Date
janniclas.loosenandClaude Opus 5.5 b0da2cdecc fix: restrict media-search to the query, not just editable() after it
I1: query the attachments themselves author-restricted when the user
lacks edit_others_posts (PostGuard::ownPostsOnly, extracted from
ContentQueryAbility so content-query reuses the exact same rule)
instead of relying solely on a post-query editable() filter, which
could leak the existence of other users' attachments via an empty
page plus next_cursor. editable() stays as a backstop.

Also: M1 document that only status=inherit attachments are listed;
M2 tie-break ordering by ID (content-query and media-search) for
stable pagination; M3 no next_cursor once the last candidate was
examined; M4 reject an explicit non-image mime with missing_alt;
M5 additional coverage (posts_per_page, cursor offset pass-through,
invalid cursor, post_status/date/mime args); mime maxLength 100.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 02:19:50 +02:00
janniclas.loosenandClaude Opus 5.5 5099c9fc89 feat: media-search with missing_alt and unused filters
Ruling Q8 caps the unused scan at 20 candidates per call (not the
brief's 500/50), and each result (search and unused alike) is
filtered to attachments the current user may edit; overview.md lists
media-search as a direct tool (ruling Q2).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 02:12:18 +02:00
janniclas.loosenandClaude Opus 5.5 b197d8171f fix: media usage search never reports a capped scan as unused
Adds widgets and header/background theme mods, integer galleryIds,
wider pre-filter, regex failures as possible usages, newest-first
candidates, and usages_complete/usages_truncated in media-get.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 02:02:21 +02:00
janniclas.loosenandClaude Opus 5.5 a2a2da1ae7 feat: media usage finder and media-get tool
Usage search distinguishes certain (ID referenced) and possible (file URL
only) usages, hides titles the user cannot read, and keeps queries bounded.
MediaGuard checks per-attachment capabilities and refuses trashed media
unless the caller opts in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:54:55 +02:00
janniclas.loosenandClaude Opus 5.5 51e8846e1f docs: record stage 1a rulings and deferred items
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:35:44 +02:00
janniclas.loosenandClaude Opus 5.5 e4d666af8d test: smoke checks for text with &, trashed entries and missed schedules
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:31:50 +02:00
janniclas.loosenandClaude Opus 5.5 fea75b03d6 docs: name tools and error codes the way the client sees them
Indirect abilities are written lit/<name> (via mcp-adapter-execute-ability) in
guides, descriptions and hints, direct tools lit-<name>; guides use the
client-visible codes (write_failed, partial_write) and say exactly which tools
return a revision_id. ToolNamingTest keeps guides and sources consistent with
Ability::isDirect() and checks the overview's direct-tool list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:28:18 +02:00
janniclas.loosenandClaude Opus 5.5 b3c9109e47 fix: log partial menu writes; describe that the link gate covers new items only
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:26:40 +02:00
janniclas.loosenandClaude Opus 5.5 ff0da9f406 fix: settings compare against the sanitized value, fail via WriteFailed
- isStored() compares the stored option with sanitize_option() of the new value
  (core esc_html()s blogname/blogdescription): setting "Müller & Söhne" again
  no longer looks like a failed write; lit/wp-settings-get and previous return
  those two decoded, as the user would type them.
- The change log runs as FollowUp (warning on failure); a failed write throws
  WriteFailed (write_failed / partial_write with keys), partial writes are
  logged with result partial.
- permalink_structure needs a real %tag% (WordPress' own /%[^/%]+%/ rule).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:26:11 +02:00
janniclas.loosenandClaude Opus 5.5 d67a1d4fb3 fix: relation fields require a readable target and hide its post type
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:24:51 +02:00
janniclas.loosenandClaude Opus 5.5 106a099483 fix: check WordPress meta capabilities per field key
Fields from the meta/sample sources need edit_post_meta (update, checked in
PostWriter::prepare) or add_post_meta (create, checked right after the insert;
a refusal deletes the new draft again) for their key; a refusal is a
fields.<key> validation error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:24:20 +02:00
janniclas.loosenandClaude Opus 5.5 563d9e8d15 fix: status tools report a hook throwing during the write as partial_write
publish/unpublish/schedule/trash run their WordPress call through StatusWrite:
a throwing hook becomes WriteFailed (rollback incomplete, with post_id) and is
logged with result partial instead of surfacing as internal_error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:23:09 +02:00
janniclas.loosenandClaude Opus 5.5 e57b53d0e5 refactor: PostChangeResult for the after-save block of the post tools
after_save hook, re-read, change log and ToolResult (rev, log_id, warnings)
were repeated in nine post abilities; they now share one helper. Behaviour
and warning texts are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:22:34 +02:00
janniclas.loosenandClaude Opus 5.5 d831cf59ca fix: never publish a missed schedule, refuse trashed posts, fail on meta read errors
- PostGuard::editable() refuses trashed entries unless the caller opts in
  (content-get, revisions-list, post-trash); publish/schedule/unpublish/update/
  restore/duplicate/replace now refuse them.
- PostGuard::statusStable(): content-update, revision-restore and
  replace-with-copy refuse a future post whose date has passed (any save would
  publish it); the content-update rollback and PostCloner row writes re-check.
- PostCloner::ownMeta() treats a failed SELECT as an error; replace() reads the
  copy before its first write.
- replace-with-copy needs create_posts (backup clone) and reports backup_id in
  write_failed/partial_write.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:20:21 +02:00
janniclas.loosenandClaude Opus 5.5 6a7193424c refactor: Rev::matches replaces the inline rev checks
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:15:29 +02:00
janniclas.loosenandClaude Opus 5.5 0c4da28855 refactor: ChangeLog::recordPartial and column-size truncation
recordPartial() replaces the partial-log FollowUp blocks in content-create,
content-update and post-replace-with-copy; record() truncates ability,
object_type and object_id to their column sizes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:15:13 +02:00
janniclas.loosenandClaude Opus 5.5 46a445fe9d test: end-to-end checks for the WordPress basics; content and workflow guides
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 01:01:39 +02:00
janniclas.loosenandClaude Opus 5.5 fc216e3eaf fix: verify option writes before treating update_option() false as failure
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 00:47:35 +02:00
janniclas.loosenandClaude Opus 5.5 9cc9d4f2dc feat: whitelisted WordPress settings tools
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 00:34:26 +02:00
janniclas.loosenandClaude Opus 5.5 a2e9dabb17 fix: refuse private/unpublished menu links, write only changed menu items
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 00:20:34 +02:00
janniclas.loosenandClaude Opus 5.5 f80cfb9e44 feat: declarative and operation-based menu item editing
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 00:14:25 +02:00
janniclas.loosenandClaude Opus 5.5 954f683dd2 fix: keep menu items in parent cycles, tighten menu write tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 00:07:44 +02:00
janniclas.loosenandClaude Opus 5.5 1d59d1d2ad feat: menu list, get, create and location tools
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 00:00:30 +02:00
janniclas.loosenandClaude Opus 5.5 236d25325d fix: guard revision-restore follow-ups, mark divi revisions
RevisionRestoreAbility now wraps the after-save action and change-log
write in FollowUp::run() and reports failures as warnings instead of
letting them turn a saved restore into an error response, matching
ContentUpdateAbility; a failed wp_restore_post_revision() is now
Errors::writeFailed() (nothing was changed) instead of unsupported().
RevisionsListAbility marks each item divi (reusing
PostPresenter::usesDivi()/DIVI_HINT) since a Divi/block revision's
excerpt is stripped markup, not prose.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:53:36 +02:00
janniclas.loosenandClaude Opus 5.5 b9dd86d223 feat: list and restore revisions
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:41:39 +02:00
janniclas.loosenandClaude Opus 5.5 e183874ff7 fix: trashed backup before replace, byte-exact meta copy, partial rollback
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:34:13 +02:00
janniclas.loosenandClaude Opus 5.5 4fed69cf65 feat: draft copies and replace-with-copy
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:27:07 +02:00
janniclas.loosenandClaude Opus 5.5 346619d6da feat: explicit publish, unpublish, schedule and trash tools
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:18:35 +02:00
janniclas.loosenandClaude Opus 5.5 a9b2f2db71 fix: typed write failures, template rollback, parent and term cleanup
WriteFailed maps to lit_write_failed / lit_partial_write (partial writes are
logged with result partial); old template read before the update; row marker
set before wp_update_post; created terms deleted on rollback; parent must be a
readable non-descendant of a hierarchical type; featured image must be
readable; snapshot keeps empty values; meta comparison after sanitize_meta;
follow-up failures (listener, change log) only add warnings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:11:18 +02:00
janniclas.loosenandClaude Opus 5.5 4f9a47fe32 feat: content-create and content-update with schema validation
All-or-nothing writes: validation (fields, terms, featured image, parent,
template) before any write; failed extras delete the new draft (create) or
restore revision, old columns, fields, terms and featured image (update).
content-update requires rev; Rev::ofPost covers content, title, excerpt,
status and post_modified_gmt. MetaFieldSource::write() now throws on failure.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 23:01:48 +02:00
janniclas.loosenandClaude Opus 5.5 b9d46f3cec fix: empty status filter, hidden-field probing, own private posts
Review fix round 2: content-query no longer lets get_posts() fall back
to 'publish' when every requested status is capability-gated out (now
short-circuits to an empty page). Its field filter shares PostPresenter's
hidden-field rule (Pods password type, protected meta) so a hidden key
is rejected like an unknown one instead of reaching the query. Password
fields are now type-checked as strings on write. A user without
edit_private_posts can still query their own private posts when the
query is already author-restricted, matching WordPress core.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:53:58 +02:00
janniclas.loosenandClaude Opus 5.5 0890e6f165 fix: restrict content-query by cap before pagination, filter hidden fields
Review fix round 1: content-query now excludes posts/statuses the user
cannot edit before querying (author restriction without edit_others_posts,
status filtering by edit_private_posts/edit_published_posts) instead of
only dropping them after fetching, and rejects unknown field/taxonomy
filters. content-types' not-found hint only lists editable types.
PostPresenter skips Pods password fields and readonly protected meta.
Query input gains search/terms length limits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:48:05 +02:00
janniclas.loosenandClaude Opus 5.5 d68e9b1f04 feat: content-query and content-get tools
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:39:43 +02:00
janniclas.loosenandClaude Opus 5.5 95aae74466 feat: field validation, term resolution, post guard and revision points
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:29:15 +02:00
janniclas.loosenandClaude Opus 5.5 98a910f056 feat: content-types tool and WordPress integration
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:21:29 +02:00
janniclas.loosenandClaude Opus 5.5 4a652240a3 feat: Pods field source for schema detection and writes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:13:41 +02:00
janniclas.loosenandClaude Opus 5.5 b3f4b7ba65 feat: content schema with registered and sampled meta fields
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 22:04:04 +02:00
janniclas.loosenandClaude Opus 5.5 261972751f feat: change log table with retention cron and limits option
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 21:58:15 +02:00
janniclas.loosenandClaude Opus 5.5 1df655a74e fix: fail closed on permission() exceptions, harden RequestContext flag reset
- AbilityRegistrar: wrap permission_callback in try/catch(Throwable) like
  execute_callback, so a throwing permission() denies (Errors::internal())
  instead of skipping rest_request_after_callbacks and leaving
  RequestContext's flag set for a later request in the same PHP worker.
- RequestContext::recordRoute() now recomputes the flag (and bearer token)
  unconditionally on every matched REST request instead of only setting it
  true for our own route, so a stale true left behind by an earlier request
  is reset by the next non-matching one (fail closed, defense in depth).
- RequestContext::recordRoute() matches the Authorization "Bearer" scheme
  case-insensitively.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 21:51:41 +02:00
janniclas.loosenandClaude Opus 5.5 720d0891c4 fix: reset request context after the MCP request, resolve client name
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 21:43:07 +02:00
janniclas.loosenandClaude Opus 5.5 f63d35c2f8 feat: structured errors with hint, ToolResult, cursor pagination
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 21:37:42 +02:00
janniclas.loosenandClaude Opus 5.5 5434cb6767 docs: implementation plans for stage 1 (1a content/menus/settings, 1b media/admin)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 21:21:18 +02:00
janniclas.loosenandClaude Opus 5.5 85bd6ca365 docs: mark delivery/update stage as open in spec
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 20:13:58 +02:00
janniclas.loosenandClaude Opus 5.5 3f30e36a73 docs: add delivery & update stage to spec
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 20:06:06 +02:00
janniclas.loosenandClaude Opus 5.5 0e3332ad3e fix: close etappe-0 whole-branch review findings
- Autoloader: add automattic/jetpack-autoloader and load
  vendor/autoload_packages.php; require wordpress/mcp-adapter directly (^0.5).
- ServerRegistrar: make the adapter create_server() call and the
  OAuthServerRegistrar class_exists() check injectable so createServer()'s
  unhook-before-create ordering and the "mcp-oauth absent" branch are unit
  tested; add a smoke-test check that booting REST triggers no
  _doing_it_wrong().
- mcp-smoke.sh: assert mcp-adapter's default server route 404s, and that the
  subscriber role-gate denial actually carries our/the adapter's denial text
  (not just any isError/error).
- Guard tests/Integration/mint-token.php and tec-probe.php with
  `defined('WP_CLI') && WP_CLI || exit;`; add .gitattributes export-ignore
  for tests/docs-superpowers/.superpowers/phpunit/phpcs config.
- Security: add Server/RequestContext, tracking via
  rest_request_before_callbacks whether the current REST dispatch is our
  OAuth-protected /mcp/mcp-oauth-server route; AbilityRegistrar's
  permission_callback now denies (after the role gate) when abilities are
  invoked outside that route. Wired in Plugin::boot().
- Minors: single Plugin::VERSION constant used by ServerRegistrar;
  IntegrationRegistry caches its computed abilities list; tec-probe.php
  checks wp_insert_post's result and cleans up its probe post in a
  try/finally.

composer test: 47/47 green. composer lint: clean. Full mcp-smoke.sh run ends
ALL CHECKS PASSED (plus the expected /.well-known WARN).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 19:23:09 +02:00
janniclas.loosenandClaude Opus 5.5 2c03286ba0 test: probe generic event creation for The Events Calendar
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 19:04:58 +02:00
janniclas.loosenandClaude Opus 5.5 3f81005a83 fix: tighten mcp-smoke.sh denial checks and escaping
- Role-gate denial checks no longer match a bare "error" substring that
  could appear inside a successful response; they now require
  "isError":true or a real JSON-RPC error object ("error":{).
- cleanup_app_passwords_for now escapes backslashes before single
  quotes when interpolating the user login into the wp eval PHP
  snippet.
- The CIMD discovery check now requires
  client_id_metadata_document_supported to be true, not just present.
- Mcp-Session-Id extraction no longer aborts under pipefail when the
  header is absent; a missing header now yields an empty SESSION, as
  already handled downstream.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 19:03:07 +02:00
janniclas.loosenandClaude Opus 5.5 ef3f73c5f2 fix: split MCP discovery check to bypass local nginx dotfile block
Local DDEV nginx denies all dotfile paths (including /.well-known/),
blocking check 1 before it reaches WordPress -- an environment issue,
not a WP-LIT-MCP/mcp-oauth defect. Check 1 now hits mcp-oauth's own
discovery rewrite target (index.php?mcp_oauth_discovery=...) with a
Host header derived from `wp option get home` as a HARD check, and
separately probes the real /.well-known/ path as a non-failing
ENVIRONMENT check that warns if the web server blocks it. Also adds
--max-time 20 to every curl call so the script can never hang.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 18:55:41 +02:00
janniclas.loosenandClaude Opus 5.5 210b1bad2f test: end-to-end MCP smoke test against DDEV
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 16:06:10 +02:00
janniclas.loosenandClaude Opus 5.5 f2580a7c64 fix: honor mcp-oauth's enabled/observability filters, disable second unprotected MCP endpoint
- Default oauthEnabled to WPMedia\MCP\OAuth\Context::is_enabled() instead of
  duplicating its filter logic, so the deprecated rocket_mcp_oauth_server_enabled
  filter and the typed wpmedia_mcp_oauth_server_enabled filter are honored
  exactly as mcp-oauth itself defines them.
- register() now disables mcp-adapter's default server
  (mcp_adapter_create_default_server => false) so no second, unauthenticated
  MCP endpoint (plain HttpTransport, is_user_logged_in()) is exposed alongside
  our OAuth-protected one; mcp-oauth's own ServerRegistrar registers the
  shared meta-abilities itself once the default server is disabled.
- Pass WPMedia\MCP\OAuth\Context::observability_handler() instead of null so
  mcp-oauth's own observability filter keeps working for our server too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:56:50 +02:00
janniclas.loosenandClaude Opus 5.5 3299034297 fix: prevent mcp-oauth's duplicate server registration instead of tolerating it
Proactively unhook wp-media/mcp-oauth's own mcp_adapter_init callback before
creating our server, rather than letting it collide and rely on WP_DEBUG=true
being harmless. The collision previously surfaced as a fatal-looking
_doing_it_wrong notice under WP-CLI (breaking every wp command while the
plugin is active) and could leak debug notices into HTTP responses on
customer sites that run WP_DEBUG.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:50:51 +02:00
janniclas.loosenandClaude Opus 5.5 3661d143fb feat: register OAuth MCP server with direct tools
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:46:24 +02:00
janniclas.loosenandClaude Opus 5.5 fb4787c032 feat: site-status test tool and core integration
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:36:53 +02:00
janniclas.loosenandClaude Opus 5.5 b54d55e5e1 feat: guide tool and server instructions
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:32:26 +02:00
janniclas.loosenandClaude Opus 5.5 3b7fabf17a feat: ability and integration infrastructure
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:27:08 +02:00
janniclas.loosenandClaude Opus 5.5 f69efcb953 fix: guard allowedRoles() against corrupt filtered results
The lit_mcp_allowed_roles filter could return a non-array, causing
array_filter() to throw TypeError in PHP 8.1+. Now fallback to
['administrator'] if filter result is not an array.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:23:56 +02:00
janniclas.loosenandClaude Opus 5.5 f945734c6c docs: code module always allowed, remove setting
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:19:05 +02:00
janniclas.loosenandClaude Opus 5.5 aee7efe0a8 feat: role gate with lit_mcp_use capability
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:18:46 +02:00
janniclas.loosenandClaude Opus 5.5 a8c8cee7e5 docs: drop per-ability isAvailable(); integrations omit unusable abilities instead
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:16:31 +02:00
janniclas.loosenandClaude Opus 5.5 9669ccb36a docs: add per-ability isAvailable() to plan and spec
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:15:28 +02:00
janniclas.loosenandClaude Opus 5.5 8c4498c207 feat: plugin scaffold with dependency check
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-25 15:13:57 +02:00
janniclas.loosenandClaude Opus 5.5 2eef4f702b Add stage 0 implementation plan; refine app-password scope handling in spec
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 15:07:51 +02:00
janniclas.loosenandClaude Opus 5.5 9b4bb6331e Add WP-LIT-MCP design specification
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:59:41 +02:00