- lint job now uses astral-sh/ruff-action instead of provisioning a
full Python toolchain via setup-python just to pip install ruff —
the action just downloads the standalone Ruff binary.
- build job's cache: pip was hanging on every run: the Gitea runner
has no reachable Actions-cache backend, so setup-python's cache
restore step was timing out on a connect (ETIMEDOUT) before
falling back and continuing anyway. Removed since deps here are
tiny and a cold install costs seconds, not minutes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- forward_to_poke now checks the response's success field instead of
trusting the HTTP status alone; Poke can return 200 with
success: false on a soft failure, which was previously logged and
treated as delivered.
- README/start.sh pointed users to Settings > Advanced for the API
key, which issues a legacy pk_ key incompatible with the
inbound/api-message endpoint this project uses. Now points to
poke.com/kitchen -> API Keys for a V2 key.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Replace type=gha cache (broken on Gitea: unparseable runtime token)
with type=registry cache using image-manifest/oci-mediatypes options
that the Gitea registry accepts.
- Push with a Gitea PAT (REGISTRY_TOKEN) since the auto job token
cannot publish OCI images to the package repo.
- Add registry login to the CI docker smoke-test job.
Add .ruff.toml ignoring BLE001/S110 (intentional mail parsing), apply
ruff auto-fixes (imports, Optional->X|None), and fix format, shebang and
PORT env default.
The IDLE watcher previously re-baselined last_seen_uid on every
reconnect (e.g. after iCloud's unsolicited FETCH FLAGS responses
during IDLE caused idle_check to raise). Mail that arrived during the
short disconnect window had a UID below the new baseline and was
silently dropped, so Poke never saw it.
Hoist last_seen_uid and last_uidvalidity to the outer reconnect loop
so the cursor survives transient disconnects. Reset only on first run
or when UIDVALIDITY changes (which means the server has reassigned
UIDs and the previous cursor is meaningless).
Apply the same fix to _poll_folder via a shared mutable cursor dict so
non-IDLE servers also retain their cursor across poll-error reconnects.
Fixes#5
start.sh previously generated and persisted an MCP_API_KEY to .env
unconditionally on first run. In tunnel mode (POKE_TUNNEL=1, the
default) the local server runs unauthenticated and the Poke tunnel
handles auth — generating a key there is at best useless and at worst
overwrites the user's pre-set key, causing 421 errors at the tunnel.
Move .env loading and POKE_TUNNEL resolution above the generation
block, and skip generation entirely when POKE_TUNNEL=1. Re-source .env
after a successful generation so the rest of the script sees the new
value.
Fixes#9
Some providers (notably netease 163.com / 126.com / yeah.net) reject
clients that don't issue an RFC 2971 ID command after login. Send a
minimal client identification when the server advertises the ID
capability. Failures are logged at debug and never break login.
Fixes#10
- New list_accounts MCP tool: cheap inbox discovery with no IMAP I/O
- account_id is now required on all 11 per-account tools (no silent
fallback to first configured account)
- resolve_account: whitespace-tolerant, case-insensitive id matching,
ambiguity detection on email-address fallback, clear errors that
point agents at list_accounts
- get_server_info: description clarifies it does live IMAP checks
- Version bumped to 1.1.0
- README: tool count 12 -> 13, breaking-change note, account_id
requirement called out
- Add __version__ = "0.1.0" to src/server.py
- Add release.yml workflow to create GitHub Releases on v* tags
- Refactor start.sh OTA to use GitHub Releases API instead of commit SHAs
Emails forwarded to Poke are no longer marked as read by default,
so users can continue using read/unread status to organize their
inbox. Set mark_as_read: true (global or per-account) to restore
the previous behavior.
Closes#5
imapclient.DRAFTS does not exist (only imapclient.DRAFT, which is a
message flag, not a folder attribute). Replaced with direct list_folders
iteration checking for the \Drafts special-use flag, matching the
approach used by detect_archive_folder.
- Add DropNonMCPRoutes middleware to return empty 404 for non-MCP paths
- Add per-IP sliding window rate limiter with separate GET/POST buckets
- Include account_id and from_address in webhook forwarding payload
- Fix IDLE watcher to always check UNSEEN instead of filtering by EXISTS
- Track seen UIDs to avoid re-forwarding on subsequent IDLE cycles
- Switch to uvicorn.run() with Starlette middleware stack
- start.sh: detect POKE_TUNNEL env var; skip MCP_API_KEY requirement and
auth when running via poke tunnel (server.py reads the same var)
- start.sh: pass POKE_TOKEN into Python via env var + use json.dumps to
safely escape quotes/backslashes in YAML (fixes shell-interpolation
injection risk, Copilot issue #5 / start.sh:72)
- start.sh: anchor MCP_API_KEY guard to non-commented line-start
assignments and also detect empty value (Copilot issues #1, #8 /
start.sh:104)
- start.sh: anchor re.sub pattern with re.MULTILINE so only the actual
assignment line is rewritten, not mid-line occurrences (Copilot
issue #2)
- start.sh: check re.sub replacement count, warn when poke_api_key key
is missing from config.yml (Copilot issue #6)
- start.sh: guard npm/npx usage with command -v check; fall back to npx
poke instead of hard-failing (Copilot issue #3)
- start.sh: use python3 consistently for server.py (Copilot issue #9 /
start.sh:134)
- start.sh: prefer npx poke tunnel; check command -v poke and fall back
gracefully (Copilot issue #10 / start.sh:135)
- server.py: honour POKE_TUNNEL=1 — skip bearer-token auth so the poke
tunnel handles identity; MCP_API_KEY becomes optional in that mode
- README.md: add Node.js/npm prerequisite note (Copilot issue #4)
- README.md: clarify server starts on first run; update AI agent prompt
(Copilot issue #11 / README.md:48)
Add a custom GET /mcp health route so health checks and client polling
return 200 instead of flooding logs with 405. Also allow resolve_account
to match by email address (from_address, imap_username, smtp_username)
in addition to account ID.
Adds a copy-pasteable prompt for non-technical users to set up poke-mail
via their AI coding agent, a start.sh script that loads .env, activates
the virtualenv, starts the server, and tunnels to Poke, and recommended
container resource limits for orchestrators.