跳到主要内容

index-search

❖ Communityv0.1.0

Search one index you run and watch one saved query, with index_search, index_health and index_watch.

Open in Hermes Desktop
hermes plugins install index-search

What it adds

Tools 3

index_searchindex_healthindex_watch

Environment variables it needs 2

MEILI_HOSTMEILI_API_KEY

README

From the reviewed commit e3303f6 ↗; it updates when the author re-pins.

index-search

Search one index you run, and watch one saved query for new ids on the first page.

The product name is index-search. The toolset is index_search. The tools are index_search, index_health, and index_watch. There is no slash command and no CLI. The plugin does not ship a search engine, does not call /mcp, and does not register a hook. register does not import tests.

Tools are on for every Hermes surface, including the gateway, until you turn a surface off with hermes tools. There is no separate user list. Anyone you allow on the gateway can search with your key and can approve a watch. hermes tools does not disable a slash command, because this plugin has none.

index_search and index_health do not ask for approval. They are not refused under cron. schedule and unschedule ask every time. They are refused before the approval call under cron, yolo, approvals off, a single-query session, an unattended session (webhook, msgraph_webhook, api_server), when Hermes isolates this plugin, and when the cron session marker cannot be read. Answering "always" adds an unused line to config.yaml. Choose once. The schedule question names the index, the query, the id field, the delivery target, and that the watch creates a cron job every 15 minutes that calls the model. The unschedule question names the same fields and that the call removes that cron job and keeps seen ids. It does not say that it creates a job. When no watch is scheduled, unschedule does not ask and says there is nothing to unschedule. If that text does not fit in one message, it is not truncated. A question within 300 characters is still refused when html.escape of it, including quotes and apostrophes, is longer than 500 characters, because an HTML approval card cuts the reason there. It is not truncated. Every value the question shows, the index uid, the query, the id field, the delivery target, and the cron job id, is refused before approval when the approval text would rewrite it, and the sentence names the field that caused it. It is not described as too long. An underscore inside a word is not an emphasis mark, so movies_en_us, created_at_id, and a_b_c are accepted. A run at a word edge, such as _bold_, and the other marks, such as *x*, are refused.

What you configure

MEILI_HOST is required. It is an origin: http or https, a host, and an optional port. There is no default host. A userinfo, path, query, or fragment is refused before HTTP. Plain http is allowed. Over plain http, MEILI_API_KEY is sent unencrypted.

MEILI_API_KEY is sent only as Authorization: Bearer on POST /indexes/{uid}/search. Use a search-only key. A master or admin key in that variable is still sent with the search. GET /health sends no key, even when the variable is set. No other route is called. Redirects (301, 302, 303, 307, 308) are not followed. A proxy set in the environment is not used, so the key is not sent to it. A response body over 2,000,000 bytes is not accepted. An IPv6 host keeps its brackets.

index_search takes index, q, id_field, and an optional title_field. An empty or whitespace-only query is refused before HTTP, because the server treats an empty q as a match of every document. q is at most 400 characters. The index uid is 1 to 64 letters, digits, hyphens, or underscores. The reply is the id and, when you name title_field, a title of at most 200 characters. The rest of the hit is not returned. 1 and "1" stay distinct ids. You name the id field, because displayed attributes can hide the primary key. A hit without a usable id is not a successful page.

The search body sends limit 20. A successful reply includes estimatedTotalHits. Only the first 20 hits are watched. When that total is over 20, matches past the page are never reported. schedule says so, and a quiet watch reports it with notify true, starting with the baseline, so the warning reaches the delivery target instead of being dropped as a quiet round. While the total stays over 20 that quiet warning repeats once a day. A check that reports new ids includes the same warning every time. It starts again when the total falls to 20 or fewer and rises past 20 later. If the page shares no saved id and the total is over 20, the result says matches between them may have been missed. schedule itself does not search. A page whose hits array has more than 20 objects does not advance seen ids. Each read stops if no data arrives for 30 seconds. There is no total deadline for one run. The index uid must be a string. A number or a boolean is refused before HTTP and is not saved.

Watch

index_watch actions are schedule, unschedule, status, and check. One watch. schedule does not search. It stores the index, query, id field, optional title field, delivery target, and job id in the state file. A cron child reads that file. It does not read config_schema.

