search_by_project
Locate saved entries for a project using the project name. Retrieve entry IDs, problems, and solutions to choose the right entry for correction or enrichment.
Instructions
List saved entries for a specific project.
Use this at the start of a new conversation when you need to find a project-specific entry to correct or enrich but no entry_id is in context. Returns entry ids, problems, and solutions so you can pick the right one and pass its id to correct_solution or enrich_solution.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project | Yes | Repository or workspace name to look up. | |
| query | No | Optional keyword to filter results — searches problem and solution text. Leave empty to list all entries for the project. | |
| limit | No | Maximum number of entries to return. Default 20. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/longmem/server.py:569-616 (handler)The MCP tool handler `search_by_project` — decorated with @mcp.tool(). It accepts project (required), query (optional keyword filter), and limit (max entries). Delegates to store.search_by_project() and returns JSON with found status, count, and formatted entries.
# ── tool: search_by_project ────────────────────────────────────────────────── @mcp.tool() async def search_by_project( project: Annotated[ str, Field(description="Repository or workspace name to look up."), ], query: Annotated[ str, Field( description=( "Optional keyword to filter results — searches problem and solution text. " "Leave empty to list all entries for the project." ), default="", ), ] = "", limit: Annotated[ int, Field(description="Maximum number of entries to return. Default 20.", default=20, ge=1, le=100), ] = 20, ) -> str: """ List saved entries for a specific project. Use this at the start of a new conversation when you need to find a project-specific entry to correct or enrich but no entry_id is in context. Returns entry ids, problems, and solutions so you can pick the right one and pass its id to correct_solution or enrich_solution. """ try: store, *_ = await _get_deps() results = await store.search_by_project(project, query=query, limit=limit) if not results: msg = f"No entries found for project '{project}'" if query: msg += f" matching '{query}'" return json.dumps({"found": False, "message": msg}, indent=2) return json.dumps({ "found": True, "project": project, "count": len(results), "entries": [_format_result(r, i + 1) for i, r in enumerate(results)], }, indent=2) except Exception as exc: return _db_error(exc) - src/longmem/server.py:103-119 (helper)Helper `_format_result` which converts a SearchResult dataclass into the JSON-serializable dict used in the tool's response.
def _format_result(r: SearchResult, rank: int) -> dict: result: dict = { "rank": rank, "id": r.id, "similarity": f"{r.similarity:.0%}" if not r.keyword_match else "keyword match", "project": r.project, "category": r.category, "tags": r.tags, "language": r.language, "problem": r.problem, "solution": r.solution, "edge_cases": r.edge_cases, "created_at": r.created_at, } if r.keyword_match: result["keyword_match"] = True return result - src/longmem/store.py:265-296 (schema)`SearchResult` dataclass — the return type for store.search_by_project(). Contains id, project, category, tags, language, problem, solution, edge_cases, similarity, created_at, keyword_match.
class SearchResult: __slots__ = ( "id", "project", "category", "tags", "language", "problem", "solution", "edge_cases", "similarity", "created_at", "keyword_match", ) def __init__( self, id: str, project: str, category: str, tags: list[str], language: str, problem: str, solution: str, edge_cases: list[str], similarity: float, created_at: str = "", keyword_match: bool = False, ) -> None: self.id = id self.project = project self.category = category self.tags = tags self.language = language self.problem = problem self.solution = solution self.edge_cases = edge_cases self.similarity = similarity # 0.0 – 1.0; 0.0 means keyword-only match self.created_at = created_at # ISO-8601 self.keyword_match = keyword_match # True when found via FTS, not vector - src/longmem/store.py:667-707 (handler)`SolutionStore.search_by_project()` — the store-level implementation that queries LanceDB by project name, optionally filters by keyword in problem/solution text, and returns SearchResult objects sorted newest-first.
async def search_by_project( self, project: str, query: str = "", limit: int = 20, ) -> list[SearchResult]: """ Return entries for a specific project, optionally filtered by a keyword query. Results are ordered newest-first. Used to find project-specific entries in a new conversation when the entry_id is not in context. """ safe_project = project.replace("'", "''") q = self._table.query().where(f"project = '{safe_project}'").limit(limit) rows = await q.to_list() rows.sort(key=lambda r: r.get("created_at") or "", reverse=True) results = [] for row in rows: # keyword filter applied in Python (no vector needed) if query: haystack = ( (row.get("problem") or "") + " " + (row.get("solution") or "") ).lower() if query.lower() not in haystack: continue results.append( SearchResult( id=row["id"], project=row["project"], category=row["category"], tags=list(row.get("tags") or []), language=row["language"], problem=row["problem"], solution=row["solution"], edge_cases=list(row.get("edge_cases") or []), similarity=1.0, # not a vector search — exact project match created_at=row.get("created_at") or "", ) ) return results - src/longmem/server.py:569-570 (registration)Registration via `@mcp.tool()` decorator on the `search_by_project` async function — this is how FastMCP registers the tool.
# ── tool: search_by_project ────────────────────────────────────────────────── @mcp.tool()