Skip to content

fix(mcp): report index-required guidance for never-indexed projects - #1545

Merged
phernandez merged 3 commits into
mainfrom
codex/finish-1535
Sep 14, 2026
Merged

phernandez merged 3 commits into
mainfrom
codex/finish-1535

Conversation

@phernandez

@phernandez phernandez commented Sep 14, 2026

Copy link
Copy Markdown
Member

Why

A project that has never completed an index pass must not answer search_notes or recent_activity with an ordinary empty result. Agents can otherwise treat “we never looked” as proof that a note does not exist.

This replaces #1535 and fixes #1534. Credit to @wangzhengzhuo05 for reporting the MCP gap and contributing the regression tests. Their signed-off commit is preserved with its original authorship (rebased onto current main), followed by the implementation and test adaptations here.

What Changed

Empty reads on a never-indexed project return explicit Project Index Required guidance in both text and JSON modes. Indexed projects retain their existing misses and activity onboarding, including when later indexing work is pending. Nonempty reads incur no readiness request.

All-project search and activity discovery preserve this guidance instead of hiding the project in an empty aggregate.

Implementation Details

The shared MCP helper calls the existing ProjectClient.get_status, checks ProjectIndexPhase.NEVER_INDEXED, and reuses ProjectIndexReadiness.describe. This builds on #1440's readiness and mode-aware remedies and #1510's first-index observation optimization; no new readiness calculation, indexing workflow, or API schema is introduced.

Both tools already allow guidance strings alongside their normal structured output. JSON callers receive that explicit guidance string rather than a success-shaped empty collection with an added field. The contributor's JSON test is adapted to this boundary. Search's existing fan-out propagates the guidance without a separate aggregation protocol.

ProjectItem does not expose the resolved routing mode. The message therefore labels the shell-quoted index command as local-only and separately gives the shared server-side cloud remedy, without guessing routing from configuration. Status failures follow existing error handling rather than silently becoming ordinary misses.

Testing

  • uv run pytest tests/mcp -q --no-cov — 1,435 passed.
  • uv run pytest test-int/mcp/test_index_readiness_integration.py test-int/mcp/test_chatgpt_tools_integration.py test-int/mcp/test_delete_note_integration.py test-int/mcp/test_search_integration.py test-int/cli/test_cli_tool_delete_note_integration.py test-int/cli/test_cli_tool_json_failure_integration.py -q --no-cov — 54 passed on SQLite; the same command prefixed with BASIC_MEMORY_TEST_POSTGRES=1 — 54 passed on Postgres.
  • uv run pytest tests/test_man_pages.py -q --no-cov — 39 passed.
  • uv run pytest tests/test_remedy_emission_sites.py -q --no-cov — 3 passed.
  • just fast-check — passed, including repository-wide type checking. Installed the declared optional Milvus extra in the fresh worktree environment first.
  • just doctor — passed the temporary-project write, index, search, and status checks.
  • Tests asserting ordinary empty searches, activity onboarding, or missing-note responses explicitly complete an initial pass through the existing index endpoint. A fresh-project regression verifies that a real first index pass replaces index-required guidance with a searchable note and honest misses.
  • Regression coverage includes actual MCP calls in both output formats, later pages and filters, all-project search and activity discovery, readiness failures, nonempty request counts, and indexed pending/idle misses.

Risks / Follow-ups

An empty read now makes one status request, which observes project files. Nonempty reads are unchanged. This is guidance content under the existing string-result contract, not a new MCP isError transport flag. Hosted deployment was not exercised locally; no merge or deployment is included.

wangzhengzhuo05 and others added 2 commits September 13, 2026 22:26
On a project whose last_indexed_at is NULL, search_notes and recent_activity
returned an ordinary empty result, indistinguishable from an honest miss on an
indexed project. Readiness already exists (ProjectIndexPhase/describe, used by
bm status) but nothing under mcp/ read it.

On the zero-result path only, read readiness via ProjectClient.get_status and,
when the phase is NEVER_INDEXED, reuse ProjectIndexReadiness.describe so the
wording cannot drift from bm status. search_notes JSON output carries
index_phase; an unreadable readiness falls back to today's copy.

Fixes #1534

Signed-off-by: wangzhengzhuo05 <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T04:01:15.577810Z 4dba385 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@phernandez
phernandez merged commit dff6656 into main Sep 14, 2026
34 checks passed
@phernandez
phernandez deleted the codex/finish-1535 branch September 14, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

search_notes returns an empty result set on a never-indexed project, indistinguishable from "no such note"

2 participants