The job is an agent cron job, not no_agent. It runs every 15 minutes. There is no daily cap in this plugin. If you do not stop it, that is 96 runs a day. Each run is a model turn. A turn that calls a tool is another model request. Unschedule before you remove the plugin. After hermes plugins remove, the job stays until you run hermes cron remove <id>. When the profile is not default, put -p <name> before the subcommand: hermes -p <name> cron remove <id>.

Seen ids advance only when check is a proven cron run and Hermes is not isolating this plugin. The cron session marker is tools.approval_context._is_cron_approval_context (the session context is read before the process environment). A manual index_search, including one made from cron, does not write seen ids. A manual check does not write them either, and when it lists ids it says this call did not write seen ids. The plugin cannot force the cron model to call check, and it cannot force the model's wording. If the model calls index_search, the cursor does not move.

The first successful cron check saves a baseline and does not report that page as new. A manual check while that baseline is still pending does not save it, and it does not say that it did. Later checks report ids that are not in the seen set. 500 ids are kept. Older ids drop off and can look new again. Real 0 hits, an empty hits array with estimatedTotalHits 0, does not clear the seen set. An empty hits array with a larger total is an unusable page, and seen ids are not advanced. An empty body is an incomplete search, not zero hits. The first time a non-empty page becomes 0 hits, the result says This page has 0 hits. Seen ids were kept. Repeats stay quiet until the page is non-empty again. The result does not say there is nothing new.

A failed search is not a change to the index. The same failure is reported on the first check, when the cause changes, again after 24 hours, and once when the search works again. Argument errors are not counted as that failure. When the cron session marker cannot be read, or Hermes isolates this plugin (HERMES_PLUGIN_HOST_PROCESS set to a value other than empty or 0), check does not call the server, does not advance seen ids, and writes nothing. In that form a cron run and a hand-typed check look the same, so the failure clock is not moved: a check you run by hand cannot silence the next cron report. The result is reported on every check, which is every 15 minutes while the watch is scheduled, until the form is fixed. schedule stops before approval in that same case. unschedule stops before approval too, and says the cron job is still there. It prints that saved job id on a hermes cron remove line, and puts -p <name> before the subcommand when the profile is not default. An unset value is normal on Hermes v0.21.4, and search and health still run.

Changing the index, query, or id field asks for a new baseline on the next successful check. A failed schedule does not clear seen ids. Changing only the title field or the delivery target keeps seen ids. A delivery-only change names the previous target. unschedule while nothing is scheduled does not ask for approval and does not delete the state file. Before status, unschedule, and check, a stored job id is read with cron.jobs.get_job. If that job is gone, the result says the job is gone, clears the stored job id, and does not say the watch is scheduled or that this call removed it. If remove_job returns false while the job was still found, the result says the removal was not confirmed, keeps the stored job id, and does not say the job was removed. The next status still says a watch is scheduled.

Delivery may be origin, local, all, a platform this Hermes already knows, or platform:chat_id. cli, cron, api_server, and bot-chat are refused. An empty delivery target does not fall back to local. Schedule words are refused. The known-platform list is whatever the running Hermes reports. It is not copied into this plugin. The words origin, local, and all are stored in lowercase. A platform name is stored as given. When delivery is origin, the job is given the session origin Hermes captures for its own cron tool (tools.cronjob_job_args._origin_from_env). If that helper cannot be loaded, no job is created. If it returns no origin, the job is still created and the result says it uses the home channel when one is set, and nowhere when it is not. That job does not return to this session.

When the active Hermes profile is not default, the printed hermes cron list, status, and remove lines put -p <name> before the subcommand. There is no cron create line. A successful unschedule prints only cron list and cron status, because the job it names is already gone. If the profile name cannot be read, the result says profile unknown and does not print a default-profile command. This plugin does not take -p or --profile as its own argument.

Where state is stored

<HERMES_HOME>/plugin-data/index-search until you delete that directory. There is no fallback to ~/.hermes. Writes go through Hermes's write check with the destination path only, then an atomic replace. If that check cannot be loaded, nothing is written. If a cron check reads a page and the watch file cannot be written, seen ids are not advanced, and the result says the page was read but the file was not written. If schedule cannot write the file, the new cron job is removed and the result says so. It does not say the search failed. If unschedule removes the job and then cannot update the file, the result says the job was removed and the file still says a watch is scheduled. A corrupt state file is not overwritten. The API key is not written to the state file, the URL, or the error text. The tool result also drops the key, including the form JSON uses when the key contains a quote or a backslash, so a hit id or title that contains the key is not returned.

