Skip to content

Matrix & Attachments

Use these tools to work inside the active Matrix room and thread, send follow-up messages, manage thread tags, resolution, summaries, and model overrides, and reuse files that belong to the current conversation.

What This Page Covers

This page documents the built-in tools in the matrix-and-attachments group. Use these tools when you need to send or inspect Matrix messages, manage thread tags, resolution, summaries, or model overrides, or handle attachment IDs that are scoped to the current room and thread.

Tools On This Page

  • [matrix_message] - Send, read, edit, and react in Matrix conversations.
  • [matrix_room] - Inspect Matrix room metadata, available agents, members, thread roots, and room state.
  • [matrix_voice_message] - Generate speech from text and send it as a Matrix voice note.
  • [thread_tags] - Add, remove, and inspect shared tags on a Matrix thread.
  • [thread_resolution] - Explicitly resolve or reopen Matrix threads in the current room.
  • [thread_summary] - Set or update a Matrix thread summary from the current room and thread context.
  • [thread_model] - List models or show, switch, and reset the model override for the current Matrix thread.
  • [matrix_api] - Use a low-level Matrix event and state API with explicit room and event IDs.
  • [attachments] - List, inspect, and register context-scoped attachment IDs for later tool calls.

Common Setup Notes

These tools depend on the active ToolRuntimeContext, so they only work when an agent is running in a Matrix-connected conversation. matrix_message implies attachments and matrix_room through Config.IMPLIED_TOOLS, so enabling it makes both companion toolkits available even when you do not list them separately. Attachment IDs are context-scoped att_* values, and the runtime only exposes IDs from the current conversation plus any IDs registered during the current tool run. Current source in this worktree exposes matrix_message, matrix_room, matrix_voice_message, thread_tags, thread_resolution, thread_summary, thread_model, matrix_api, and attachments in this area.

[matrix_message]

matrix_message supports four actions: send, read, edit, and react. Sending and reading use the current conversation by default. Set recipient to an available agent or team name to request a response; without a recipient, text mentions do not dispatch agents. Set new_thread=True to start a separate conversation, an explicit thread_id to continue another thread, or thread_id="room" for the room timeline. Cross-room calls never inherit the origin room's thread. edit and react require the target message's event_id. read returns recent messages and edit options, with limit clamped to 1–50 and defaulting to 20.

Configuration

This tool has no tool-specific inline configuration fields.

agents:
  assistant:
    tools:
      - matrix_message

Example

matrix_room(action="room-info")
matrix_room(action="agents")
matrix_message(recipient="code", message="Review this export.", new_thread=True)
matrix_message(message="Here is the report.", attachments=["exports/report.csv", "att_chart"])
matrix_message(action="react", event_id="$event123", message="✅")

Notes

See Matrix Message Full Semantics for the complete argument schema, conversation selection, attachments, and collapsible sections. Use matrix_room(action="threads") for thread discovery and matrix_room(action="room-info") for current targeting metadata. attachments accepts up to five ordered context-scoped att_* IDs or file paths. Relative paths resolve from the agent workspace and must stay inside it. When sending to a recipient, all files arrive before the task text starts its response. For durable text-only retries, supply idempotency_key; the same requester, agent, room, and key reuse the first prepared payload and receipt for eight days after completion. Send results include the conversation thread_id and delivered event IDs, including partial delivery details on failure.

[matrix_room]

matrix_room provides read-only room introspection through matrix_room(action="room-info", room_id=None, limit=None, event_type=None, state_key=None, page_token=None).

What It Does

The supported actions are room-info, members, agents, threads, and state. room-info returns cached room metadata including name, topic, encryption status, membership count, join rule, canonical alias, version, guest access, creator, and a power-level summary. It also includes current thread_id, reply_to_event_id, requester_id, and agent_name; inspecting another room does not expose the origin thread. members returns joined users with display names, avatar URLs, and power levels. agents returns the agents currently eligible to answer this requester in the selected room as a sorted agents array with name, matrix_user_id, description, and thread_mode (thread or room). It includes the caller when eligible and applies current authorization, configured room scope, and live responder availability. These are conversation targets; the separate run_subagent tool describes the caller's allowed subagents. Pass each returned name as recipient in matrix_message to address that agent. threads returns paginated thread-root previews with sender, timestamp, reply count, and latest activity when available; it defaults limit to 20, clamps it from 1 through 50, and returns next_token plus has_more for pagination. state returns one exact state event when event_type is supplied, using an empty state_key by default. Without event_type, state returns a room-state summary with at most 100 non-member event previews and elides m.room.member events. room_id defaults to the active Matrix room. An alternate room is allowed only when the requester is authorized there under the configured room-access policy. The tool requires an active Matrix ToolRuntimeContext and rate-limits each (agent_name, requester_id, room_id) combination to 20 actions per 30 seconds.

Configuration

This tool has no tool-specific inline configuration fields. It can be listed explicitly, and matrix_message also enables it automatically through Config.IMPLIED_TOOLS.

Example

agents:
  assistant:
    tools:
      - matrix_room
matrix_room(action="room-info")
matrix_room(action="members")
matrix_room(action="agents")
matrix_room(action="threads", limit=10)
matrix_room(action="threads", page_token="next-page-token")
matrix_room(action="state", event_type="m.room.topic")

Notes

  • page_token applies to threads, while event_type and state_key apply to state.
  • This tool reads room data only; use matrix_message or matrix_api for supported writes.

[matrix_voice_message]

matrix_voice_message lets agents generate speech from text and send it as a Matrix voice message in one tool call.

What It Does

matrix_voice_message(text, room_id=None, thread_id=None, caption=None, companion_message=None) calls the configured text-to-speech endpoint and sends one Opus m.audio event with Matrix voice-note metadata. When both room_id and thread_id are omitted, it targets the active Matrix room and active thread. Pass thread_id="room" to force room-level delivery. Use caption for the audio event body and companion_message for a separate readable text event in the same target.

Configuration

The optional tool configuration fields are api_key, model, base_url, voice, and response_format. By default matrix_voice_message uses OpenAI text-to-speech through an explicit or stored credential, or OPENAI_API_KEY / OPENAI_API_KEY_FILE. Provider-prefixed model IDs such as hexgrad/kokoro-82m route through OpenRouter and use an explicit api_key or OPENROUTER_API_KEY; OpenRouter output is always requested as MP3. Set the base_url tool config (or the TTS_URL secret) to target any OpenAI-compatible speech endpoint instead, such as a local Kokoro server. When a non-OpenRouter base_url is configured without an explicit api_key, the tool sends a dummy API key so the real OpenAI credential never leaves the machine. base_url accepts a bare host, a /v1 URL, or a full /audio/speech endpoint URL, and full endpoint URLs are honored verbatim. Defaults: model=gpt-4o-mini-tts, voice=alloy, response_format=opus. response_format must be one of aac, flac, mp3, opus, or wav, matching what the endpoint returns. Generated audio is probed with ffprobe and, when not already Opus/Ogg, transcoded with ffmpeg into a Matrix voice payload with duration and waveform metadata. Non-opus response formats therefore require ffmpeg and ffprobe on the backend host; opus output still works without them but is sent without duration metadata.

Example

agents:
  assistant:
    tools:
      - matrix_voice_message
matrix_voice_message("Here is the quick audio version.")
matrix_voice_message(
    "The build finished successfully.",
    companion_message="The build finished successfully.",
)

Notes

  • The tool returns event_id for the voice event and companion_event_id when companion text was sent.
  • The tool rate-limits each (agent_name, requester_id, room_id) combination to six voice sends per 30 seconds.

[thread_tags]

thread_tags lets agents add, remove, and inspect shared thread tags using Matrix room state.

What It Does

thread_tags exposes tag_thread(), untag_thread(), and list_thread_tags(). All three operations default to the current room and active resolved thread context. When there is no active resolved thread context, pass thread_id explicitly. The tool normalizes the supplied event into the canonical thread root before reading or writing state. Tags are stored as com.mindroom.thread.tags room state. Each (thread_root_id, tag) pair uses its own state event, and the state key is the JSON array [thread_root_id, tag]. Writes fail unless both the running Matrix client and the human requester have enough power to send that state event in the target room. When the requester differs from the bot account, the requester must also be joined to the target room.

Configuration

This tool has no tool-specific inline configuration fields.

Example

agents:
  assistant:
    tools:
      - thread_tags
tag_thread("blocked")
untag_thread("blocked")
list_thread_tags(thread_id="$threadRootEvent")
list_thread_tags(exclude_tag="resolved", include_untagged=True)

