Connecting an AI agent
Who's this for? Anyone using an AI chat client that speaks MCP — Claude Desktop, Claude Code, Codex, Copilot, or your own. Connect once and your agent can read, search, summarise, and reply to mail in your inboxy inbox.
The short setup. Most chat clients only need one URL: https://mcp.inboxy.net. Paste it into your client's MCP config, restart, sign in once via OAuth. Skip to Connecting Claude Desktop for the click-through, or read on for the lay of the land.
What changes after you connect: your agent gains a set of tools grouped by prefix — mail_* for inbox, feed_* and article_* for RSS, address_* for your aliases, account_* for your plan. Read tools are unlimited; the four send-mail tools are scoped so a leaked credential can't be turned into a spam cannon (full rules under What an agent can send).
What the agent can do
Tools are grouped by prefix; most chat clients render them under headings.
mail_* — your inbox
| Tool | Purpose |
|---|---|
mail_list |
Recent mail, filterable by classification, unread, address, since |
mail_count |
{ total, unread } matching a filter — single round-trip |
mail_get |
One message: metadata + attachment list |
mail_get_body |
Parsed text body + headers |
mail_show |
Render an email as Markdown with inline images (proxied) |
mail_summarise |
One-line summary via your client's LLM (MCP sampling — temporarily disabled; degrades to subject + classification) |
mail_forward |
Forward to your verified personal_email (recipient locked server-side, idempotent). Does not consume mail_send_quota |
mail_reply |
Reply to the original sender — recipient + From locked server-side, 5/hour cap, spam-classified messages refused, 30-day age window, every reply audit-logged. Does not consume mail_send_quota |
mail_mark_read / mail_mark_unread |
Single-message read state |
mail_mark_all_read |
Bulk; optional address_id / classification / before filter |
mail_keep / mail_unkeep |
Pin/unpin a message so the retention sweep won't delete it. Cap: 10 currently kept |
mail_unsubscribe |
Surface the unsubscribe methods for a message — List-Unsubscribe header (mailto + URL), RFC 8058 one-click POST flag, and body links matching /unsubscribe|opt-out/. Read-only: returns the URL, never visits or POSTs it |
mail_send |
Send a brand-new message from one of your @inboxy.net aliases to any recipient (external or @inboxy.net). Debits one unit of mail_send_quota per recipient — the same counter the web compose UI debits. Refusal quota_exhausted when the bucket is empty. Internal @inboxy.net → @inboxy.net is DB-only (no SMTP, no footer); external recipients go via the sender worker and get the inboxy attribution footer. Self-loops and reserved @inboxy.net local-parts are refused |
mail_invite |
Invite a friend to sign up for inboxy. to is any external email. Capped at 10 per rolling 30 days per user — independent counter from mail_send_quota, refusal invite_cap_reached. Caller supplies personal_note ≤ 500 chars; subject + body template are operator-controlled. Sent from the mail. transactional subdomain. If the invitee signs up via the included one-time link, the inviter is recorded for a future reward feature. Refused if the address already belongs to an inboxy user (recipient_already_user) |
mail_get_attachment |
Attachment bytes (inline for small images) + signed download URL |
mail_search |
Semantic search across your messages |
mail_delete |
Permanently delete a message and its embedding. Refused for kept messages |
outbox_* — sent replies
| Tool | Purpose |
|---|---|
outbox_list |
List replies you have sent via mail_reply, newest first. Filter by status / kind / in_reply_to_message_id |
outbox_get |
One sent reply — header block + full body_text and body_html |
outbox_count |
{ total } for a status / kind filter — single round-trip |
outbox_delete |
Permanently delete one sent reply. Refused if the row is kept |
Status reflects the send pipeline: queued (in-flight), sent (delivered to the recipient's MX), rejected (refused by the sender worker — see status_reason), suppressed (recipient on the hard-bounce list), failed (queue exhausted retries).
feed_* — RSS feeds
| Tool | Purpose |
|---|---|
feed_list |
List subscriptions with status + per-feed { total, unread } counts |
feed_add |
Subscribe to a feed URL |
feed_remove |
Unsubscribe |
feed_get |
Detail for one subscription |
feed_refresh |
Force a re-poll of one feed (benefits every subscriber) |
feed_summarise |
One-paragraph digest of one feed via your LLM (sampling temporarily disabled) |
article_* — feed articles
| Tool | Purpose |
|---|---|
article_list |
Articles across your feeds, newest first |
article_count |
{ total, unread } across your subscriptions or one feed |
article_get |
One article — link-rewritten HTML + plain text |
article_mark_read / article_mark_unread |
Per-article state |
article_mark_all_read |
Bulk; optional feed_id / before filter |
article_summarise |
One-line summary of one article via your LLM (sampling temporarily disabled) |
address_* — your aliases
| Tool | Purpose |
|---|---|
address_list |
Your @inboxy.net aliases with per-alias { total, unread } counts (include_retired opt-in) |
address_mint |
Mint a new alias (adjective-noun or opaque scheme) |
address_retire |
Stop accepting mail on an alias |
account_* — plan + quota
| Tool | Purpose |
|---|---|
account_get |
One blob: user, subscription (name / since / renews_at), quota (used vs limit, including kept_messages, mail_send_remaining, invites_remaining), rate-limits, retention |
mark_* — cross-domain bulk
| Tool | Purpose |
|---|---|
mark_all_read |
Zero both unread mail AND unread articles in one call. Optional before ISO-8601 scopes the cut-off. Returns { mail_marked, articles_marked } |
What an agent CAN send
inboxy is a "give your AI an inbox" service, not "give your AI your Gmail". Every outbound surface is scoped server-side so a leaked credential can't be turned into a spam cannon.
| Surface | Recipient rule | Quota | Footer? | Where it ends up |
|---|---|---|---|---|
mail_reply |
Locked to the original sender of the message you're replying to. | Own 5/hour cap; no mail_send_quota debit. |
Yes (external). | The sender's MX, via the outbound queue. |
mail_forward |
Locked to your verified personal_email. |
Idempotent; no mail_send_quota debit. |
Yes (external). | Your verified inbox. |
mail_send |
Any address, external or @inboxy.net. Refused if it resolves to your own personal_email / own alias / reserved local-part. |
Debits mail_send_quota (one per recipient). Shared with the web compose UI. Refusal quota_exhausted. |
Yes for external; no for @inboxy.net. |
Recipient's mailbox (DB-only for @inboxy.net). |
mail_invite |
Any external address that isn't already an inboxy user (recipient_already_user). |
10 per rolling 30 days, independent counter. Refusal invite_cap_reached. |
Yes (the friend should know who's inviting them). | Recipient's mailbox, via the mail. transactional subdomain. Signup link is one-time and binds the inviter→invitee relationship in the invites table for a future reward. |
The internal-vs-external rule: an @inboxy.net → @inboxy.net send (whether between two aliases on your account or across users) is delivered in-network — a messages row on the recipient with the raw MIME stored in our EU R2 bucket, fed into the same classifier pipeline that handles inbound MX mail. No SMTP egress, no Cloudflare Email Sending binding involved. Any other recipient is queued onto outbound-send and the sender worker emits SMTP via the apex inboxy.net domain — that's where the attribution footer lands.
Why a footer at all? When inboxy is acting as your agentic sender to a third party, that third party deserves to know what they're replying to. The footer is one line of plain text plus a small HTML block; the sender worker is the single place it's injected, so all four tools above behave identically.
Drop-in skill file
If your client supports user-installed skills (e.g. Claude Code's ~/.claude/skills/), grab the inboxy skill from https://inboxy.net/docs/skill (raw markdown) and drop it at ~/.claude/skills/inboxy/SKILL.md. The rendered version lives at https://inboxy.net/help/skill. The skill briefs the agent on every tool plus the workflow patterns we recommend.
Available prompts
If your chat client supports MCP prompts (e.g. Claude Desktop's / menu), inboxy publishes the following templated prompts. Selecting one seeds a complete playbook into the conversation:
| Prompt | What it does |
|---|---|
triage_unread |
Walks your unread inbox, marks spam-ish stuff read, proposes drafts |
find_receipts_this_month |
Extracts vendor / amount / date from this month's receipts |
summarise_feed |
24-hour digest of one feed (asks you for a feed_id) |
draft_unsubscribe_list |
Finds dead newsletters, surfaces unsubscribe URLs |
daily_brief |
One paragraph: urgent mail, headlines, account warnings |
verify_login_codes |
Lists OTPs / login links from the last hour |
REST callers can read the same prompt bodies via the MCP prompts/get endpoint or hard-code the equivalent system message in their own client.
Connecting Claude Desktop
The MCP server speaks OAuth — no API key needed for chat clients.
Open Claude Desktop → Settings → Developer → Edit Config.
Add:
{ "mcpServers": { "inboxy": { "url": "https://mcp.inboxy.net" } } }Restart Claude Desktop. On first use, it will redirect to inboxy.net to sign you in via OAuth.
Connecting Claude Code / other MCP clients
Same URL: https://mcp.inboxy.net. The client handles OAuth itself.
For clients that need a static API key (CI scripts, server-side automations), use the REST API at https://api.inboxy.net with a key minted under Account → API keys. The MCP server and REST API expose the same data.
Connecting OpenClaw (and other unattended agents)
OpenClaw — and similar third-party agent runners (Codex, Microsoft Scout, Hermes Agent, …) — should be wired against a dedicated alias so the credential, the inbound mailbox, and the outbound From: are all scoped to that one agent. If the key leaks, blast radius is one alias.
1. Mint an alias for the agent
- Account → Addresses → New address. Pick the
adjective-nounscheme (memorable) oropaque(un-guessable). - Label it for the agent ("openclaw", "codex", etc.) so the inbox view stays legible.
2. Mint an address-scoped API key
- Account → API keys → New key.
- Set Scope =
manageand Address =<your-alias>@inboxy.net(the dropdown lists your active aliases). - Copy the token — it's shown once. Store it in OpenClaw's secret manager (never in repo).
A key scoped to an address can only act on that one alias: mail_list / mail_search only see that alias's messages, and mail_send (or mail_reply / mail_forward) auto-pin from_local_part to the bound alias — a caller-supplied value is ignored. Unscoped or wrong-kind keys get a 403.
3. Wire the credential into OpenClaw
mcp.inboxy.net accepts either an OAuth grant (Claude Desktop, Claude Code) or an Authorization: Bearer ik_... API key. For unattended agents like OpenClaw, the bearer path is the right one — it skips the browser sign-in and carries your key's address scope through every tool call.
One-shot install (from your OpenClaw shell):
export INBOXY_KEY=ik_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
openclaw mcp add inboxy \
--url https://mcp.inboxy.net \
--header "Authorization=Bearer $INBOXY_KEY"
A couple of OpenClaw-specific quirks to know:
--headerparses asName=Value, notName: Value. Using a colon produces"Authorization: Bearer …" is an invalid header name.- Set
INBOXY_KEYin your shell or secret store first so the token never lands in OpenClaw's~/.openclaw/openclaw.json(where--headervalues are persisted) or your shell history. Treat~/.openclaw/openclaw.jsonlike a credential file.
If your OpenClaw build prefers a JSON config block instead of the CLI, the equivalent is:
{
"mcpServers": {
"inboxy": {
"url": "https://mcp.inboxy.net",
"headers": { "Authorization": "Bearer ik_…" }
}
}
}
3b. Wire the credential into Hermes Agent
Hermes (Nous Research) reads its MCP server list from ~/.hermes/config.yaml. Add an inboxy entry under mcp_servers using the bearer-token form:
mcp_servers:
inboxy:
url: "https://mcp.inboxy.net"
headers:
Authorization: "Bearer ${INBOXY_KEY}"
Hermes resolves ${VAR} placeholders against ~/.hermes/.env, so drop the token there rather than committing it inline:
# ~/.hermes/.env
INBOXY_KEY=ik_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Hermes-specific quirks vs OpenClaw:
- Headers use colon syntax (
Authorization: "Bearer …"), notName=Value. That's the opposite of OpenClaw's CLI quirk — if you copy-paste between the two, watch the separator. - The config is YAML, indentation matters; mis-indenting
headers:puts the token on the wrong server entry. - Hermes also supports
auth: oauthinstead of static headers, which triggers the same OAuth 2.1 + DCR flow Claude Desktop uses. We recommend the bearer path for unattended setups because address-scoping carries through every tool call.
4. Verify
From OpenClaw, ask the agent to call account_get — should return your plan + quota (including mail_send_remaining). Then address_list should return only the one alias (the key is scoped — if you see other addresses, the scope didn't take). Finally test outbound:
mail_send to=<your-personal-email> subject="hello" text="ping"
You'll see a row in Account → Outbox with source = mail_send and your mail_send_remaining decremented by 1.
Caps and limits to remember
mail_send_quotais per-user, not per-alias — the same counter the web compose UI debits. A leaked address-scoped key can still burn through your bucket, but only from one alias and only against the same per-user cap.- Reserved local-parts (
abuse,security,support,privacy,dpo,dmarc-rua) are refused as recipients. - Self-loops are refused — the agent can't mail your own
personal_emailor any of your own aliases viamail_send. Usemail_forward(which is itself locked to yourpersonal_email) if you want a copy of an inbox item delivered upstream. - Invites have their own counter: 10 per rolling 30 days, independent of
mail_send_quota. Usemail_inviterather thanmail_sendwhen introducing a friend to inboxy.
Why attachment URLs don't need an API key
mail_get_attachment returns either inline bytes (small images) or a short-lived signed URL pointing at api.inboxy.net/v1/attachments/{token}/download. That URL does not require a separate Authorization header — the embedded HMAC token is the auth, validated locally by the api worker. The api bearer-auth middleware explicitly bypasses this path for that reason.
So an MCP client that has OAuth-authorised against mcp.inboxy.net can follow the returned URL with a plain fetch — no extra credentials, no separate API key.
The URL is single-attachment-scoped and expires (TTL 1 hour). If a token leaks, only that one attachment is exposed, and only until it expires.
Browser extensions
For Chrome / Firefox / Edge extensions that want to read inboxy mail without going through an agent, use the browser extension — it does device-bound (DPoP) auth, which doesn't need a long-lived bearer key.
Revoking access
- Revoke an OAuth grant from Account → MCP / OAuth clients.
- Revoke an API key from Account → API keys.
Revocation is immediate; the next request returns 401.
Still need help? support@inboxy.net