Skip to content

MCP server: DM thread replies, safer attachments, image reads, and message priority - #888

Open
chesterXalan wants to merge 4 commits into
mattermost:masterfrom
chesterXalan:fix/mcp-usability
Open

MCP server: DM thread replies, safer attachments, image reads, and message priority#888
chesterXalan wants to merge 4 commits into
mattermost:masterfrom
chesterXalan:fix/mcp-usability

Conversation

@chesterXalan

@chesterXalan chesterXalan commented Jul 17, 2026

Copy link
Copy Markdown

Summary

A set of usability fixes for the standalone MCP server, found while running it as a stdio server against a Mattermost instance without the plugin installed. Happy to split this into smaller PRs if preferred.

Thread replies in DMs and group messages

  • dm and group_message now accept an optional root_id to reply in an existing thread. Previously there was no way to reply to a DM thread at all (see below).
  • Reply targets are resolved to the actual thread root (resolveThreadRoot, also applied to create_post): models frequently pass the ID of the latest post in a thread instead of the root, which the server would reject.

create_post no longer dead-ends on DM/GM channels

  • DM and group channels have no display name and no team, so the display-name context check could never pass (channel_display_name requires minLength=1 while the channel's display name is "", and GetTeam("") errors). The check now only applies to regular team channels; transparency validation for team channels is unchanged.

Attachment handling

  • Upload failures now abort the post instead of silently posting the message without its files. Previously a failed upload still created the post, so retries left a trail of file-less messages.
  • New optional MM_MCP_ATTACHMENT_ROOTS environment variable (path-list of directories): absolute attachment paths are allowed when they fall under one of the configured roots, each opened via os.Root so symlinks cannot escape. Default behavior (data directory only) is unchanged when the variable is unset.

read_file improvements

  • Images (JPEG/PNG/GIF/WebP, up to 5 MB) are returned as inline MCP image content via a new optional RichResolver on MCPTool; larger images return metadata plus a hint to use get_file_link.
  • When the plugin's extraction endpoint is unreachable (e.g. a standalone server pointed at a Mattermost without the plugin), read_file falls back to reading plain-text files directly through the user's client, with the same rune-windowed paging. Document extraction (PDF/Office) still requires the plugin.
  • format.WritePost now lists file attachments as metadata (name, type, size, File ID), so File IDs are discoverable from read_post/read_channel/search output without an extra get_post_files call.

Message priority

  • create_post, dm, and group_message accept priority (important/urgent) and request_ack. Both are rejected with a clear error on thread replies (Mattermost only supports priority on roots) before any post is created. request_ack on an unlicensed server surfaces the server's license error without creating the post.
QA test steps

Run the standalone server over stdio against any Mattermost instance (mattermost-mcp-server -s <url> -t <token>), then via an MCP client:

  1. dm yourself a message; dm again with root_id set to the returned post ID → reply lands in the thread (also works passing a non-root post ID from the thread).
  2. create_post into a DM channel with channel_display_name: "Direct Message", team_display_name: "N/A" → posts successfully (previously always failed validation).
  3. Start the server with MM_MCP_ATTACHMENT_ROOTS=/some/dir and attach /some/dir/file.png → uploads; attach a path outside the roots → tool errors and no post is created.
  4. Post an image, read_post → attachment metadata with File ID; read_file that ID → inline image content. read_file a .md/.txt attachment on a server without the plugin → text content.
  5. dm with priority: "important" → post carries the priority label; retry with root_id set → rejected without posting.

Unit tests added for the new helpers and behaviors (mcpserver/tools, format), table-driven per the repo conventions.

Ticket Link

None — found while using the standalone MCP server. Happy to file issues for each item if that helps triage.

Release Note

Added root_id thread-reply support to the dm and group_message MCP tools. Fixed create_post always failing display-name validation on DM and group channels. MCP attachment upload failures now abort the post instead of posting without files. Added the MM_MCP_ATTACHMENT_ROOTS environment variable to allow absolute-path attachments from configured directories on the standalone MCP server. read_file now returns images as inline image content and reads plain-text files directly when the plugin extraction endpoint is unavailable. read_post and read_channel now list file attachment metadata including File IDs. Added priority and request_ack parameters to create_post, dm, and group_message MCP tools.

Summary by CodeRabbit

  • New Features

    • Added post priority and request_ack support for new messages, including threaded DM and group messages.
    • Enhanced read_file to return richer output with inline image previews and ranged text, plus sensible fallbacks.
    • Added secure local attachment reading restricted to configured absolute path roots.
    • Post rendering now includes an “Attached files” section with metadata (or file IDs when metadata is unavailable).
  • Bug Fixes

    • Attachment upload failures now abort post creation and return clear errors; remote attachment uploads now fail fast.

…t handling

- dm/group_message accept root_id to reply in existing threads; reply
  targets resolve to the actual thread root so any post ID in the
  thread works (create_post too)
- create_post no longer dead-ends on DM/GM channels: the display-name
  context check only applies to regular team channels (DM channels have
  no display name and no team, so validation could never pass)
- absolute attachment paths can be allowed via MM_MCP_ATTACHMENT_ROOTS
  (path-list of directories, opened through os.Root so symlinks cannot
  escape); relative paths still resolve inside the data directory
- attachment upload failures now abort the post instead of silently
  posting the message without its files
- add an optional RichResolver to MCPTool so tools can return non-text
  MCP content blocks; read_file now returns JPEG/PNG/GIF/WebP
  attachments as inline image content (5 MB cap, metadata-only reply
  above it)
- when the plugin's extraction endpoint is unreachable (e.g. a
  standalone server pointed at a Mattermost without the plugin),
  read_file falls back to reading plain-text files directly through
  the user's client with the same rune-windowed paging
