fix(admin): extend server-side content search to the content picker and MCP - #752
fix(admin): extend server-side content search to the content picker and MCP#752edrpls wants to merge 5 commits into
Conversation
🦋 Changeset detectedLatest commit: 3b98339 The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Scope checkThis PR changes 1,912 lines across 29 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
Lunaria Status Overview🌕 This pull request will trigger status changes. Learn moreBy default, every PR changing files present in the Lunaria configuration's You can change this by adding one of the keywords present in the Tracked Files
Warnings reference
|
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
|
This PR has been inactive for 14 days. It will be closed automatically in 7 days if there is no further activity. If you're still working on this, please push an update or leave a comment. |
129448f to
08fca45
Compare
|
Rebased on upstream main ( Local test runs after rebase, on top of
Bumping out of stale-warning territory. |
b8f394c to
6704774
Compare
|
Update — scope narrowed after rebase. Since my review note above, #1226 merged an independent implementation of the core server-side content search (
That moots most of the items above. Re-mapping each: Design calls — now #1226's territory
Lower-priority findings
All inline threads have been resolved accordingly. No maintainer decision is blocking this PR anymore — it's a focused follow-up on top of #1226. 🤖 update drafted with Claude Code. |
c4cb0e9 to
e45bf3b
Compare
There was a problem hiding this comment.
This is the right fix for a real bug. Moving the content picker and MCP content_list to the server-side q search introduced in #1226 is the correct architecture, and the implementation fits EmDash's patterns. The picker now uses useInfiniteQuery with keepPreviousData to avoid flashing empty lists between keystrokes, and it correctly keeps the load-more button available while searching. The MCP tool exposes q and passes it through to the handler layer, reusing the same SQL-safe repository path as the REST route.
I checked the picker refactor, the MCP schema and tool wiring, the new MCP integration test, the underlying handleContentList/ContentRepository.findMany search implementation, and the current ContentList component. The four findings from the prior review are addressed: the three ContentList issues (trusting the server total in search mode, keeping the search input visible, and showing a "no results" state instead of the empty-collection prompt for zero-result searches) are already fixed on this branch, and this PR fixes the picker's load-more-during-search issue.
Headline conclusion: The PR is clean and ready to land after addressing two minor nits — an MCP schema inconsistency and a test organization issue. No blocking logic, security, or convention problems.
…h test Address review feedback on emdash-cms#752: - MCP `content_list` `q` now uses `.trim().min(1).max(200)`, matching the REST `contentListQuery` contract. It previously accepted empty/whitespace `q` and silently ignored it; it now rejects it, so the MCP and REST interfaces accept and reject the same inputs. - Move the `content_list` search test out of the unrelated "soft-delete visibility" describe into a dedicated "content_list search" block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Merged current On the two red E2E checks from the previous run: they're the Local verification on the merged head: full |
…h test Address review feedback on emdash-cms#752: - MCP `content_list` `q` now uses `.trim().min(1).max(200)`, matching the REST `contentListQuery` contract. It previously accepted empty/whitespace `q` and silently ignored it; it now rejects it, so the MCP and REST interfaces accept and reject the same inputs. - Move the `content_list` search test out of the unrelated "soft-delete visibility" describe into a dedicated "content_list search" block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
36e9e66 to
02e5c05
Compare
|
@ascorbic gentle ping on this one — it's been open since April and is in a ready state: rebased onto current main, CI fully green, both emdashbot review passes addressed (the second concluded "clean and ready to land after two nits", and those nits landed with regression tests). Zero unresolved threads. If you'd rather route it through the bot first, I can't apply |
There was a problem hiding this comment.
This is the right fix for a real bug. Moving ContentPickerModal and the MCP content_list tool onto the server-side ?q= search introduced in #1226 is the correct architecture for large collections, and the implementation follows EmDash’s existing patterns.
I checked the picker refactor, the MCP tool/schema wiring, the new MCP integration tests, the underlying handleContentList/ContentRepository search path, and the REST contentListQuery schema. The four ContentList issues from the prior review remain resolved on this branch, and the previous MCP schema inconsistency is now fixed: the MCP q shape matches the REST schema exactly (z.string().trim().min(1).max(200).optional()).
Headline conclusion: the PR is clean and nearly ready to land. There are a couple of remaining low-grade nits — one stale test-file header and an explanatory JSX comment that violates the comment-discipline convention — plus a minor UX inconsistency in the picker empty state. None of these are blockers.
Findings
-
[suggestion]
packages/admin/src/components/ContentPickerModal.tsx:203This JSX comment narrates the change rather than explaining non-obvious code. AGENTS.md says comments must not be PR/reviewer-facing narrative; the
hasNextPage &&condition already expresses that load-more stays available. Remove it.{hasNextPage && ( -
[suggestion]
packages/admin/src/components/ContentPickerModal.tsx:156The empty-state uses the raw
searchQueryto decide whether to show the search-specific prompt. BecausesearchParamfalls back toundefinedwhen the input is whitespace-only, a user typing only spaces sees "Try adjusting your search" even though the server is not actually filtering. Use the effective search term instead.{searchParam ? ( -
[suggestion]
packages/core/tests/integration/mcp/content-misc.test.ts:2The file header no longer matches the file contents: the new
content_list searchdescribe block is not listed. Update the docblock so future readers know this file covers thecontent_listqparameter./** * MCP content tools — coverage for the remaining tools and edges. * * Covers: * - content_duplicate * - content_permanent_delete * - content_translations + locale handling on create/get * - _rev optimistic concurrency (happy + race) * - Soft-delete visibility (content_get / content_list filtering) * - content_list search by `q` * - Edit-while-trashed * - Idempotency (publish twice, unpublish-on-draft, schedule + publish) */
|
All three findings from the fresh review pass are addressed in 12f892e:
Verified locally: lint clean, MCP suite 29/29, full admin browser suite 1363/1363. |
The core server-side content search (?q=) landed via emdash-cms#1226. Extend it to two surfaces that still post-filtered in memory: - ContentPickerModal now pushes its search box to the server (search option -> ?q=), so it finds entries anywhere in a large collection instead of only the rows already loaded. Uses keepPreviousData to avoid flashing to empty between keystrokes and keeps load-more available while searching. - MCP content_list gains a q parameter, so agents search server-side rather than post-filtering a page of results. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the manual accumulator (allItems/nextCursor mirrored from a useQuery) with useInfiniteQuery, matching the ContentList pattern. The accumulator could lose loaded pages on any background refetch (window focus, cache invalidation) and, with keepPreviousData, could fire load-more with a stale cursor against a freshly-changed search. Deriving the list straight from query pages removes both windows; search stays in the query key so changing it starts a fresh page chain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…h test Address review feedback on emdash-cms#752: - MCP `content_list` `q` now uses `.trim().min(1).max(200)`, matching the REST `contentListQuery` contract. It previously accepted empty/whitespace `q` and silently ignored it; it now rejects it, so the MCP and REST interfaces accept and reject the same inputs. - Move the `content_list` search test out of the unrelated "soft-delete visibility" describe into a dedicated "content_list search" block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The q schema gained min(1) to match the REST contract, but nothing asserted it — the constraint could be dropped without a test noticing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12f892e to
822fd3f
Compare
A whitespace-only query showed "Try adjusting your search" while the server wasn't filtering. Also drops a narrative JSX comment and lists the content_list search coverage in the MCP test header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0175x2Nu2m7LZUznaEQUAVQd
822fd3f to
3b98339
Compare
What does this PR do?
Extends the server-side content list search (
?q=) — which shipped via #1226 — to two surfaces that still post-filtered in memory:ContentPickerModal(used when linking content from the editor) now pushes its search box to the server (searchoption →?q=) instead of filtering only the rows already loaded, so it finds entries anywhere in a large collection. It useskeepPreviousDataso the list doesn't flash to empty between keystrokes, and keeps load-more available while searching (results can span multiple pages).content_listtool gains aqparameter, so agents can search a collection server-side rather than post-filtering a single page of results.This branch was rebased onto
mainafter #1226 merged an independent implementation of the core feature. The overlapping core-search work (handler, repository, schemas,ContentList, router, API client, index migration) is now provided by #1226; this PR keeps only the picker + MCP extensions on top of it.Related: #1219, #1226
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output
MCP integration tests pass, including a new case asserting
content_listfilters byq: