sendmux-attachments

内容来源:clawhub · 原始地址 · 查看安装指南

原始内容


name: "sendmux-attachments" description: "Move email attachments through Sendmux without putting file bytes in model context, using file paths, presigned URLs, CLI, SDKs, or MCP." version: "1.3.0" metadata: openclaw: skillKey: "sendmux-attachments" homepage: "https://github.com/Sendmux/skills" primaryEnv: "SENDMUX_API_KEY" envVars: - name: "SENDMUX_API_KEY" required: false description: "Optional Sendmux API key or scoped agent token used by CLI, SDK, HTTP, or MCP examples." - name: "SENDMUX_MBX_KEY" required: false description: "Optional Sendmux mailbox key for Mailbox and send-capable mailbox workflows."


Sendmux attachments

ClawHub account note

This ClawHub skill connects OpenClaw agents to Sendmux. Some workflows require a Sendmux account and an appropriate Sendmux API key or agent token. Sendmux account usage is external to ClawHub; do not ask users to paste secrets into chat.

Use this skill whenever a Sendmux task involves attachment bytes.

Core rule

Do not pipe real files through model context as base64 unless the file is tiny and agent-authored. Prefer paths or signed URLs.

Mode Use when Token cost Limit
Local file_path Local stdio MCP can read the user-shared file root. tiny Mailbox cap: 7,500,000 bytes; Sending upload cap: 18 MiB.
Presigned upload URL Hosted MCP, shell-capable agents, or large local files. tiny Mailbox cap: 7,500,000 bytes; Sending upload cap: 18 MiB; exact size is signed.
CLI --attach / SDK file helpers Terminal or application code can read the file. tiny Mailbox cap for mailbox sends; Sending upload cap: 18 MiB and final message cap: 25 MB.
Inline base64 Small generated text/files only. high MCP inline cap is 32 KiB decoded.

Approximate base64 cost: 25 KB becomes about 11K generated tokens; 1 MB is impractical. A file path is usually under 100 tokens.

Security model

  • A caller must authenticate to mint upload URLs or upload directly.
  • The later presigned PUT has no Authorization header, but it only works with the unguessable short-lived signed URL and exact headers returned by Sendmux.
  • Do not invent file-type allow-lists. Set the best Content-Type; let Sendmux return the real validation error if a file is rejected.
  • For presigned PUT, send the exact Content-Type and Content-Length returned with the URL.
  • Direct Sending API binary uploads require exact Content-Length. CLI, SDK, and MCP file helpers calculate it for you.
  • Do not try to bypass upload size caps. For mailbox uploads, split or externally host files over 7,500,000 bytes.
  • For MCP reads, call mailbox_read_attachment first. It returns inline text for text-like attachments and a link for binary or oversized files.
  • For direct downloads, use the download_url in attachment metadata promptly. If it expires, fetch the message or attachment metadata again.
  • Sending API sends use attachment_id refs returned by Sending upload endpoints. Mailbox sends use blob_id refs returned by mailbox upload endpoints. Do not mix them.

MCP

Mailbox upload and read

Use mailbox_upload_attachment before mailbox_send_message.

Local stdio, cheapest path:

mailbox_upload_attachment
filename: report.pdf
content_type: application/pdf
file_path: /absolute/path/report.pdf

The file path must be inside a filesystem root shared by the MCP client. Hosted MCP rejects file_path.

Hosted or shell-capable path:

mailbox_upload_attachment
filename: report.pdf
content_type: application/pdf
size_bytes: 5242880
presign_upload_url: true

Then upload without an API key:

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  -H "Content-Length: 5242880" \
  --data-binary @./report.pdf

Use the returned blob_id in mailbox_send_message:

{
  "attachments": [
    {
      "blob_id": "blob_...",
      "filename": "report.pdf",
      "content_type": "application/pdf"
    }
  ]
}

For tiny generated content only, use content_base64. If the tool rejects size, switch to file_path, presigned upload, CLI, or SDK file helpers.

To read inbound attachments, call mailbox_read_attachment with message_id and attachment_id.

mailbox_read_attachment
message_id: msg_...
attachment_id: att_...

Use returned text directly for text-like files. If the tool returns resource_link / download_url, fetch the link promptly outside model context. Use mailbox_get_attachment only when metadata is enough or you need to refresh an expired link. Do not construct attachment URLs manually.

Sending API upload

Local stdio, cheapest path:

sending_upload_attachment
filename: report.pdf
content_type: application/pdf
file_path: /absolute/path/report.pdf

Use the returned attachment_id in sending_send_email or sending_send_email_batch:

{
  "attachments": [{ "attachment_id": "att_..." }]
}

Hosted or shell-capable path:

sending_create_attachment_upload
filename: report.pdf
content_type: application/pdf
size_bytes: 5242880

Then PUT the file bytes to the returned upload_url with the returned headers, including X-Sendmux-Upload-Token; do not add a Sendmux API key:

