Skip to content

Commit 1c7b1c7

Browse files
authored
feat(mcp): mask credentials by default, add file handoff (#1)
The MCP server returned raw xoxc-* tokens and d/d-s cookie values directly in tool results, placing live Slack credentials into the calling model's context window, transcript, and provider-side logs (OWASP LLM Top 10 #2, sensitive information disclosure). The four read tools now return only masked previews plus metadata. A new write_credentials_file tool writes the real credentials to a 0600-mode file in a private per-run directory and returns only the path — secrets travel via the filesystem, never the transcript. The directory is removed on shutdown. SLACKTOKENS_MCP_ALLOW_RAW=1 is a documented opt-in that restores raw inlining for users who knowingly accept the exposure.
1 parent 9326c26 commit 1c7b1c7

4 files changed

Lines changed: 641 additions & 105 deletions

File tree

README.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,25 +123,47 @@ curl.exe 'https://slack.com/api/auth.test' -d "token=$token" --cookie "d=$dcooki
123123

124124
## MCP server
125125

126-
A standards-compliant [Model Context Protocol](https://modelcontextprotocol.io) server is shipped under `cmd/slacktokens-mcp/`. It exposes the library as four read-only tools to MCP-capable clients (Claude Code, Claude Desktop, Cursor, etc.) over stdio.
126+
A standards-compliant [Model Context Protocol](https://modelcontextprotocol.io) server is shipped under `cmd/slacktokens-mcp/`. It exposes the library to MCP-capable clients (Claude Code, Claude Desktop, Cursor, etc.) over stdio.
127127

128128
```sh
129129
go install github.com/hishamkaram/slacktokens/cmd/slacktokens-mcp@latest
130130
```
131131

132+
### Secure by default — credentials never enter the AI's context
133+
134+
A Slack token or auth cookie is a live credential. Returning one in a tool result would drop it into the calling model's context window, its transcript, and any provider-side logs — a sensitive-information-disclosure risk. So the MCP server is **masked by default**:
135+
136+
- The four read tools return only a **masked preview** (e.g. `xoxc-2…3f9a`) — enough for a human to recognise their own credential, useless as a credential itself — plus workspace/cookie metadata.
137+
- To hand over **real, usable credentials**, call `write_credentials_file`. It writes them to a freshly created local file readable only by your OS user (mode `0600`) and returns **only the path** — the credential values never enter the model context. The file is removed when the server stops.
138+
139+
The credentials file holds the same JSON as `slacktokens` with no flags — `{ "tokens": …, "cookie": …, "cookies": … }` — so you or a script can consume it directly:
140+
141+
```sh
142+
TOKEN=$(jq -r '.tokens["https://your-workspace.slack.com"].token' "$CREDS_FILE")
143+
DCOOKIE=$(jq -r '.cookie.value' "$CREDS_FILE")
144+
curl 'https://slack.com/api/auth.test' -d "token=$TOKEN" --cookie "d=$DCOOKIE"
145+
```
146+
132147
Tools:
133148

134149
| Name | Returns |
135150
| --- | --- |
136-
| `get_tokens` | `xoxc-*` tokens for every workspace |
137-
| `get_cookie` | the `d` auth cookie |
138-
| `get_cookies` | both `d` and `d-s` when present |
139-
| `get_tokens_and_cookie` | tokens + cookies in one call |
151+
| `get_tokens` | per-workspace name + **masked** `xoxc-*` token preview |
152+
| `get_cookie` | the `d` auth cookie, **masked** |
153+
| `get_cookies` | `d` and `d-s` (when present), **masked** |
154+
| `get_tokens_and_cookie` | masked tokens + cookies in one call |
155+
| `write_credentials_file` | path to a `0600` JSON file holding the real credentials |
140156

141-
All tools advertise `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true`, `openWorldHint: false`. Each tool's title and description flag the result as **sensitive** so a compliant MCP client surfaces a confirmation prompt before invoking. The server also opts out of the `logging` capability so secrets cannot leak via `notifications/message`.
157+
The four read tools advertise `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true`, `openWorldHint: false`. `write_credentials_file` advertises `readOnlyHint: false` and `idempotentHint: false` (it creates a file) and is otherwise non-destructive and offline. The server opts out of the `logging` capability so secrets cannot leak via `notifications/message`.
142158

143159
Built against the official Go SDK (`github.com/modelcontextprotocol/go-sdk@v1.6.0`) and the **MCP 2025-11-25** specification.
144160

161+
> Note: file handoff keeps secrets out of the model context, transcript, and logs. An agent that *also* has shell/file-read tools can still be explicitly instructed to open the file — that is a deliberate user-directed act, not the silent exposure this design prevents.
162+
163+
### Opting in to raw output
164+
165+
If you understand the exposure and still want the read tools to inline raw `xoxc-*` tokens and cookie values (the previous behaviour), start the server with the `SLACKTOKENS_MCP_ALLOW_RAW=1` environment variable. Leaving it unset keeps every read tool masked.
166+
145167
### Claude Code / Claude Desktop config
146168

147169
```jsonc
@@ -154,6 +176,19 @@ Built against the official Go SDK (`github.com/modelcontextprotocol/go-sdk@v1.6.
154176
}
155177
```
156178

179+
To opt in to raw output, add the environment variable:
180+
181+
```jsonc
182+
{
183+
"mcpServers": {
184+
"slacktokens": {
185+
"command": "slacktokens-mcp",
186+
"env": { "SLACKTOKENS_MCP_ALLOW_RAW": "1" }
187+
}
188+
}
189+
}
190+
```
191+
157192
(Or use the absolute path to the binary if it isn't on PATH.)
158193

159194
## How it works

0 commit comments

Comments
 (0)