MAX

Search query language

Documentation: v0.29.0

Every field, operator, preset and limit of MAX message search, with date rules, regular expressions and the JSON answer for scripts and agents.

The reference for queries of max messages search, max messages stats and saved searches. For everyday examples start with message search.

The language is a strict profile of Apache Lucene's query syntax: words, phrases, AND/OR/NOT, groups, fields, ranges, bounded wildcards and regular expressions. The full reference (in Russian) has the generated tables of fields, operators, presets and limits, and executable examples; the technical specification describes the grammar and the compiler.

Operators

OperatorExampleMeaning
wordsсчёт оплаченboth words
phrase"счёт оплачен"the words in this order
AND, &&alpha AND betaboth
OR, ||alpha OR betaeither
NOT, !, -alpha NOT betathe first without the second
++alpha OR betaalpha required, beta optional
group(alpha OR beta) gammabrackets set the order
field groupfrom:(alice OR bob)the field applies to each value
rangedate:[2026-01-01 TO 2026-02-01}[ ] include, { } exclude, * open
comparisonsize>10MB, date>=7dan open range
wildcardквартир*, т?кст* any characters, ? one
regextext:/pass(port)?/a bounded Lucene regular expression

alpha OR beta gamma means (alpha OR beta) AND gamma; alpha OR beta AND gamma means alpha OR (beta AND gamma). Use brackets for clarity. Lowercase and, or, not are plain words. A query with only NOT finds nothing: give a positive condition, for example kind:group NOT preset:secret. Fuzzy ~, proximity, boosts and intervals are refused with an error, not ignored.

Fields

FieldFindsExample
textmessage words (the default field)text:счёт
bodythe whole original text, case-sensitivebody:/.*счёт.*/
fromsender by name, @username or id; me means youfrom:"Алиса Тестова"
chatchat by title, @username or idchat:"Книжный клуб"
datewhen sentdate:today, date:7d, date:[2026-01-01 TO 2026-02-01}
kindchat kind: private, group, channel, saved, bot, service, unknownkind:private
hasattachment, link, file, photo, image, video, audio, voice, sticker, contact, location, pollhas:file
topicone discussion thread; needs one chatchat:"Книжный клуб" AND topic:42
inwhich accounts: provider or botsin:bots
presettext resembling a secret or contactpreset:secret
contentretained attachment textcontent:договор
filenamewhole attachment filenamefilename:*.pdf
mimeattachment type; MAX does not report itmime:image
sizeattachment size in bytes or 1,024-based KB/MB/GBsize>10MB
tagyour local tag on the message, its chat or sendertag:work

Field names are case-sensitive. An unknown field, value or combination is an error, never an empty answer and never plain text. A name that the archive does not know is not looked up on MAX.

kind:bot selects a chat with a bot; in:bots selects the archives of max bot. topic: requires exactly one chat in chat: or --chat: topic numbers repeat across chats. filename, mime and size match a message if at least one of its files matches. MAX does not report file types, so mime: finds nothing here: search by extension.

Presets

PresetA candidate is
passworda password label followed by a value
codea verification-code label and 4–8 digits
api-keyan API-key label and a value
secreta password, secret, token or API-key label and a value
card13–19 digits with optional spaces or hyphens
bankan IBAN-shaped value
passporta labelled passport value or a Russian 4+6 digit shape
phonea plus-prefixed international phone shape
emailan email-address shape
telegram-linka t.me or telegram.me link
urlan HTTP(S) link
contacta contact attachment, or an email or phone
locationa location attachment or a geo: link

A preset reports a candidate by its shape. It does not verify a password, a card or a document, and it can match something harmless. Do not delete or forward messages automatically on its word.

Dates

--timezone takes an IANA zone such as Europe/Madrid; without it, the computer's zone is used and returned in the answer. A date without a time is a whole calendar day. An inclusive upper day includes that whole day, an exclusive one excludes it; a day when clocks change can last 23 or 25 hours.

date:today and date:yesterday are calendar days. date:7d means from 7 days ago until now (also 30m, 2h); date>=7d and date:[30d TO 7d} work in comparisons and ranges, counted from the moment the query runs. An exact time is quoted, with seconds and an offset: date>="2026-01-01T10:00:00+02:00".

Words, wildcards and regular expressions

Before indexing and searching, text is converted to lower case and loses accents; ё becomes е, and й becomes и. Some different words therefore match: мой also finds «мои». Regex and wildcards on text: are normalized the same way.

text:/счёт/ matches the whole word «счёт», but not «счётом». body:/счёт/ matches only a message whose entire text is «счёт», case-sensitive; to find it anywhere, use body:/.*счёт.*/, and for the start of a sentence, body:/.*[Сс]чёт.*/. This is Lucene regular-expression syntax, without lookaround, backreferences, anchors or JavaScript flags.

On a large archive a short prefix such as к* can expand to more than 10,000 words and is refused; lengthen it. Long queries, deep nesting, large patterns and slow scans are refused with query_limit, not cut short: narrow the chat, the dates or the pattern.

The answer

--json returns { items, page, limit, hasMore, corrections, completeness, wordsReady, query, coverage }, even when nothing matched. --jsonl streams the items only.

  • query — the language version, the time zone and the order used.
  • coverage — which accounts and chats were searched. lastSyncedAt is the oldest time a chat in scope was fetched by store fetch, null if any chat never was. inventoryComplete means every account in scope has once listed all its chats; it does not promise a complete history.
  • completeness — per chat: whether its stored history reaches the start and has gaps.
  • wordsReady — whether the word index is complete. When it is false, a query with words fails with index_not_ready and the command that finishes it, max store migrate; a query without words runs.
  • hasMore is about the page, not about whether MAX holds more.

An error carries the position of the problem in the query and a hint.

In MCP

max_messages_search accepts the query as text or as a versioned syntax tree in ast (not both); language selects lucene or legacy, and timezone sets the calendar time zone. chat accepts an id or stored name; source, newest, context and limit work like the command options. record: false excludes the call from query history. The answer has the same fields as --json. max_messages_stats counts matches of the same queries.

The older modes

max messages search 'from:alice after:7d invoice -draft' --language legacy --json
max messages search --regex 'invoice\s+\d+' --json

--language legacy keeps the earlier filters and its correction of typos. --regex is a separate mode: a JavaScript regular expression, case-insensitive, over the full text, in an isolated worker with time and size limits. --regex cannot be combined with --language lucene.

LegacyStrict
after:2026-01-01date:[2026-01-01 TO *]
before:2026-02-01date:[* TO 2026-02-01}
after:7ddate:7d
automatic prefix and typo correctionквартир* explicitly; typos only in --language legacy

--thread follows the stored reply graph; in messages context it replaces chronological neighbours. Defaults are 8 hops, 50 messages, 65,536 bytes and one day around each hit. Change them with --thread-hops, --thread-messages, --thread-bytes, --thread-within. Without a graph it falls back to chronological context; stale links are marked and not traversed.

Search reads the local archive by default. --sync-first explicitly fetches new messages before searching and marks nothing read: at most 5 chats, 500 messages and 30 seconds. Change these bounds with --max-chats, --max-messages, --sync-time. Failed or incomplete refresh retains local results with stale coverage and refresh details.

MCP uses thread, thread_hops, thread_messages, thread_bytes, thread_within and sync_first. sync_first is exposed only with messages.sync-first: allow. Ordinary messages_context with offline: true reads stored messages.