MFS — find / read across configured sources
1. What MFS is
A retrieval layer that exposes many kinds of content as a unified path
tree and makes that tree searchable through one hybrid index:
- One CLI (
mfs), one mental model. Local dir, Postgres, GitHub repo,
Slack workspace, S3 bucket, BigQuery dataset — all addressed as paths
under their <scheme>:// URI. Same verbs everywhere: ls / tree / cat / head / tail / grep / search / export.
- One hybrid index. Dense vectors (semantic) + BM25 (keyword) fused
per query — covers conceptual recall and exact-token recall in one call.
- POSIX-style locators. Every search hit carries a
locator that
reopens the exact unit: {"lines":[s,e]} for text/code, a PK dict for
rows/issues/threads.
2. When to use MFS — and when NOT to
| Situation |
Use MFS? |
| 1000+ files / rows / pages, you don't know where the answer is |
✅ |
| Cross-source question ("any past tickets / commits / RFCs about X") |
✅ --all |
| Concept-style query that won't match literally |
✅ --mode semantic |
| You already know the exact file + roughly where to look |
❌ plain cat/grep |
| Exact identifier / error code in 5 files you can list |
❌ plain grep/rg |
| Real-time tailing of a live log |
❌ index lags ingest |
| The source isn't in MFS yet |
wrong skill, use mfs-ingest to register first |
Rule: use the smallest tool that answers the question. MFS pays off
when the scope is too big for rg.
Borderline — ASK the user:
| Ask |
Likely answer |
Why |
| "Summarise these 10 PDFs" |
✅ mfs search + cat --peek per hit |
each PDF gets a converted_md artifact + searchable chunks |
| "Find similar tickets to this one" |
✅ paste the ticket text as the search query |
semantic over row_text does similarity matching |
| "Watch for new slack messages" |
❌ no watch capability; use Slack's API |
index lags ingest |
| "Look up user 12345" |
❌ mfs cat <source> --locator '{"id":12345}' directly (skip search) |
one-record-by-id doesn't need ranking |
3. Pre-flight — confirm the source is indexed
Before running any query, especially on cross-source asks:
mfs status # server up? any connectors registered?
mfs connector inspect <uri> # this connector's object/job summary
mfs ls <uri> --json # per-entry capabilities + indexable / search_status
- Server unreachable → tell user to start it (
mfs serve start if
self-hosted), or this skill can't proceed.
connectors empty → user hasn't ingested anything yet. Redirect to
mfs-ingest — don't try to search nothing.
search_status: unavailable for the target URI → only grep / ls /
cat work; offer those or redirect to mfs-ingest for a re-sync.
building → sync in flight; fall back to mfs grep (works without an
index) until done.
partial → recall incomplete but usable; flag the caveat to the user.
4. The core workflow: search → locate → browse
search locate browse
┌──────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ semantic + BM25 │ → │ result has lines │ → │ cat --range / cat │
│ finds candidates│ │ or a locator │ │ --peek to confirm │
└──────────────────┘ └──────────────────┘ └─────────────────────┘
On large corpora this loop is the whole point: read only the part that
matters. On small corpora it's still fine, just lighter.
Concrete:
- Search:
mfs search "<what the user actually wants>" <path-or-uri> --top-k 10
- Locate — every hit's envelope carries
locator:
- text/code →
{"lines":[start,end]} → mfs cat <source> --range start:end
- structured (row/issue/thread) → PK dict →
mfs cat <source> --locator '{...}'
- once-per-object (dir/schema summary, image VLM) →
null → mfs cat <source>
- Browse — verify only what's needed:
mfs cat --peek <file> # outline (headings / function signatures)
mfs cat --skim <file> # peek + one-line summaries per section
mfs head -n 20 <uri> # first records of a structured object
mfs tree <uri> -L 2 # subtree shape
5. Index requirement rules of thumb
mfs search requires an index.
mfs grep works without — pushdown → BM25 → linear scan fallback.
mfs ls / tree / cat / head / tail browse without an index.
6. Search modes
mfs search defaults to hybrid. Override only when you know why.
--mode |
Mechanic |
When |
hybrid (default) |
dense + BM25 fused with RRF |
almost always |
semantic |
dense only |
conceptual query, wording won't match literally |
keyword |
BM25 only |
exact-term (config key, error code) without semantic drift |
Other useful flags:
--top-k N — default 10; raise to 20-30 on a weak first round.
--all — search every registered connector. Otherwise scope to a path/URI prefix.
--kind <list> — restrict chunk kinds (row_text, thread_aggregate,
body, summary, vlm_description, …).
--collapse — keep only the top-scoring chunk per object; later chunks
from the same source are dropped, not merged. Recall stays as-is (the
query still hits the same candidates), but the visible result count can
fall below --top-k — collapse is a post-filter, not a re-rank. If you
want N distinct objects, raise --top-k (e.g. --top-k 30 --collapse).
--all: when yes, when no
- ✅ Cross-source recall — "any past tickets / commits / RFCs / slack about X".
- ❌ You know the source — scope to
slack://; postgres + jira + docs together aren't comparable.
- ⚠ More than ~5 registered connectors — ASK the user whether to fan out
widely or scope to the 2-3 likeliest sources first.
7. Decision tree — pick the smallest useful tool
| Signal in the ask |
Sub-task |
Use |
| natural-language question / sentence |
exploratory |
mfs search "<q>" <scope> |
| paraphrased / conceptual wording |
semantic-only |
mfs search --mode semantic |
| exact identifier / config key / unique phrase |
literal anchor |
mfs grep "<lit>" <path> (or rg) |
| filename / directory pattern |
path lookup |
find / shell glob / fd |
| known file + needs outline |
structural overview |
mfs cat --peek <file> |
| known file + compact summary |
dense overview |
mfs cat --skim <file> |
| search hit + surrounding context |
reopen |
mfs cat <file> --range s:e |
| structured hit (row/issue/thread) |
reopen by PK |
mfs cat <source> --locator '{...}' |
| several close candidates |
compare |
mfs cat --peek each, then pick |
| single record + known key |
no-search lookup |
mfs cat <source> --locator '{"id":12}' |
| first / last N |
sample |
mfs head -n N / mfs tail -n N |
| subtree shape |
orient |
mfs tree -L 2 <uri> |
| full object for offline tooling |
export |
mfs export <uri> <file> |
mfs search requires an explicit scope or --all.
8. Command cheat sheet
Search
mfs search "<query>" <path-or-uri> # default: hybrid, top-k=10
mfs search "<query>" --all # whole namespace
mfs search "<query>" <path> --top-k 20 # more candidates
mfs search "<query>" <path> --mode semantic # dense-only
mfs search "<query>" <path> --mode keyword # BM25-only
mfs search "<query>" <path> --kind row_text # restrict chunk kinds
mfs search "<query>" <path> --collapse # keep top hit per object (post-filter, may return < top-k)
Grep
mfs grep "<pattern>" <path> # pushdown -> BM25 -> linear
mfs grep is not grep. The three-tier dispatch is:
- Pushdown — for structured connectors (
postgres, mongo,
jira, …) the pattern is shipped to the source as a LIKE / regex
filter. Literal-exact, token-level; no regex on most structured
connectors.
- BM25 over indexed objects — for
body/code/document chunks
already in Milvus, the pattern is fed through the same sparse
index search --mode keyword uses. That is a tokenized,
ranked lookup, not a literal substring scan: an analyzer split
like getUserId → get, user, id will rank
userId as a hit; a CJK pattern with no analyzer match returns
nothing even when the literal bytes are present. If you need
"does this exact byte string appear anywhere?", mfs export the
object and run rg locally — mfs grep has no "force linear over
indexed objects" flag.
- Linear scan — only for not-indexed files in scope (file
connector before
mfs add). True substring / regex.
For exact-exhaustive on a huge structured object, mfs export then
local grep / rg.
Read
mfs cat <path> # full content (refused if "lazy")
mfs cat <path> --range A:B # lines A..B-1 (1-based, end-exclusive)
mfs cat <path> --locator '{"id":12}' # reopen a structured record
mfs cat <path> --peek # outline only
mfs cat <path> --skim # peek + per-section summaries
mfs cat <path> --meta # stat-style, not content
Density ladder:
| Mode |
Use it when |
--peek |
"show me the outline" |
--skim |
+ one-line summary per section, still concise |
| (default) |
full content; small file or really need it |
--range A:B |
already know which lines matter (e.g. search hit) |
mfs head -n 50 <path> # first 50 lines/records
mfs tail -n 50 <path> # last 50; native-accel reverse read
For a lazy rows.jsonl / messages.jsonl, head is how to see record
shape without paying full-scan cost.
Browse
mfs ls <uri> # one level
mfs tree <uri> -L 2 # depth-bounded recursive
NOT a substitute for search when the target is unknown and conceptual.
Export
mfs export <uri> <out-file> # full object to disk for jq/awk pipelines
cat of a huge lazy object is refused — use export for bulk processing.
Status (useful before AND during search work)
mfs status # server + all connectors
mfs connector inspect <uri> # one connector's object/job summary
mfs connector list # list registered connectors
mfs job list # recent indexing jobs (background re-syncs)
Always prefer --json when output will be parsed.
9. Weak results → recover, don't thrash
If top hits look off-topic:
- Rewrite with synonyms / domain terms. ASK the user for the domain
term they'd actually use if vague. One clarifier beats five blind queries.
- Raise
--top-k to compare distinct candidates.
mfs cat --peek the top few to compare structure.
- Switch mode — semantic if hybrid was keyword-noisy; keyword if
specific terms should be the anchor.
- Then literal
grep — only if the task has a real literal anchor
(error code, config key, identifier).
Literal search is a different tool, not a stronger version of semantic.
10. Candidate selection
Think at object level, not just chunk level:
- Merge repeated hits from the same object into one candidate.
- Compare the top distinct candidates'
--peek when titles or
snippets look adjacent.
- Prefer an object whose main topic directly matches the request
over a broad overview that mentions it.
- Multi-part prompts (two entities, setup + troubleshooting, migration
source + target) — check whether more than one object is needed.
mfs search "<query>" <path> --top-k 20
mfs cat --peek <candidate-a>
mfs cat --peek <candidate-b>
mfs cat <best> --range <start>:<end>
11. Anti-patterns
- Don't grep to "confirm" a successful semantic hit. The hit's
snippet IS the source content; trust it.
- Don't read a whole large file when
--peek / --skim / --range
can answer.
- Don't blindly pick rank #1 when #1-#3 are clearly different objects.
- Don't stop at one match if the prompt mentions multiple entities.
- Don't search the same vague words after a weak first round — fix
the query or escalate to literal anchors.
- Don't
cat a lazy object (DB rows.jsonl, SaaS records.jsonl,
chat messages.jsonl). Use head, --range, --locator, or export.
- Don't use MFS for sources you'd just clone/download anyway — pull
locally and use the
file connector.
12. When search returns nothing on a freshly indexed connector
This is the most common diagnostic case. Walk this ladder, stop on first
hit:
# 1. THIS connector's object/job summary
mfs connector inspect <uri>
# 2. failed sync jobs?
mfs job list
mfs job show <job-id> # if any failed, read the job's error field
# 3. per-object granularity
mfs ls <uri> --json # each entry's search_status
# 4. JSON error code path
# If --json output carried a `code` field, see reference/error-codes.md
# 5. otherwise treat as query-level — §9 above
| Signal |
Meaning |
Action |
building |
sync in flight |
wait, or mfs grep until done |
partial |
chunks dropped (chunk_max / max_read_rows) |
usable but incomplete; user may want to re-ingest with raised caps (→ redirect to mfs-ingest) |
unavailable |
nothing indexed |
only grep / ls / cat work; redirect to mfs-ingest |
available but smoke search empty |
wrong text_fields / source empty / wrong scope |
check reference/connectors/<scheme>.md for that connector's shape; mfs cat a known object to confirm content |
When the diagnosis points to "ingest config is wrong" or "needs re-sync
with different settings" — don't try to fix it from this skill. Tell
the user to invoke mfs-ingest for that connector.
13. Reference routing
These reference files are loaded ONLY when the situation matches — don't
open speculatively.
reference/json-envelope.md —
WHEN parsing a --json search/grep result and the locator shape is
unfamiliar (composite PKs, thread_ts, nested keys); OR when uncertain
how to feed a hit back into mfs cat (range vs locator dispatch).
reference/error-codes.md —
WHEN an mfs command returned --json error output with a code
field. Read the message first — don't open just because a command
failed.
reference/connectors/<scheme>.md —
WHEN searching a specific connector and you need its tree shape, record
field semantics, locator format, or search-strategy tips. STOP and read
the matching one BEFORE guessing how that source enumerates objects
or what fields its records carry. Schemes: file, web, s3,
gdrive, postgres, mysql, snowflake, bigquery, mongo,
github, jira, linear, hubspot, notion,
zendesk, slack, discord, gmail, feishu.
Runtime capability for a specific URI is queried structurally via
mfs ls <uri> --json; the static per-connector references describe what
the connector exposes by design.
1---2name: mfs-find3description: Search, grep, browse, and read across registered MFS data sources via the `mfs` CLI — codebases, docs, PDFs, web crawls, databases (postgres/mysql/mongo/snowflake/bigquery), issue trackers (jira/linear/github), CRMs (hubspot), chat (slack/discord/gmail/feishu), object stores (s3/gdrive). Use whenever the user asks to find, locate, look up, look across, or read something out of an already-configured MFS index. Trigger phrases include "search the codebase for", "find anywhere about", "where is X mentioned", "look across our [slack/jira/postgres/etc]", "any past tickets/RFCs/commits about", "what does our wiki say about", "cat / head / tail / ls / tree this MFS path". Do NOT use for: registering a NEW data source (use `mfs-ingest`), changing connector config, kicking off re-ingest, or any write/delete operation — `mfs` is read-only.4---56# MFS — find / read across configured sources78## 1. What MFS is910A retrieval layer that exposes many kinds of content as a unified path11tree and makes that tree searchable through one hybrid index:1213- **One CLI (`mfs`), one mental model.** Local dir, Postgres, GitHub repo,14 Slack workspace, S3 bucket, BigQuery dataset — all addressed as paths15 under their `<scheme>://` URI. Same verbs everywhere: `ls / tree / cat /16 head / tail / grep / search / export`.17- **One hybrid index.** Dense vectors (semantic) + BM25 (keyword) fused18 per query — covers conceptual recall and exact-token recall in one call.19- **POSIX-style locators.** Every search hit carries a `locator` that20 reopens the exact unit: `{"lines":[s,e]}` for text/code, a PK dict for21 rows/issues/threads.2223## 2. When to use MFS — and when NOT to2425| Situation | Use MFS? |26|---|---|27| 1000+ files / rows / pages, you don't know where the answer is | ✅ |28| Cross-source question ("any past tickets / commits / RFCs about X") | ✅ `--all` |29| Concept-style query that won't match literally | ✅ `--mode semantic` |30| You already know the exact file + roughly where to look | ❌ plain `cat`/`grep` |31| Exact identifier / error code in 5 files you can list | ❌ plain `grep`/`rg` |32| Real-time tailing of a live log | ❌ index lags ingest |33| The source isn't in MFS yet | wrong skill, use `mfs-ingest` to register first |3435**Rule:** use the smallest tool that answers the question. MFS pays off36when the scope is too big for `rg`.3738**Borderline — ASK the user:**3940| Ask | Likely answer | Why |41|---|---|---|42| "Summarise these 10 PDFs" | ✅ `mfs search` + `cat --peek` per hit | each PDF gets a `converted_md` artifact + searchable chunks |43| "Find similar tickets to this one" | ✅ paste the ticket text as the search query | semantic over `row_text` does similarity matching |44| "Watch for new slack messages" | ❌ no `watch` capability; use Slack's API | index lags ingest |45| "Look up user 12345" | ❌ `mfs cat <source> --locator '{"id":12345}'` directly (skip search) | one-record-by-id doesn't need ranking |4647## 3. Pre-flight — confirm the source is indexed4849Before running any query, especially on cross-source asks:5051```bash52mfs status # server up? any connectors registered?53mfs connector inspect <uri> # this connector's object/job summary54mfs ls <uri> --json # per-entry capabilities + indexable / search_status55```5657- Server unreachable → tell user to start it (`mfs serve start` if58 self-hosted), or this skill can't proceed.59- `connectors` empty → user hasn't ingested anything yet. **Redirect to60 `mfs-ingest`** — don't try to search nothing.61- `search_status: unavailable` for the target URI → only `grep` / `ls` /62 `cat` work; offer those or redirect to `mfs-ingest` for a re-sync.63- `building` → sync in flight; fall back to `mfs grep` (works without an64 index) until done.65- `partial` → recall incomplete but usable; flag the caveat to the user.6667## 4. The core workflow: search → locate → browse6869```70 search locate browse71 ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────────┐72 │ semantic + BM25 │ → │ result has lines │ → │ cat --range / cat │73 │ finds candidates│ │ or a locator │ │ --peek to confirm │74 └──────────────────┘ └──────────────────┘ └─────────────────────┘75```7677On large corpora this loop is the whole point: read only the part that78matters. On small corpora it's still fine, just lighter.7980Concrete:81821. **Search:**83 ```bash84 mfs search "<what the user actually wants>" <path-or-uri> --top-k 1085 ```862. **Locate** — every hit's envelope carries `locator`:87 - text/code → `{"lines":[start,end]}` → `mfs cat <source> --range start:end`88 - structured (row/issue/thread) → PK dict → `mfs cat <source> --locator '{...}'`89 - once-per-object (dir/schema summary, image VLM) → `null` → `mfs cat <source>`903. **Browse** — verify only what's needed:91 ```bash92 mfs cat --peek <file> # outline (headings / function signatures)93 mfs cat --skim <file> # peek + one-line summaries per section94 mfs head -n 20 <uri> # first records of a structured object95 mfs tree <uri> -L 2 # subtree shape96 ```9798## 5. Index requirement rules of thumb99100- `mfs search` **requires** an index.101- `mfs grep` works **without** — pushdown → BM25 → linear scan fallback.102- `mfs ls / tree / cat / head / tail` browse **without** an index.103104## 6. Search modes105106`mfs search` defaults to `hybrid`. Override only when you know why.107108| `--mode` | Mechanic | When |109|---|---|---|110| **`hybrid`** *(default)* | dense + BM25 fused with RRF | almost always |111| `semantic` | dense only | conceptual query, wording won't match literally |112| `keyword` | BM25 only | exact-term (config key, error code) without semantic drift |113114Other useful flags:115116- `--top-k N` — default 10; raise to 20-30 on a weak first round.117- `--all` — search every registered connector. Otherwise scope to a path/URI prefix.118- `--kind <list>` — restrict chunk kinds (`row_text`, `thread_aggregate`,119 `body`, `summary`, `vlm_description`, …).120- `--collapse` — keep only the top-scoring chunk per object; later chunks121 from the same source are dropped, not merged. Recall stays as-is (the122 query still hits the same candidates), but the visible result count can123 fall below `--top-k` — collapse is a post-filter, not a re-rank. If you124 want N distinct objects, raise `--top-k` (e.g. `--top-k 30 --collapse`).125126### `--all`: when yes, when no127128- ✅ Cross-source recall — "any past tickets / commits / RFCs / slack about X".129- ❌ You know the source — scope to `slack://`; postgres + jira + docs together aren't comparable.130- ⚠ More than ~5 registered connectors — ASK the user whether to fan out131 widely or scope to the 2-3 likeliest sources first.132133## 7. Decision tree — pick the smallest useful tool134135| Signal in the ask | Sub-task | Use |136|---|---|---|137| natural-language question / sentence | exploratory | `mfs search "<q>" <scope>` |138| paraphrased / conceptual wording | semantic-only | `mfs search --mode semantic` |139| exact identifier / config key / unique phrase | literal anchor | `mfs grep "<lit>" <path>` (or `rg`) |140| filename / directory pattern | path lookup | `find` / shell glob / `fd` |141| known file + needs outline | structural overview | `mfs cat --peek <file>` |142| known file + compact summary | dense overview | `mfs cat --skim <file>` |143| search hit + surrounding context | reopen | `mfs cat <file> --range s:e` |144| structured hit (row/issue/thread) | reopen by PK | `mfs cat <source> --locator '{...}'` |145| several close candidates | compare | `mfs cat --peek` each, then pick |146| single record + known key | no-search lookup | `mfs cat <source> --locator '{"id":12}'` |147| first / last N | sample | `mfs head -n N` / `mfs tail -n N` |148| subtree shape | orient | `mfs tree -L 2 <uri>` |149| full object for offline tooling | export | `mfs export <uri> <file>` |150151`mfs search` requires an explicit scope or `--all`.152153## 8. Command cheat sheet154155### Search156157```bash158mfs search "<query>" <path-or-uri> # default: hybrid, top-k=10159mfs search "<query>" --all # whole namespace160mfs search "<query>" <path> --top-k 20 # more candidates161mfs search "<query>" <path> --mode semantic # dense-only162mfs search "<query>" <path> --mode keyword # BM25-only163mfs search "<query>" <path> --kind row_text # restrict chunk kinds164mfs search "<query>" <path> --collapse # keep top hit per object (post-filter, may return < top-k)165```166167### Grep168169```bash170mfs grep "<pattern>" <path> # pushdown -> BM25 -> linear171```172173**`mfs grep` is not `grep`.** The three-tier dispatch is:1741751. **Pushdown** — for structured connectors (`postgres`, `mongo`,176 `jira`, …) the pattern is shipped to the source as a LIKE / regex177 filter. Literal-exact, token-level; no regex on most structured178 connectors.1792. **BM25** over indexed objects — for `body`/`code`/`document` chunks180 already in Milvus, the pattern is fed through the same sparse181 index `search --mode keyword` uses. That is a tokenized,182 ranked lookup, **not** a literal substring scan: an analyzer split183 like `getUserId` → `get`, `user`, `id` will rank184 `userId` as a hit; a CJK pattern with no analyzer match returns185 nothing even when the literal bytes are present. If you need186 "does this exact byte string appear anywhere?", `mfs export` the187 object and run `rg` locally — `mfs grep` has no "force linear over188 indexed objects" flag.1893. **Linear scan** — only for not-indexed files in scope (file190 connector before `mfs add`). True substring / regex.191192For exact-exhaustive on a huge structured object, `mfs export` then193local `grep` / `rg`.194195### Read196197```bash198mfs cat <path> # full content (refused if "lazy")199mfs cat <path> --range A:B # lines A..B-1 (1-based, end-exclusive)200mfs cat <path> --locator '{"id":12}' # reopen a structured record201mfs cat <path> --peek # outline only202mfs cat <path> --skim # peek + per-section summaries203mfs cat <path> --meta # stat-style, not content204```205206Density ladder:207208| Mode | Use it when |209|---|---|210| `--peek` | "show me the outline" |211| `--skim` | + one-line summary per section, still concise |212| (default) | full content; small file or really need it |213| `--range A:B` | already know which lines matter (e.g. search hit) |214215```bash216mfs head -n 50 <path> # first 50 lines/records217mfs tail -n 50 <path> # last 50; native-accel reverse read218```219220For a lazy `rows.jsonl` / `messages.jsonl`, `head` is how to see record221shape without paying full-scan cost.222223### Browse224225```bash226mfs ls <uri> # one level227mfs tree <uri> -L 2 # depth-bounded recursive228```229230NOT a substitute for `search` when the target is unknown and conceptual.231232### Export233234```bash235mfs export <uri> <out-file> # full object to disk for jq/awk pipelines236```237238`cat` of a huge lazy object is refused — use `export` for bulk processing.239240### Status (useful before AND during search work)241242```bash243mfs status # server + all connectors244mfs connector inspect <uri> # one connector's object/job summary245mfs connector list # list registered connectors246mfs job list # recent indexing jobs (background re-syncs)247```248249Always prefer `--json` when output will be parsed.250251## 9. Weak results → recover, don't thrash252253If top hits look off-topic:2542551. **Rewrite** with synonyms / domain terms. ASK the user for the domain256 term they'd actually use if vague. One clarifier beats five blind queries.2572. **Raise `--top-k`** to compare distinct candidates.2583. **`mfs cat --peek`** the top few to compare structure.2594. **Switch mode** — semantic if hybrid was keyword-noisy; keyword if260 specific terms should be the anchor.2615. **Then** literal `grep` — only if the task has a real literal anchor262 (error code, config key, identifier).263264Literal search is a *different* tool, not a stronger version of semantic.265266## 10. Candidate selection267268Think at object level, not just chunk level:269270- **Merge** repeated hits from the same object into one candidate.271- **Compare** the top distinct candidates' `--peek` when titles or272 snippets look adjacent.273- **Prefer** an object whose main topic directly matches the request274 over a broad overview that mentions it.275- **Multi-part prompts** (two entities, setup + troubleshooting, migration276 source + target) — check whether more than one object is needed.277278```bash279mfs search "<query>" <path> --top-k 20280mfs cat --peek <candidate-a>281mfs cat --peek <candidate-b>282mfs cat <best> --range <start>:<end>283```284285## 11. Anti-patterns286287- **Don't grep to "confirm" a successful semantic hit.** The hit's288 snippet IS the source content; trust it.289- **Don't read a whole large file** when `--peek` / `--skim` / `--range`290 can answer.291- **Don't blindly pick rank #1** when #1-#3 are clearly different objects.292- **Don't stop at one match** if the prompt mentions multiple entities.293- **Don't search the same vague words after a weak first round** — fix294 the query or escalate to literal anchors.295- **Don't `cat` a lazy object** (DB `rows.jsonl`, SaaS `records.jsonl`,296 chat `messages.jsonl`). Use `head`, `--range`, `--locator`, or `export`.297- **Don't use MFS for sources you'd just clone/download anyway** — pull298 locally and use the `file` connector.299300## 12. When search returns nothing on a freshly indexed connector301302This is the most common diagnostic case. Walk this ladder, stop on first303hit:304305```bash306# 1. THIS connector's object/job summary307mfs connector inspect <uri>308309# 2. failed sync jobs?310mfs job list311mfs job show <job-id> # if any failed, read the job's error field312313# 3. per-object granularity314mfs ls <uri> --json # each entry's search_status315316# 4. JSON error code path317# If --json output carried a `code` field, see reference/error-codes.md318319# 5. otherwise treat as query-level — §9 above320```321322| Signal | Meaning | Action |323|---|---|---|324| `building` | sync in flight | wait, or `mfs grep` until done |325| `partial` | chunks dropped (`chunk_max` / `max_read_rows`) | usable but incomplete; user may want to re-ingest with raised caps (→ redirect to `mfs-ingest`) |326| `unavailable` | nothing indexed | only `grep` / `ls` / `cat` work; redirect to `mfs-ingest` |327| `available` but smoke search empty | wrong `text_fields` / source empty / wrong scope | check `reference/connectors/<scheme>.md` for that connector's shape; `mfs cat` a known object to confirm content |328329When the diagnosis points to "ingest config is wrong" or "needs re-sync330with different settings" — **don't try to fix it from this skill**. Tell331the user to invoke `mfs-ingest` for that connector.332333## 13. Reference routing334335These reference files are loaded ONLY when the situation matches — don't336open speculatively.337338- **[`reference/json-envelope.md`](reference/json-envelope.md)** —339 WHEN parsing a `--json` search/grep result and the `locator` shape is340 unfamiliar (composite PKs, `thread_ts`, nested keys); OR when uncertain341 how to feed a hit back into `mfs cat` (range vs locator dispatch).342343- **[`reference/error-codes.md`](reference/error-codes.md)** —344 WHEN an `mfs` command returned `--json` error output with a `code`345 field. Read the message first — don't open just because a command346 failed.347348- **`reference/connectors/<scheme>.md`** —349 WHEN searching a specific connector and you need its tree shape, record350 field semantics, locator format, or search-strategy tips. STOP and read351 the matching one BEFORE guessing how that source enumerates objects352 or what fields its records carry. Schemes: `file`, `web`, `s3`,353 `gdrive`, `postgres`, `mysql`, `snowflake`, `bigquery`, `mongo`,354 `github`, `jira`, `linear`, `hubspot`, `notion`,355 `zendesk`, `slack`, `discord`, `gmail`, `feishu`.356357Runtime capability for a specific URI is queried structurally via358`mfs ls <uri> --json`; the static per-connector references describe what359the connector exposes by design.