email.notetoself
Allows sending email notes to yourself via Gmail SMTP, using an app password for authentication.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@email.notetoselfsend me a reminder to water the plants"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
notetoself
A tiny MCP server that emails a note to yourself.
I set this up for personal use with the index01 ring.
Server name:
notetoselfTool:
send_note(message: str) -> strSubject:
NTS:+ first 20 characters of the noteBody: the full message, exactly as received
Transport: legacy SSE. Clients
GET /mcpto open the event stream and POST to/mcp/posts/for messages back.Auth: every request must carry
Authorization: Bearer <token>matching an entry inTOKENS_JSON
1. Install
cd notetoself
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txtRelated MCP server: Gmail MCP Server
2. Create a Gmail App Password
The server uses Gmail SMTP with an App Password (not your normal Gmail password).
Enable 2-Step Verification on the Google account that will send the email (https://myaccount.google.com/security).
Open https://myaccount.google.com/apppasswords and create an App Password.
App name: anything, e.g.
notetoselfGoogle shows you a 16-character password; copy it.
That's your
SMTP_PASS.
3. Configure
Copy .env.example to .env and fill in:
cp .env.example .env
$EDITOR .envSet at minimum:
Variable | Value |
|
|
| The 16-char App Password from step 2 |
|
|
| JSON array of allowed bearer tokens (see below) |
| Comma-separated list of allowed |
Generate a token:
python -c "import secrets; print(secrets.token_urlsafe(32))"Example TOKENS_JSON with a single trusted token:
TOKENS_JSON=["PASTE_TOKEN_HERE"]Multiple clients (each gets its own token):
TOKENS_JSON=["token-aaa","token-bbb"]ALLOWED_HOSTS lists the Host header values the MCP transport security
layer will accept. Defaults to loopback only. If you reach the server via
a reverse proxy on a public domain (e.g. https://yatesframe.com/mcp),
that domain must appear here or every request gets HTTP 421
("Invalid Host header"). The corresponding Origin header is whitelisted
automatically: loopback hosts pair with http://, everything else with
https://.
ALLOWED_HOSTS=127.0.0.1,localhost,[::1],yatesframe.com4. Run
python server.pyYou should see:
notetoself ready on 0.0.0.0:3001 (tokens=1, transport=sse)Smoke test from another shell:
curl http://localhost:3001/health
# {"status":"ok","server":"notetoself"}
# Open the SSE stream. The first event the server sends is the message
# post URL it expects clients to use. Use -N so curl streams the output
# instead of buffering until disconnect.
curl -N http://localhost:3001/mcp \
-H "Authorization: Bearer PASTE_TOKEN_HERE" \
-H "Accept: text/event-stream"The first line you receive is the endpoint event telling you where to
POST messages (it'll be /mcp/posts/?session_id=<hex>). You won't get a
plain tools/list response from curl -- real MCP clients (Claude
Desktop, Cursor, etc.) handle the SSE protocol end-to-end.
The body "remember to buy milk" will arrive at beernutz@gmail.com with subject NTS:remember to buy milk.
5. Wire up an MCP client
Claude Desktop
~/.config/Claude/claude_desktop_config.json:
{
"mcpServers": {
"notetoself": {
"type": "sse",
"url": "http://localhost:3001/mcp",
"headers": {
"Authorization": "Bearer PASTE_TOKEN_HERE"
}
}
}
}Note the type: "sse": this server uses the legacy SSE transport, not
streamable HTTP. The URL is /mcp (the SSE stream endpoint); the message
post URL is discovered automatically from the server's endpoint SSE
event.
If Claude Desktop runs on a different machine on the LAN, replace localhost with the server's LAN IP. Restart Claude Desktop. The send_note tool will show up in the tools list.
Other clients
Any MCP client that supports streamable HTTP works. Point it at
http://localhost:3001/mcp with the single Authorization header above.
File layout
notetoself/
|-- server.py # MCP server entry + Starlette wiring
|-- auth.py # ASGI middleware: bearer-token allowlist
|-- mailer.py # SMTP send + subject/body rules
|-- config.py # .env loading + validation
|-- requirements.txt
|-- .env.example # copy to .env and edit
|-- tests/
| `-- test_mailer_logic.py
|-- deploy/ # systemd unit + env file template + nginx config
| |-- notetoself.service
| |-- notetoself.env.example
| `-- nginx.conf.example
`-- README.md6. Run as a systemd service (Linux)
The repo ships a unit file in deploy/notetoself.service that runs the
server under a dedicated unprivileged user, restarts on failure, and reads
its config from /opt/notetoself/.env (in the app folder).
# 1. Pick an install root and clone/copy the repo there.
sudo install -d -o notetoself -g notetoself -m 0750 /opt/notetoself
sudo cp -r . /opt/notetoself/
sudo -u notetoself python3 -m venv /opt/notetoself/.venv
sudo -u notetoself /opt/notetoself/.venv/bin/pip install -r /opt/notetoself/requirements.txt
# 2. Install the env file in the app folder (mode 0600, owned by the service user).
sudo install -o notetoself -g notetoself -m 0644 \
deploy/notetoself.env.example /opt/notetoself/.env
sudo chmod 0600 /opt/notetoself/.env
sudo -u notetoself $EDITOR /opt/notetoself/.env
# 3. Install + enable the unit.
sudo cp deploy/notetoself.service /etc/systemd/system/notetoself.service
sudo systemctl daemon-reload
sudo systemctl enable --now notetoself.service
sudo systemctl status notetoself.serviceThe unit binds to 0.0.0.0:3001 (configurable via the env file) and logs to
the journal (journalctl -u notetoself.service -f).
7. Reverse proxy with nginx
If you're exposing the server on a public domain (e.g.
https://yatesframe.com/mcp), put nginx in front of uvicorn. SSE is
notoriously easy to break with default nginx settings, so use the
config in deploy/nginx.conf.example rather than rolling your own.
The two non-obvious requirements:
proxy_buffering off;— without it, nginx buffers the SSE stream and your MCP client times out before the first event arrives.No URI in
proxy_pass. Useproxy_pass http://127.0.0.1:3001;andlocation /mcp, notproxy_pass http://127.0.0.1:3001/mcp;. The URI form rewrites/mcp/posts/to/mcpposts/at the backend and breaks the message path.
Also make sure ALLOWED_HOSTS in the env file includes the public
domain — otherwise transport security rejects every request with 421.
# /etc/nginx/sites-enabled/notetoself (or included from your server block)
location /mcp {
proxy_pass http://127.0.0.1:3001;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header Authorization $http_authorization;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
chunked_transfer_encoding off;
}Security notes
The server binds to
0.0.0.0by default, which exposes it to anything that can reach the host onPORT. Put the box behind a firewall (drop inbound traffic to3001from untrusted networks), a reverse proxy with TLS, or restrictHOSTto a loopback address in the env file if you only need local clients.Authorizationheaders carry the bearer token in cleartext without TLS.MCP DNS-rebinding protection is on by default. The
ALLOWED_HOSTSlist governs whichHostheader values are accepted;Originheaders are matched against the same list with a sensible scheme prefix. Add every public domain the server will be reached under, or clients will see HTTP 421 "Invalid Host header".Tokens are compared with
secrets.compare_digest(constant-time).The server never logs message bodies, only
subject=...+body_chars=N.TOKENS_JSONshould be treated as a secret. Anyone with one of the listed tokens can email as you. Don't commit.envor the systemd env file.SMTP errors (wrong password, blocked sign-in, etc.) are returned to the calling LLM as plain text. Don't expose this server on a public network without thinking about that.
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage Gmail messages, threads, labels, drafts, and settings from your workflows. Send and organiz…
Permissioned access to Gmail, Drive and Calendar via the user's own Google account
Let AI agents send email from your own Gmail, Microsoft 365 or SMTP inbox, with guardrails.
1
Related MCP Servers
- FlicenseBqualityCmaintenanceEnables sending emails through Gmail using your Gmail credentials. Provides a simple send_email tool that accepts recipient, subject, and plain text body parameters.1-
- FlicenseNot gradedqualityDmaintenanceEnables sending emails through Gmail using Google's Gmail API. Requires OAuth authentication setup through Google Cloud Console.-
- AlicenseAqualityDmaintenanceEnables sending emails and creating drafts through the Gmail API with OAuth 2.0 authentication.27 npmMIT
- FlicenseNot gradedqualityDmaintenanceEnables sending emails via Gmail API, checking thread replies, and listing sent emails, all logged locally in SQLite.-