The job prompt tells the model that a quiet check, notify false, must reply with exactly [SILENT].

This plugin reads these Hermes names, which are not a published tool API. If one cannot be loaded, raises, or no longer has that name, the watch fails closed:

  • plugins.plugin_storage.plugin_data_dir
  • tools.approval_context._is_cron_approval_context
  • tools.approval_context._get_approval_mode
  • tools.approval_context._is_single_query_approval_context
  • tools.approval_context._is_unattended_platform_approval_context
  • tools.approval._yolo_active
  • tools.approval.request_tool_approval
  • agent.file_safety.get_write_denied_error
  • cron.jobs.create_job
  • cron.jobs.remove_job
  • cron.jobs.get_job
  • tools.cronjob_job_args._origin_from_env
  • hermes_cli.profiles.get_active_profile_name
  • cron.scheduler_delivery._is_known_delivery_platform

Imports are flat only when __package__ is empty. Tests ship beside the plugin and are not loaded as the plugin.

Server behavior that was checked

Checked on 2026-10-08 against a local Community Edition image getmeili/meilisearch:v1.12 (package 1.12.8). That build's license is MIT. Enterprise sharding is not used. This plugin does not ship the engine. Your server is not described as MIT.

On that build, a search did not enqueue a task. An empty q matched every document. Displayed attributes can omit the primary key from hits. A missing key was HTTP 401. A bad key was HTTP 403. A missing index was HTTP 404. The default keys observed were two (search, and admin), not four.

The server can send anonymous telemetry until the operator turns it off. On the tested 1.12.8 image, an empty MEILI_NO_ANALYTICS value failed to start the process, because --no-analytics required a value. Setting MEILI_NO_ANALYTICS=true disabled anonymous telemetry. This plugin does not set that variable. If the host bills for search, those calls spend that account. A self-hosted Community Edition has no fee from this plugin.

What was not tested

  • index_watch was called through real Hermes cron runs with a stand-in model on 0.21.4 and current main. index_search and index_health were called through Hermes's tool dispatch only. No live model chose any of them in a chat.
  • Meilisearch Cloud was not used. If that host bills for search, the account is the host's.
  • Builds other than the image above, Enterprise binaries, a mustRestart health body, and the four default keys described in the current docs were not observed here.
  • Attribute names are refused only when they contain ., /, or a control character. The server's full attribute grammar was not copied.
  • The plugin cannot stop a cron model from calling index_search.
  • The plugin does not turn engine telemetry off.

manifest_version is 2. requires_hermes is >=0.21.4. The plugin version is 0.1.0. The license of this plugin is MIT. Dependencies are the Python standard library only.

doc-markdown❖ Community★ 0

Convert one local file to Markdown with doc_convert on Linux and macOS, built on Microsoft's MarkItDown. Microsoft and MarkItDown are trademarks of the Microsoft group of companies. Disclosure — the doc_markdown toolset is on by default on every surface, including a gateway, so anyone you allow there can call doc_convert; stop it with hermes tools. It does not fetch pages. Unattended sessions still convert when Hermes calls the tool. This plugin creates no cron job. A child process runs MarkItDown with no Hermes keys in its environment; on macOS the plugin runs ps about every 10 ms to watch that child's memory. It stops 8 seconds after its converters load, at 60 seconds if loading never finishes, or above 922746880 bytes. It is not a sandbox: after realpath it may read any allowlisted path it can open. Unpublished Hermes code locates the Hermes home, and a failed check refuses the path. Removal deletes the plugin directory, not the markitdown package. Windows was not tested.

Tools
event-read❖ Community

Read one CalDAV calendar collection you run, with list_events and read_event. Disclosure — Sends CALDAV_USER and CALDAV_PASSWORD as Basic auth to your CALDAV_URL, does not follow redirects, and starts no local program. Over plain http that password travels unencrypted. The tools are on for every surface, including gateways, and the plugin has no user list of its own: anyone you allow on your Hermes gateway reads the calendar with your password. Turn a surface off with hermes tools. A watch is a cron job you create yourself with hermes cron create. Each run is at least one model turn, there is no daily cap, and the job stays until you run hermes cron remove. Under plugins.isolation: host the watch cannot run and posts a notice saying so on every run. When the watch can run, it reports a problem on the first run, when the cause changes and again after 23 hours, or on every run when its data directory cannot be written. Each request gives up after 20 seconds without data, and a whole call has no time limit. The watch relies on private parts of Hermes, so an update can stop it. State lives in plugin-data/event-read. Not tested against a vendor-hosted calendar.

