Building a Mattermost Deployment Notifier Plugin: From Slash Command to Real‑Time UI
Create a Mattermost deployment notifier plugin that registers a slash command, streams status updates, and runs safely in the plugin sandbox. Follow the step‑by‑step guide, trade‑off analysis, and verification checklist.
05 May 2026, 17:07 UTC

Why a Deployment Notifier Plugin?
Teams often rely on a single channel to stay in the loop about CI/CD pipelines. A Mattermost slash command that triggers a deployment and pushes live status updates keeps everyone in sync without leaving the chat. This article walks you through the exact steps to create, register, and deploy that plugin, highlighting the key trade‑offs and how to verify each piece.
1️⃣ Problem Statement & Thesis
You want a lightweight, maintainable way to:
- Trigger a deployment from a channel using a slash command.
- Receive real‑time progress updates in the same channel.
- Keep the solution isolated from core Mattermost code.
Solution: Use Mattermost’s plugin framework. The server side runs Go, the client side can push UI components via WebSocket. The plugin is isolated in a sandbox but shares the server’s OS user, so secure coding is essential.
2️⃣ Plugin Skeleton
Start with the official SDK template. The folder layout looks like this:
deployment-notifier/
├─ cmd/
│ └─ deployment_notifier.go
├─ assets/
├─ vendor/
├─ config.json
└─ plugin.json
Key files:
plugin.json– metadata and entry points.config.json– optional configuration for the deployment endpoint.deployment_notifier.go– the Go code that implements the command and WebSocket push.
plugin.json
{
"id": "com.example.deployment_notifier",
"name": "Deployment Notifier",
"description": "Trigger deployments and stream status updates.",
"server": {
"main": "cmd/deployment_notifier.go",
"dependencies": ["github.com/mattermost/mattermost-server/v6/plugin"]
}
}
deployment_notifier.go
package main
import (
"encoding/json"
"net/http"
"github.com/mattermost/mattermost-server/v6/plugin"
)
// Server is the main plugin struct.
type Server struct {
plugin.MattermostPlugin
}
// OnActivate registers the slash command.
func (p *Server) OnActivate() error {
cmd := &plugin.Command{
Trigger: "deploy",
AutoComplete: true,
AutoCompleteDesc: "Trigger a deployment.",
DisplayName: "Deploy",
Description: "Starts a deployment and streams progress.",
}
return p.API.RegisterCommand(cmd)
}
// ExecuteCommand handles the /deploy slash command.
func (p *Server) ExecuteCommand(c *plugin.Context, cmd *plugin.Command, args string) (*plugin.CommandResponse, *plugin.AppError) {
// Call external deployment API (placeholder).
resp, err := http.Post("https://ci.example.com/api/deploy", "application/json", nil)
if err != nil {
return &plugin.CommandResponse{Text: "Deployment failed."}, nil
}
defer resp.Body.Close()
// Assume the API returns a job ID.
var payload struct{ JobID string }
json.NewDecoder(resp.Body).Decode(&payload)
// Push initial status.
p.API.SendEphemeralPost(c.UserId, &model.Post{ChannelId: c.ChannelId, Message: "Deployment started: " + payload.JobID})
// In a real implementation you would start a goroutine that polls for status
// and uses p.API.PublishWebSocketEvent to push updates.
return &plugin.CommandResponse{Text: "Deployment triggered."}, nil
}
3️⃣ Registering the Slash Command
The OnActivate hook registers a command with Mattermost. The JSON schema is simple: Trigger is the command name, and AutoComplete enables inline suggestions. After the plugin is activated, the command appears automatically in the UI.
Verification Steps
- Compile the plugin:
go build -o deployment_notifier.so cmd/deployment_notifier.go. - Package:
tar -czf deployment_notifier.tar.gz deployment_notifier.so plugin.json config.json. - Upload via the Mattermost Admin Console → Plugins → Upload Plugin.
- Restart the server (or enable auto‑reload if configured).
- In any channel, type
/deployand verify the response.
4️⃣ Real‑Time UI Updates
To stream progress, the plugin can publish WebSocket events. The client side (React) listens for these events and renders a status component. For simplicity, the example above just sends an initial post, but a production plugin would:
- Start a goroutine that polls the deployment API.
- Use
p.API.PublishWebSocketEventto senddeployment_statusevents. - Bundle a small React component that subscribes to the event stream and updates the UI.
Client‑Side Bundle
Place the React code in assets/ and include it in plugin.json:
"client": {
"main": "assets/index.js"
}
When the plugin is loaded, Mattermost injects the component into the channel view. Ensure the React version matches the server’s bundled React (currently 17.x for Mattermost 6.x). A mismatch can cause rendering failures.
5️⃣ Trade‑Offs & Limitations
- Sandboxing: Plugins run in the same OS user as the Mattermost process. A buggy plugin can consume resources or leak data. Always run
go vetand review logs. - API Version Drift: The SDK changes between Mattermost 6.x and 7.x. Verify the SDK package version (
github.com/mattermost/mattermost-server/v6/plugin) matches your server. - Client Bundling: Client components require a build step with the Mattermost build system. If you skip this, the UI will not load.
- Deployment Endpoint: The example uses a placeholder URL. In production, secure the endpoint with mutual TLS or an API key.
6️⃣ Actionable Checklist
- Clone the SDK repo and generate a new plugin skeleton.
- Implement the slash command and status push logic.
- Bundle a minimal React component to display progress.
- Package, upload, and restart the server.
- Test the slash command and verify real‑time updates.
- Run
go vetand inspectmattermost.logfor errors. - Document the plugin’s configuration and security considerations.
Conclusion
Mattermost’s plugin framework lets you extend the platform without touching core code. By following the steps above, you can deliver a deployment notifier that keeps your team informed in real time. Remember to respect the sandbox, keep an eye on API changes, and test thoroughly before rolling out to production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.