owlpost 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- owlpost-0.1.0/.gitignore +40 -0
- owlpost-0.1.0/LICENSE +21 -0
- owlpost-0.1.0/PKG-INFO +224 -0
- owlpost-0.1.0/README.md +176 -0
- owlpost-0.1.0/accounts.example.toml +46 -0
- owlpost-0.1.0/pyproject.toml +61 -0
- owlpost-0.1.0/src/owlpost/__init__.py +1 -0
- owlpost-0.1.0/src/owlpost/account.py +63 -0
- owlpost-0.1.0/src/owlpost/config.py +73 -0
- owlpost-0.1.0/src/owlpost/folders.py +97 -0
- owlpost-0.1.0/src/owlpost/messages.py +401 -0
- owlpost-0.1.0/src/owlpost/sending.py +245 -0
- owlpost-0.1.0/src/owlpost/server.py +313 -0
- owlpost-0.1.0/src/owlpost/threads.py +209 -0
owlpost-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
*.egg-info/
|
|
8
|
+
*.egg
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
wheels/
|
|
12
|
+
.eggs/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
ENV/
|
|
19
|
+
|
|
20
|
+
# uv
|
|
21
|
+
.uv/
|
|
22
|
+
|
|
23
|
+
# Tools
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
|
|
30
|
+
# Editors
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
.DS_Store
|
|
35
|
+
|
|
36
|
+
# Secrets — never commit real credentials
|
|
37
|
+
accounts.toml
|
|
38
|
+
*.local.toml
|
|
39
|
+
.env
|
|
40
|
+
.env.*
|
owlpost-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sumeet Singh Mankoo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
owlpost-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: owlpost
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-fidelity IMAP/SMTP MCP server for iCloud, Gmail, and other mail providers
|
|
5
|
+
Project-URL: Homepage, https://github.com/smankoo/owlpost
|
|
6
|
+
Project-URL: Repository, https://github.com/smankoo/owlpost
|
|
7
|
+
Project-URL: Issues, https://github.com/smankoo/owlpost/issues
|
|
8
|
+
Author-email: Sumeet Singh Mankoo <sumeet@mankoo.ca>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Sumeet Singh Mankoo
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: claude,email,gmail,icloud,imap,mcp,model-context-protocol,smtp
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
42
|
+
Classifier: Topic :: Communications :: Email
|
|
43
|
+
Classifier: Topic :: Communications :: Email :: Post-Office :: IMAP
|
|
44
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
45
|
+
Requires-Python: >=3.11
|
|
46
|
+
Requires-Dist: mcp>=1.2.0
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# owlpost
|
|
50
|
+
|
|
51
|
+
A high-fidelity IMAP/SMTP MCP server for iCloud, Gmail, and other mail
|
|
52
|
+
providers. Built because every other email MCP server I tried got tripped up
|
|
53
|
+
by real-world IMAP quirks (CRLF line endings, `RFC822` vs `BODY.PEEK[]`,
|
|
54
|
+
provider-specific Sent folders, multi-word search values).
|
|
55
|
+
|
|
56
|
+
owlpost is the one I actually use day-to-day from Claude Code to read,
|
|
57
|
+
search, follow conversations, and send mail.
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
- **Multi-account** — configure as many mailboxes as you want, switch by name
|
|
62
|
+
- **iCloud and Gmail tested end-to-end**, with provider-specific quirks handled:
|
|
63
|
+
- iCloud's `RFC822` fetch returns empty bodies → uses `BODY.PEEK[]`
|
|
64
|
+
- iCloud's IMAP `APPEND` requires CRLF line endings → all outbound mail uses `policy.SMTP`
|
|
65
|
+
- iCloud's SMTP doesn't auto-save sent mail → owlpost APPENDs to Sent for you
|
|
66
|
+
- Gmail's SMTP *does* auto-save → owlpost skips the duplicate APPEND
|
|
67
|
+
- Folder names auto-discovered via RFC 6154 SPECIAL-USE (no hardcoding `Sent Messages` vs `[Gmail]/Sent Mail`)
|
|
68
|
+
- **Conversation following** — Gmail's `X-GM-THRID` extension where available, otherwise a generic Message-ID/References BFS across folders
|
|
69
|
+
- **Reliable connections** — short-lived per-operation IMAP sessions instead of long-lived ones that drop randomly
|
|
70
|
+
- **MIME-aware** — parses multipart messages, decodes headers, lists attachments, downloads them to disk
|
|
71
|
+
- **Threaded reply/forward** — preserves `In-Reply-To`/`References`, quotes the original
|
|
72
|
+
|
|
73
|
+
## Tools
|
|
74
|
+
|
|
75
|
+
| Tool | What it does |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `list_accounts` | List configured mail accounts |
|
|
78
|
+
| `list_folders` | List folders for an account, with detected special-use roles |
|
|
79
|
+
| `resolve_folder` | Resolve a role (`sent`, `trash`, `drafts`, `inbox`, `all`, `archive`) to a folder name |
|
|
80
|
+
| `search_messages` | Structured IMAP search (from/to/cc/subject/body/since/before/unseen/flagged/has_attachment), newest first |
|
|
81
|
+
| `read_email` | Read one message: headers, plaintext/HTML body, attachment list. Truncates by default to fit MCP result limits |
|
|
82
|
+
| `save_attachment` | Download an attachment to disk by part index |
|
|
83
|
+
| `get_conversation` | Return all messages in the same thread (Gmail X-GM-THRID or generic reference walking) |
|
|
84
|
+
| `send_email` | Send mail. Auto-saves to Sent on providers that don't (e.g. iCloud). Supports attachments, threading headers |
|
|
85
|
+
| `reply_email` | Reply (with optional reply-all), preserving threading and quoting |
|
|
86
|
+
| `forward_email` | Forward, re-attaching original attachments by default |
|
|
87
|
+
| `mark_read` | Set/unset `\Seen` |
|
|
88
|
+
| `flag_message` | Set/unset `\Flagged` (star) |
|
|
89
|
+
| `move_email` | Move to another folder |
|
|
90
|
+
| `delete_email` | Move to Trash |
|
|
91
|
+
|
|
92
|
+
## Install
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install owlpost
|
|
96
|
+
# or with uv:
|
|
97
|
+
uv tool install owlpost
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Configure
|
|
101
|
+
|
|
102
|
+
Copy [`accounts.example.toml`](accounts.example.toml) to
|
|
103
|
+
`~/.config/owlpost/accounts.toml` and fill in your credentials. **Use
|
|
104
|
+
app-specific passwords**, not your real password:
|
|
105
|
+
|
|
106
|
+
- **iCloud**: [account.apple.com → Sign-In and Security → App-Specific Passwords](https://account.apple.com)
|
|
107
|
+
- **Gmail**: [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords) (2FA must be enabled)
|
|
108
|
+
|
|
109
|
+
```toml
|
|
110
|
+
[accounts.icloud]
|
|
111
|
+
email = "you@icloud.com"
|
|
112
|
+
password = "xxxx-xxxx-xxxx-xxxx"
|
|
113
|
+
provider = "icloud"
|
|
114
|
+
imap_host = "imap.mail.me.com"
|
|
115
|
+
imap_port = 993
|
|
116
|
+
smtp_host = "smtp.mail.me.com"
|
|
117
|
+
smtp_port = 587
|
|
118
|
+
auto_save_sent = true # iCloud SMTP doesn't auto-save sent
|
|
119
|
+
|
|
120
|
+
[accounts.gmail]
|
|
121
|
+
email = "you@gmail.com"
|
|
122
|
+
password = "xxxx xxxx xxxx xxxx"
|
|
123
|
+
provider = "gmail"
|
|
124
|
+
imap_host = "imap.gmail.com"
|
|
125
|
+
imap_port = 993
|
|
126
|
+
smtp_host = "smtp.gmail.com"
|
|
127
|
+
smtp_port = 587
|
|
128
|
+
auto_save_sent = false # Gmail SMTP auto-saves
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
You can override the config path with the `OWLPOST_CONFIG` environment
|
|
132
|
+
variable.
|
|
133
|
+
|
|
134
|
+
## Use with Claude Code
|
|
135
|
+
|
|
136
|
+
Add to `~/.claude.json` under `mcpServers`:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"mcpServers": {
|
|
141
|
+
"owlpost": {
|
|
142
|
+
"type": "stdio",
|
|
143
|
+
"command": "owlpost",
|
|
144
|
+
"args": [],
|
|
145
|
+
"env": {}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or, if you installed with `uv tool install`, the binary will be at
|
|
152
|
+
`~/.local/bin/owlpost`.
|
|
153
|
+
|
|
154
|
+
Restart Claude Code and the tools will appear under the `owlpost` namespace.
|
|
155
|
+
|
|
156
|
+
## Use with Claude Desktop
|
|
157
|
+
|
|
158
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"mcpServers": {
|
|
163
|
+
"owlpost": {
|
|
164
|
+
"command": "owlpost"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Use with other MCP clients
|
|
171
|
+
|
|
172
|
+
owlpost speaks the standard MCP stdio transport — any MCP-compatible client
|
|
173
|
+
can talk to it by spawning the `owlpost` binary.
|
|
174
|
+
|
|
175
|
+
## Examples
|
|
176
|
+
|
|
177
|
+
Once registered, you can ask Claude things like:
|
|
178
|
+
|
|
179
|
+
- *"Find emails from priya about the kids' school in the last month"*
|
|
180
|
+
- *"Show me the full thread for the latest mortgage email"*
|
|
181
|
+
- *"Reply to the most recent message from the landlord saying I'll be in touch tomorrow"*
|
|
182
|
+
- *"Forward the Manulife policy PDFs to Mom and Dad with Priyanka in CC"*
|
|
183
|
+
- *"Save the attachment from the Toronto Hydro bill to ~/Downloads"*
|
|
184
|
+
|
|
185
|
+
## Provider notes
|
|
186
|
+
|
|
187
|
+
### iCloud
|
|
188
|
+
|
|
189
|
+
- Use an [app-specific password](https://account.apple.com), not your Apple ID password.
|
|
190
|
+
- iCloud throttles aggressive reconnection. owlpost uses short-lived per-op sessions but doesn't pool — if you get `SSLEOFError: EOF`, back off for ~30 seconds.
|
|
191
|
+
- Sent folder is `Sent Messages`, Trash is `Deleted Messages`. Both are auto-detected.
|
|
192
|
+
|
|
193
|
+
### Gmail
|
|
194
|
+
|
|
195
|
+
- Requires [2FA](https://myaccount.google.com/security) and an [app password](https://myaccount.google.com/apppasswords).
|
|
196
|
+
- IMAP must be enabled in [Gmail settings → Forwarding and POP/IMAP](https://mail.google.com/mail/u/0/#settings/fwdandpop).
|
|
197
|
+
- owlpost uses Gmail's `X-GM-THRID` extension for reliable thread detection.
|
|
198
|
+
|
|
199
|
+
### Other providers
|
|
200
|
+
|
|
201
|
+
Set `provider = "generic"` and configure `imap_host`, `smtp_host`, ports, and
|
|
202
|
+
`smtp_security` (`starttls` for 587 or `ssl` for 465). Folder roles will
|
|
203
|
+
still be auto-detected if your provider supports SPECIAL-USE (most modern
|
|
204
|
+
ones do).
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
git clone https://github.com/smankoo/owlpost
|
|
210
|
+
cd owlpost
|
|
211
|
+
uv venv
|
|
212
|
+
uv pip install -e .
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Run the server directly to verify it starts:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
.venv/bin/owlpost
|
|
219
|
+
# (waits for MCP stdio input)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT — see [LICENSE](LICENSE).
|
owlpost-0.1.0/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# owlpost
|
|
2
|
+
|
|
3
|
+
A high-fidelity IMAP/SMTP MCP server for iCloud, Gmail, and other mail
|
|
4
|
+
providers. Built because every other email MCP server I tried got tripped up
|
|
5
|
+
by real-world IMAP quirks (CRLF line endings, `RFC822` vs `BODY.PEEK[]`,
|
|
6
|
+
provider-specific Sent folders, multi-word search values).
|
|
7
|
+
|
|
8
|
+
owlpost is the one I actually use day-to-day from Claude Code to read,
|
|
9
|
+
search, follow conversations, and send mail.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Multi-account** — configure as many mailboxes as you want, switch by name
|
|
14
|
+
- **iCloud and Gmail tested end-to-end**, with provider-specific quirks handled:
|
|
15
|
+
- iCloud's `RFC822` fetch returns empty bodies → uses `BODY.PEEK[]`
|
|
16
|
+
- iCloud's IMAP `APPEND` requires CRLF line endings → all outbound mail uses `policy.SMTP`
|
|
17
|
+
- iCloud's SMTP doesn't auto-save sent mail → owlpost APPENDs to Sent for you
|
|
18
|
+
- Gmail's SMTP *does* auto-save → owlpost skips the duplicate APPEND
|
|
19
|
+
- Folder names auto-discovered via RFC 6154 SPECIAL-USE (no hardcoding `Sent Messages` vs `[Gmail]/Sent Mail`)
|
|
20
|
+
- **Conversation following** — Gmail's `X-GM-THRID` extension where available, otherwise a generic Message-ID/References BFS across folders
|
|
21
|
+
- **Reliable connections** — short-lived per-operation IMAP sessions instead of long-lived ones that drop randomly
|
|
22
|
+
- **MIME-aware** — parses multipart messages, decodes headers, lists attachments, downloads them to disk
|
|
23
|
+
- **Threaded reply/forward** — preserves `In-Reply-To`/`References`, quotes the original
|
|
24
|
+
|
|
25
|
+
## Tools
|
|
26
|
+
|
|
27
|
+
| Tool | What it does |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `list_accounts` | List configured mail accounts |
|
|
30
|
+
| `list_folders` | List folders for an account, with detected special-use roles |
|
|
31
|
+
| `resolve_folder` | Resolve a role (`sent`, `trash`, `drafts`, `inbox`, `all`, `archive`) to a folder name |
|
|
32
|
+
| `search_messages` | Structured IMAP search (from/to/cc/subject/body/since/before/unseen/flagged/has_attachment), newest first |
|
|
33
|
+
| `read_email` | Read one message: headers, plaintext/HTML body, attachment list. Truncates by default to fit MCP result limits |
|
|
34
|
+
| `save_attachment` | Download an attachment to disk by part index |
|
|
35
|
+
| `get_conversation` | Return all messages in the same thread (Gmail X-GM-THRID or generic reference walking) |
|
|
36
|
+
| `send_email` | Send mail. Auto-saves to Sent on providers that don't (e.g. iCloud). Supports attachments, threading headers |
|
|
37
|
+
| `reply_email` | Reply (with optional reply-all), preserving threading and quoting |
|
|
38
|
+
| `forward_email` | Forward, re-attaching original attachments by default |
|
|
39
|
+
| `mark_read` | Set/unset `\Seen` |
|
|
40
|
+
| `flag_message` | Set/unset `\Flagged` (star) |
|
|
41
|
+
| `move_email` | Move to another folder |
|
|
42
|
+
| `delete_email` | Move to Trash |
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install owlpost
|
|
48
|
+
# or with uv:
|
|
49
|
+
uv tool install owlpost
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configure
|
|
53
|
+
|
|
54
|
+
Copy [`accounts.example.toml`](accounts.example.toml) to
|
|
55
|
+
`~/.config/owlpost/accounts.toml` and fill in your credentials. **Use
|
|
56
|
+
app-specific passwords**, not your real password:
|
|
57
|
+
|
|
58
|
+
- **iCloud**: [account.apple.com → Sign-In and Security → App-Specific Passwords](https://account.apple.com)
|
|
59
|
+
- **Gmail**: [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords) (2FA must be enabled)
|
|
60
|
+
|
|
61
|
+
```toml
|
|
62
|
+
[accounts.icloud]
|
|
63
|
+
email = "you@icloud.com"
|
|
64
|
+
password = "xxxx-xxxx-xxxx-xxxx"
|
|
65
|
+
provider = "icloud"
|
|
66
|
+
imap_host = "imap.mail.me.com"
|
|
67
|
+
imap_port = 993
|
|
68
|
+
smtp_host = "smtp.mail.me.com"
|
|
69
|
+
smtp_port = 587
|
|
70
|
+
auto_save_sent = true # iCloud SMTP doesn't auto-save sent
|
|
71
|
+
|
|
72
|
+
[accounts.gmail]
|
|
73
|
+
email = "you@gmail.com"
|
|
74
|
+
password = "xxxx xxxx xxxx xxxx"
|
|
75
|
+
provider = "gmail"
|
|
76
|
+
imap_host = "imap.gmail.com"
|
|
77
|
+
imap_port = 993
|
|
78
|
+
smtp_host = "smtp.gmail.com"
|
|
79
|
+
smtp_port = 587
|
|
80
|
+
auto_save_sent = false # Gmail SMTP auto-saves
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You can override the config path with the `OWLPOST_CONFIG` environment
|
|
84
|
+
variable.
|
|
85
|
+
|
|
86
|
+
## Use with Claude Code
|
|
87
|
+
|
|
88
|
+
Add to `~/.claude.json` under `mcpServers`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"owlpost": {
|
|
94
|
+
"type": "stdio",
|
|
95
|
+
"command": "owlpost",
|
|
96
|
+
"args": [],
|
|
97
|
+
"env": {}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or, if you installed with `uv tool install`, the binary will be at
|
|
104
|
+
`~/.local/bin/owlpost`.
|
|
105
|
+
|
|
106
|
+
Restart Claude Code and the tools will appear under the `owlpost` namespace.
|
|
107
|
+
|
|
108
|
+
## Use with Claude Desktop
|
|
109
|
+
|
|
110
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"owlpost": {
|
|
116
|
+
"command": "owlpost"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Use with other MCP clients
|
|
123
|
+
|
|
124
|
+
owlpost speaks the standard MCP stdio transport — any MCP-compatible client
|
|
125
|
+
can talk to it by spawning the `owlpost` binary.
|
|
126
|
+
|
|
127
|
+
## Examples
|
|
128
|
+
|
|
129
|
+
Once registered, you can ask Claude things like:
|
|
130
|
+
|
|
131
|
+
- *"Find emails from priya about the kids' school in the last month"*
|
|
132
|
+
- *"Show me the full thread for the latest mortgage email"*
|
|
133
|
+
- *"Reply to the most recent message from the landlord saying I'll be in touch tomorrow"*
|
|
134
|
+
- *"Forward the Manulife policy PDFs to Mom and Dad with Priyanka in CC"*
|
|
135
|
+
- *"Save the attachment from the Toronto Hydro bill to ~/Downloads"*
|
|
136
|
+
|
|
137
|
+
## Provider notes
|
|
138
|
+
|
|
139
|
+
### iCloud
|
|
140
|
+
|
|
141
|
+
- Use an [app-specific password](https://account.apple.com), not your Apple ID password.
|
|
142
|
+
- iCloud throttles aggressive reconnection. owlpost uses short-lived per-op sessions but doesn't pool — if you get `SSLEOFError: EOF`, back off for ~30 seconds.
|
|
143
|
+
- Sent folder is `Sent Messages`, Trash is `Deleted Messages`. Both are auto-detected.
|
|
144
|
+
|
|
145
|
+
### Gmail
|
|
146
|
+
|
|
147
|
+
- Requires [2FA](https://myaccount.google.com/security) and an [app password](https://myaccount.google.com/apppasswords).
|
|
148
|
+
- IMAP must be enabled in [Gmail settings → Forwarding and POP/IMAP](https://mail.google.com/mail/u/0/#settings/fwdandpop).
|
|
149
|
+
- owlpost uses Gmail's `X-GM-THRID` extension for reliable thread detection.
|
|
150
|
+
|
|
151
|
+
### Other providers
|
|
152
|
+
|
|
153
|
+
Set `provider = "generic"` and configure `imap_host`, `smtp_host`, ports, and
|
|
154
|
+
`smtp_security` (`starttls` for 587 or `ssl` for 465). Folder roles will
|
|
155
|
+
still be auto-detected if your provider supports SPECIAL-USE (most modern
|
|
156
|
+
ones do).
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
git clone https://github.com/smankoo/owlpost
|
|
162
|
+
cd owlpost
|
|
163
|
+
uv venv
|
|
164
|
+
uv pip install -e .
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Run the server directly to verify it starts:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
.venv/bin/owlpost
|
|
171
|
+
# (waits for MCP stdio input)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# owlpost account configuration — example
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to ~/.config/owlpost/accounts.toml and fill in your own
|
|
4
|
+
# credentials. Each [accounts.<name>] block defines one mailbox. The <name>
|
|
5
|
+
# is what you'll pass to MCP tools as the `account` argument.
|
|
6
|
+
#
|
|
7
|
+
# IMPORTANT: use app-specific passwords, not your real password.
|
|
8
|
+
# - iCloud: https://account.apple.com → Sign-In and Security → App-Specific Passwords
|
|
9
|
+
# - Gmail: https://myaccount.google.com/apppasswords (requires 2FA enabled)
|
|
10
|
+
|
|
11
|
+
[accounts.icloud]
|
|
12
|
+
email = "you@icloud.com" # or your @me.com / custom domain alias
|
|
13
|
+
password = "xxxx-xxxx-xxxx-xxxx" # iCloud app-specific password
|
|
14
|
+
provider = "icloud"
|
|
15
|
+
imap_host = "imap.mail.me.com"
|
|
16
|
+
imap_port = 993
|
|
17
|
+
smtp_host = "smtp.mail.me.com"
|
|
18
|
+
smtp_port = 587
|
|
19
|
+
# iCloud SMTP does NOT auto-save sent messages — owlpost will APPEND to Sent.
|
|
20
|
+
auto_save_sent = true
|
|
21
|
+
|
|
22
|
+
[accounts.gmail]
|
|
23
|
+
email = "you@gmail.com"
|
|
24
|
+
password = "xxxx xxxx xxxx xxxx" # Gmail app password (16 chars, spaces ok)
|
|
25
|
+
provider = "gmail"
|
|
26
|
+
imap_host = "imap.gmail.com"
|
|
27
|
+
imap_port = 993
|
|
28
|
+
smtp_host = "smtp.gmail.com"
|
|
29
|
+
smtp_port = 587
|
|
30
|
+
# Gmail SMTP auto-saves to "Sent Mail" — APPENDing would create duplicates.
|
|
31
|
+
auto_save_sent = false
|
|
32
|
+
|
|
33
|
+
# Generic example for any other IMAP/SMTP provider (Fastmail, ProtonMail
|
|
34
|
+
# Bridge, your own server, etc.). Set provider = "generic" to skip
|
|
35
|
+
# provider-specific behaviors.
|
|
36
|
+
#
|
|
37
|
+
# [accounts.work]
|
|
38
|
+
# email = "you@example.com"
|
|
39
|
+
# password = "..."
|
|
40
|
+
# provider = "generic"
|
|
41
|
+
# imap_host = "imap.example.com"
|
|
42
|
+
# imap_port = 993
|
|
43
|
+
# smtp_host = "smtp.example.com"
|
|
44
|
+
# smtp_port = 587
|
|
45
|
+
# smtp_security = "starttls" # or "ssl" for port 465
|
|
46
|
+
# auto_save_sent = true
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "owlpost"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "High-fidelity IMAP/SMTP MCP server for iCloud, Gmail, and other mail providers"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { file = "LICENSE" }
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Sumeet Singh Mankoo", email = "sumeet@mankoo.ca" },
|
|
10
|
+
]
|
|
11
|
+
keywords = [
|
|
12
|
+
"mcp",
|
|
13
|
+
"model-context-protocol",
|
|
14
|
+
"imap",
|
|
15
|
+
"smtp",
|
|
16
|
+
"email",
|
|
17
|
+
"icloud",
|
|
18
|
+
"gmail",
|
|
19
|
+
"claude",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 4 - Beta",
|
|
23
|
+
"Environment :: Console",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"Intended Audience :: End Users/Desktop",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Topic :: Communications :: Email",
|
|
33
|
+
"Topic :: Communications :: Email :: Post-Office :: IMAP",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
]
|
|
36
|
+
dependencies = [
|
|
37
|
+
"mcp>=1.2.0",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/smankoo/owlpost"
|
|
42
|
+
Repository = "https://github.com/smankoo/owlpost"
|
|
43
|
+
Issues = "https://github.com/smankoo/owlpost/issues"
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
owlpost = "owlpost.server:main"
|
|
47
|
+
|
|
48
|
+
[build-system]
|
|
49
|
+
requires = ["hatchling"]
|
|
50
|
+
build-backend = "hatchling.build"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/owlpost"]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.sdist]
|
|
56
|
+
include = [
|
|
57
|
+
"src/owlpost",
|
|
58
|
+
"README.md",
|
|
59
|
+
"LICENSE",
|
|
60
|
+
"accounts.example.toml",
|
|
61
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""owlpost — high-fidelity IMAP/SMTP MCP server."""
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""IMAP / SMTP connection helpers and the Account wrapper."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import imaplib
|
|
6
|
+
import smtplib
|
|
7
|
+
import ssl
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from typing import Iterator
|
|
10
|
+
|
|
11
|
+
from .config import AccountConfig
|
|
12
|
+
|
|
13
|
+
# IMAP literal limit bumped — APPEND of large messages with attachments needs this.
|
|
14
|
+
imaplib._MAXLINE = 10 * 1024 * 1024 # 10 MB
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Account:
|
|
18
|
+
"""Wraps an AccountConfig with connection helpers.
|
|
19
|
+
|
|
20
|
+
Connections are short-lived and re-opened per operation. This is the most
|
|
21
|
+
reliable model for IMAP — long-lived idle connections drop randomly and
|
|
22
|
+
state (selected folder, FETCH cursor, etc.) is hard to recover.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, cfg: AccountConfig):
|
|
26
|
+
self.cfg = cfg
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def name(self) -> str:
|
|
30
|
+
return self.cfg.name
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def imap(self) -> Iterator[imaplib.IMAP4_SSL]:
|
|
34
|
+
conn = imaplib.IMAP4_SSL(self.cfg.imap_host, self.cfg.imap_port)
|
|
35
|
+
try:
|
|
36
|
+
conn.login(self.cfg.email, self.cfg.password)
|
|
37
|
+
yield conn
|
|
38
|
+
finally:
|
|
39
|
+
try:
|
|
40
|
+
conn.logout()
|
|
41
|
+
except Exception:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
@contextmanager
|
|
45
|
+
def smtp(self) -> Iterator[smtplib.SMTP]:
|
|
46
|
+
ctx = ssl.create_default_context()
|
|
47
|
+
if self.cfg.smtp_security == "ssl":
|
|
48
|
+
conn = smtplib.SMTP_SSL(
|
|
49
|
+
self.cfg.smtp_host, self.cfg.smtp_port, context=ctx
|
|
50
|
+
)
|
|
51
|
+
else:
|
|
52
|
+
conn = smtplib.SMTP(self.cfg.smtp_host, self.cfg.smtp_port)
|
|
53
|
+
conn.ehlo()
|
|
54
|
+
conn.starttls(context=ctx)
|
|
55
|
+
conn.ehlo()
|
|
56
|
+
try:
|
|
57
|
+
conn.login(self.cfg.email, self.cfg.password)
|
|
58
|
+
yield conn
|
|
59
|
+
finally:
|
|
60
|
+
try:
|
|
61
|
+
conn.quit()
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|