Add search_notes MCP tool #18

Closed
opened 2026-03-06 20:34:09 +00:00 by forgejo_admin · 1 comment

Plan

plan-2026-02-26-tf-modularize-postgres -- Phase 5

Repo

forgejo_admin/pal-e-docs-mcp

User Story

As an AI agent using MCP tools
I want a search_notes tool that finds notes by text query
So that I can find relevant knowledge in one MCP call instead of 12+ list/get loops

Context

This is the second PR of Phase 5. The pal-e-docs API will have a new GET /notes/search?q=... endpoint (deployed first via separate PR on pal-e-docs). This issue adds the MCP tool that wraps it.

All existing MCP tools in this repo are thin httpx wrappers over the pal-e-docs API. Follow the same pattern.

Do NOT start this until the pal-e-docs search endpoint is deployed. The API must exist before the MCP tool can call it.

File Targets

Files to modify or create:

  • src/pal_e_docs_mcp/tools/notes.py — add search_notes tool function
  • src/pal_e_docs_mcp/server.py — register the new tool (if tools aren't auto-discovered)

Files NOT to touch:

  • Existing tool functions — don't modify list_notes, get_note, etc.
  • Sprint tools — unrelated

Acceptance Criteria

  • search_notes(query="secrets management") returns ranked results with snippets
  • Parameters: query (required), limit (optional, default 10), note_type (optional), project (optional), status (optional), tags (optional)
  • Returns: list of objects with slug, title, note_type, status, headline, rank
  • No full html_content in response (token efficiency)
  • Follows existing tool patterns (httpx wrapper, error handling)

Test Expectations

  • Verify tool is registered and callable
  • Run: whatever test command the repo uses

Constraints

  • Match existing tool style in tools/notes.py
  • Use the same httpx client pattern as other tools
  • Endpoint: GET /notes/search?q={query}&limit={limit}&note_type={note_type}&project={project}&status={status}&tags={tags}
  • Only pass optional params if provided (don't send empty strings)

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • pal-e-docs-mcp project
  • Phase note: phase-postgres-5-fulltext-search
  • Blocked-by: forgejo_admin/pal-e-docs search endpoint (must be deployed first)
### Plan `plan-2026-02-26-tf-modularize-postgres` -- Phase 5 ### Repo `forgejo_admin/pal-e-docs-mcp` ### User Story As an AI agent using MCP tools I want a `search_notes` tool that finds notes by text query So that I can find relevant knowledge in one MCP call instead of 12+ list/get loops ### Context This is the second PR of Phase 5. The pal-e-docs API will have a new `GET /notes/search?q=...` endpoint (deployed first via separate PR on pal-e-docs). This issue adds the MCP tool that wraps it. All existing MCP tools in this repo are thin httpx wrappers over the pal-e-docs API. Follow the same pattern. **Do NOT start this until the pal-e-docs search endpoint is deployed.** The API must exist before the MCP tool can call it. ### File Targets Files to modify or create: - `src/pal_e_docs_mcp/tools/notes.py` — add `search_notes` tool function - `src/pal_e_docs_mcp/server.py` — register the new tool (if tools aren't auto-discovered) Files NOT to touch: - Existing tool functions — don't modify list_notes, get_note, etc. - Sprint tools — unrelated ### Acceptance Criteria - [ ] `search_notes(query="secrets management")` returns ranked results with snippets - [ ] Parameters: `query` (required), `limit` (optional, default 10), `note_type` (optional), `project` (optional), `status` (optional), `tags` (optional) - [ ] Returns: list of objects with `slug`, `title`, `note_type`, `status`, `headline`, `rank` - [ ] No full `html_content` in response (token efficiency) - [ ] Follows existing tool patterns (httpx wrapper, error handling) ### Test Expectations - [ ] Verify tool is registered and callable - Run: whatever test command the repo uses ### Constraints - Match existing tool style in `tools/notes.py` - Use the same httpx client pattern as other tools - Endpoint: `GET /notes/search?q={query}&limit={limit}&note_type={note_type}&project={project}&status={status}&tags={tags}` - Only pass optional params if provided (don't send empty strings) ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `pal-e-docs-mcp` project - Phase note: `phase-postgres-5-fulltext-search` - Blocked-by: `forgejo_admin/pal-e-docs` search endpoint (must be deployed first)
Author
Owner

PR #19 Post-Merge Review

BLOCKERS

None.

NITS

  1. Missing status filter parameter. The API endpoint GET /notes/search accepts an optional status query parameter (e.g. active, completed, in-progress), but search_notes does not expose it. The sibling tool list_notes does expose status. This is a functional gap -- callers cannot filter search results by status without a follow-up list_notes call.

    API signature (pal-e-docs routes/notes.py:178):

    status: str | None = Query(None, description="Filter by status")
    

    Recommend adding a status parameter to search_notes in a follow-up.

  2. Truthiness check vs. is not None for note_type, project, tags. The tool uses if note_type: (truthiness) rather than if note_type is not None: like it does for limit. This means an empty string "" would silently be dropped instead of being sent to the API. This is consistent with list_notes so it is not a deviation, but worth noting -- both tools have this same minor inconsistency between how they guard limit (identity check) vs. string params (truthiness check). In practice, MCP callers will never send empty strings, so this is cosmetic.

  3. No tests. The repo has zero test files (only an empty tests/__init__.py). This is not a regression -- no tool in the repo has tests. Recommend tracking test infrastructure as a separate issue.

SOP COMPLIANCE

  • PR body has Summary, Changes, Test Plan, Related sections
  • Related section references plan slug (plan-2026-02-26-tf-modularize-postgres)
  • No secrets or credentials committed
  • No unnecessary file changes (single file, 35 additions)
  • Commit messages are descriptive
  • Follows existing tool pattern (_ok/_error_response, get_client().get(), Annotated + Field)
  • Correct API endpoint path (/notes/search)
  • Required param query mapped correctly to q query param
  • Optional params conditionally included (not sent when None)

VERDICT: APPROVED

Clean, minimal, pattern-compliant. The missing status parameter is a gap but non-blocking -- it can be added in a follow-up. The code is correct for the parameters it does expose.

## PR #19 Post-Merge Review ### BLOCKERS None. ### NITS 1. **Missing `status` filter parameter.** The API endpoint `GET /notes/search` accepts an optional `status` query parameter (e.g. `active`, `completed`, `in-progress`), but `search_notes` does not expose it. The sibling tool `list_notes` does expose `status`. This is a functional gap -- callers cannot filter search results by status without a follow-up `list_notes` call. API signature (pal-e-docs `routes/notes.py:178`): ```python status: str | None = Query(None, description="Filter by status") ``` Recommend adding a `status` parameter to `search_notes` in a follow-up. 2. **Truthiness check vs. `is not None` for `note_type`, `project`, `tags`.** The tool uses `if note_type:` (truthiness) rather than `if note_type is not None:` like it does for `limit`. This means an empty string `""` would silently be dropped instead of being sent to the API. This is consistent with `list_notes` so it is not a deviation, but worth noting -- both tools have this same minor inconsistency between how they guard `limit` (identity check) vs. string params (truthiness check). In practice, MCP callers will never send empty strings, so this is cosmetic. 3. **No tests.** The repo has zero test files (only an empty `tests/__init__.py`). This is not a regression -- no tool in the repo has tests. Recommend tracking test infrastructure as a separate issue. ### SOP COMPLIANCE - [x] PR body has Summary, Changes, Test Plan, Related sections - [x] Related section references plan slug (`plan-2026-02-26-tf-modularize-postgres`) - [x] No secrets or credentials committed - [x] No unnecessary file changes (single file, 35 additions) - [x] Commit messages are descriptive - [x] Follows existing tool pattern (`_ok`/`_error_response`, `get_client().get()`, `Annotated` + `Field`) - [x] Correct API endpoint path (`/notes/search`) - [x] Required param `query` mapped correctly to `q` query param - [x] Optional params conditionally included (not sent when None) ### VERDICT: APPROVED Clean, minimal, pattern-compliant. The missing `status` parameter is a gap but non-blocking -- it can be added in a follow-up. The code is correct for the parameters it does expose.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/pal-e-mcp#18
No description provided.