Skip to content

Commit fb17c92

Browse files
feat: download and view attachments
1 parent 8faa37f commit fb17c92

4 files changed

Lines changed: 162 additions & 4 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A Model Context Protocol (MCP) server for querying your local [`mu`](https://git
1111
## Features
1212

1313
- **Stdio MCP server** for easy integration
14-
- **Two tools:** query and view emails.
14+
- **Three tools:** query, view emails and open attachments (using default OS viewer).
1515
- **Fast, flexible mail search** using the `mu` index
1616
- **Claude Desktop ready**: simple installation and config
1717
- **Python, uv, and MCP SDK** based
@@ -67,9 +67,9 @@ Replace `PROJECT_PATH` with the path to your cloned repo.
6767

6868
## Query
6969

70-
Ask Claude to find emails, e.g. "Find emails with a PDF attachment that were sent last April", "Show me the email I received from Alice last week", or "Find emails with the subject 'Meeting Notes'".
70+
Ask Claude to find emails, e.g. "Find emails with a PDF attachment that were sent last April and open the PDF", "Show me the email I received from Alice last week", or "Find emails with the subject 'Meeting Notes'".
7171

7272
## Development
7373

7474
- [x] Adding a tool to view an email.
75-
- [ ] Adding a tool to find and download attachments.
75+
- [x] Adding a tool to find and download attachments.

mu_mcp/mu-extract.txt

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
MU EXTRACT(1) General Commands Manual MU EXTRACT(1)
2+
3+
NAME
4+
mu-extract - display and save message parts (attachments), and open
5+
them with other tools.
6+
7+
SYNOPSIS
8+
mu [COMMON-OPTIONS] extract [OPTIONS] [FILE]
9+
10+
11+
mu [COMMON-OPTIONS] extract [OPTIONS] FILE PATTERN
12+
13+
DESCRIPTION
14+
mu extract is the mu sub-command for extracting MIME-parts (e.g.,
15+
attachments) from mail messages. The sub-command works on message
16+
files, and does not require the message to be indexed in the database.
17+
18+
19+
For attachments, the file name used when saving it is the name of the
20+
attachment in the message. If there is no such name, or when saving
21+
non-attachment MIME-parts, a name is derived from the message-id of the
22+
message.
23+
24+
25+
If you specify a regular express pattern as the second argument, all
26+
attachments with filenames matching that pattern will be extracted. The
27+
regular expressions are basic PCRE, and are case-sensitive by default;
28+
see pcre(3) for more details.
29+
30+
31+
Without any options, mu extract simply outputs the list of leaf MIME-
32+
parts in the message. Only `leaf' MIME-parts (including RFC822
33+
attachments) are considered, multipart/* etc. are ignored.
34+
35+
36+
Without a filename parameter, mu extract reads a message from standard-
37+
input. In that case, you cannot use the second, PATTERN parameter as
38+
this would be ambiguous; instead, use the --matches option.
39+
40+
EXTRACT OPTIONS
41+
-a, --save-attachments
42+
Save all MIME-parts that look like attachments.
43+
44+
--save-all
45+
Save all non-multipart MIME-parts.
46+
47+
--parts parts
48+
Only consider the following numbered parts (comma-separated list). The
49+
numbers for the parts can be seen from running mu extract without any
50+
options but only the message file.
51+
52+
--target-dir dir
53+
Save the parts in dir rather than the current working directory.
54+
55+
--overwrite
56+
Overwrite existing files with the same name; by default overwriting is
57+
not allowed.
58+
59+
-u,--uncooked
60+
By default, mu transforms the attachment filenames a bit (such as by
61+
replacing spaces by dashes); with this option, leave that to the
62+
minimum for creating a legal filename in the target directory.
63+
64+
--matches pattern
65+
Attachments with filenames matching pattern will be extracted. The
66+
regular expressions are basic PCRE, and are case-sensitive by default;
67+
see pcre(3) for more details.
68+
69+
--play
70+
Try to `play' (open) the attachment with the default application for
71+
the particular file type. On MacOS, this uses the open program, on
72+
other platforms it uses xdg-open. You can choose a different program by
73+
setting the MU_PLAY_PROGRAM environment variable.
74+
75+
COMMON OPTIONS
76+
-d, --debug
77+
Makes mu generate extra debug information, useful for debugging the
78+
program itself. Debug information goes to the standard logging
79+
location; see mu(1).
80+
81+
-q, --quiet
82+
Causes mu not to output informational messages and progress information
83+
to standard output, but only to the log file. Error messages will still
84+
be sent to standard error. Note that mu index is much faster with
85+
--quiet, so it is recommended you use this option when using mu from
86+
scripts etc.
87+
88+
--log-stderr
89+
Causes mu to not output log messages to standard error, in addition to
90+
sending them to the standard logging location.
91+
92+
--nocolor
93+
Do not use ANSI colors. The environment variable NO_COLOR can be used
94+
as an alternative to --nocolor.
95+
96+
-V, --version
97+
Prints mu version and copyright information.
98+
99+
-h, --help
100+
Lists the various command line options.
101+
102+
EXAMPLES
103+
To display information about all the MIME-parts in a message file:
104+
$ mu extract msgfile
105+
106+
107+
108+
To extract MIME-part 3 and 4 from this message, overwriting existing
109+
files with the same name:
110+
$ mu extract --parts=3,4 --overwrite msgfile
111+
112+
113+
114+
To extract all files ending in `.jpg' (case-insensitive):
115+
$ mu extract msgfile '.*\.jpg'
116+
117+
118+
119+
To extract an mp3-file, and play it in the default mp3-playing
120+
application:
121+
$ mu extract --play msgfile 'whoopsididitagain.mp3'
122+
123+
124+
125+
when reading from standard-input, you need --matches, so:
126+
$ cat msgfile | mu extract --play --matches 'whoopsididitagain.mp3'

mu_mcp/mu_mcp.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
mu_query_man = open("mu_mcp/mu-query.txt", "r").read().strip()
88
mu_find_man = open("mu_mcp/mu-find.txt", "r").read().strip()
9+
mu_extract_man = open("mu_mcp/mu-extract.txt", "r").read().strip()
910

1011

1112
# Add MCP health check tool
@@ -66,5 +67,36 @@ def view(paths: str) -> str:
6667
return f"Error: {e.stderr.strip()}"
6768

6869

70+
def get_attachment(command: str) -> str:
71+
r"""
72+
Open attachments in email by providing the email path.
73+
74+
The tool downloads the attachment into a temp dir and open it.
75+
76+
The `command` includes the paths and the pattern of attachment files.
77+
78+
The prefix `mu extract --target-dir /tmp --overwrite --play` SHOULD NOT appear in `command`.
79+
80+
See the man page for `mu extract`.
81+
"""
82+
import subprocess
83+
84+
try:
85+
result = subprocess.run(
86+
["mu", "extract", "--target-dir", "/tmp", "--overwrite", "--play"] + command.split(),
87+
capture_output=True,
88+
text=True,
89+
check=True,
90+
)
91+
return result.stdout.strip()
92+
except subprocess.CalledProcessError as e:
93+
return f"Error: {e.stderr.strip()}"
94+
95+
96+
get_attachment.__doc__ += "\n\n" + mu_extract_man
97+
98+
mcp.tool("get_attachment")(get_attachment)
99+
100+
69101
if __name__ == "__main__":
70102
mcp.run(transport="stdio")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mu_mcp"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
description = "A Model Context Protocol (MCP) server for mail-utils (mu) email indexing and search tool."
55
authors = [{ name = "Daniel Fleischer" }]
66
requires-python = ">=3.10"

0 commit comments

Comments
 (0)