Skip to content

search_graph: non-ASCII queries return noise, and docstrings are never indexed #1401

Description

@Metatau

Summary

search_graph(query=...) returns unranked noise for any query written in a non-Latin script, and no amount of documentation helps, because docstrings are not part of the full-text index either. On a codebase documented in Russian (or Greek, or Japanese) the BM25 mode is effectively unusable.

Two independent causes, both small:

1. The query tokenizer drops every non-ASCII byte. bm25_build_match() (src/mcp/mcp.c) accepts only a-z, A-Z, 0-9 and _ as token bytes; everything else is a separator. A Cyrillic query is therefore tokenized to nothing, reaches FTS5 as an empty MATCH, and the caller gets whatever the fallback path returns.

2. Docstrings are not indexed. nodes_fts is declared as fts5(name, qualified_name, label, file_path, ...) in src/store/store.c. Identifiers in real projects are overwhelmingly English even when the team writes prose in another language, so the only text that could match a native-language query is exactly the text that never enters the index.

Reproduction

Index any project that has a non-English docstring, e.g.

def _validate_args(name: str, arguments: dict) -> str | None:
    """Провалидировать аргументы по схеме инструмента."""
codebase-memory-mcp cli search_graph --project <p> --query 'провалидировать аргументы схеме'

Expected: the documented function.
Actual (v0.9.0): unrelated nodes — in my case .env.example, a Coordinates class and a README section; the target function is nowhere in the results.

The English control query behaves as expected, which is what makes the failure easy to miss.

Suggested fix

  1. Treat bytes >= 0x80 as token bytes in bm25_build_match(). No UTF-8 decoding is required — the point is only to keep multi-byte sequences intact and let the FTS5 unicode61 tokenizer apply the real word-boundary rules, which it already does correctly on both sides.
  2. Add a docstring column to nodes_fts and populate it from json_extract(properties, '$.docstring') in both backfill paths (pipeline.c, pipeline_incremental.c). Existing databases carry the 4-column table, so the schema probe has to drop and recreate it; the table is contentless, so nothing is lost — the next backfill repopulates it from nodes.

Result after the fix

Same query, same project:

total: 1
search_mode: bm25
  ...server._validate_args  Function  src/yandex_biz_mcp/server.py  78-96  -18.89

The English query improves as well — validate arguments schema now ranks _validate_args first, where previously it returned noise: docstrings carry intent that identifiers alone do not.

I have a patch with a regression test ready and will open a PR referencing this issue.

Environment

  • v0.9.0 (release build, UI variant), macOS 15.5 arm64
  • Reproduced on several projects; the smallest is an 8-tool Python MCP server, 137 nodes

Metadata

Metadata

Assignees

No one assigned

    Labels

    parsing/qualityGraph extraction bugs, false positives, missing edgesux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions