HomeiOS DevelopmentPublish | Cocoanetics

Publish | Cocoanetics


Many moons in the past I had the concept I would really like for an agentic system to have the ability to entry my e-mail servers. That got here to me after I automated gathering incoming invoices for my firm with a make.com workflow. However that didn’t quantity to a lot till OpenClaw hit the world’s stage.

The second key challenge in addition to SwiftMail was SwiftMCP. These two collectively had been the primary hyperlink I had between ChatGPT and my e-mail server. SwiftMCP exposes an OpenAPI interface which you could possibly connect with customized GPTs to question and browse your emails. And naturally as native MCP server you could possibly connect with it with MCP from native AI consumer apps as nicely.

After which OpenClaw entered my life and – virtually all the things modified. Lastly I may pursue my imaginative and prescient from two years in the past of having an organization of AI Brokers inside 5 years. The entire sudden I’ve brokers working for me and a few of them may do superb work if solely they may learn my e-mail.

However OpenClaw doesn’t do MCP. The issue with LLM instruments on the whole is that an agentic session wants so as to add numerous schema data into the context. With each server you’ve features for which you want many a instrument’s JSON schema and preserve that in context in order that the LLM can select to emit instrument calls to them in the appropriate format.

LLMs do have skilled information the way to work with scripts and CLIs although. That’s why Peter Steinberger’s major physique of labor in 2025 revolved round constructing CLIs for many issues. And this pattern culminated within the Agent Expertise normal. The idea being that the agent will get a brief description of the talent into reminiscence and if he wants it he can learn up on it in SKILL.md. And most expertise additionally include scripts that the agent then can name. And since good scripts or CLIs have a --help choice, LLMs can simply determine the way to name them for the meant function.

This works so nicely, that I personally made a ton of expertise for myself, about half of which I put up on GitHub. Python is the de issue chief when it comes to portability for expertise.

So I wanted/needed to have a CLI for e-mail. The issue for that although is that CLIs are stateless: you run them, they produce a end result after which they terminate. For one-shot e-mail queries this is likely to be okay, however often you need to do a number of issues in a piece stream: record new emails, obtain some, flag them as seen or junk, trash them, or archive them. With a basic CLI you would need to join, login, do the operation, logout and disconnect each time.

That felt foolish to me.

Additionally I needed to utilize IMAP IDLE the place you get a notification each time one thing modifications in your Inbox. So I needed to have a course of that you just kick off after which stays resident, holding open connections to a number of e-mail servers. So how will you have each issues on the identical time?

The reply is that this: the background course of holding the connections is a daemon. And the CLI connects to it domestically and quick.

That is what I’m supplying you with at this time. Publish has a easy publish command that communicates by way of a neighborhood bonjour+tcp MCP transport to postd working on the identical machine. The daemon is the one the place you configure the server connections. The CLI is the one that permits you to work together with the servers.

🏤Publish – Safe.Agentic.Publish.Workplace

Publish shops server configurations in ~/.publish.json. You possibly can embrace credentials straight within the config file or retailer them securely in a personal macOS Keychain.

Choice 1: Credentials in Config File

Easy however much less safe. Good for improvement or non-sensitive accounts.

{
  "servers": {
    "private": {
      "credentials": {
        "host": "imap.gmail.com",
        "port": 993,
        "username": "[email protected]",
        "password": "your-app-password"
      }
    }
  }
}

Choice 2: Credentials in Keychain (Beneficial)

Publish routinely creates and manages a personal keychain at ~/.publish.keychain-db. The keychain is encrypted with a passphrase derived out of your Mac’s {hardware} UUID, so it’s locked to your particular machine.

{
  "servers": {
    "private": {
      "command": "imap.gmail.com:993"
    }
  }
}

Then add the credentials to the keychain:

# Publish will immediate for username and password
publish keychain add private --host imap.gmail.com --port 993

Your credentials at the moment are saved securely. Publish will retrieve them routinely when wanted.

Primary Operations

For the CLI to do something the daemon must be working. Default mode is for it to run in background, with --foreground you may have it in foreground as nicely.