curl -X PUT "$UPLOAD_URL" \
  -H "X-Sendmux-Upload-Token: $UPLOAD_TOKEN" \
  -H "Content-Type: application/pdf" \
  -H "Content-Length: 5242880" \
  --data-binary @./report.pdf

Use the attachment_id from the upload response in the send request. Use sending_get_attachment only for metadata checks.

CLI

Mailbox send with a local attachment in one command:

SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux mailbox:send-message \
  --idempotency-key "$IDEMPOTENCY_KEY" \
  --attach ./report.pdf \
  --body '{
    "to": [{ "email": "user@example.com", "name": null }],
    "subject": "Report",
    "text_body": "Attached."
  }' \
  --json

Sending API with a local attachment:

SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux sending:send \
  --idempotency-key "$IDEMPOTENCY_KEY" \
  --attach ./report.pdf \
  --body-file ./email.json \
  --json

Upload a Sending attachment first, then send by attachment_id:

SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux sending:upload-attachment \
  --body-file ./report.pdf \
  --query filename=report.pdf \
  --query content_type=application/pdf \
  --json

Presigned mailbox upload from a local file:

SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux mailbox:upload-attachment \
  --file ./report.pdf \
  --via-presigned \
  --json

Mint only:

SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux mailbox:create-attachment-upload \
  --file ./report.pdf \
  --json

Override MIME type with --content-type only when inference is wrong.

Direct HTTP

Sending API direct upload with an API key:

SIZE_BYTES="$(wc -c < ./report.pdf | tr -d '[:space:]')"

curl -X POST "https://smtp.sendmux.ai/api/v1/emails/attachments?filename=report.pdf&content_type=application/pdf" \
  -H "Authorization: Bearer $SENDMUX_MBX_KEY" \
  -H "Content-Type: application/pdf" \
  -H "Content-Length: $SIZE_BYTES" \
  --data-binary @./report.pdf

Sending API delegated upload:

curl -X POST "https://smtp.sendmux.ai/api/v1/emails/attachment-uploads" \
  -H "Authorization: Bearer $SENDMUX_MBX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename":"report.pdf","content_type":"application/pdf","size_bytes":5242880}'

Then PUT to the returned upload_url with returned headers and no Sendmux API key. Use GET /emails/attachments/{attachment_id} only for metadata checks.

TypeScript

Node file helpers live under Node subpaths so browser bundles stay clean.

Mailbox:

import { createMailboxClient } from "@sendmux/mailbox";
import {
  readMailboxTextAttachment,
  sendMailboxMessageWithFiles,
} from "@sendmux/mailbox/node";

const client = createMailboxClient({ apiKey: process.env.SENDMUX_API_KEY! });

await sendMailboxMessageWithFiles({
  client,
  files: ["./report.pdf"],
  headers: { "Idempotency-Key": idempotencyKey },
  body: {
    to: [{ email: "user@example.com", name: null }],
    subject: "Report",
    text_body: "Attached.",
  },
});

const text = await readMailboxTextAttachment({
  client,
  messageId: "msg_...",
  attachmentId: "att_...",
});

Sending API:

import { createSendingClient } from "@sendmux/sending";
import { sendEmailWithFiles } from "@sendmux/sending/node";

const client = createSendingClient({ apiKey: process.env.SENDMUX_API_KEY! });

await sendEmailWithFiles({
  client,
  files: ["./report.pdf"],
  headers: { "Idempotency-Key": idempotencyKey },
  body: {
    from: { email: "sender@example.com" },
    to: { email: "user@example.com" },
    subject: "Report",
    html_body: "<p>Attached.</p>",
  },
});

The combined package also exposes @sendmux/sdk/node.

Python

Mailbox:

from sendmux_mailbox import create_mailbox_client, read_mailbox_text_attachment, send_mailbox_message_with_files

client = create_mailbox_client(api_key=api_key)

send_mailbox_message_with_files(
    client,
    files=["./report.pdf"],
    body={
        "to": [{"email": "user@example.com", "name": None}],
        "subject": "Report",
        "text_body": "Attached.",
    },
    idempotency_key=idempotency_key,
)

text = read_mailbox_text_attachment(
    client,
    message_id="msg_...",
    attachment_id="att_...",
)

Sending API:

from sendmux_sending import create_sending_client, send_email_with_files

client = create_sending_client(api_key=api_key)

send_email_with_files(
    client,
    files=["./report.pdf"],
    body={
        "from": {"email": "sender@example.com"},
        "to": {"email": "user@example.com"},
        "subject": "Report",
        "html_body": "<p>Attached.</p>",
    },
    idempotency_key=idempotency_key,
)

Routing

  • Sending content and recipient approval: sendmux-send-email.
  • Mailbox search, triage, reply flow: sendmux-mailbox-agent.
  • Exact terminal command mechanics: sendmux-cli.
  • MCP installation or hosted/local setup: sendmux-mcp-setup.
  • General token-efficiency decisions: sendmux-token-efficient-usage.