You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Web UI at **http://localhost:8080**, SMTP on **localhost:2525**. Details on [Docker Hub](https://hub.docker.com/r/changemakerstudiosus/papercut-smtp).
35
35
36
+
## MCP Server (AI Agents)
37
+
38
+
The Papercut Service includes an optional [Model Context Protocol](https://modelcontextprotocol.io/) server, so AI agents like Claude Code can inspect the email your app sends — list messages, assert on bodies and headers, verify attachment content, and clean up between test runs. Off by default; enable it with the `EnableMcpServer` setting, then connect:
39
+
40
+
```bash
41
+
claude mcp add --transport http papercut http://localhost:8080/mcp
42
+
```
43
+
44
+
See the [MCP Server documentation](https://www.papercut-smtp.com/mcp/) for setup and the full tool reference.
45
+
36
46
## Documentation
37
47
38
48
**[www.papercut-smtp.com](https://www.papercut-smtp.com/)** — full documentation:
39
49
40
50
-[How It Works](https://www.papercut-smtp.com/how-it-works/) — what Papercut is (and isn't), in two minutes
41
51
-[Getting Started](https://www.papercut-smtp.com/getting-started/) — install, first run, first test email
42
52
-[Send Email from Your App](https://www.papercut-smtp.com/send-from-your-app/) — copy-paste config for .NET, Node, Python, PHP, Java, Ruby
The Papercut SMTP Service includes an optional **[Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server** that lets AI agents and coding assistants — Claude Code, and any other MCP-capable client — inspect the email your application sends during development.
4
+
5
+
A typical agent-driven test loop:
6
+
7
+
1. The agent triggers your app to send an email
8
+
2.`list_messages` — confirm it arrived
9
+
3.`get_message` — assert on subject, recipients, and body
10
+
4.`get_message_section` — verify an attachment's actual content
11
+
5.`delete_all_messages` — reset for the next test
12
+
13
+
!!! note "Off by default"
14
+
The MCP server is disabled unless you explicitly enable it. The service logs its status at startup either way:
15
+
16
+
```
17
+
[INF] MCP server is enabled -- serving MCP endpoint at /mcp
18
+
[INF] MCP server is disabled (set EnableMcpServer to true to enable)
19
+
```
20
+
21
+
## Enabling
22
+
23
+
Set `EnableMcpServer` to `true` using any of the service's [configuration layers](service.md#configuration):
24
+
25
+
=== "appsettings.json"
26
+
27
+
```json
28
+
{ "EnableMcpServer": true }
29
+
```
30
+
31
+
=== "Environment variable"
32
+
33
+
```powershell
34
+
$env:EnableMcpServer = 'true'
35
+
```
36
+
37
+
=== "Docker"
38
+
39
+
```bash
40
+
docker run -d -p 8080:8080 -p 2525:2525 \
41
+
-e EnableMcpServer=true \
42
+
changemakerstudiosus/papercut-smtp:latest
43
+
```
44
+
45
+
Restart the service after changing it. When enabled, the endpoint is served at:
46
+
47
+
```
48
+
http://localhost:8080/mcp
49
+
```
50
+
51
+
(Streamable HTTP transport; the path respects `HttpPathPrefix` if configured.) The web UI shows an **MCP** badge in the navigation bar when the server is on — hover it for the endpoint URL, click to copy. The URL is also available programmatically at `GET /api/mcp`.
52
+
53
+
## Connecting a client
54
+
55
+
**Claude Code:**
56
+
57
+
```bash
58
+
claude mcp add --transport http papercut http://localhost:8080/mcp
59
+
```
60
+
61
+
**Generic MCP client configuration:**
62
+
63
+
```json
64
+
{
65
+
"mcpServers": {
66
+
"papercut": {
67
+
"type": "http",
68
+
"url": "http://localhost:8080/mcp"
69
+
}
70
+
}
71
+
}
72
+
```
73
+
74
+
## Tools
75
+
76
+
| Tool | Description |
77
+
|------|-------------|
78
+
|`list_messages`| Paged message summaries, newest first (`limit`, `start`) |
79
+
|`get_message`| Full detail for one message: from/to/cc/bcc, subject, text and HTML bodies, headers, and a manifest of MIME sections (index, contentId, media type, filename, attachment flag, size) |
80
+
|`get_message_section`| Decoded content of a single MIME part, selected by `index` or `contentId` from the manifest — text parts return as text, binary parts as base64 |
81
+
|`get_message_raw`| The raw RFC 822 (`.eml`) source of a message |
82
+
|`delete_message`| Delete one message by id |
83
+
|`delete_all_messages`| Clear the message store |
84
+
85
+
Large content is truncated to keep responses manageable (raw messages at 200K characters, binary sections at 512KB) with a `truncated` flag pointing to the full-content REST endpoints (`/api/messages/{id}/raw` and `/api/messages/{id}/sections/{index}`).
86
+
87
+
!!! warning "Network exposure"
88
+
Like the REST API and web UI, the MCP endpoint has **no built-in authentication** — anyone who can reach the HTTP port can read and delete messages. Keep the binding on `localhost`, or put a reverse proxy with auth in front of it. See the [network exposure warning](service.md#configuration).
Copy file name to clipboardExpand all lines: docs/service.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -68,3 +68,5 @@ Use `http://0.0.0.0:8080` to listen on all interfaces — but read the warning b
68
68
## API
69
69
70
70
The web UI is backed by a small HTTP API (`/api/messages`, etc.) you can script against — handy for asserting "an email was sent" in end-to-end tests. Explore the endpoints via your browser's dev tools on the web UI.
71
+
72
+
For AI agents and coding assistants, the service can also expose these operations over the Model Context Protocol — see [MCP Server](mcp.md).
[Description("Lists received email messages, newest first. Returns the total message count and a page of message summaries (id, subject, size, created date).")]
37
+
publicasyncTask<GetMessagesResponse>GetAll(
38
+
[Description("Maximum number of messages to return (default 10)")]intlimit=10,
39
+
[Description("Zero-based offset to start from, for paging (default 0)")]intstart=0,
[Description("Gets the full detail of a received email message by id: from/to/cc/bcc addresses, subject, text and HTML bodies, headers, and a manifest of MIME sections (body parts and attachments).")]
108
+
publicasyncTask<MimeMessageEntry.DetailDto>Get(
109
+
[Description("The message id (as returned by list_messages)")]stringid,
0 commit comments