# Daemon Instructions
postd begin
postd standing
postd cease
postd reload

Emails are recognized by the mixture of server, mailbox and UID. Please notice that UID may develop into invalid and for those who transfer an e-mail to a distinct mailbox it additionally will get a brand new UID. A number of the instructions default to --mailbox INBOX.

Record Unseen Emails

# Record latest 10 messages in INBOX
publish record --server private --limit 10

Fetch an Electronic mail as Markdown

Publish converts HTML emails to wash, readable markdown.

# Fetch UID 12345 and show as markdown
publish fetch 12345 --server private

The output contains headers, physique textual content transformed from HTML, and a listing of attachments. With the --json choice you get beautify JSON for all instructions.

Downloading Attachments

# Obtain first attachment from UID 12345 to present listing
publish attachment --server private --uid 12345

# Or specify which attachment you need and a distinct output listing
publish attachment --filename bill.pdf --server private --uid 12345 --out ~/Downloads

Publish will save every attachment with its unique filename.

Making Drafts

With publish Brokers may also draft emails – however not ship them. You possibly can merely create a wealthy textual content e-mail from a markdown file:

# Create a markdown file
cat > e-mail.md 

Publish will use the markdown for the textual content/plain header and generate a pleasant textual content/html model.

Enabling IDLE for Actual-Time Electronic mail Processing

IDLE is an IMAP extension that retains a connection open and notifies you immediately when new mail arrives. That is excellent for automation.

Configure IDLE in .publish.json

Add idle: true and specify a handler script:

{
  "servers": {
    "private": {
      "command": "imap.gmail.com:993",
      "idle": true,
      "idleMailbox": "INBOX",
      "command": "python3 /Customers/you/process-email.py"
    }
  }
}

Create a Easy Handler Script

Your handler receives e-mail information as JSON on stdin:

#!/usr/bin/env python3
import json
import sys

# Learn e-mail notification
e-mail = json.load(sys.stdin)

topic = e-mail['headers'].get('topic', 'No topic')
sender = e-mail['headers'].get('from', 'Unknown')

print(f"New e-mail from {sender}: {topic}")

# Exit 0 = success, 1 = error, 2 = deliberately skipped
sys.exit(0)

The daemon will watch all configured IDLE mailboxes and name your handler script each time new mail arrives. The script receives full e-mail particulars (headers, markdown physique, attachments) as structured JSON.

Handler JSON Format

Right here’s what your script receives:

{
  "uid": 12345,
  "date": "2026-02-27T10:30:00Z",
  "from": ["[email protected]"],
  "to": ["[email protected]"],
  "topic": "Your e-mail topic",
  "headers": {
    "from": "Sender Identify ",
    "topic": "Your e-mail topic",
    "list-id": "",
    ...
  },
  "markdown": "# Electronic mail bodynnConverted to markdown...",
  "attachments": [
    {
      "filename": "document.pdf",
      "contentType": "application/pdf",
      "size": 102400
    }
  ]
}

Your handler can then resolve what to do: archive it, ahead it, extract information, set off a workflow, and so on.

A number of Scope Assist

You possibly can simply have a number of totally different brokers in OpenClaw, some is likely to be sandboxed, some should not. And also you don’t need each agent to have the ability to entry all configured e-mail servers. That’s the rationale why Postd has the (elective) means to arrange a number of API keys that may solely entry sure servers.

Setting Up Two Scoped API Tokens in Publish

This walkthrough creates two totally different API keys, every restricted to particular mail server IDs, and permits strict token enforcement in postd.

1. Configure your servers

Outline a minimum of two server IDs in ~/.publish.json:

{
  "servers": {
    "work": {},
    "private": {}
  }
}

Set credentials as standard (keychain or inline credentials).

2. Create two scoped tokens

Create one token for work solely:

publish api-key create --servers work

Create one token for private solely:

publish api-key create --servers private

Every command prints a UUID token. Save each.

You possibly can examine what exists with:

publish api-key record

3. Begin/restart daemon so scopes are loaded

postd now preloads API-key scopes at startup. Restart it after key modifications:

postd restart
# or:
postd cease && postd begin

If keychain prompts seem, authorize postd as soon as (want All the time Enable). Opposite to the personal keychain (which is protected by a really lengthy password), the tokens are saved in your login keychain. This fashion you realize if an unknown course of tries to entry them. Due to this, you need to approve postd as soon as for each token you arrange.

Now you may add the POST_API_KEY particular to every agent of their .env. Having an API key within the setting hides the instructions for configuration within the CLI. So if you wish to make modifications you’d must unset the important thing. However notice, if there’s a minimum of one token configured, then all instructions accessing the daemon want you to specify the token.

  • If a minimum of one API key exists, MCP entry requires a token.
  • No token: request fails (API key's required.).
  • Unknown token: request fails (Invalid API key.).
  • Legitimate token, mistaken server: request fails (not approved for server).

You possibly can rotate tokens by simply deleting and recreating them.
publish api-key delete --token
publish api-key create --servers work,private

Engaged on a Mailroom Talent

Giant firms have a mail room, the place all new publish is being delivered, earlier than it will get sorted and routed by the corporate. With Publish now you can have the identical.

I’m engaged on a mail-room talent that already does numerous sorting for me. With immediate injections being the fear of the day, some preliminary sorting could be executed programmatically. Most e-newsletter and notification emails have sure header fields or sender addresses by which they are often acknowledged.

What It Does

The mail-room talent routinely processes incoming emails in real-time as they arrive. Consider it as a sensible assistant sitting in your inbox, sorting mail earlier than you even see it.

Programmatic Sorting (Rule-Based mostly)

The primary line of protection makes use of easy sample matching – no AI wanted:

Newsletters & Mailing Lists are recognized by normal e-mail headers like Record-ID or Record-Unsubscribe. These get filed away to a separate archive, holding your inbox clear.

Service Notifications from identified corporations (GitHub, Amazon, Apple, xAI, LinkedIn, and so on.) are acknowledged by their sender addresses and routinely archived to a notifications folder.

This rule-based sorting is quick, deterministic, and resistant to immediate injection assaults – it’s simply checking headers and e-mail addresses towards a whitelist.

Safe AI-Powered Categorization

For emails that don’t match the straightforward guidelines, the talent makes use of AI to deal with the trickier circumstances. However I’m not having my OpenClaw deal with this. As an alternative I’ve a easy agent that I constructed with the OpenAI Brokers SDK do the categorization. This agent has neither information nor entry to any of my private data. I solely has a instrument with which it will possibly request a markdown illustration of a file attachment.

The Aim: Inbox Zero

After processing:

  • Newsletters → Archived, faraway from inbox
  • Service notifications → Archived, faraway from inbox
  • Spam → Moved to Junk folder
  • Private & enterprise mail → Stays in inbox (unread, to your consideration)

All archived emails are saved as readable markdown recordsdata, organized by sender, with full metadata preserved. Nothing is completely deleted – all the things could be recovered if wanted.

The purpose is easy: solely the emails that want your consideration keep in your inbox. All the things else is routinely sorted, archived, and out of your manner.

Now about these newsletters and notifications: having them as markdown on my laborious drive permits me to have one other course of that picks up fascinating gadgets and compiles a personalised information briefing based mostly on what actually pursuits me.

Conclusion

Apart from its utility, Publish has rapidly develop into my go-to challenge for furthering improvement on SwiftMail, SwiftMCP and SwiftText. The latter I haven’t even talked about till now: It’s a set of features to get markdown from PDFs, HTML recordsdata or DOCX recordsdata, and extra.

Within the mixture of those threes and dealing on real-life utility of agentically dealing with my e-mail proved to be an especially fertile floor for what to enhance and which features are helpful. I haven’t even touched on seek for emails. I plan on placing it on homebrew along with a talent on clawhub as nicely, quickly.

I may write a e book about this all, however for now, this text should suffice. I’m completely satisfied to speak with you about your utilization eventualities, get your bug stories or pull requests on GitHub.


Classes: Administrative

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments