bash-send-kindle-icloud
Generated from plugins/bash-send-kindle-icloud/README.md.
Bash tool plugin that exposes three small tools:
build_epub_from_markdownsend_to_kindlesend_markdown_to_kindle
The tools are intentionally separate:
build_epub_from_markdowncreates an EPUB from local Markdown filessend_to_kindlesends an already-existing local file through iCloud Mail SMTPsend_markdown_to_kindlebuilds and sends in one step using a temporary EPUB
The transport is specifically implemented with iCloud Mail SMTP on macOS.
What this plugin does
- Tool name:
build_epub_from_markdown - builds an EPUB from an ordered list of local Markdown files using
pandoc - defaults to a built-in Lua filter that converts local file links into plain text so EPUB readers do not show broken local links
- Tool name:
send_to_kindle - sends a local file as an email attachment
- defaults such as recipient email and Keychain service are read from agent config keys
- uses macOS Keychain to retrieve the iCloud app-specific password at send time
- Tool name:
send_markdown_to_kindle - builds an EPUB from an ordered list of Markdown files
- sends that EPUB through the existing iCloud Mail SMTP path
- removes the temporary EPUB afterward
This plugin is a good fit if you want a model-callable EPUB builder, a separate model-callable sender, and a combined one-shot workflow while keeping SMTP secrets out of plain text config.
Platform and dependency requirements
This plugin is specific to iCloud Mail on macOS for the sending half.
Required dependencies:
- macOS
bashjqpandocpython3- the
securitycommand-line tool from macOS - an iCloud Mail account
- an Apple app-specific password stored in macOS Keychain
iCloud and Kindle setup guide
This plugin sends mail through iCloud SMTP. The safest setup is:
- keep non-secret defaults in config or
.env - keep the iCloud app-specific password only in macOS Keychain
1. Confirm iCloud Mail is enabled
Make sure the Apple account you want to use has iCloud Mail enabled and that you can send mail from it normally.
Useful Apple docs:
- iCloud Mail overview: https://www.icloud.com/mail/
- iCloud Mail server settings: https://support.apple.com/en-us/102525
2. Turn on two-factor authentication for the Apple account
Apple requires two-factor authentication before you can create app-specific passwords.
Apple docs:
- Two-factor authentication: https://support.apple.com/en-us/102660
- App-specific passwords: https://support.apple.com/en-us/102654
3. Generate an Apple app-specific password
- Open the Apple account portal: https://account.apple.com/
- Sign in with the Apple account used for iCloud Mail.
- Go to
Sign-In and Security. - Open
App-Specific Passwords. - Create a new password with a label such as
kindle-smtporcrystal-lattice-kindle. - Copy the generated password immediately.
Important:
- this is not your normal Apple account password
- the generated password is shown once
- if you later rotate or revoke it, update the Keychain entry described below
4. Find your Kindle email address and approve the sender
If you are sending to Kindle, you also need Amazon-side setup.
Amazon docs:
- Send to Kindle overview and supported formats: https://www.amazon.com/sendtokindle
- Kindle personal document email help: https://www.amazon.com/sendtokindle/email
Typical steps:
- Find the Kindle
Send to Kindleemail address for the device or account. - Add your iCloud sender address to Amazon’s approved sender list.
- Confirm Amazon accepts
EPUBfor your target workflow.
5. Store the app-specific password in macOS Keychain
Store the generated password once using the macOS security CLI.
Example:
security add-generic-password \
-U \
-a 'your-icloud-address@icloud.com' \
-s 'kindle-smtp' \
-w 'PASTE_THE_APP_SPECIFIC_PASSWORD_HERE'
Meaning of the fields:
-a: Keychain account name, usually your iCloud SMTP username-s: Keychain service name, which should match your config-w: the app-specific password value
After that, the plugin reads the password from Keychain at send time. The raw password does not need to be stored in repo config, .env, or shell history again.
6. SMTP settings used by this plugin
The plugin uses the standard iCloud SMTP settings documented by Apple:
- host:
smtp.mail.me.com - port:
587 - transport security:
STARTTLS - username: your full iCloud email address
- password: the app-specific password retrieved from Keychain
Plugin installation
Add the plugin to your config and enable bash tools:
{
"plugin_policy": {
"allow_bash_tools": true
},
"mixins": {
"shared_agent_defaults": {
"send_to_kindle_smtp_user": "${env:ICLOUD_KINDLE_SMTP_USER}",
"send_to_kindle_from_addr": "${env:ICLOUD_KINDLE_FROM_ADDR}",
"send_to_kindle_default_to": "${env:ICLOUD_KINDLE_DEFAULT_TO}",
"send_to_kindle_keychain_service": "${env:ICLOUD_KINDLE_KEYCHAIN_SERVICE}"
}
},
"plugins": [
"bash:${env:BUILTIN_PLUGINS}/bash-send-kindle-icloud"
],
"agents": {
"default": {
"provider": "your-provider-id",
"mixin_refs": ["shared_agent_defaults"],
"disabled_plugins": ["send_to_kindle"]
}
}
}
If your config already has a plugins list or plugin_policy, merge the new
entries into the existing objects instead of replacing them wholesale.
Agent configuration keys
Only send_to_kindle reads resolved config keys from the agent config:
send_to_kindle_smtp_usersend_to_kindle_from_addrsend_to_kindle_default_tosend_to_kindle_keychain_service
Recommended pattern:
- keep raw values in
CONFIG_DIR/.env - reference them from config using
${env:...} - let the bash tool host pass those resolved config values into the tool
Example .env values:
ICLOUD_KINDLE_SMTP_USER=your-icloud-address@icloud.com
ICLOUD_KINDLE_FROM_ADDR=your-icloud-address@icloud.com
ICLOUD_KINDLE_DEFAULT_TO=your-kindle-address@kindle.com
ICLOUD_KINDLE_KEYCHAIN_SERVICE=kindle-smtp
Notes:
send_to_kindle_from_addrdefaults tosend_to_kindle_smtp_userif omitted.send_to_kindle_keychain_servicedefaults tokindle-smtpif omitted.- The tool can override the recipient per call using the
toargument.
Tool arguments
build_epub_from_markdown
markdown_filesrequired: ordered list of Markdown file pathsoutput_epubrequired: output path for the generated EPUBtitleoptionalauthoroptionallanguageoptional, defaults toen-UStoc_depthoptional, defaults to2lua_filteroptional, defaults to the plugin's built-in local-link filter
The tool is intentionally simple. It only creates the EPUB.
send_to_kindle
attachmentrequired: local path to the file to attachsubjectoptionalbodyoptionaltooptional
The tool rejects missing files. It does not enforce the .epub suffix itself.
This keeps the helper simple and lets the downstream recipient system decide
whether the attachment is acceptable.
send_markdown_to_kindle
markdown_filesrequired: ordered list of Markdown file pathstitleoptionalauthoroptionallanguageoptional, defaults toen-UStoc_depthoptional, defaults to2lua_filteroptional, defaults to the plugin's built-in local-link filteroutput_filenameoptionaltooptionalsubjectoptional, defaults totitlewhen providedbodyoptional
This tool builds a temporary EPUB, sends it, and removes it. It is the combined convenience tool for the same workflow that the other two tools can perform separately.
When title is omitted, the tool uses the first Markdown heading it finds
across markdown_files as the effective title. When output_filename is
omitted, the tool generates a Kindle-visible attachment filename from the
effective title by lowercasing it, replacing non-alphanumeric runs with -,
and appending a local timestamp in YYYYMMDD-HHMMSS format plus .epub.
Example autogenerated filename:
auth-and-notifications-staged-rollout-20260516-231500.epub
Example tool calls
Build an EPUB from multiple Markdown files
{
"markdown_files": [
"/absolute/path/000-main.md",
"/absolute/path/010-local-notification-polling.md",
"/absolute/path/020-bridge-push-delivery.md",
"/absolute/path/030-authentik-auth-integration.md"
],
"output_epub": "/absolute/path/rollout.epub",
"title": "Auth And Notifications Staged Rollout",
"author": "OpenAI Codex"
}
Send an already-built EPUB
{
"attachment": "/absolute/path/rollout.epub"
}
Override the recipient and subject when sending
{
"attachment": "/absolute/path/to/book.epub",
"to": "someone@example.com",
"subject": "EPUB delivery test",
"body": "Sending this EPUB from the agent tool."
}
Build and send Markdown in one step
{
"markdown_files": [
"/absolute/path/000-main.md",
"/absolute/path/010-local-notification-polling.md",
"/absolute/path/020-bridge-push-delivery.md"
],
"title": "Auth And Notifications Staged Rollout",
"author": "OpenAI Codex",
"subject": "Auth And Notifications Staged Rollout"
}
Build and send Markdown with an explicit attachment filename
{
"markdown_files": [
"/absolute/path/000-main.md",
"/absolute/path/010-local-notification-polling.md"
],
"output_filename": "rollout-for-kindle.epub"
}
Direct shell testing
When invoking send_to_kindle.bash directly, there is no Python host to
inject agent config values. For direct shell tests, export the expected
AGENT_TOOL_CONFIG_* variables manually first.
Example:
export AGENT_TOOL_CONFIG_SEND_TO_KINDLE_SMTP_USER='your-icloud-address@icloud.com'
export AGENT_TOOL_CONFIG_SEND_TO_KINDLE_FROM_ADDR='your-icloud-address@icloud.com'
export AGENT_TOOL_CONFIG_SEND_TO_KINDLE_DEFAULT_TO='your-kindle-address@kindle.com'
export AGENT_TOOL_CONFIG_SEND_TO_KINDLE_KEYCHAIN_SERVICE='kindle-smtp'
Schema for the EPUB builder:
bash plugins/bash-send-kindle-icloud/build_epub_from_markdown.bash schema
Preview for the EPUB builder:
printf '%s' '{
"markdown_files": [
"/tmp/a.md",
"/tmp/b.md"
],
"output_epub": "/tmp/book.epub",
"title": "Demo Book"
}' | bash plugins/bash-send-kindle-icloud/build_epub_from_markdown.bash preview --args-json
Build an EPUB directly:
printf '%s' '{
"markdown_files": [
"/tmp/a.md",
"/tmp/b.md"
],
"output_epub": "/tmp/book.epub",
"title": "Demo Book"
}' | bash plugins/bash-send-kindle-icloud/build_epub_from_markdown.bash run --args-json
Schema for the combined tool:
bash plugins/bash-send-kindle-icloud/send_markdown_to_kindle.bash schema
Dry run for the combined tool:
export SEND_TO_KINDLE_DRY_RUN=1
printf '%s' '{
"markdown_files": [
"/tmp/a.md",
"/tmp/b.md"
],
"title": "Demo Book"
}' | bash plugins/bash-send-kindle-icloud/send_markdown_to_kindle.bash run --args-json
Schema for the sender:
bash plugins/bash-send-kindle-icloud/send_to_kindle.bash schema
Preview for the sender:
printf '%s' '{"attachment":"/tmp/test.epub"}' | \
bash plugins/bash-send-kindle-icloud/send_to_kindle.bash preview --args-json
Dry run for the sender:
export SEND_TO_KINDLE_DRY_RUN=1
printf '%s' '{"attachment":"/tmp/test.epub"}' | \
bash plugins/bash-send-kindle-icloud/send_to_kindle.bash run --args-json
Why this is iCloud-specific
The sending tool always uses:
- SMTP host:
smtp.mail.me.com - SMTP port:
587 - TLS/STARTTLS
It expects an Apple app-specific password stored in Keychain, not a generic arbitrary SMTP configuration. If you later want a provider-neutral SMTP tool, that should be a separate plugin.
Optional interpreter override
If the host process uses an unexpected python3, you can override the helper
interpreter with:
export SEND_TO_KINDLE_PYTHON=/absolute/path/to/python3
The sender tool automatically prefers:
SEND_TO_KINDLE_PYTHONAGENT_TOOL_PYTHONsupplied by the bash tool hostPYTHON${VIRTUAL_ENV}/bin/python3python3fromPATH
License
Copyright 2026 Dynamic Programming Solutions Kft.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.