There is already a great project for an OpenCode chat plugin with Mattermost support called opencode-chat-bridge, so you may want to look there first to see if it provides what you need.
Because that already exists, you might be wondering: why create a standalone Mattermost plugin?
opencode-chat-bridge only works through ACP and we wanted to use Mattermost with a headless OpenCode server that would:
- Communicate only the final response back to the channel rather than spamming reasoning, tool calls, and updates which could cause hundreds of push notfications to devices. We just needed the summary after a session ended.
- Provide a mechanism for agents to send messages to channels as part of their workflows.
- Handle this through HTTP rather than ACP so we could attach OpenCode clients to the OpenCode server if we wanted to see the full session details and interact with the session through OpenCode.
That is the entire use case, so this a very tight plugin. It uses the sync prompt() because we only needed a summary posted in Mattermost when the session ended.
- Integrates with OpenCode's HTTP server
- Works with both the standalone internal HTTP server and
opencode serveandopencode web - Allows you to communicate through Mattermost, getting only the final response there, while being able to
opencode attachto the session for full detail and control
- Works with both the standalone internal HTTP server and
- Channel ↔ Session binding
- Each Mattermost channel gets its own isolated OpenCode session
- Session bindings persist to
~/.local/state/opencode/opencode-mattermost.json
- Slash commands
/new— Abort existing session, create new one, bind channel/stop— Abort the current session/agent <name>— Switch agent for channel's session
- Response chunking
- Responses over 3900 chars split at paragraph boundaries into threaded replies
- Typing indicator
typingevent fired immediately after beginning the response and every 5 seconds during prompt so you know if the bot is still working
- Bot tools for agents
mm-list-channels— List Mattermost channels (supportslimit)mm-send-message— Post a message to a channel by name
git clone https://github.com/<your-username>/opencode-mattermost.git
cd opencode-mattermost
bun install{
"plugin": ["opencode-mattermost@latest"]
}| Variable | Required | Default | Description |
|---|---|---|---|
MATTERMOST_URL |
yes | — | Mattermost server URL |
MATTERMOST_TOKEN |
yes | — | Bot user access token |
MATTERMOST_TEAM_ID |
no | — | Team ID to restrict channel listing to |
OPENCODE_LOG_LEVEL |
no | info |
Log level: debug, info, warn, error |
Config precedence: plugin options in opencode.jsonc > environment variables > .env file (first found wins).
- You can follow the instructions for setting up bot accounts here.
- Make sure you use the access token that is only displayed once during setup for the
MATTERMOST_TOKEN. - Add your bot to a dedicated channel for use with OpenCode. There is no need to use
@Mentions. The bot will respond to every post in the channel. - The bot will use the default OpenCode agent. You can set this at startup in the
opencode.jsoncwith thedefault_agentproperty. The property takes thenameof the agent. - You can change the agent in Mattermost with the
/agentcommand.
You can follow the standard setup for opencode serve, but you need to know about a quirk with plugins in headless mode. Plugins are lazy-loaded so if you are starting it up as a daemon or in a Docker container, you need to add a curl call to your startup script that will cause the plugin to load so you can interact with it through Mattermost without needing to attach an OpenCode client first.
You'll need this to load the plugin:
opencode serve &
curl -s http://localhost:4096/project/currentIn Docker, you'll want to add something like this to your entrypoint script since exec will kill the old shell:
(sleep 5 && curl -s http://localhost:4096/project/current) &
exec opencode serve --hostname=0.0.0.0 - In production you want to make sure there is a one-to-one relationship between MATTERMOST_TOKEN and OpenCode server, "server" meaning both the internal one in the standalone client and the headless server. If you install the plugin on the headless server and all of your OpenCode clients with the same token, the plugin will create multiple sessions when it receives a message in Mattermost and respond to each of them separately on the OpenCode servers bound to those sessions.
- If you send a message to any channel you have added your Mattermost bot to, they will always respond, so for this plugin to work properly you want to treat the channels with bots attached to the plugin as OpenCode sessions, rather than a human discussion board. If you use the channel for conversations, the bot will respond to every post.
- If a session is deleted in OpenCode the plugin session store will become stale and the bot will report an SDK error that it cannot find the session. Simply create a new session with the
/newcommand. - If you encounter an unknown session bug that
/newdoesn't correct, you can delete the session store file here:~/.local/state/opencode/opencode-mattermost.json. The plugin will create a new one at startup.
NOTE: Prefix a space in front of the slash command to bypass Mattermost's slash command validation
| Command | Description |
|---|---|
/new |
Abort existing session, create new one, bind channel |
/stop |
Abort the current session |
/agent <name> |
Switch agent for channel's session |
Unknown commands post: Unknown command: /<cmd>. Available: /new, /stop, /agent.
Two tools are exposed to OpenCode agents:
| Tool | Description |
|---|---|
mm-list-channels |
List Mattermost channels for the configured team (supports limit, default 20) |
mm-send-message |
Post a message to a Mattermost channel by name (searches up to 200 channels) |
- The log file for the plugin is found at
~/.local/share/opencode/opencode-mattermost.log - The log shares its levels with the OpenCode log level. You can set that in OpenCode with either the
--log-levelarg orOPENCODE_LOG_LEVELenvvar.
bun run build # Compile TypeScript to dist/
bun run bundle # Bundle with bun build for a single file bundle
bun clean # Remove build output, tarball and coverage reports
bun run deploy # Build, pack, and install to local OpenCode cache
bun all # Full cycle: clean → build → test → deploy → clean
bun dev # Watch mode for development
bun start # Run plugin directly (for debugging)
bun test # Run tests
bun lint # Lint source files with biome
bun coverage # Run tests with coverage report. This command requires `genhtml` to be installed. Run `bun test --coverage` for CLI reports.MIT