Skip to content

Channel automation additions#558

Open
BenCookie95 wants to merge 155 commits intomasterfrom
automation-demo
Open

Channel automation additions#558
BenCookie95 wants to merge 155 commits intomasterfrom
automation-demo

Conversation

@BenCookie95
Copy link
Copy Markdown
Contributor

@BenCookie95 BenCookie95 commented Mar 13, 2026

Summary

  • Added a new bridge function for other plugins to discover tools that are available to the requesting user. Useful for plugins if they are going to use the new bridge completion functionality. It won't return embedded mm mcp server tools unless a userID is passed.
  • Added a new field to the completion bridge endpoints called allowed_tools. This allows completion calls to use from tools from a pre-defined list. This list is checked at runtime each time to ensure the requesting user actually permission to use those tools. You can service account tools if you don't pass a requesting userID
  • Added MCP tools to interact with the new automation plugin. You can create, modify, delete and list automations. These tools are only added to context in channels where you are a channel admin or if you are in a DM with Matty. They are filtered out for other users in order to not pollute context.
  • Added a new ListAgents tool so that Matty can see what agents are available on the server when we are creating automations. These Agent IDs are needed at automation creation time.

Ticket Link

https://mattermost.atlassian.net/browse/MM-67712

Screenshots

Release Note

Allow tool execution through bridge and add mcp tools for managing automations

Summary by CodeRabbit

Release Notes

  • New Features

    • Added tool allowlist support for agent completions—control which tools agents can execute
    • Added endpoint to discover eligible tools for a given agent
    • Added agent discovery and listing capabilities
    • Added channel automation management—create, list, update, and delete automations
  • Bug Fixes

    • Improved input validation and error handling across bridge endpoints
    • Enhanced tool eligibility filtering based on user permissions
  • Documentation

    • Updated bridge client documentation with tool allowlist usage and agent tools discovery

cursoragent and others added 30 commits February 14, 2026 19:08
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
…iscovery

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
@dryrunsecurity
Copy link
Copy Markdown

dryrunsecurity bot commented Mar 18, 2026

DryRun Security

This pull request introduces a critical prompt-injection risk in the automation AI action: user-provided trigger data is interpolated directly into AI prompt/system_prompt fields via Go text/template, letting anyone who can post in a monitored channel inject instructions that may cause the AI—running with the automation creator's permissions and allowed tools—to read private data or perform unauthorized actions. This should be treated as a high-risk security issue and mitigated (e.g., by sanitizing or separating user content from system prompts, restricting tool scopes, or changing how prompts are constructed).

🔴 Prompt Injection in Automation Actions in mcpserver/tools/automations.go (drs_a4fb1ddb)
Vulnerability Prompt Injection in Automation Actions
Description The automation system allows users to define AI prompts using Go text/template syntax, which interpolates trigger data (like .Trigger.Post.Message) directly into the prompt or system_prompt field of an ai_prompt action. Because the AI agent can be granted sensitive tools via allowed_tools and executes using the automation creator's permissions, an attacker who can post a message in a monitored channel can inject instructions into the AI agent's context. This allows the attacker to potentially manipulate the agent into misusing its granted tools, such as reading private data or performing unauthorized actions within the Mattermost instance, as the agent may fail to distinguish between system instructions and injected user input.

