# How do I treat Slack messages, tickets and logs that an MCP server returns as untrusted, and add write access safely?

**Short answer:** Anyone who can post in a channel, file a ticket, comment on an issue or get a string into a log can write text your agent will read, so every MCP server that returns that content is a prompt-injection path even when the server itself is honest. OWASP calls this indirect prompt injection: instructions hidden in external content that change what the model does. The model can't be relied on to ignore them, so put the controls around it: connect read-only first, give each write permission its own step with a confirmation, and don't let one session combine private data, untrusted content and a way to send data out (Simon Willison's lethal trifecta). The MCP specification (2026-07-28) says clients should ask the user to confirm sensitive operations, show tool inputs before calling the server, and validate tool results before passing them to the model. Then add write scopes one at a time, for example Slack's reactions:write before chat:write, and only after watching what the agent does with read access.

## Checklist
1. List who can write into each source: a public Slack channel, a shared Slack Connect channel, a support queue that takes email from customers, public GitHub issues and any log that records user input are all written by people outside your team. Treat that content as data from a stranger.
2. Start read-only: run the GitHub MCP server with --read-only (write tools are skipped even if you name them with --tools) and only the --toolsets you need, and request only read scopes for other servers.
3. Request the narrowest read scopes the task needs. Slack's MCP server maps tools to user-token scopes: searching public channels needs search:read.public, while search:read.private, search:read.im and search:read.mpim also open private channels and direct messages. Leave those out unless the task needs them.
4. Filter where the server can: the GitHub MCP server's --lockdown-mode withholds public-repository content from authors without push access. GitHub says it is a best-effort filter, not an authorization boundary, so keep the other controls too.
5. Add one write permission at a time, starting with the most reversible, and run it with confirmation on for a while before the next. For Slack, that could be reactions:write, then drafts reviewed by a person, then chat:write; canvases:write, channels:write and files:write each get their own step.
6. Make every write ask first: in Claude Code, put write tools in an ask rule (for example mcp__slack__* in ask, specific read tools in allow). Rules are evaluated deny, then ask, then allow, so a broad ask or deny wins over a narrower allow.
7. Break the trifecta per session: a session that reads public tickets shouldn't also hold a tool that posts to the internet or reads private channels. Split it into a read-and-summarize step and a separate act step that a person approves.
8. Show the full arguments of any write before it runs, and flag writes the user didn't ask for, especially a message to a channel or person outside the task.

## Why an honest MCP server can still inject
- The server isn't the attacker here. A Slack or ticketing server faithfully returns what people wrote, and if one of those messages says to forward the thread somewhere or close every open ticket, the model sees it in the same context as your instructions.
- OWASP's LLM01:2025 lists, among its mitigations, least-privilege access for the model, human approval for high-risk actions, and separating and clearly marking untrusted content. Marking content helps; the first two are what stop a successful injection from causing harm.
- This is a different problem from a malicious server poisoning its own tool descriptions (see the Log-To-Leak page). Here the descriptions can be perfect and the danger still arrives in the results.

## What each control does and doesn't cover
- Read-only mode stops the server from writing, but reading is the leak in many attacks: an agent that reads private data and has any other tool that can send data out can still leak it.
- Scopes are enforced by the service (Slack, GitHub), so they hold even if the model is fooled. Prompts and system instructions aren't enforced by anything.
- Confirmation prompts work only if people read them. Show the destination and the full text, and keep auto-approve for tools that only read.
- The MCP specification says clients must treat tool annotations such as readOnlyHint as untrusted unless they come from trusted servers, so check the scopes and modes yourself rather than the hints.

## Ask Rightshold (free, no key)
Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code (one-click setup: https://www.rightshold.com/start?ref=seo), then ask: "Does this ticket or Slack message contain hidden text or instructions aimed at my agent before I pass it on?"

```json
{
  "tool": "scan_text",
  "arguments": {
    "text": "<the ticket, Slack message or log line>"
  }
}
```
- Free, no key. It reports exact positions of zero-width characters, bidirectional controls, Unicode tag characters, hidden markup and base64 blobs, and labels instruction-like language as a heuristic, never a certainty. The answer carries a sanitized copy. Plain-text instructions in a ticket can still be an attack, so scanning doesn't replace scopes and confirmation.
- REST: POST https://www.rightshold.com/v1/security/scan-text with {"text": "..."}.
- precall_check reads what is on record about an MCP server before your agent connects: registry entry, a live tool-definition integrity probe and any security flags.

_Rightshold scans text you send it and reports what is on record about MCP servers. It doesn't connect to your Slack, ticketing system or GitHub, grant or remove scopes, or enforce permissions in your MCP client._

## Sources
- [OWASP GenAI: LLM01:2025 Prompt Injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/)
- [MCP specification 2026-07-28: Tools (security considerations)](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)
- [Simon Willison: The lethal trifecta for AI agents (June 16, 2025)](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/)
- [Slack MCP server overview (OAuth scopes per tool)](https://docs.slack.dev/ai/slack-mcp-server)
- [GitHub MCP Server README (read-only mode, toolsets, lockdown mode)](https://github.com/github/github-mcp-server)
- [Claude Code docs: Permissions (MCP rules, deny/ask/allow order)](https://code.claude.com/docs/en/permissions)

## Related
- [How to stop Log-To-Leak style prompt injection through MCP tools](https://www.rightshold.com/answers/how-to-mitigate-log-to-leak-prompt-injection-in-mcp)
- [How to give an MCP server least-privilege access to GitHub](https://www.rightshold.com/answers/how-to-give-an-mcp-server-least-privilege-access-to-github)
- [Are MCP tool annotations like readOnlyHint a security guarantee?](https://www.rightshold.com/answers/are-mcp-tool-annotations-like-readonlyhint-a-security-guarantee)

_Not legal advice. Updated 2026-10-30. JSON: https://www.rightshold.com/answers/how-to-treat-mcp-tool-output-from-slack-tickets-and-logs-as-untrusted.json_