Tools
ja-writing-guard❖ Community★ 0

Rule-based proofreading for Japanese business text: finds and fixes AI-style phrasing (stock phrases, monotonous sentence endings, overused symbols and Markdown, English-calque grammar) with line, column and a fix hint. It checks wording, not authorship, and rarely flags plain model output. A transform_llm_output hook checks every final Japanese answer and only reports by default; opt-in enforce mode rewrites flagged answers with the user's own model and delivers the rewrite only if numbers, names, URLs, code and negation survive and the score drops under the threshold. Disclosure: no network calls of its own; enforce mode sends each flagged answer to the user's configured model (one extra call, a second only when the first rewrite fails the checks or stays over the threshold; extra cost and up to 25 s delay); a read-only post_tool_call hook notes turns with MEDIA: lines so attachments from other plugins are kept; reports stay in process memory on local surfaces only. Disclosure — no network calls of its own; mode report (default) only logs scores; opt-in mode enforce sends each flagged final answer (with finding excerpts) to the user's configured model via ctx.llm (1–3 calls, up to 25 s delay) and replaces the delivered and stored answer when its preservation checks pass, which do not prove meaning is unchanged; reads plugins.hook_callback_timeout via a private read-only Hermes helper; eval/ dev scripts ship in the tree but are never loaded.

Tools
jp-address❖ Community★ 0

Splits one Japanese address into prefecture, city, town and the remainder with address_split, built on Geolonia's address data (CC BY 4.0, from the Digital Agency address base registry). Disclosure — Each call sends at most two HTTPS GET requests, with no API key, to japanese-addresses-v2.geoloniamaps.com or the JP_ADDRESS_BASE_URL you set (plain http only for 127.0.0.1 and localhost). The matched prefecture and municipality names go into the URL path; the house number, the lot and the building name are not sent. The tool is on for every surface, including gateways, and asks for no approval: anyone you allow on your Hermes gateway can call it. Turn a surface off with hermes tools. It writes no files and creates no cron job. A point can be coarser than the town and is not a rooftop geocode, and an address it cannot place for certain comes back as ambiguous or at the municipality instead of a guess. The publisher may change or stop the free host and suggests self-hosting for commercial use. Not tested from a Hermes model.

Tools
jp-charts❖ Community★ 0

Bar, line, stacked and 100% band charts as phone-sized PNGs for chat replies and reports, with Japanese text, 万/億 units and a required unit and source line. Every bar, point, label and tick is read back from the finished figure and compared with the parsed data before the image is returned. Disclosure: no network calls at run time; reads a local .csv/.tsv/.txt/.json only when given its path (never hidden files or the Hermes home); writes PNGs to the Hermes image cache; a transform_llm_output hook adds a forgotten MEDIA line to replies on chat platforms and cron deliveries (setting auto_attach, on by default).

Tools
jp-chatwork❖ Community★ 0

Talk to Hermes from Chatwork rooms, built on the Chatwork API: put Hermes in a Chatwork room and ask it with [To:] or a reply, like a colleague; the answer comes back as a Chatwork reply. Polls the Chatwork API (no public URL or webhook), never answers a message twice across restarts, and while CHATWORK_ALLOWED_USERS is unset only the token's own account can use Hermes commands beyond /help, /whoami, /new and /reset or answer approval prompts. Disclosure: talks only to api.chatwork.com with your own API token; polls every 5 s by default while the gateway runs; stores read positions and the ids of its posted messages in <HERMES_HOME>/plugin-data/jp-chatwork/state.json. Disclosure — while CHATWORK_ALLOWED_USERS is unset everyone in the rooms listed in CHATWORK_ROOMS (external guests included) can prompt the agent and use its enabled tools, with Hermes commands and approval answers kept to the token's own account; talks only to api.chatwork.com with your API token, polling every 5 s while the gateway runs; stores read positions and posted message ids in <HERMES_HOME>/plugin-data/jp-chatwork/state.json; logs each asker's name and account id.

Platforms

← Back to the catalog · catalog built Oct 13, 2026