courier 1.1.15__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.
- courier-1.1.15/LICENSE +21 -0
- courier-1.1.15/PKG-INFO +264 -0
- courier-1.1.15/README.md +224 -0
- courier-1.1.15/courier/__init__.py +3 -0
- courier-1.1.15/courier/__main__.py +3160 -0
- courier-1.1.15/courier/_claude_command.py +320 -0
- courier-1.1.15/courier/app_password.py +99 -0
- courier-1.1.15/courier/auth_setup.py +130 -0
- courier-1.1.15/courier/browser_auth.py +400 -0
- courier-1.1.15/courier/config.py +879 -0
- courier-1.1.15/courier/gmail_auth.py +55 -0
- courier-1.1.15/courier/identity.py +210 -0
- courier-1.1.15/courier/imap_client.py +1595 -0
- courier-1.1.15/courier/local_cache.py +456 -0
- courier-1.1.15/courier/logging_setup.py +87 -0
- courier-1.1.15/courier/markdown_render.py +49 -0
- courier-1.1.15/courier/mcp_protocol.py +89 -0
- courier-1.1.15/courier/mcp_server.py +166 -0
- courier-1.1.15/courier/models.py +695 -0
- courier-1.1.15/courier/oauth2.py +164 -0
- courier-1.1.15/courier/oauth2_config.py +178 -0
- courier-1.1.15/courier/query_parser.py +547 -0
- courier-1.1.15/courier/resources.py +242 -0
- courier-1.1.15/courier/sieve_filter.py +249 -0
- courier-1.1.15/courier/smtp_client.py +437 -0
- courier-1.1.15/courier/smtp_transport.py +210 -0
- courier-1.1.15/courier/tools.py +744 -0
- courier-1.1.15/courier/workflows/__init__.py +1 -0
- courier-1.1.15/courier/workflows/calendar_mock.py +164 -0
- courier-1.1.15/courier/workflows/invite_parser.py +342 -0
- courier-1.1.15/courier/workflows/meeting_reply.py +260 -0
- courier-1.1.15/courier.egg-info/PKG-INFO +264 -0
- courier-1.1.15/courier.egg-info/SOURCES.txt +75 -0
- courier-1.1.15/courier.egg-info/dependency_links.txt +1 -0
- courier-1.1.15/courier.egg-info/entry_points.txt +2 -0
- courier-1.1.15/courier.egg-info/requires.txt +20 -0
- courier-1.1.15/courier.egg-info/top_level.txt +1 -0
- courier-1.1.15/pyproject.toml +100 -0
- courier-1.1.15/setup.cfg +4 -0
- courier-1.1.15/tests/test_app_password.py +46 -0
- courier-1.1.15/tests/test_auth_setup.py +7 -0
- courier-1.1.15/tests/test_browser_auth.py +300 -0
- courier-1.1.15/tests/test_chain.py +866 -0
- courier-1.1.15/tests/test_cli_send.py +1221 -0
- courier-1.1.15/tests/test_config.py +1058 -0
- courier-1.1.15/tests/test_copy.py +392 -0
- courier-1.1.15/tests/test_gmail_auth.py +35 -0
- courier-1.1.15/tests/test_identity.py +263 -0
- courier-1.1.15/tests/test_imap_client.py +2074 -0
- courier-1.1.15/tests/test_imap_client_drafts.py +151 -0
- courier-1.1.15/tests/test_imap_client_threading.py +672 -0
- courier-1.1.15/tests/test_infrastructure.py +375 -0
- courier-1.1.15/tests/test_install_claude_command.py +63 -0
- courier-1.1.15/tests/test_local_cache.py +525 -0
- courier-1.1.15/tests/test_markdown_render.py +76 -0
- courier-1.1.15/tests/test_models.py +139 -0
- courier-1.1.15/tests/test_oauth2_config.py +191 -0
- courier-1.1.15/tests/test_query_parser.py +451 -0
- courier-1.1.15/tests/test_resources.py +291 -0
- courier-1.1.15/tests/test_search_formatters.py +194 -0
- courier-1.1.15/tests/test_send_draft.py +231 -0
- courier-1.1.15/tests/test_server.py +177 -0
- courier-1.1.15/tests/test_sieve_filter.py +230 -0
- courier-1.1.15/tests/test_smtp_client_composition.py +491 -0
- courier-1.1.15/tests/test_smtp_transport.py +226 -0
- courier-1.1.15/tests/test_status_command.py +267 -0
- courier-1.1.15/tests/test_tools.py +624 -0
- courier-1.1.15/tests/test_tools_attachments.py +630 -0
- courier-1.1.15/tests/test_tools_compose.py +389 -0
- courier-1.1.15/tests/test_tools_html_export.py +694 -0
- courier-1.1.15/tests/test_tools_link_extraction.py +512 -0
- courier-1.1.15/tests/test_tools_orchestration.py +113 -0
- courier-1.1.15/tests/test_tools_read.py +149 -0
- courier-1.1.15/tests/test_tools_reply.py +531 -0
- courier-1.1.15/tests/test_tools_reply_drafting.py +163 -0
- courier-1.1.15/tests/test_trash.py +91 -0
- courier-1.1.15/tests/test_utils.py +222 -0
courier-1.1.15/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Weiwu Zhang
|
|
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.
|
courier-1.1.15/PKG-INFO
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: courier
|
|
3
|
+
Version: 1.1.15
|
|
4
|
+
Summary: Email toolkit for AI assistants and command-line scripting
|
|
5
|
+
Author: Weiwu Zhang
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/overseers-desk/courier
|
|
8
|
+
Project-URL: Issues, https://github.com/overseers-desk/courier/issues
|
|
9
|
+
Project-URL: Author, https://www.linkedin.com/in/weiwuzhang/
|
|
10
|
+
Keywords: email,imap,mcp,ai,cli,gmail,automation
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: imapclient>=3.0.0
|
|
22
|
+
Requires-Dist: markdown-it-py>=3.0.0
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
24
|
+
Requires-Dist: requests>=2.32.0
|
|
25
|
+
Requires-Dist: sievelib>=1.5
|
|
26
|
+
Requires-Dist: typer>=0.15.0
|
|
27
|
+
Provides-Extra: mcp
|
|
28
|
+
Requires-Dist: flask>=3.1.0; extra == "mcp"
|
|
29
|
+
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-asyncio>=0.25.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.11.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=0.982; extra == "dev"
|
|
38
|
+
Requires-Dist: pre-commit>=2.19.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# 
|
|
42
|
+
|
|
43
|
+
[](https://github.com/overseers-desk/courier/actions/workflows/code_checks.yml)
|
|
44
|
+
[](https://www.python.org/downloads/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
|
|
47
|
+
Give your script or AI assistant access to your email.
|
|
48
|
+
|
|
49
|
+
## Contents
|
|
50
|
+
|
|
51
|
+
- [What your AI can do with it](#what-your-ai-can-do-with-it)
|
|
52
|
+
- [Installation](#installation)
|
|
53
|
+
- [Configuration](#configuration)
|
|
54
|
+
- [Quick test](#quick-test)
|
|
55
|
+
- [CLI usage](#cli-usage)
|
|
56
|
+
- [Claude Code (terminal-based Claude)](#claude-code-terminal-based-claude)
|
|
57
|
+
- [MCP server](#mcp-server)
|
|
58
|
+
- [Scripting and automation](#scripting-and-automation)
|
|
59
|
+
- [Faster searches](#faster-searches)
|
|
60
|
+
- [Connection handling](#connection-handling)
|
|
61
|
+
- [Security](#security)
|
|
62
|
+
- [License](#license)
|
|
63
|
+
|
|
64
|
+
Courier connects to your existing mailbox on Gmail, Outlook, Fastmail, or any IMAP provider. It does not create new email addresses or route mail through a third-party service.
|
|
65
|
+
|
|
66
|
+
Commandline users, script authors, and AI assistants can search, read, download, reply, send, and organize email. Two interfaces serve different environments: a CLI that outputs JSON (for terminal-based agents, scripts, and automation) and an MCP server (for web-based AI chats and MCP clients). Both expose the same operations.
|
|
67
|
+
|
|
68
|
+
## What your AI can do with it
|
|
69
|
+
|
|
70
|
+
- Courier runs on your machine, with no service of ours between you and your mailbox.
|
|
71
|
+
- Keep banking, OTPs, and sensitive senders out of the LLM's view, using per-mailbox Sieve rules.
|
|
72
|
+
- Reply from the right alias, using our identity feature.
|
|
73
|
+
- Gmail-style queries (`from:alice newer:3d is:unread`) work on any IMAP provider.
|
|
74
|
+
- An optional local `mu` index makes archive-grade searches near-instant.
|
|
75
|
+
- Search every mailbox in one call with `-A`.
|
|
76
|
+
- Drafts on your server by default; `--send` is opt-in.
|
|
77
|
+
- Handle a meeting invite (parse the `.ics`, draft an RSVP).
|
|
78
|
+
- Chain searches and reads in one call: `courier search foo search bar read -f INBOX -u 42`.
|
|
79
|
+
- Move, flag, or archive messages.
|
|
80
|
+
- Download attachments, export messages as HTML, extract links.
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
See [INSTALLATION.md](docs/INSTALLATION.md) for Homebrew, Debian/Ubuntu (.deb), Fedora/RHEL (.rpm), and source installs.
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
Copy the sample and fill in your credentials:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
cp examples/config.sample.toml ~/.config/courier/config.toml
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A small config has three top-level named-entity tables: an `[imap.NAME]` mailbox, an `[smtp.NAME]` outgoing endpoint, and an `[identity.NAME]` describing one sendable address pointing at the IMAP block:
|
|
95
|
+
|
|
96
|
+
```toml
|
|
97
|
+
[smtp.gmail]
|
|
98
|
+
host = "smtp.gmail.com"
|
|
99
|
+
port = 587
|
|
100
|
+
|
|
101
|
+
[imap.personal]
|
|
102
|
+
host = "imap.gmail.com"
|
|
103
|
+
port = 993
|
|
104
|
+
username = "you@gmail.com"
|
|
105
|
+
# For Gmail, generate this at https://myaccount.google.com/apppasswords
|
|
106
|
+
password = "abcdefghijklmnop"
|
|
107
|
+
default_smtp = "gmail"
|
|
108
|
+
|
|
109
|
+
[identity.personal]
|
|
110
|
+
imap = "personal"
|
|
111
|
+
address = "you@gmail.com"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
(Smaller is also valid: `[imap.*]` alone reads but cannot send; `[identity.*]` with `bcc` plus `[smtp.*]` sends but cannot read.)
|
|
115
|
+
|
|
116
|
+
For Gmail, the simpler path is the app-password example above. The alternative is OAuth2, which needs a Google Cloud project set up through Google's developer console (a much messier path); if you have already done that, the same `[imap.NAME]` block carries the OAuth2 keys instead of `password`:
|
|
117
|
+
|
|
118
|
+
```toml
|
|
119
|
+
[imap.personal]
|
|
120
|
+
host = "imap.gmail.com"
|
|
121
|
+
port = 993
|
|
122
|
+
username = "you@gmail.com"
|
|
123
|
+
client_id = "YOUR_CLIENT_ID"
|
|
124
|
+
client_secret = "YOUR_CLIENT_SECRET"
|
|
125
|
+
refresh_token = "YOUR_REFRESH_TOKEN"
|
|
126
|
+
default_smtp = "gmail"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`[smtp.NAME]` blocks declare named SMTP endpoints. When the block omits credentials, courier inherits them from the `[imap.NAME]` block in scope at send time, the right shape for Gmail and Fastmail where IMAP and SMTP share one credential. When the block carries its own `username` and `password`, courier uses those, the right shape for AWS SES and similar smarthosts where one IAM SMTP user serves many From addresses.
|
|
130
|
+
|
|
131
|
+
Sending requires at least one `[identity.NAME]` block pointing at the `[imap.NAME]`. A block with no identities is read-only for sending; drafting and reading still work. This is a valid state, not an error. Declaring identities explicitly avoids the registration-handle hazard, where an IMAP login (e.g. a Gmail handle that is not an intended sender) could otherwise become a sendable identity by accident.
|
|
132
|
+
|
|
133
|
+
`courier config-check` validates the config (cross-references, identity addresses, send-route resolution) without performing any IMAP or SMTP traffic. The same warnings surface on `courier`, `courier --help`, `courier status`, and `courier list`.
|
|
134
|
+
|
|
135
|
+
Gmail OAuth2 setup requires a Google Cloud project with the Gmail API enabled. See [GMAIL_SETUP.md](docs/GMAIL_SETUP.md) for the full walkthrough.
|
|
136
|
+
|
|
137
|
+
For multi-account configs (multiple `[imap.*]` blocks, send-as through several identities, SES smarthost routing), see [docs/CONFIGURATION.md](docs/CONFIGURATION.md).
|
|
138
|
+
|
|
139
|
+
## Quick test
|
|
140
|
+
|
|
141
|
+
With uv (any platform):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
uvx courier search "subject:invoice"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
No installation step; `uvx` runs it directly. To install permanently:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
uv tool install courier
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
On Debian and Ubuntu, the default install path is the `.deb` package from the GitHub release (see [INSTALLATION.md](docs/INSTALLATION.md)). As an alternative for running from a clone without installing, on Ubuntu 25.04 or later the CLI dependencies are all in the standard repositories:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
sudo apt-get install python3-typer python3-dotenv python3-imapclient python3-requests
|
|
157
|
+
python3 -m courier search "subject:invoice"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Courier looks for a config file at `~/.config/courier/config.toml`. Use `--config /path/to/config.toml` to point to a different location.
|
|
161
|
+
|
|
162
|
+
The MCP server (`courier mcp`) requires the `mcp` Python package, which is not in apt. Use `uv` or `pip` for that. Many users prefer the CLI to MCP because the latter loads 80+ tools into every conversation; the CLI is the lighter footprint.
|
|
163
|
+
|
|
164
|
+
## CLI usage
|
|
165
|
+
|
|
166
|
+
Every command outputs JSON to stdout. Errors go to stderr. This makes Courier composable with `jq`, shell scripts, and AI agent skill definitions.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Look up several keywords in one invocation; each gets its own outer key
|
|
170
|
+
# in the result, so hits stay attributed to the keyword that matched them
|
|
171
|
+
courier search "from:alice" search 'subject:"hotel booking"' search "is:unread"
|
|
172
|
+
|
|
173
|
+
# What's unread in INBOX?
|
|
174
|
+
courier search "is:unread" --folder INBOX --limit 10
|
|
175
|
+
|
|
176
|
+
# Read an email
|
|
177
|
+
courier read -f INBOX -u 4523
|
|
178
|
+
|
|
179
|
+
# List and download attachments
|
|
180
|
+
courier attachments -f INBOX -u 4523
|
|
181
|
+
courier save -f INBOX -u 4523 --attachment itinerary.pdf -o /tmp/itinerary.pdf
|
|
182
|
+
|
|
183
|
+
# Export an HTML email as a standalone file (images embedded)
|
|
184
|
+
courier export -f INBOX -u 4523 -o /tmp/email.html
|
|
185
|
+
courier export -f INBOX -u 4523 -o /tmp/email.eml --raw
|
|
186
|
+
|
|
187
|
+
# Extract all links from several emails
|
|
188
|
+
courier links -f INBOX -u 4523 -u 4524 -u 4525
|
|
189
|
+
|
|
190
|
+
# Reply, saved to drafts by default; --send transmits via SMTP
|
|
191
|
+
courier reply -f INBOX -u 4523 -b "Thanks, confirmed."
|
|
192
|
+
courier reply -f INBOX -u 4523 -b "Invoice attached." --attach /tmp/invoice.pdf
|
|
193
|
+
courier reply -f INBOX -u 4523 -b "Thanks, confirmed." --send
|
|
194
|
+
|
|
195
|
+
# Compose a new message (--send requires --identity NAME, or
|
|
196
|
+
# --smtp NAME --from EMAIL; see docs/CONFIGURATION.md)
|
|
197
|
+
courier compose --to alice@example.com --subject "Meeting" \
|
|
198
|
+
-b "See attached." --send --identity work
|
|
199
|
+
|
|
200
|
+
# Send a draft
|
|
201
|
+
courier send-draft -f Drafts -u 4530
|
|
202
|
+
|
|
203
|
+
# Organize
|
|
204
|
+
courier move -f INBOX -u 4523 -t Archive
|
|
205
|
+
courier mark-read -f INBOX -u 4524
|
|
206
|
+
courier flag -f INBOX -u 4525
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Run `courier --help` for the full command list.
|
|
210
|
+
|
|
211
|
+
## Claude Code (terminal-based Claude)
|
|
212
|
+
|
|
213
|
+
Run `courier install-claude-command` once after installation. This writes `~/.claude/commands/courier.md`, which tells Claude Code how to use the courier CLI for email tasks. After that, prompts like "find the booking confirmation from last week" or "reply to Alice's message" route through courier automatically.
|
|
214
|
+
|
|
215
|
+
## MCP server
|
|
216
|
+
|
|
217
|
+
For AI environments that cannot run shell commands (Claude web, Cursor, or any MCP client):
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
courier mcp
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
This starts an MCP server exposing the same operations as tools. The MCP package is only imported when this subcommand runs, so the CLI stays lightweight.
|
|
224
|
+
|
|
225
|
+
## Scripting and automation
|
|
226
|
+
|
|
227
|
+
Because every command returns JSON and uses non-zero exit codes on failure, Courier works as a building block in pipelines and cron jobs.
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# Forward all emails from a sender to another folder
|
|
231
|
+
courier search "from:sender@example.com" --folder INBOX \
|
|
232
|
+
| jq -r '.[].uid' \
|
|
233
|
+
| xargs -I{} courier move -f INBOX -u {} -t Forwarded
|
|
234
|
+
|
|
235
|
+
# Daily digest: save today's unread subjects to a file
|
|
236
|
+
courier search "is:unread" --folder INBOX \
|
|
237
|
+
| jq -r '.[].subject' > ~/daily-digest.txt
|
|
238
|
+
|
|
239
|
+
# Auto-acknowledge incoming invoices
|
|
240
|
+
courier search "is:unread subject:invoice" --folder INBOX \
|
|
241
|
+
| jq -r '.[].uid' \
|
|
242
|
+
| xargs -I{} courier reply -f INBOX -u {} -b "Received, processing." \
|
|
243
|
+
--send --identity work
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
AI agents with skill/hook systems call Courier the same way: define a skill that runs a shell command and parses the JSON output.
|
|
247
|
+
|
|
248
|
+
## Faster searches
|
|
249
|
+
|
|
250
|
+
Courier can answer `search` from a local Xapian index instead of IMAP, orders of magnitude faster, with transparent fallback to IMAP when the index can't serve the query. See [docs/LOCAL_CACHE.md](docs/LOCAL_CACHE.md) for the offlineimap+mu setup.
|
|
251
|
+
|
|
252
|
+
## Connection handling
|
|
253
|
+
|
|
254
|
+
IMAP servers drop idle connections after 10-30 minutes. AI assistants work in bursts: a flurry of operations, then thinking time. Courier tracks connection age and reconnects transparently before operations fail. The default idle timeout is 300 seconds; set `idle_timeout` in the config to adjust.
|
|
255
|
+
|
|
256
|
+
## Security
|
|
257
|
+
|
|
258
|
+
Courier accesses your email account. Store credentials outside your repository (environment variables, a secrets manager, or a config file in `.gitignore`). Use app-specific passwords or OAuth2 rather than your main account password. Restrict `allowed_folders` in the config to limit what the tool can see.
|
|
259
|
+
|
|
260
|
+
For per-message control over what reaches the LLM, point an `[imap.NAME]` block's `redact` field at a Sieve script. Matching messages have subject, body, and party addresses blanked before courier returns them, so banking notices, OTPs, and other sensitive content stay out of the model's context window. See [examples/work-only.sieve](examples/work-only.sieve) for a starting policy.
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT. See [LICENSE](LICENSE).
|
courier-1.1.15/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# 
|
|
2
|
+
|
|
3
|
+
[](https://github.com/overseers-desk/courier/actions/workflows/code_checks.yml)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Give your script or AI assistant access to your email.
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
- [What your AI can do with it](#what-your-ai-can-do-with-it)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Configuration](#configuration)
|
|
14
|
+
- [Quick test](#quick-test)
|
|
15
|
+
- [CLI usage](#cli-usage)
|
|
16
|
+
- [Claude Code (terminal-based Claude)](#claude-code-terminal-based-claude)
|
|
17
|
+
- [MCP server](#mcp-server)
|
|
18
|
+
- [Scripting and automation](#scripting-and-automation)
|
|
19
|
+
- [Faster searches](#faster-searches)
|
|
20
|
+
- [Connection handling](#connection-handling)
|
|
21
|
+
- [Security](#security)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
|
|
24
|
+
Courier connects to your existing mailbox on Gmail, Outlook, Fastmail, or any IMAP provider. It does not create new email addresses or route mail through a third-party service.
|
|
25
|
+
|
|
26
|
+
Commandline users, script authors, and AI assistants can search, read, download, reply, send, and organize email. Two interfaces serve different environments: a CLI that outputs JSON (for terminal-based agents, scripts, and automation) and an MCP server (for web-based AI chats and MCP clients). Both expose the same operations.
|
|
27
|
+
|
|
28
|
+
## What your AI can do with it
|
|
29
|
+
|
|
30
|
+
- Courier runs on your machine, with no service of ours between you and your mailbox.
|
|
31
|
+
- Keep banking, OTPs, and sensitive senders out of the LLM's view, using per-mailbox Sieve rules.
|
|
32
|
+
- Reply from the right alias, using our identity feature.
|
|
33
|
+
- Gmail-style queries (`from:alice newer:3d is:unread`) work on any IMAP provider.
|
|
34
|
+
- An optional local `mu` index makes archive-grade searches near-instant.
|
|
35
|
+
- Search every mailbox in one call with `-A`.
|
|
36
|
+
- Drafts on your server by default; `--send` is opt-in.
|
|
37
|
+
- Handle a meeting invite (parse the `.ics`, draft an RSVP).
|
|
38
|
+
- Chain searches and reads in one call: `courier search foo search bar read -f INBOX -u 42`.
|
|
39
|
+
- Move, flag, or archive messages.
|
|
40
|
+
- Download attachments, export messages as HTML, extract links.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
See [INSTALLATION.md](docs/INSTALLATION.md) for Homebrew, Debian/Ubuntu (.deb), Fedora/RHEL (.rpm), and source installs.
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
Copy the sample and fill in your credentials:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cp examples/config.sample.toml ~/.config/courier/config.toml
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
A small config has three top-level named-entity tables: an `[imap.NAME]` mailbox, an `[smtp.NAME]` outgoing endpoint, and an `[identity.NAME]` describing one sendable address pointing at the IMAP block:
|
|
55
|
+
|
|
56
|
+
```toml
|
|
57
|
+
[smtp.gmail]
|
|
58
|
+
host = "smtp.gmail.com"
|
|
59
|
+
port = 587
|
|
60
|
+
|
|
61
|
+
[imap.personal]
|
|
62
|
+
host = "imap.gmail.com"
|
|
63
|
+
port = 993
|
|
64
|
+
username = "you@gmail.com"
|
|
65
|
+
# For Gmail, generate this at https://myaccount.google.com/apppasswords
|
|
66
|
+
password = "abcdefghijklmnop"
|
|
67
|
+
default_smtp = "gmail"
|
|
68
|
+
|
|
69
|
+
[identity.personal]
|
|
70
|
+
imap = "personal"
|
|
71
|
+
address = "you@gmail.com"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
(Smaller is also valid: `[imap.*]` alone reads but cannot send; `[identity.*]` with `bcc` plus `[smtp.*]` sends but cannot read.)
|
|
75
|
+
|
|
76
|
+
For Gmail, the simpler path is the app-password example above. The alternative is OAuth2, which needs a Google Cloud project set up through Google's developer console (a much messier path); if you have already done that, the same `[imap.NAME]` block carries the OAuth2 keys instead of `password`:
|
|
77
|
+
|
|
78
|
+
```toml
|
|
79
|
+
[imap.personal]
|
|
80
|
+
host = "imap.gmail.com"
|
|
81
|
+
port = 993
|
|
82
|
+
username = "you@gmail.com"
|
|
83
|
+
client_id = "YOUR_CLIENT_ID"
|
|
84
|
+
client_secret = "YOUR_CLIENT_SECRET"
|
|
85
|
+
refresh_token = "YOUR_REFRESH_TOKEN"
|
|
86
|
+
default_smtp = "gmail"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`[smtp.NAME]` blocks declare named SMTP endpoints. When the block omits credentials, courier inherits them from the `[imap.NAME]` block in scope at send time, the right shape for Gmail and Fastmail where IMAP and SMTP share one credential. When the block carries its own `username` and `password`, courier uses those, the right shape for AWS SES and similar smarthosts where one IAM SMTP user serves many From addresses.
|
|
90
|
+
|
|
91
|
+
Sending requires at least one `[identity.NAME]` block pointing at the `[imap.NAME]`. A block with no identities is read-only for sending; drafting and reading still work. This is a valid state, not an error. Declaring identities explicitly avoids the registration-handle hazard, where an IMAP login (e.g. a Gmail handle that is not an intended sender) could otherwise become a sendable identity by accident.
|
|
92
|
+
|
|
93
|
+
`courier config-check` validates the config (cross-references, identity addresses, send-route resolution) without performing any IMAP or SMTP traffic. The same warnings surface on `courier`, `courier --help`, `courier status`, and `courier list`.
|
|
94
|
+
|
|
95
|
+
Gmail OAuth2 setup requires a Google Cloud project with the Gmail API enabled. See [GMAIL_SETUP.md](docs/GMAIL_SETUP.md) for the full walkthrough.
|
|
96
|
+
|
|
97
|
+
For multi-account configs (multiple `[imap.*]` blocks, send-as through several identities, SES smarthost routing), see [docs/CONFIGURATION.md](docs/CONFIGURATION.md).
|
|
98
|
+
|
|
99
|
+
## Quick test
|
|
100
|
+
|
|
101
|
+
With uv (any platform):
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
uvx courier search "subject:invoice"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
No installation step; `uvx` runs it directly. To install permanently:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv tool install courier
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
On Debian and Ubuntu, the default install path is the `.deb` package from the GitHub release (see [INSTALLATION.md](docs/INSTALLATION.md)). As an alternative for running from a clone without installing, on Ubuntu 25.04 or later the CLI dependencies are all in the standard repositories:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
sudo apt-get install python3-typer python3-dotenv python3-imapclient python3-requests
|
|
117
|
+
python3 -m courier search "subject:invoice"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Courier looks for a config file at `~/.config/courier/config.toml`. Use `--config /path/to/config.toml` to point to a different location.
|
|
121
|
+
|
|
122
|
+
The MCP server (`courier mcp`) requires the `mcp` Python package, which is not in apt. Use `uv` or `pip` for that. Many users prefer the CLI to MCP because the latter loads 80+ tools into every conversation; the CLI is the lighter footprint.
|
|
123
|
+
|
|
124
|
+
## CLI usage
|
|
125
|
+
|
|
126
|
+
Every command outputs JSON to stdout. Errors go to stderr. This makes Courier composable with `jq`, shell scripts, and AI agent skill definitions.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Look up several keywords in one invocation; each gets its own outer key
|
|
130
|
+
# in the result, so hits stay attributed to the keyword that matched them
|
|
131
|
+
courier search "from:alice" search 'subject:"hotel booking"' search "is:unread"
|
|
132
|
+
|
|
133
|
+
# What's unread in INBOX?
|
|
134
|
+
courier search "is:unread" --folder INBOX --limit 10
|
|
135
|
+
|
|
136
|
+
# Read an email
|
|
137
|
+
courier read -f INBOX -u 4523
|
|
138
|
+
|
|
139
|
+
# List and download attachments
|
|
140
|
+
courier attachments -f INBOX -u 4523
|
|
141
|
+
courier save -f INBOX -u 4523 --attachment itinerary.pdf -o /tmp/itinerary.pdf
|
|
142
|
+
|
|
143
|
+
# Export an HTML email as a standalone file (images embedded)
|
|
144
|
+
courier export -f INBOX -u 4523 -o /tmp/email.html
|
|
145
|
+
courier export -f INBOX -u 4523 -o /tmp/email.eml --raw
|
|
146
|
+
|
|
147
|
+
# Extract all links from several emails
|
|
148
|
+
courier links -f INBOX -u 4523 -u 4524 -u 4525
|
|
149
|
+
|
|
150
|
+
# Reply, saved to drafts by default; --send transmits via SMTP
|
|
151
|
+
courier reply -f INBOX -u 4523 -b "Thanks, confirmed."
|
|
152
|
+
courier reply -f INBOX -u 4523 -b "Invoice attached." --attach /tmp/invoice.pdf
|
|
153
|
+
courier reply -f INBOX -u 4523 -b "Thanks, confirmed." --send
|
|
154
|
+
|
|
155
|
+
# Compose a new message (--send requires --identity NAME, or
|
|
156
|
+
# --smtp NAME --from EMAIL; see docs/CONFIGURATION.md)
|
|
157
|
+
courier compose --to alice@example.com --subject "Meeting" \
|
|
158
|
+
-b "See attached." --send --identity work
|
|
159
|
+
|
|
160
|
+
# Send a draft
|
|
161
|
+
courier send-draft -f Drafts -u 4530
|
|
162
|
+
|
|
163
|
+
# Organize
|
|
164
|
+
courier move -f INBOX -u 4523 -t Archive
|
|
165
|
+
courier mark-read -f INBOX -u 4524
|
|
166
|
+
courier flag -f INBOX -u 4525
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Run `courier --help` for the full command list.
|
|
170
|
+
|
|
171
|
+
## Claude Code (terminal-based Claude)
|
|
172
|
+
|
|
173
|
+
Run `courier install-claude-command` once after installation. This writes `~/.claude/commands/courier.md`, which tells Claude Code how to use the courier CLI for email tasks. After that, prompts like "find the booking confirmation from last week" or "reply to Alice's message" route through courier automatically.
|
|
174
|
+
|
|
175
|
+
## MCP server
|
|
176
|
+
|
|
177
|
+
For AI environments that cannot run shell commands (Claude web, Cursor, or any MCP client):
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
courier mcp
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This starts an MCP server exposing the same operations as tools. The MCP package is only imported when this subcommand runs, so the CLI stays lightweight.
|
|
184
|
+
|
|
185
|
+
## Scripting and automation
|
|
186
|
+
|
|
187
|
+
Because every command returns JSON and uses non-zero exit codes on failure, Courier works as a building block in pipelines and cron jobs.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Forward all emails from a sender to another folder
|
|
191
|
+
courier search "from:sender@example.com" --folder INBOX \
|
|
192
|
+
| jq -r '.[].uid' \
|
|
193
|
+
| xargs -I{} courier move -f INBOX -u {} -t Forwarded
|
|
194
|
+
|
|
195
|
+
# Daily digest: save today's unread subjects to a file
|
|
196
|
+
courier search "is:unread" --folder INBOX \
|
|
197
|
+
| jq -r '.[].subject' > ~/daily-digest.txt
|
|
198
|
+
|
|
199
|
+
# Auto-acknowledge incoming invoices
|
|
200
|
+
courier search "is:unread subject:invoice" --folder INBOX \
|
|
201
|
+
| jq -r '.[].uid' \
|
|
202
|
+
| xargs -I{} courier reply -f INBOX -u {} -b "Received, processing." \
|
|
203
|
+
--send --identity work
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
AI agents with skill/hook systems call Courier the same way: define a skill that runs a shell command and parses the JSON output.
|
|
207
|
+
|
|
208
|
+
## Faster searches
|
|
209
|
+
|
|
210
|
+
Courier can answer `search` from a local Xapian index instead of IMAP, orders of magnitude faster, with transparent fallback to IMAP when the index can't serve the query. See [docs/LOCAL_CACHE.md](docs/LOCAL_CACHE.md) for the offlineimap+mu setup.
|
|
211
|
+
|
|
212
|
+
## Connection handling
|
|
213
|
+
|
|
214
|
+
IMAP servers drop idle connections after 10-30 minutes. AI assistants work in bursts: a flurry of operations, then thinking time. Courier tracks connection age and reconnects transparently before operations fail. The default idle timeout is 300 seconds; set `idle_timeout` in the config to adjust.
|
|
215
|
+
|
|
216
|
+
## Security
|
|
217
|
+
|
|
218
|
+
Courier accesses your email account. Store credentials outside your repository (environment variables, a secrets manager, or a config file in `.gitignore`). Use app-specific passwords or OAuth2 rather than your main account password. Restrict `allowed_folders` in the config to limit what the tool can see.
|
|
219
|
+
|
|
220
|
+
For per-message control over what reaches the LLM, point an `[imap.NAME]` block's `redact` field at a Sieve script. Matching messages have subject, body, and party addresses blanked before courier returns them, so banking notices, OTPs, and other sensitive content stay out of the model's context window. See [examples/work-only.sieve](examples/work-only.sieve) for a starting policy.
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT. See [LICENSE](LICENSE).
|