Notes

  • This tool writes shared room state, so it is stricter than matrix_message about Matrix permissions.
  • Tag writes and removals return the updated canonical tag state for the target thread.
  • tag_thread(), untag_thread(), and every list_thread_tags() tag filter share one normalizer: valid canonical IDs up to 50 characters are preserved, while other free-form input is coerced to a short lowercase hyphenated tag.
  • resolved is lifecycle state rather than a topic tag, so tag_thread() and untag_thread() reject it.
  • Use the separately configured thread_resolution tool when an agent should be allowed to resolve or reopen threads.
  • list_thread_tags() can inspect the active thread or an explicitly provided thread_id.
  • list_thread_tags(include_tag=..., exclude_tag=...) filters which threads are returned: include_tag keeps only threads with that tag, exclude_tag removes threads with that tag.
  • Both filters can be combined.
  • For full filter semantics, see tools.
  • list_thread_tags(exclude_tag="resolved", include_untagged=True) lists unresolved room threads, including threads that have no tag state yet.
  • include_untagged=True forces a room-wide query and cannot be combined with thread_id.
  • It enumerates Matrix /threads and may stop at the 2000-root safety cap.
  • The response includes include_untagged: bool and truncated: bool.
  • Callers must check truncated before claiming the unresolved list is complete.
  • The tag_thread() description includes up to 20 of the current room's most-used tags that are short enough for model output, from a cache-stable daily snapshot, so agents prefer existing vocabulary.
  • The vocabulary is room-scoped, so tags from other rooms are never included.

[thread_resolution]

thread_resolution gives an agent explicit permission to resolve or reopen Matrix threads in the current room.

What It Does

thread_resolution exposes resolve_thread(thread_id=None) and reopen_thread(thread_id=None). Without thread_id, both functions require an active thread and target its canonical thread root. Pass a thread root or reply event ID to target another thread in the current room, including when calling from the room timeline. Explicit IDs are normalized to their canonical thread root, and unresolved targets return an error without changing any thread. resolve_thread() adds the resolved lifecycle tag, while reopen_thread() removes it.

Configuration

This tool has no tool-specific inline configuration fields. It is not included in starter configs or default tool sets, so ordinary agents cannot resolve threads. Operators may grant it directly, and agents explicitly granted self_config may add it to their own tool list.

Example

agents:
  triage:
    tools:
      - thread_tags
      - thread_resolution
resolve_thread()
reopen_thread()
resolve_thread(thread_id="$completed-thread:example.org")
reopen_thread(thread_id="$completed-thread:example.org")

To clear completed project threads, use list_thread_tags(exclude_tag="resolved", include_untagged=True) to find candidates, then call resolve_thread(thread_id=...) for each selected thread. Check the listing's truncated flag before treating it as complete.

Notes

  • Resolution uses the same com.mindroom.thread.tags state as thread-card filtering and does not restore the removed experimental resolution event type.
  • The generic thread_tags tool can still list and filter by resolved, but it cannot add or remove that lifecycle state.
  • Low-level Matrix state APIs remain separate broad capabilities and should only be granted to agents that need raw Matrix access.

[thread_summary]

thread_summary lets agents set or replace the current thread summary explicitly instead of waiting for the automatic summarizer.

What It Does

thread_summary exposes set_thread_summary(summary, thread_id=None, room_id=None, pin=True). The tool defaults to the active room and current resolved thread from ToolRuntimeContext. When there is no active resolved thread context, pass thread_id explicitly. The tool normalizes the target to the canonical thread root before sending a new m.notice summary event with io.mindroom.thread_summary metadata. Manual summaries are marked with model_name="manual" and pin the thread by default, which stops automatic summaries from overwriting the title. Pass pin=False to write a summary that later automatic summaries may replace; that also releases a thread pinned by an earlier call. A per-thread async lock prevents concurrent duplicate manual summaries from racing each other.

Configuration

This tool has no tool-specific inline configuration fields.

Example

agents:
  assistant:
    tools:
      - thread_summary
set_thread_summary("Decision: ship the current plan and revisit logs tomorrow.")
set_thread_summary("Routine status update.", pin=False)
set_thread_summary(
    "Summary for the import thread.",
    thread_id="$threadRoot",
    room_id="!ops:example.org",
)

Notes

  • summary must be a non-empty string up to 300 characters after whitespace normalization.
  • The tool writes a normal Matrix notice event, so the updated summary remains visible in the thread timeline.
  • Automatic thread summaries still exist, but this tool gives an agent an explicit override path when a human asks for a manual summary refresh.
  • Pin state lives in the summary notice's io.mindroom.thread_summary metadata, so it survives restarts and every runtime reads the same decision.
  • Pinning stops the whole automatic pass, not just the title. A thread pinned before its automatic topic tags are inferred will not receive them; tag it explicitly with thread_tags instead.
  • Automatic summaries are also skipped on threads carrying the resolved tag.

[thread_model]

thread_model lets agents list configured models or show, switch, and reset the model override for the current Matrix thread, mirroring the !model chat command.

What It Does

thread_model exposes list_models(), get_thread_model(), switch_thread_model(model_name, when), and reset_thread_model(). list_models returns every configured model alias with its provider and provider model ID and does not require an active thread. The other three functions require an active thread context and return an error outside a thread. switch_thread_model accepts a configured model name from the models: section of config.yaml and rejects unknown names with the available model list. Its optional when argument accepts after-toolcall or next-turn and defaults to next-turn. With after-toolcall, MindRoom rebuilds the current agent or team with the selected model and continues the same response after the tool call. With next-turn, the current response continues with the model it started with and the selected model begins on the next user turn. The override applies to all agents and teams in the thread and persists across restarts. get_thread_model returns the active override and the available model names. When a stored override names a model that has been removed from config.models, runtime resolution ignores it, and get_thread_model reports override: null plus a stale_override field instead of an active override. reset_thread_model removes the thread override so room-level model selection applies: an active runtime !room_model override, then configured room_models, then each entity's configured model.

Configuration

This tool has no tool-specific inline configuration fields.

Example

agents:
  assistant:
    tools:
      - thread_model
list_models()
get_thread_model()
switch_thread_model("opus", when="after-toolcall")
reset_thread_model()

Notes

  • The override is stored per thread root in mindroom_data/tracking/thread_models.json.
  • Users can manage the same override with the !model chat command; see Chat Commands.
  • An explicit active_model_name (for example a delegated child run) still beats the thread override, and the thread override beats the runtime !room_model choice, configured room_models, and the authored entity model.

[matrix_api]

matrix_api exposes a small low-level Matrix API surface for explicit room, event, and state operations, including room-scoped search.

What It Does

matrix_api supports send_event, get_state, put_state, redact, get_event, and search. It defaults room_id to the active room, but it also supports authorized cross-room access when the requester is allowed to act there. It never infers thread IDs, event IDs, or state keys from thread context, so callers must pass those identifiers explicitly for low-level operations. send_event, put_state, and redact are rate-limited per (agent_name, requester_id, room_id) and audited in logs. Dangerous state event types like m.room.power_levels and m.room.encryption are blocked by default. Pass allow_dangerous=true only when you intentionally want to change critical room state. Hard-blocked state event types like m.room.create remain blocked. search is read-only, scopes results to one room via room_id, uses the top-level limit parameter, and rejects filter.limit. When event_context={"include_profile": true} is requested, returned context preserves profile_info for matching senders.

Configuration

This tool has no tool-specific inline configuration fields.

Example

agents:
  assistant:
    tools:
      - matrix_api
matrix_api(action="get_event", event_id="$event123")
matrix_api(action="get_state", event_type="m.room.topic")
matrix_api(
    action="put_state",
    event_type="com.example.marker",
    state_key="status",
    content={"value": "ready"},
)
matrix_api(action="redact", event_id="$event123", reason="Cleanup")
matrix_api(
    action="search",
    search_term="deployment incident",
    keys=["content.body"],
    event_context={"before_limit": 1, "after_limit": 1, "include_profile": True},
)

Notes

  • Use this tool when you need exact Matrix event or state control rather than the higher-level matrix_message convenience actions.
  • Use action="search" when you need one-room full-text event search without falling back to homeserver-wide or ad-hoc history scans.
  • The tool returns structured JSON payloads for both success and error cases.
  • Because it is intentionally low-level, it requires explicit IDs instead of deriving them from reply or thread context.

[attachments]

attachments lets agents inspect and register files that are scoped to the current Matrix conversation.

What It Does