- WritePost lists file attachments as metadata (name, type, size,
  File ID) so models can discover File IDs from read_post/read_channel
  and fetch content on demand via read_file
…sage

Adds priority ('important'/'urgent') and request_ack arguments. Priority
metadata is only valid on thread roots, so replies with priority are
rejected before posting; request_ack requires a licensed server and
surfaces the server's license error without creating the post.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 844f8a74-0c9d-4e2e-9240-945552aed452

📥 Commits

Reviewing files that changed from the base of the PR and between acf3104 and d9131c5.

📒 Files selected for processing (6)
  • format/format.go
  • format/format_test.go
  • mcpserver/tools/files.go
  • mcpserver/tools/files_test.go
  • mcpserver/tools/posts.go
  • mcpserver/tools/posts_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • format/format.go
  • format/format_test.go
  • mcpserver/tools/files.go
  • mcpserver/tools/posts_test.go
  • mcpserver/tools/posts.go

📝 Walkthrough

Walkthrough

The changes constrain absolute-path attachment reads, abort post creation on upload failures, add rich read_file responses, format attached files, and support priority, acknowledgement, and thread-root options across post creation tools.

Changes

Attachment handling

Layer / File(s) Summary
Attachment access and upload failures
mcpserver/tools/file_utils.go, mcpserver/tools/file_utils_test.go
Absolute attachment paths are restricted to configured roots, while upload failures return errors that prevent post creation.
Attached-file output
format/format.go, format/format_test.go
Post output includes attached file metadata or file IDs, with coverage for both representations.

Rich file reading

Layer / File(s) Summary
Rich resolver plumbing
mcpserver/tools/provider.go
MCP tools can return typed rich content blocks through RichResolver, with plain-text resolver fallback.
read_file content paths
mcpserver/tools/files.go, mcpserver/tools/files_test.go
read_file returns inline images or text from extraction and direct-download paths, with coverage for access errors, oversized images, and binary files.

Post messaging metadata

Layer / File(s) Summary
Post arguments and validation helpers
mcpserver/tools/posts.go, mcpserver/tools/posts_test.go
Post tools accept priority, acknowledgement, and thread-root options, with validation and root-resolution helpers.
Post creation flows
mcpserver/tools/posts.go, mcpserver/tools/posts_test.go
Channel, direct-message, group-message, and user-post creation apply the new metadata and handle attachment errors and channel validation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant MCPToolProvider
  participant PostTool
  participant FileUtils
  participant MattermostAPI
  Caller->>MCPToolProvider: invoke post tool
  MCPToolProvider->>PostTool: decode arguments
  PostTool->>MattermostAPI: resolve thread root
  PostTool->>FileUtils: upload attachments
  FileUtils-->>PostTool: file IDs or error
  PostTool->>MattermostAPI: create post with metadata
  MattermostAPI-->>PostTool: created post
  PostTool-->>MCPToolProvider: formatted result
  MCPToolProvider-->>Caller: tool response
Loading

Possibly related PRs

Suggested labels: Setup Cloud Test Server

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main MCP server changes: threaded DM replies, attachment handling, image reads, and message priority.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@format/format.go`:
- Around line 172-188: Route all new attachment-facing text through the existing
i18n extraction mechanism: update the attachment heading and metadata templates
in format/format.go, the absolute-path rejection guidance at
mcpserver/tools/file_utils.go lines 147-147, and the upload failure, retry
guidance, and success status at lines 335-347. Preserve the current formatting
and behavior while replacing hardcoded user-facing strings with localized
messages.
- Around line 175-187: Update the attached-files formatting logic to track
whether at least one non-nil metadata entry was rendered; if none were rendered,
fall back to entry.Post.FileIds instead of leaving an empty section. Preserve
the existing metadata output for usable files and the bare-ID output for valid
FileIds.

In `@mcpserver/tools/file_utils.go`:
- Around line 339-344: Update the uploadFilesForLocal/CreatePost flow to clean
up any file IDs uploaded before a later attachment failure. Track the
successfully uploaded IDs and delete them when uploadFilesForLocal returns an
error, before returning the existing post-not-created error; preserve the
current error messaging and successful upload behavior.

In `@mcpserver/tools/files.go`:
- Around line 166-168: Change the ErrForbidden branch in the file resolver to
return nil with svcErr (or a wrapped error) so permission denial is reported as
a tool error rather than successful content. Update
mcpserver/tools/files_test.go lines 119-124 to assert the resolver returns an
error instead of permission-denial text content.
- Line 34: Route every new tool-facing string through the repository’s i18n
extraction mechanism: extract the read_file description in
mcpserver/tools/files.go:34, argument-decoding and rich-result errors in
mcpserver/tools/provider.go:67-75 and 314-324, standalone-read errors in
mcpserver/tools/files.go:108-122, file-info/image-error/limit/metadata output in
mcpserver/tools/files.go:125-154, and permission/fallback errors in
mcpserver/tools/files.go:161-176; preserve the existing messages and behavior
while replacing hardcoded strings with localized lookups.
- Line 122: Update the file-content return path around files.Slice to pass the
uploaded data as raw text without applying strings.TrimSpace, preserving leading
and trailing characters so paging offsets remain aligned with the original file.
- Around line 169-176: Update the fallback flow around the file content service
error and readFileContentStandalone: only invoke direct reading when the file’s
MIME type is text-like, and return the original extraction service error for
non-text documents instead of allowing them to be reported as having no
extractable text. Preserve the existing forbidden-service handling and
successful text fallback behavior.
- Around line 117-122: Update the standalone text download fallback around
GetFile to enforce a size limit before materializing the full attachment, or use
ranged/streamed reads so only the requested offset and limit are loaded.
Preserve the existing files.Slice result semantics while preventing unbounded
memory usage and avoid unnecessary full-buffer/string copies.

In `@mcpserver/tools/posts.go`:
- Around line 414-430: Reorder the post creation flow in the affected handlers,
including the paths using toolDM and toolGroupMessage: construct the model.Post
and call applyPostPriority before uploadFilesAndUrlsForLocal, then assign the
returned fileIDs to post.FileIds only after a successful upload. Preserve
existing error returns and add regression coverage confirming invalid
priority/acknowledgement combinations perform no upload.
- Around line 439-440: Update the success response in the post-creation flow to
format the created channel, team, and post using the existing format/ package
helpers before interpolation, rather than directly using channelDisplayName,
teamDisplayName, and createdPost.Id in fmt.Sprintf. Preserve the current
response structure and attachmentMessage handling.
- Around line 28-29: Replace the newly hardcoded user-facing strings in the post
tool schema fields, tool descriptions, validation errors, and DM/group labels
with the repository’s established i18n extraction mechanism. Update the affected
symbols around the schema definitions and the logic near the referenced
validation and label sections, preserving the existing messages and behavior
while ensuring every tool-facing string is sourced through i18n.

In `@mcpserver/tools/provider.go`:
- Around line 314-331: Instrument the RichResolver execution in the mcpTool
handler by starting a span from ctx with the repository’s standard telemetry
helper and attaching the standard tool attribute keys. Ensure the span is ended
on every return path, and record resolver errors on the span before returning
the existing error result; retain the current debug logging and response
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 8b4f6526-c24e-4fd0-b064-46892e1a2daf

📥 Commits

Reviewing files that changed from the base of the PR and between 51487f9 and acf3104.

📒 Files selected for processing (9)
  • format/format.go
  • format/format_test.go
  • mcpserver/tools/file_utils.go
  • mcpserver/tools/file_utils_test.go
  • mcpserver/tools/files.go
  • mcpserver/tools/files_test.go
  • mcpserver/tools/posts.go
  • mcpserver/tools/posts_test.go
  • mcpserver/tools/provider.go

Comment thread format/format.go
Comment thread format/format.go Outdated
Comment thread mcpserver/tools/file_utils.go
Comment thread mcpserver/tools/files.go
Comment thread mcpserver/tools/files.go Outdated
Comment thread mcpserver/tools/files.go
Comment thread mcpserver/tools/posts.go
Comment thread mcpserver/tools/posts.go Outdated
Comment thread mcpserver/tools/posts.go
Comment thread mcpserver/tools/provider.go
- read_file: permission denials and extraction failures for non-text
  documents are tool errors instead of successful text; the standalone
  fallback no longer trims raw text (keeps paging offsets aligned) and
  rejects text files above 10 MB (mirrors files.Service)
- post tools validate priority metadata before uploading attachments so
  validation failures cannot leave orphaned uploads
- WritePost falls back to bare FileIds when metadata contains only nil
  entries
@mattermost-build

Copy link
Copy Markdown
Collaborator

This PR has been automatically labelled "stale" because it hasn't had recent activity.
A core team member will check in on the status of the PR to help with questions.
Thank you for your contribution!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants