Background
PR #30916 migrated the Mattermost adapter to a bundled plugin (gateway/platforms/mattermost.py → plugins/platforms/mattermost/adapter.py). During the migration we left three behavior / surface gaps as explicit follow-ups rather than expanding the PR's scope. Filing here so they're tracked.
The migration itself is behavior-preserving (6220/6220 tests pass with zero regressions). These follow-ups are real but non-blocking enhancements.
Follow-up 1 — Batch multi-file uploads on POST /api/v4/files
Status: Mattermost's POST /api/v4/files endpoint accepts multiple files in a single multipart form-data POST, but both the live adapter (MattermostAdapter._upload_file, plugins/platforms/mattermost/adapter.py:166) and the standalone sender (_standalone_send, ~line 920) currently upload one file per POST in a loop.
Impact: For multi-file cron deliveries or agent send_message calls that include multiple attachments, this is N round trips instead of 1. Latency-bound.
Why this is mattermost-specific:
- Discord's
POST /channels/{id}/messages actually accepts only one attachment per request — Discord can't batch.
- Mattermost's
POST /api/v4/files accepts multiple files form fields (API ref) — Mattermost can.
Proposed change:
- In
_upload_file and _standalone_send, collect all files into one aiohttp.FormData() (multiple files fields) and POST once.
- The response's
file_infos[] array already returns one entry per uploaded file in order — just collect every id instead of [0]["id"].
- Preserve the existing per-file error-recovery semantics: if any one file fails (e.g. exceeds Mattermost's max file size), the current code logs and continues. Batched-upload semantics should match (i.e. catch + retry remaining files in a smaller batch on partial failure, or accept all-or-nothing if simpler).
Sizing: Small — ~40 LoC change in two places, with tests in tests/gateway/test_mattermost.py for the live path and tests/tools/test_send_message_missing_platforms.py for the standalone path.
Follow-up 2 — Surface MATTERMOST_PROXY in plugin.yaml optional_env
Status: PR #30916 added MATTERMOST_PROXY support to _standalone_send (via gateway.platforms.base.resolve_proxy_url(platform_env_var="MATTERMOST_PROXY")), but the env var is not declared in plugin.yaml under optional_env. Users won't see it in hermes setup or hermes plugins inspect mattermost, even though the code reads it.
Impact: Discoverability — users on corporate networks needing a proxy will not know the env var exists unless they read the source.
Proposed change:
# plugins/platforms/mattermost/plugin.yaml — add to optional_env:
- name: MATTERMOST_PROXY
description: "HTTP(S) proxy URL for outbound Mattermost API calls (cron / standalone delivery)."
prompt: "Mattermost proxy URL"
password: false
Sizing: Trivial — one YAML block.
Follow-up 3 — Honor MATTERMOST_PROXY in the live adapter's aiohttp.ClientSession
Status: MattermostAdapter.connect() creates its aiohttp.ClientSession at plugins/platforms/mattermost/adapter.py:203 without any proxy configuration. Only the standalone sender (_standalone_send) honors MATTERMOST_PROXY. This is asymmetric — the in-process live adapter (used while the gateway is running) silently ignores the proxy env var that the cron/standalone path respects.
Impact: Users behind a corporate proxy who configure MATTERMOST_PROXY will see cron deliveries succeed but live gateway messages fail with a connection error. Confusing.
Why this happens: When MattermostAdapter was originally written, proxy support wasn't wired in. The standalone path got proxy support during this PR because it's the path Discord/Teams plugins established the pattern on. The live adapter retained the older session-construction pattern.
Proposed change:
# plugins/platforms/mattermost/adapter.py — in MattermostAdapter.connect()
# around line 203:
from gateway.platforms.base import resolve_proxy_url, proxy_kwargs_for_aiohttp
_proxy = resolve_proxy_url(platform_env_var="MATTERMOST_PROXY")
_sess_kw, self._proxy_req_kw = proxy_kwargs_for_aiohttp(_proxy)
self._session = aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=30),
**_sess_kw,
)
# Then thread self._proxy_req_kw into every _api_get / _api_post / _api_delete
# request kwargs so per-request proxy options apply.
Sizing: Medium — needs careful threading of _proxy_req_kw through every session.get/post call site in the adapter (~10 sites). Compare to gateway/platforms/discord/adapter.py which already does this for the in-process path; mirror that pattern.
Test plan: Add a unit test that constructs MattermostAdapter with MATTERMOST_PROXY=http://proxy.example and asserts the aiohttp.ClientSession is built with the right connector (or _req_kw carries proxy= on calls).
Related issues
Coordination
Wait for #30916 to merge before opening fix PRs for any of these.
cc @teknium1
Background
PR #30916 migrated the Mattermost adapter to a bundled plugin (
gateway/platforms/mattermost.py→plugins/platforms/mattermost/adapter.py). During the migration we left three behavior / surface gaps as explicit follow-ups rather than expanding the PR's scope. Filing here so they're tracked.The migration itself is behavior-preserving (6220/6220 tests pass with zero regressions). These follow-ups are real but non-blocking enhancements.
Follow-up 1 — Batch multi-file uploads on
POST /api/v4/filesStatus: Mattermost's
POST /api/v4/filesendpoint accepts multiple files in a single multipart form-data POST, but both the live adapter (MattermostAdapter._upload_file,plugins/platforms/mattermost/adapter.py:166) and the standalone sender (_standalone_send, ~line 920) currently upload one file per POST in a loop.Impact: For multi-file cron deliveries or agent
send_messagecalls that include multiple attachments, this is N round trips instead of 1. Latency-bound.Why this is mattermost-specific:
POST /channels/{id}/messagesactually accepts only one attachment per request — Discord can't batch.POST /api/v4/filesaccepts multiplefilesform fields (API ref) — Mattermost can.Proposed change:
_upload_fileand_standalone_send, collect all files into oneaiohttp.FormData()(multiplefilesfields) and POST once.file_infos[]array already returns one entry per uploaded file in order — just collect everyidinstead of[0]["id"].Sizing: Small — ~40 LoC change in two places, with tests in
tests/gateway/test_mattermost.pyfor the live path andtests/tools/test_send_message_missing_platforms.pyfor the standalone path.Follow-up 2 — Surface
MATTERMOST_PROXYinplugin.yamloptional_envStatus: PR #30916 added
MATTERMOST_PROXYsupport to_standalone_send(viagateway.platforms.base.resolve_proxy_url(platform_env_var="MATTERMOST_PROXY")), but the env var is not declared inplugin.yamlunderoptional_env. Users won't see it inhermes setuporhermes plugins inspect mattermost, even though the code reads it.Impact: Discoverability — users on corporate networks needing a proxy will not know the env var exists unless they read the source.
Proposed change:
Sizing: Trivial — one YAML block.
Follow-up 3 — Honor
MATTERMOST_PROXYin the live adapter'saiohttp.ClientSessionStatus:
MattermostAdapter.connect()creates itsaiohttp.ClientSessionatplugins/platforms/mattermost/adapter.py:203without any proxy configuration. Only the standalone sender (_standalone_send) honorsMATTERMOST_PROXY. This is asymmetric — the in-process live adapter (used while the gateway is running) silently ignores the proxy env var that the cron/standalone path respects.Impact: Users behind a corporate proxy who configure
MATTERMOST_PROXYwill see cron deliveries succeed but live gateway messages fail with a connection error. Confusing.Why this happens: When
MattermostAdapterwas originally written, proxy support wasn't wired in. The standalone path got proxy support during this PR because it's the path Discord/Teams plugins established the pattern on. The live adapter retained the older session-construction pattern.Proposed change:
Sizing: Medium — needs careful threading of
_proxy_req_kwthrough everysession.get/postcall site in the adapter (~10 sites). Compare togateway/platforms/discord/adapter.pywhich already does this for the in-process path; mirror that pattern.Test plan: Add a unit test that constructs
MattermostAdapterwithMATTERMOST_PROXY=http://proxy.exampleand asserts theaiohttp.ClientSessionis built with the rightconnector(or_req_kwcarriesproxy=on calls).Related issues
MattermostAdapter._upload_file. Worth verifying against the reporter's environment after refactor(gateway): migrate Mattermost adapter to bundled plugin #30916 lands.apply_yaml_config_fnfor mattermost partially addresses this (YAML keys now flow through to env vars), butis_connectedandcheck_fnstill gate on the env vars. Needs a separate audit of allis_connectedhooks across plugin platforms to read fromPlatformConfig.token/.extrafirst.Coordination
Wait for #30916 to merge before opening fix PRs for any of these.
cc @teknium1