Install
$ agentstack add mcp-coopergwrenn-postals-mcp ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Postals
> The agent skill that turns any AI into a direct mail machine. > One tool call. Real handwritten postcards. Delivered by USPS.
[](https://www.npmjs.com/package/postals-mcp) [](https://modelcontextprotocol.io) [](#license)
Send physical handwritten postcards from any MCP-compatible AI agent — Claude Desktop, Cursor, Claude Code, OpenClaw, n8n, LangChain, CrewAI, or your own code. Postals handles image upload, card creation, robotic handwriting (real pen and ink), and USPS mailing.
Website: postals.ai
- 💌 $4.99 per card — no subscriptions, no monthly minimums
- ✍️ Real handwriting — robotic pens hold real ink (not font simulation)
- 📬 3–5 business days — printed and mailed via USPS
- 🤖 Built for agents — one tool call, spend controls, idempotency, test mode
- 🌎 US + international — worldwide delivery, pricing varies by destination
Table of Contents
- [Why this exists](#why-this-exists)
- [Quick start (under 5 minutes)](#quick-start)
- [Client configuration](#client-configuration)
- [Tools reference](#tools-reference)
- [The pool builder workflow](#the-pool-builder-workflow)
- [Message generation](#message-generation)
- [Image handling](#image-handling)
- [Test mode](#test-mode)
- [Spend control](#spend-control)
- [Error handling](#error-handling)
- [Configuration reference](#configuration-reference)
- [Handwriting styles](#handwriting-styles)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [What's next (roadmap)](#whats-next)
- [Contributing](#contributing)
- [License](#license)
Why this exists
A new pattern is emerging in the AI agent ecosystem: autonomous bots that scan the world (satellite imagery, social media, public records), identify opportunities, and reach out with real physical mail. Pool builders, landscapers, roofers, solar installers, real estate agents — they're closing five- and six-figure deals from postcards mailed by bots.
Before Postals, every builder hand-rolled their own mailing stack: image upload, card templating, print API, address validation, USPS handoff, tracking. It took weeks and was fragile.
We ship this as one tool. Your agent calls send_postcard with a recipient and a message. We do the rest.
Quick start
1. Sign up and get an API key
> Coming soon — the developer portal at postals.ai/developers is launching shortly. Once live, the flow is: > > 1. Go to postals.ai/developers > 2. Create an account with your email (one-click magic link — no password) > 3. Fund your wallet ($25 / $50 / $100 / $250 via Stripe or crypto) > 4. Copy your postals_live_ API key > 5. Paste it into your MCP config (see step 2 below) > > That's it. No external accounts needed. Just Postals.
2. Add the server to your MCP client
Option A — ask your coding agent to install it (fastest path)
If you're in Claude Code, Cursor, Continue, Aider, or any other MCP client with filesystem access, just tell your agent:
> Install the postals-mcp MCP server for this client. My Postals API key is: PASTE_YOUR_KEY_HERE. Start in test mode so I can verify the setup before sending real cards.
Your agent will locate your MCP config file, merge the postals entry into your existing mcpServers (preserving any other servers you have), wire up the env vars, and tell you to restart the client. You never touch JSON.
Optional — add return-address details to the same prompt so send_postcard works without per-call overrides:
> ...and use Jane Smith, 123 Main St, Austin TX 78701 as my return address.
Then fully quit and relaunch the client (Cmd+Q on macOS — just closing the window isn't enough).
Option B — edit your MCP config file directly
For Claude Desktop (no built-in filesystem access by default), or any client where you'd rather set it up manually, edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"postals": {
"command": "npx",
"args": ["-y", "postals-mcp"],
"env": {
"POSTALS_API_KEY": "your_api_key",
"POSTALS_SENDER_NAME": "Jane Smith",
"POSTALS_SENDER_ADDRESS": "123 Main St",
"POSTALS_SENDER_CITY": "Austin",
"POSTALS_SENDER_STATE": "TX",
"POSTALS_SENDER_ZIP": "78701",
"POSTALS_TEST_MODE": "true"
}
}
}
}
Configs for other clients (Cursor, Claude Code, OpenClaw) are in the [Client configuration](#client-configuration) section below.
Fully restart the client after editing. (Not just close the window — quit from the menu.)
3. Verify it's working
Open a new chat and ask:
> "List the MCP tools you have available."
You should see send_postcard and generate_message.
4. Send a test card
Still in test mode (POSTALS_TEST_MODE=true), ask:
> "Send a postcard to Jane Smith at 123 Oak St, Austin TX 78701 saying 'Thanks for the great meeting yesterday — looking forward to next steps.'"
The agent will call send_postcard. You'll get back a mock order_id and confirmation that test mode is on. No card mailed, no money spent.
5. Go live
Remove POSTALS_TEST_MODE from your config (or set to false), restart, and your next send will mail a real card.
Client configuration
Copy-paste-ready configs for every major MCP client. If you're in Claude Code, Cursor, Continue, or Aider, you can skip this section entirely — just tell your agent to install Postals (see [Option A in Quick Start](#option-a--ask-your-coding-agent-to-install-it-fastest-path)) and it'll handle the config for you.
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
"mcpServers": {
"postals": {
"command": "npx",
"args": ["-y", "postals-mcp"],
"env": {
"POSTALS_API_KEY": "your_key"
}
}
}
}
Cursor
Global: ~/.cursor/mcp.json Per-project: .cursor/mcp.json in repo root
{
"mcpServers": {
"postals": {
"command": "npx",
"args": ["-y", "postals-mcp"],
"env": {
"POSTALS_API_KEY": "your_key"
}
}
}
}
Claude Code
claude mcp add postals npx -y postals-mcp
Then set env vars in ~/.claude/settings.json under mcpServers.postals.env.
OpenClaw / InstaClaw
See the bundled [SKILL.md](./SKILL.md) for the full agent-facing skill definition. For standalone OpenClaw usage:
mcp_servers:
postals:
command: npx
args: ["-y", "postals-mcp"]
env:
POSTALS_API_KEY: ${POSTALS_API_KEY}
n8n (via Composio — v1.1+)
Install the Postals toolkit from the Composio marketplace. One-click add to any workflow. See Composio docs when available.
Custom agents (programmatic)
Any MCP client library works. Example with the official TypeScript SDK:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "npx",
args: ["-y", "postals-mcp"],
env: { POSTALS_API_KEY: "your_key", POSTALS_TEST_MODE: "true" },
});
const client = new Client({ name: "my-agent", version: "1.0" }, { capabilities: {} });
await client.connect(transport);
const result = await client.callTool({
name: "send_postcard",
arguments: {
to_name: "John Smith",
to_address: "123 Oak St",
to_city: "Austin",
to_state: "TX",
to_zip: "78701",
message: "Hey John — thought you'd love this.",
front_image_url: "https://example.com/render.png",
idempotency_key: "john-smith-123-oak-001",
},
});
Tools reference
send_postcard
Mails one physical handwritten postcard to a US address.
Input
| Field | Type | Required | Description | |-------|------|----------|-------------| | to_name | string | ✓ | Recipient's full name | | to_address | string | ✓ | Street address line 1 | | to_address_2 | string | | Apt/suite/unit | | to_city | string | ✓ | City | | to_state | string | ✓ | 2-letter US state code | | to_zip | string | ✓ | 5-digit ZIP (or 5+4) | | message | string | ✓ | Handwritten message (max 400 chars) | | front_image_url | string | | HTTPS URL to card front image (PNG/JPEG). Falls back to POSTALS_DEFAULT_CARD_IMAGE. | | handwriting_style | string | | Font name. Default: Joyful Jennifer | | return_address | object | | Per-call sender override. Falls back to POSTALS_SENDER_* env vars. | | idempotency_key | string | | Unique dedup key. Same key = same result, no duplicate send. 24h TTL. |
Output (success)
{
"success": true,
"order_id": 196937,
"card_id": 305570,
"recipient": "John Smith",
"estimated_delivery": "3-5 business days via USPS",
"balance_remaining": -1,
"cards_available": -1,
"cards_sent_today": 7,
"daily_limit": 25
}
balance_remaining and cards_available return real values when using a postals_live_ API key from postals.ai/developers.
Output (test mode) adds test_mode: true and mock_order_id. No card is mailed.
Output (error) returns isError: true with an actionable message. See [Error handling](#error-handling).
generate_message
AI-drafts a personalized postcard message. Uses Cloudflare AI if configured; otherwise returns a structured prompt your agent can use to self-generate (which is often just as good, since the agent is already an LLM).
Input
| Field | Type | Required | Description | |-------|------|----------|-------------| | recipient_context | string | ✓ | Who the recipient is and why you're writing | | tone | enum | | warm (default), professional, casual, excited | | max_chars | number | | Max message length. Default 350, max 400. | | sender_context | string | | Who the sender is |
Output (AI configured)
{
"message": "Hey John — drove by your place on Oak St and couldn't help but picture how a pool would transform that backyard. Happy to share a quick render if you're curious. — Kevin, Blue Wave Pools",
"char_count": 212,
"tone": "warm",
"ai_generated": true
}
Output (AI not configured) — returns the system + user prompts your agent can feed into its own LLM:
{
"message": null,
"ai_generated": false,
"prompt": {
"system": "You write short, personal messages for physical handwritten postcards...",
"user": "Recipient: John Smith, homeowner at 123 Oak St...\nSender: Blue Wave Pools\n\nWrite the postcard message now."
},
"instructions": "Cloudflare AI not configured. Use the system/user prompts above to generate the message...",
"tone": "warm",
"max_chars": 350
}
compose_postcard_message (MCP prompt)
An MCP prompt primitive (not a tool). Returns a formatted prompt template for your agent to use with its own LLM. Zero external API dependencies. Useful when you want deterministic, dependency-free message composition.
Arguments: recipient, sender (optional), tone (optional).
The pool builder workflow
This is the viral use case that put autonomous postcard sending on the map. Pool builders run agents that:
- Scan satellite imagery of a neighborhood (Google Maps, Nearmap, etc.)
- Classify — find homes without pools
- Render — generate a personalized before/after showing a pool in that exact backyard
- Compose — write a warm, specific message about that home
- Send — mail the handwritten postcard
- Close — five-figure deals landing weekly
Postals owns steps 4–5 with two tool calls. You own 1–3 (any image pipeline works).
Full example
// Your pipeline produced:
// - homeowner: { name, street, city, state, zip }
// - renderUrl: "https://cdn.mybucket.com/renders/123-oak-st-pool.jpg"
const msg = await generate_message({
recipient_context: `${homeowner.name}, homeowner at ${homeowner.street}. Corner lot with large flat backyard, no pool.`,
sender_context: "Blue Wave Pools — Austin, TX. Specializing in fiberglass pools installed in 14 days.",
tone: "warm",
});
const result = await send_postcard({
to_name: homeowner.name,
to_address: homeowner.street,
to_city: homeowner.city,
to_state: homeowner.state,
to_zip: homeowner.zip,
message: msg.message,
front_image_url: renderUrl,
idempotency_key: `prospect-${homeowner.id}`, // crucial for retries
});
console.log(`Mailed card ${result.order_id}. ${result.cards_sent_today}/${result.daily_limit} today.`);
Why this works
- Real handwriting beats printed mail 5–10× on open rate
- Hyper-personalized image (their actual house!) makes it unignorable
- Warm, specific message feels human, not mass-mailed
- USPS delivery triggers the "this is important" instinct
Variations
| Industry | Scan for | Render | Tone | |----------|----------|--------|------| | Pool builders | No pool in backyard | Pool in their yard | Warm, aspirational | | Landscapers | Overgrown / dead yards | Manicured landscape | Warm, visual | | Roofers | Damaged or aged roofs | New roof render | Professional, urgent | | Solar installers | Large south-facing roofs | Solar panel render | Professional, savings-focused | | Real estate agents | FSBO or expired listings | Staged interior render | Professional, helpful | | Home remodelers | Outdated exteriors | Modern renovation render | Excited, transformative |
All use the same two tool calls. Only the scanning, rendering, tone, and message change.
Message generation
You have three ways to compose the handwritten message. Pick based on your setup:
Option A — generate_message tool with Cloudflare AI
Set CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN. The server calls Llama 3.1 8B Instruct via Cloudflare Workers AI with Postals' proven tone profiles. Returns a ready-to-use message.
"env": {
"POSTALS_API_KEY": "...",
"CLOUDFLARE_ACCOUNT_ID": "...",
"CLOUDFLARE_API_TOKEN": "..."
}
Option B — generate_message tool without AI (recommended for most agents)
Don't set the CF vars. The tool returns a structured prompt (system + user) that your agent uses with its own LLM. Since the calling agent is already an LLM (Claude, GPT, etc.), it generates a message at least as good as Llama 3.1 — often better.
Option C — compose_postcard_message MCP prompt
An MCP prompt primitive (not a tool). Claude Desktop surfaces this in the slash-command menu. The agent injects Postals' guidelines directly into its context and composes.
Option D — Your agent writes the message itself
Skip generate_message entirely. Your agent already knows how to write. Just pass a message directly to send_postcard.
Image handling
The front_image_url must be a public HTTPS URL to a PNG or JPEG. The server fetches the image, uploads it to our print partner and creates a custom card.
Recommended specs
- Format: PNG or JPEG
- Orientation: Landscape (wider than tall)
- Size: 2100 × 1500 px (7×5 inches at 300 DPI) — print quality
- Minimum: 1050 × 750 px
- Max file size: 10 MB
- Accessibility: Public URL, no auth headers, no signed URLs with short TTLs
Common sources
| Source | Example | |--------|---------| | Your own CDN / S3 / Cloudflare R2 | https://cdn.yoursite.com/renders/xyz.png | | AI image generation output | Most services return a public URL | | Static templates | Pre-designed company cards hosted on your site | | Recraft V3 (coming in a future release) | generate_card_image tool will return a URL |
No image?
Set POSTALS_DEFAULT_CARD_IMAGE to your branded fallback. Every call without front_image_url uses the default. This is ideal for business use cases where every ca
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: coopergwrenn
- Source: coopergwrenn/postals-mcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.