Files
Сергей Маринкевич 6f8648f7fa Run mattermost-search MCP over shared HTTP and fix FTS hangs.
Avoid per-agent stdio contention on the sqlite index, sort FTS by rank with created tie-break, and add mm_search_tag for dated #plan/#report substring search.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 15:05:01 +07:00

216 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: mattermost-search
description: |
Search Mattermost export dumps via mattermost-search MCP (mm_search_mcp.py):
message text, attachment names, attachment text content, filters by user/channel/team/date.
Use when the user asks to find messages, posts, or files in Mattermost exports,
mattermost-dl results, or the local sqlite index; or about daily #plan/#report in System/HW
(see everyday.md). Requires MCP server mattermost-search.
Index bypass (no_index, regex, CLI, grep) needs explicit user permission.
---
# Mattermost Search (MCP)
Search local Mattermost JSON exports produced by `mattermost-dl.py`. **Always use MCP
tools** (`mm_search`, `mm_search_tag`, `mm_resolve_user`, `mm_search_users`,
`mm_user_directory_refresh`, `mm_search_index_status`) as the default path.
## Mandatory rules
**Поиск в обход индекса и MCP — только с явного разрешения пользователя.**
Не делать самостоятельно, пока пользователь явно не попросил:
Всё про ежедневные планы или отчёты смотри: [everyday.md](everyday.md). Там и формат описан, и каналы.
My username: `sergey.marinkevich`. Resolve others via `mm_resolve_user(ref="Цисык")` →
`vladislav.tsisyk` (needs user directory — see below).
| Обход | Примеры |
|---|---|
| Индекс | `no_index=true`, `regex=true` (сканирует JSON), прямое чтение/grep по `results/` |
| MCP | запуск `mm_search.py`, `python mm_search.py …`, любой shell вместо MCP tools |
**Разрешено без спроса:** `mm_search` с индексом (`used_index: true`) — обычный
`search_mode="message"`, `file_name`, `file_content`.
**Если индекс недоступен или нужен regex/scan:** остановиться, сообщить пользователю
(нет `db_exists`, нужен `mm_index.py`, или требуется медленный обход JSON) и **дождаться
явного «да»** перед `no_index`, `regex`, CLI или чтением файлов экспорта.
## Prerequisites
- **`mattermost-search`** MCP server connected (`mm_search`, `mm_search_index_status` visible).
- Export JSON under `results/` (or `MM_SEARCH_ROOT`).
- SQLite index `mattermost_index.sqlite` (or `MM_SEARCH_DB`) for fast search and
**required** for attachment search. Build/update: `python mm_index.py`.
Prefer **one shared HTTP instance** (not stdio per agent). User systemd unit:
`mm-search-mcp.service` → `http://127.0.0.1:8766/mcp`. In `~/.cursor/mcp.json`:
```json
"mattermost-search": { "url": "http://127.0.0.1:8766/mcp" }
```
Server env (HTTP service / stdio `env`):
| Variable | Default |
|---|---|
| `MM_SEARCH_ROOT` | `results` |
| `MM_SEARCH_DB` | `mattermost_index.sqlite` |
| `MM_CONFIG_PATH` | `config.json` (for `mm_user_directory_refresh(from_api=true)`) |
| `MM_MCP_TRANSPORT` | `stdio` (`http` for shared server) |
| `MM_MCP_HOST` / `MM_MCP_PORT` | `127.0.0.1` / `8766` |
## Quick start
1. `mm_search_index_status()` without paramters — confirm `db_exists`, `root_exists`, `json_files`.
2. Pick **search_mode** (see table below).
3. `mm_search(query=…, …)` with filters; keep `limit` small (10–50) unless the user needs all hits.
4. Read `matches[].post.message`; for attachments also `matches[].attachment`.
## Tools
| Need | Tool |
|---|---|
| Search messages / attachments | `mm_search(query, …)` |
| `#plan` / `#report` in a date range | `mm_search_tag(keyword, after, before, …)` |
| Resolve display name → username | `mm_resolve_user(ref)` |
| Search users by name | `mm_search_users(query)` |
| Refresh user directory | `mm_user_directory_refresh(from_api=true)` |
| Check index & export layout | `mm_search_index_status(db, root)` |
User directory is stored in the sqlite DB (`users` table). Sources: `users.json` in export
root (written by `mattermost-dl.py`), Mattermost API (`from_api=true`), and distinct
usernames from indexed posts.
## Choose search_mode
| Goal | `search_mode` | Notes |
|---|---|---|
| Text in post body | `message` (default) | Uses FTS index; JSON scan only with user permission |
| Attachment filename | `file_name` | Index **required**; no `regex` |
| Text inside attachment | `file_content` | Index **required**; UTF-8 text files only |
| Regex in message | `message` + `regex=true` | **User permission required** — scans JSON, skips index |
| Tag `#plan` / `#report` by date | use `mm_search_tag` | Required `after`/`before`; substring `#keyword`; newest first |
## mm_search parameters
| Param | Role |
|---|---|
| `query` | Search string (or regex pattern) |
| `user` | Exact username filter |
| `channel` | Exact `display_name` or `name` |
| `team` | Exact team name |
| `after` / `before` | `YYYY-MM-DD` or ISO datetime |
| `limit` | Max matches (apply early — large exports) |
| `ignore_case` | Default: on for substring, off for regex |
| `no_index` | Force JSON file scan — **user permission required** |
| `output` | `json` (default) or `human` (CLI-style lines) |
| `show_file` | Prefix `file:idx:` in human output |
## Response shape (`output=json`)
```json
{
"count": 2,
"used_index": true,
"search_mode": "message",
"root": "results",
"db": "mattermost_index.sqlite",
"matches": [
{
"file": "results/20260605/user/channel.json",
"channel": "general",
"team": "myteam",
"post": {
"id": "…",
"idx": 42,
"created": "2026-01-15T10:00:00Z",
"username": "alice",
"message": "…",
"files": ["path/to/file.pdf"]
},
"attachment": {
"filename": "doc.txt",
"path": "…",
"is_text": true
}
}
]
}
```
`attachment` is present only for `file_name` / `file_content` hits.
## Common workflows
**Find what someone wrote about a topic**
```
mm_search(query="deploy rollback", user="alice", after="2026-01-01", limit=20)
```
**Find posts in a channel**
```
mm_search(query="incident", channel="alerts", limit=30)
```
**Resolve user, then search their posts**
```
mm_user_directory_refresh(from_api=true)
mm_resolve_user(ref="Цисык")
mm_search(query="#report", user="vladislav.tsisyk", channel="System/HW", limit=30)
```
**Check somebody report** (when username is known)
```
mm_search(query="#report", user="ivan.pavlov", channel="System/HW", limit=30)
```
**Find attachment by name**
```
mm_search(query="report.pdf", search_mode="file_name", limit=10)
```
**Regex over raw JSON (no index)** — только после явного разрешения пользователя
```
mm_search(query="ERROR-\\d+", regex=true, no_index=true, limit=50)
```
**Human-readable listing for the user**
```
mm_search(query="standup", output="human", show_file=true, limit=15)
```
## Pitfalls
- `file_name` / `file_content` + `regex=true` → error.
- Attachment search without index → error; run `mm_index.py` first.
- `channel` / `user` / `team` are **exact** matches, not substring — resolve display names
with `mm_resolve_user` first.
- User directory empty → run `mm_user_directory_refresh(from_api=true)` or re-export with
`mattermost-dl.py` (writes `users.json`).
- Token budget: prefer `limit`, narrow `after`/`before`, then widen if empty.
- If `count=0` with index: broaden `query`, drop filters, suggest `mm_index.py` — **не**
переключаться на `no_index` / `regex` / CLI без явного разрешения.
## Related CLI
Использовать **только по явной просьбе пользователя** (обход MCP):
| CLI | MCP equivalent |
|---|---|
| `mm_search.py QUERY` | `mm_search(query=QUERY)` |
| `--file-name` | `search_mode="file_name"` |
| `--file-content` | `search_mode="file_content"` |
| `--regex` | `regex=true` (permission required) |
| `--no-index` | `no_index=true` (permission required) |
| `mm_index.py` | not in MCP — предложить пользователю, не запускать самому |