attachments exposes list_attachments(target=None), get_attachment(), and register_attachment(). list_attachments() returns the attachment IDs currently available in tool runtime context, the resolved metadata payloads, and any missing_attachment_ids. Pass a context-available attachment ID as target to return only that attachment; an ID outside the current context returns an error. get_attachment() returns a single attachment record, including the runtime-local path, when called with only an attachment ID. get_attachment(attachment_id, view=True) sends image, audio, video, or document content (including PDF) to the model, including local files and attachments from earlier in the conversation. Viewing requires a model and provider adapter that support the media type, and a readable, context-scoped file no larger than 20 MiB. Rejected media requests retry without the media and give the agent explicit guidance to use the attachment ID/path with other available tools; known adapter omissions receive the same guidance. It cannot be combined with mindroom_output_path. get_attachment(attachment_id, mindroom_output_path="relative/path") saves the attachment bytes into the agent workspace and returns a mindroom_tool_output save receipt with the saved path, byte count, binary format, and SHA256 digest. Use mindroom_output_path before handing attachments to worker-routed workspace tools such as file, coding, python, or shell, because the runtime-local path may not exist inside the worker workspace. In shell tools, the agent workspace is exposed as $MINDROOM_AGENT_WORKSPACE; in worker-routed shell and python tools it is also ~ and $HOME, so a saved path like incoming/file.txt can also be read as ~/incoming/file.txt. The path must be relative to the workspace and must not be empty, absolute, point at the workspace root, contain .. or NUL bytes, start with ~, or contain $ or % characters. register_attachment() turns a local file path into a new context-scoped att_* ID and appends that ID to the current runtime context so later tool calls in the same run can reuse it. Relative register_attachment() paths resolve from the agent workspace when one is available, and they must stay inside that workspace. Attachment records include kind, filename, MIME type, room ID, thread ID, sender, creation time, and an available flag that reports whether the local file still exists. This tool does not send files by itself, but its IDs can be passed to matrix_message for send.

Configuration

This tool has no tool-specific inline configuration fields.

Example

agents:
  assistant:
    tools:
      - attachments
list_attachments()
list_attachments(target="att_abc123")
get_attachment("att_abc123")
get_attachment("att_abc123", mindroom_output_path="incoming/plan.pdf")
register_attachment("incoming/plan.pdf")
matrix_message(message="Sharing the plan here.", attachments=["att_abc123"])

Notes

  • attachment_id values must be non-empty att_* IDs that are already present in the current tool runtime context.
  • Registering a new file attaches it to the current room_id and thread_id, which prevents accidental reuse across unrelated conversations.
  • For the full attachment lifecycle, media kinds, retention rules, and Matrix ingestion flow, use the dedicated Attachments guide.

Automatic thread summaries are still implemented in src/mindroom/thread_summary.py as bot runtime behavior. The summarizer posts one m.notice summary after a successful response brings a thread to the configured first threshold (one message by default), and then again every ten additional messages by default, using defaults.thread_summary_model or default. Set room_thread_summary_models to override the automatic summary model for a managed room alias or raw Matrix room ID. MindRoom uses defaults.thread_summary_temperature for automatic summaries when the provider supports runtime temperature overrides. MindRoom always uses provider temperature defaults for Vertex Claude, Claude Opus 5, Sonnet 5, Fable 5.1, and direct Google Gemini 3.8 Flash and Gemini 3.5 Flash-Lite summaries. The thread_summary tool complements that automatic behavior by letting an agent publish a manual summary immediately and advance the stored summary baseline. When no trusted prior summary exists, the first automatic summary is summary-only so a useful thread title appears early. The next scheduled refresh uses one structured model call to update the summary and produce up to three normalized topic tags, whether the prior summary was automatic or manual. The background task bypasses inherited per-turn history memoization so the model sees fresh authoritative full history including the delivered response. Existing tags win, including tags observed after the model call finishes. MindRoom serializes automatic and tool-driven tag mutations per thread within one running process, and persisted removal tombstones prevent a later automatic batch from repopulating a deliberately untagged thread. The initial tags use the same summary model, room override, temperature, prompt, lock, and background lifecycle as the refreshed summary. Expected Matrix write failures are isolated so a failed tag write does not block the summary and a failed summary write does not undo tags. Failed initial tag reads or all-failed tag writes leave the summary's durable enrichment marker incomplete, so the next summary threshold retries structured enrichment. Once existing tags, prior tag-state history, or newly written tags mark initial enrichment complete, later summary refreshes use a summary-only schema and never regenerate or replace tags. Each room has its own vocabulary snapshot built only from that room's tag state. The first successful post-response check after midnight in the configured timezone refreshes a stale room snapshot for the day. The refresh reads Matrix tag state and writes a durable local snapshot under mindroom_data/tracking/thread_tag_vocabulary/. Initial enrichment uses the summary call that would already run and can add up to three Matrix state writes.