result.WriteString(fmt.Sprintf(", prompt=%s", a.AIPrompt.Prompt))
}
if a.AIPrompt.SystemPrompt != "" {
result.WriteString(fmt.Sprintf(", system_prompt=%s", a.AIPrompt.SystemPrompt))


Comment to provide feedback on these findings.

Report false positive: @dryrunsecurity fp [FINDING ID] [FEEDBACK]
Report low-impact: @dryrunsecurity nit [FINDING ID] [FEEDBACK]

Example: @dryrunsecurity fp drs_90eda195 This code is not user-facing

All finding details can be found in the DryRun Security Dashboard.

@BenCookie95
Copy link
Copy Markdown
Contributor Author

@coderabbitai resolve

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 18, 2026

✅ Actions performed

Comments resolved and changes approved.

@BenCookie95 BenCookie95 requested a review from crspeller March 18, 2026 23:26
@BenCookie95 BenCookie95 changed the title WIP: Automation POC Channel automation additions Mar 18, 2026
mcpTools, mcpErrors := b.mcpToolProvider.GetToolsForUser(userID)

// Filter out automation tools for users without channel admin (or sysadmin) permission
showAutomation := shouldShowAutomationTools(b.pluginAPI, userID, c.Channel)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't feel like it belongs here. Shouldn't these be permission filtered out on an MCP server level?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could add a layer to the mcp server to filter stuff like this but it would mean passing down channel and team info through the embedded context. We already have this stuff available right here so I took the easier route. I think we can remove this filtering once we add lazy loading of tools? Which would make this addition temporary.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still want to filter out tools that you don't even have permission to use, even from search. Let's go ahead and pass whatever we need to the MCP server to make this determination. This also means that we can make this determination for external MCP users as well as this solution currently does not cover those.

// isAutomationPluginInstalled probes the channel automation plugin API to check if
// the plugin is installed and reachable. Returns true if the plugin responds (even
// with an auth error), false if it 404s or is unreachable.
func (p *MattermostToolProvider) isAutomationPluginInstalled() bool {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we implement a way for plugins to register their own MCP tools instead of having these here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will have to come later after the initial release. The goal is to remove this as soon as we have plugin registered tools

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security Review — No Actionable Findings

I reviewed the full diff focusing on the areas outlined in the review checklist. Here is a summary of the analysis:

Bridge tool execution (new allowed_tools feature):

  • The bridge remains behind interPluginAuthorizationRequired middleware; only trusted plugins can invoke it.
  • allowed_tools is correctly rejected on service endpoints and only accepted on agent endpoints.
  • Tool scoping is enforced server-side: requested tools must exist in the agent's eligible tool set, tools-disabled agents reject the request, and empty/blank tool names are rejected.
  • WithAutoRunTools is applied only when the calling plugin explicitly provides an allowed_tools list, preserving the default tools-disabled behavior for bridge requests without it.
  • User/channel ID validation (ValidateID) is applied consistently via middleware and request-body checks across all endpoints.

Automation tools (new create_automation, update_automation, delete_automation, list_automations):

  • All CRUD operations forward the user's own Bearer token to the automation plugin API via doAutomationRequest, so the automation plugin enforces per-user and per-channel authorization.
  • shouldShowAutomationTools restricts visibility in public/private channels to channel admins and sysadmins, defaulting to visible in DMs/groups where the backend enforces per-channel checks.
  • The isAutomationPluginInstalled probe correctly uses an unauthenticated request (checking route existence only) and doesn't leak credentials.

Agent discovery (list_agents, handleGetAgentTools):

  • Both are read-only discovery endpoints behind inter-plugin auth. User permission filtering is applied when user_id is supplied.

File access in bridge posts:

  • convertBridgePostsToInternal uses server-level file access, which is pre-existing behavior unchanged by this PR. The bridge contract places permission responsibility on the calling plugin.

Reviewed and found adequate: ID validation, auth token scoping, tool store isolation, service vs. agent endpoint separation, WithToolsDisabled placement, automation tool filtering middleware, and prompt injection mitigations (user profile field sanitization was already in place).

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

@BenCookie95 BenCookie95 requested a review from crspeller March 27, 2026 20:56
// with an auth error), false if it 404s or is unreachable.
func (p *MattermostToolProvider) isAutomationPluginInstalled() bool {
reqURL := p.automationAPIURL("/flows")
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using some client4 doRequestWithHeaders function before but that changed in the latest release where it automatically prepends <serverURL>/api/v4 so it no longer works for us with plugins


// Add middleware to dynamically filter automation tools from tools/list
// when the channel automation plugin is not installed.
mcpServer.AddReceivingMiddleware(p.automationToolFilterMiddleware())
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved into a middleware to check for plugin existence when an agent requests a tool list. Previously if the automation plugin was uploaded after the agents plugin started up, we would never find the automation tools. We are caching the 'plugin installed` flow for 30 mins so we don't make redundant requests

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security Review — No Actionable Findings

Re-reviewed the updated diff covering the new allowed_tools bridge feature, automation CRUD tools, agent discovery, and related refactors. All prior conclusions remain valid; no new vulnerabilities introduced.

Key controls verified:

  • Bridge auth boundary intact: All new endpoints (/agents/:agent/tools, agent completion with allowed_tools) remain behind interPluginAuthorizationRequired. The calling plugin owns user permission verification per the bridge contract.
  • Tool scoping enforced server-side: prepareAgentBridgeCompletion validates each requested tool exists in the agent's eligible set, rejects tools-disabled agents, and rejects empty/blank tool names. WithToolsDisabled() is correctly applied when no allowed_tools is provided, and explicitly on both service endpoints.
  • WithToolsDisabled() removal from convertRequestToLLMOptions is safe: Coverage is maintained — agent path handles it in prepareAgentBridgeCompletion (line 302-303), and service streaming/non-streaming handlers append it explicitly (lines 728, 808).
  • Automation tools delegate auth correctly: All CRUD operations in toolCreateAutomation, toolUpdateAutomation, toolDeleteAutomation, and toolListAutomations forward the user's own Bearer token via doAutomationRequest. The automation plugin enforces per-user and per-channel authorization. shouldShowAutomationTools gates tool visibility to channel admins/sysadmins in public/private channels.
  • isAutomationPluginInstalled probe: Uses an unauthenticated request to check route existence only. mmServerURL is server-configured, not user-controlled. No SSRF vector.
  • list_agents tool: Uses the user's auth token to call /ai_bots, which is behind MattermostAuthorizationRequired. Properly scoped.
  • Input validation consistent: ValidateID applied via middleware (validateAgentParam, validateUserIDQuery) and in request body parsing (validateCompletionRequestIDs). url.QueryEscape used for channel_id query parameter in automation list.
  • toolCreatePostAsUser URL change: mmInternalServerURL was consolidated into mmServerURL which still resolves to the internal URL when configured. No behavioral change.
Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

Resolve merge conflicts in api/api_test.go and add missing fmt import
in llm/stream.go.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review — No Actionable Findings

Re-reviewed the updated diff covering the allowed_tools bridge feature, automation CRUD tools, agent/tool discovery endpoints, and associated refactors. No medium, high, or critical vulnerabilities identified.

Controls verified:

  • Inter-plugin auth boundary: All new bridge endpoints (/agents/:agent/tools, agent completion with allowed_tools) remain behind interPluginAuthorizationRequired. Tool scoping and execution trust follows the established bridge contract where the calling plugin owns user permission verification.
  • Server-side tool scoping: prepareAgentBridgeCompletion validates every requested tool against the agent's eligible set, rejects agents with tools disabled, rejects empty/blank tool names via normalizeAllowedTools, and applies WithToolsDisabled() when no allowed_tools is provided. Service endpoints explicitly reject allowed_tools and append WithToolsDisabled().
  • WithToolsDisabled() removal from convertRequestToLLMOptions is safe: The agent path handles it in prepareAgentBridgeCompletion (lines 302-303), and both service handlers append it explicitly (lines 728, 808).
  • Automation tool auth delegation: All CRUD operations (toolCreateAutomation, toolUpdateAutomation, toolDeleteAutomation, toolListAutomations) forward the user's own Bearer token via doAutomationRequest. The automation plugin enforces per-user and per-channel authorization server-side.
  • Automation tool visibility gating: shouldShowAutomationTools restricts tool visibility to sysadmins and channel admins in public/private channels. The channel == nil case (DMs, bridge requests) returns true by design — the automation plugin backend enforces per-channel permissions at execution time.
  • isAutomationPluginInstalled probe: Uses server-configured mmServerURL (not user-controlled) with an unauthenticated request to check route existence only. No SSRF vector.
  • list_agents tool: Uses the user's auth token to call /ai_bots, which is behind MattermostAuthorizationRequired. Properly scoped.
  • Input validation: ValidateID applied consistently via middleware (validateAgentParam, validateUserIDQuery) and request-body checks (validateCompletionRequestIDs). url.QueryEscape used for channel_id query parameter.
  • mmServerURL consolidation: mmInternalServerURL merged into mmServerURL which still resolves to the internal URL when configured. The toolCreatePostAsUser URL change is functionally equivalent.
  • No secrets exposure: No hardcoded credentials, tokens, or keys in the diff. Test tokens are isolated to test helpers.
Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

mcpTools, mcpErrors := b.mcpToolProvider.GetToolsForUser(userID)

// Filter out automation tools for users without channel admin (or sysadmin) permission
showAutomation := shouldShowAutomationTools(b.pluginAPI, userID, c.Channel)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still want to filter out tools that you don't even have permission to use, even from search. Let's go ahead and pass whatever we need to the MCP server to make this determination. This also means that we can make this determination for external MCP users as well as this solution currently does not cover those.

Name: "list_agents",
Description: `List all available AI agents (bots). Returns each agent's ID, display name, and username.
Use this tool to discover valid provider_id values for the ai_prompt action type when creating automations.
The agent ID (26-character Mattermost user ID) is what you pass as config.provider_id with provider_type "agent".`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this last line makes sense. Is it specific to some automatons tool? If so it should be with that.

var result strings.Builder
result.WriteString(fmt.Sprintf("Found %d agent(s):\n\n", len(bots)))

for i, a := range bots {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All formatting should now be done in the format package. Can you move this there?
The idea is to reference standardization so we always display entities in the same way.

// isAutomationPluginAvailable checks if the automation plugin is installed,
// caching positive results for automationCacheTTL to avoid repeated probes.
// Negative results are never cached so newly installed plugins are detected immediately.
func (p *MattermostToolProvider) isAutomationPluginAvailable() bool {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems over-complicated specifically since the common case of the plugin not being installed is not cached. In the ultimate design where we fetch the tools from the plugin we are going to be making a request anyway. Why the complexity here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants