Skip to main content
Glama
coopergwrenn

postals-mcp

by coopergwrenn

Postals

The agent skill that turns any AI into a direct mail machine. One tool call. Real handwritten postcards. Delivered by USPS.

npm version MCP compatible 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


Related MCP server: SolMail MCP

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 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) 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 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.

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:

  1. Scan satellite imagery of a neighborhood (Google Maps, Nearmap, etc.)

  2. Classify — find homes without pools

  3. Render — generate a personalized before/after showing a pool in that exact backyard

  4. Compose — write a warm, specific message about that home

  5. Send — mail the handwritten postcard

  6. 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.

  • 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 card shares the same template.


Test mode

Zero-cost, zero-risk testing. Set POSTALS_TEST_MODE=true in your env.

When enabled:

  • All inputs are validated normally (state code, ZIP format, message length, HTTPS URL)

  • Daily limit counter increments (so you can test spend controls)

  • Rate limiter enforces 2-second delays (so you can test timing)

  • The fulfillment API is not called — no cards printed, no money spent

  • Response includes test_mode: true and mock_order_id

{
  "success": true,
  "test_mode": true,
  "message": "Test mode — no card sent. Set POSTALS_TEST_MODE=false to send real cards.",
  "mock_order_id": "test-1713099999999",
  "recipient": "Jane Smith",
  "estimated_delivery": "3-5 business days via USPS",
  "cards_sent_today": 3,
  "daily_limit": 25
}

Best practice: start with POSTALS_TEST_MODE=true, verify your agent's tool calls look right, then flip it off for your first real send.


Spend control

Agents run autonomously. Bad config + no limits = expensive bug. This server ships with four safety layers:

1. Daily limit (POSTALS_DAILY_LIMIT, default 50)

Hard cap on cards per day. Counter resets at midnight UTC. Once reached:

isError: true
"Daily limit reached (25/25). Resets at midnight UTC. Adjust with POSTALS_DAILY_LIMIT env var."

Set this low (5–10) while developing. Raise it once your agent is proven.

2. Rate limiter (2-second minimum between sends)

Firm server-side throttle. If you call send_postcard twice in rapid succession, the second call waits 2 seconds before executing. Prevents API abuse and gives you time to notice runaway behavior.

3. Idempotency keys

Pass idempotency_key in send_postcard args. Same key = same result, no duplicate send. Keys are cached for 24 hours.

await send_postcard({ ..., idempotency_key: "prospect-123-oak-st" });
// If the network hiccups and your agent retries with the same key,
// you get back the original response — no second card mailed.

Critical for autonomous agents. Without this, a network timeout + retry = two postcards.

4. Test mode

See above. Belt and suspenders during development.

Coming in v1.1

Prepaid wallet (pay ahead, spend down) with per-card billing, auto-reload with monthly cap, three funding paths (Stripe, USDC/WLD crypto, agent self-funding), instant pause (revoke API key from dashboard), and balance tracking in every response so agents can self-regulate.


Error handling

Errors return isError: true with an actionable message written for the agent to relay to the human. Examples:

Scenario

Error message

Missing API key

"POSTALS_API_KEY not set. Add it to your MCP server env config. Get a key at postals.ai/developers."

Invalid state code

"Invalid state code \"ZZ\". Must be a 2-letter US state (e.g. CA, NY, TX)."

Invalid ZIP

"Invalid ZIP \"ABC12\". Must be 5 digits (e.g. 78701) or 5+4 (e.g. 78701-1234)."

Missing image + no default

"No card image. Provide front_image_url or set POSTALS_DEFAULT_CARD_IMAGE env var."

Non-HTTPS image URL

"front_image_url must be an HTTPS URL."

Missing return address

"No return address. Provide return_address or set POSTALS_SENDER_* env vars."

Daily limit hit

"Daily limit reached (25/25). Resets at midnight UTC. Adjust with POSTALS_DAILY_LIMIT env var."

Image fetch failed

"Send failed: Cannot fetch image from URL (HTTP 404). Ensure the URL is publicly accessible."

Fulfillment error

"Send failed: <classified error — address, message length, auth, etc.>"

Error messages are designed so your agent can read them and either self-correct or relay the fix to the human in plain language.


Configuration reference

All config is via environment variables in your MCP client config.

Variable

Required

Default

Description

POSTALS_API_KEY

Yes

—

Your API key from postals.ai/developers.

POSTALS_SENDER_NAME

Recommended

—

Default sender full name

POSTALS_SENDER_ADDRESS

Recommended

—

Default sender street address

POSTALS_SENDER_CITY

Recommended

—

Default sender city

POSTALS_SENDER_STATE

Recommended

—

Default sender 2-letter state code

POSTALS_SENDER_ZIP

Recommended

—

Default sender ZIP

POSTALS_DEFAULT_CARD_IMAGE

Recommended

—

HTTPS URL of default card front image (used when caller omits front_image_url)

POSTALS_HANDWRITING_STYLE

Optional

Joyful Jennifer

Default handwriting font

POSTALS_DAILY_LIMIT

Optional

50

Max cards per day. Set low while developing.

POSTALS_TEST_MODE

Optional

false

true = skip real sends, return mock responses

CLOUDFLARE_ACCOUNT_ID

Optional

—

Enables AI message generation via Llama 3.1

CLOUDFLARE_API_TOKEN

Optional

—

Required with CLOUDFLARE_ACCOUNT_ID

Per-call overrides: return_address, handwriting_style, and front_image_url in send_postcard args override the env defaults for that one send.


Handwriting styles

Default: Joyful Jennifer — warm, friendly, genuine handwriting.

Common handwriting style options:

  • Joyful Jennifer — warm, friendly feminine

  • Tarzan — bold, energetic

  • Casual Craig — relaxed masculine

  • Fancy Fiona — elegant script

  • Precise Paul — neat, business-like

Pass the exact style name as handwriting_style in the tool args, or set POSTALS_HANDWRITING_STYLE globally. To see all styles available on your account, browse available styles at postals.ai/developers.


Troubleshooting

Claude Desktop doesn't show the tools

  1. Fully quit Claude Desktop (menu → Quit, not just close window) and reopen. Config changes require a full restart.

  2. Verify the config JSON is valid (no trailing commas, correct quotes).

  3. On macOS, check the path: ~/Library/Application\ Support/Claude/claude_desktop_config.json.

  4. Run npx -y postals-mcp manually in a terminal. If it errors, fix that first.

npx -y postals-mcp hangs

That's expected — it's waiting for MCP protocol input on stdin. To test it standalone, use the included test harness:

cd src/MCP/postals-mcp
npm install
npm run build
POSTALS_API_KEY=test POSTALS_TEST_MODE=true node test-harness.mjs

"POSTALS_API_KEY not set"

The env var isn't being passed to the server. Check that:

  • Your MCP client config has the env block set correctly

  • You restarted the client after editing config

  • The key is wrapped in quotes in JSON

"Cannot fetch image from URL"

The image URL must be:

  • A public HTTPS URL (no http://, no file://, no data:)

  • Accessible without auth headers or cookies

  • Returning a valid PNG or JPEG with Content-Type: image/png or image/jpeg

Test with curl -I <url> — should return 200 and a valid image content-type.

Cards aren't arriving

  1. Is POSTALS_TEST_MODE set to true? If so, no cards are actually mailed. Remove it or set to false.

  2. Check the order status at postals.ai/developers for the order_id returned by the call.

  3. Delivery is 3–5 business days after the card is mailed (not ordered). Printing happens within 1 business day.

Rate limiter seems too slow

The 2-second minimum is intentional and firm. It prevents API abuse and gives you a chance to notice runaway behavior.


FAQ

How much does it cost? $4.99 per card. No monthly minimums, no subscriptions. Volume discounts starting at 101 cards/month (v1.1).

How fast does it arrive? 3–5 business days via USPS after mailing. Cards are printed and mailed within 1 business day of the API call.

Is the handwriting really handwritten? Yes. Custom-built robots hold real pens and write in real ink on real paper. Recipients can't tell it's robotic.

What's the open rate on handwritten mail? Typically 5–10× higher than printed direct mail. Personalized imagery on the front (AI-rendered) further lifts response rates.

What countries are supported? US addresses ship for $4.99/card with 3-5 business day USPS delivery. International addresses are supported — delivery takes longer and pricing varies by destination. Contact us for international rates.

Can I send letters or packages? Postcards only in v1.0. Letters, envelopes, and greeting cards planned for later releases.

What image formats work? PNG or JPEG. Landscape 7×5 inches (2100×1500 px at 300 DPI) is ideal.

Can I cancel a send? Not in v1.0. Cancel-within-printing-window (cancel_postcard tool) is planned for v1.1.

Can I check delivery status? Not in v1.0. check_order_status tool is planned for v1.1.

What happens if the agent has a bug and tries to send 1000 cards? Set POSTALS_DAILY_LIMIT low (5–10) while developing. Send #11 returns an error instead of mailing. Combined with idempotency_key for retries and 2-second rate limiting, the three most common runaway scenarios are covered. v1.1 adds a wallet with a hard balance cap — once you hit $0, the agent stops.

Can the agent fund itself from crypto earnings? Coming in v1.1. Three funding paths: Stripe (human pays), crypto deposit (human sends USDC/WLD), and agent self-funding (agent spends from its own wallet — human-authorized with monthly cap). This enables fully autonomous business loops: agent earns crypto → funds wallet → sends cards → closes deals → earns more.

Can I use this commercially? Yes. MIT license. Cards are billed per-send, no license fees.

How does pricing compare to Lob or PostGrid? Lob and PostGrid do printed postcards at $0.48–$0.77. We do handwritten postcards at $4.99. Different product. The handwriting premium pays for itself via the dramatically higher open and response rates for the kinds of outbound use cases where this matters (real estate, home services, high-ticket sales).

Can multiple agents share one API key? Yes, but they share the daily limit. Use separate keys per agent for isolation (v1.1+ supports multiple keys per account).

Does this work with langchain / crewai / autogen? Any framework with MCP client support works. The TypeScript/Python MCP SDK connects to this server like any other MCP tool. See the Custom agents section for code.

What data do you log? Structured JSON events to stderr only. We log: event name, timestamp, city/state (for debugging), counter values, order IDs. We never log: full addresses, full messages, recipient names, API keys, or image URLs with auth tokens.


What's next

Current: v1.0 — $4.99/card.

Coming in v1.1 (2–3 weeks):

  • Postals developer signup at postals.ai/developers

  • Prepaid wallet (pay ahead, spend down) with Stripe + USDC crypto

  • check_balance tool — agents see their balance in every response

  • check_order_status tool — poll fulfillment status

  • cancel_postcard tool — cancel within printing window

  • No external account required

Coming in v2.0 (6–8 weeks):

  • Full developer dashboard at postals.ai/developers

  • Auto-reload with monthly cap

  • Webhook events (card_sent, low_balance, daily_limit_hit)

  • Multiple API keys per account

  • Path 3: Agent self-funding from its own crypto wallet (human-authorized)

  • USPS CASS-certified address validation

  • Volume pricing tiers (101–500: $4.49, 501–2000: $3.99, 2000+: custom)

Coming in v3.0+:

  • generate_card_image tool — Recraft V3 AI card art

  • Card template library (thank-you, birthday, business, real estate, etc.)

  • Multi-provider fulfillment (handwritten + printed options)

  • International mail (Canada, UK, EU)

  • Batch send tool for high-volume workflows

  • Letters and envelopes

More on the roadmap and architecture at postals.ai.


Contributing

Issues and PRs welcome at the GitHub repo. For major changes, open an issue first to discuss.

Running the test harness:

npm install
npm run build
POSTALS_API_KEY=test POSTALS_TEST_MODE=true node test-harness.mjs

All 37 checks should pass.



License

MIT © Postals

Available Tools

2 tools
generate_messageA

Generate a personalized handwritten message for a postcard based on context about the recipient. Returns a ready-to-use message you can pass directly to send_postcard. Uses AI if Cloudflare credentials are configured (CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN); otherwise returns a structured prompt you can use to compose the message yourself.

ParametersJSON Schema
NameRequiredDescriptionDefault
toneNoMessage tonewarm
max_charsNoMax character count for the message
sender_contextNoWho is sending the card. Example: 'Blue Wave Pools — local pool builder in Austin, TX'
recipient_contextYesWho the recipient is and why you're writing. Example: 'John Smith, homeowner at 123 Oak St. Sending a before/after postcard showing what a pool would look like in his backyard.'

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It discloses that the tool uses AI when credentials are present and otherwise falls back to producing a structured prompt, and it names the exact environment variables. This adds real behavioral context beyond the schema, though it stops short of describing error handling or the exact shape of the structured prompt.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler: the first names the action and output, the second covers the conditional AI behavior and fallback. Inessential details are omitted, and the key pipeline relationship is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description fills that gap by explaining the return value ('ready-to-use message') and how it feeds into send_postcard. It also documents the credential condition. The main missing piece is the structure of the fallback prompt, but the rich input schema and clear pipeline make the tool callable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and each parameter already has detailed descriptions, examples, defaults, and an enum. The description reinforces that recipient_context is the central input but adds little semantic value beyond the schema, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Generate a personalized handwritten message for a postcard' based on 'context about the recipient.' It also ties the output directly to the sibling tool ('pass directly to send_postcard'), clearly distinguishing this generation step from the sending step.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes the usage context clear: generate first, then pass the result to send_postcard. It also gives conditional guidance based on whether Cloudflare credentials are configured, though it does not explicitly list when not to use the tool or describe alternative approaches.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_postcardA

Send a physical handwritten postcard to a US mailing address. Postals prints it with real robotic handwriting (pen and ink) and mails it via USPS. Delivery in 3-5 business days. Cost: $4.99 per card. Provide the recipient's address, a message (max 400 chars), and optionally an image URL for the card front. If no image is provided, uses the configured default card.

ParametersJSON Schema
NameRequiredDescriptionDefault
to_zipYes5-digit ZIP code (e.g. 78701 or 78701-1234)
messageYesThe handwritten message for the back of the card. Max 400 characters. Keep it personal and concise.
to_cityYesCity
to_nameYesRecipient's full name (e.g. 'Jane Smith')
to_stateYesTwo-letter US state code (e.g. CA, NY, TX)
to_addressYesStreet address line 1
to_address_2NoStreet address line 2 (apt, suite, etc.)
return_addressNoSender's return address. If omitted, uses POSTALS_SENDER_* env defaults.
front_image_urlNoPublic HTTPS URL of the image for the card front (PNG or JPEG, landscape 7x5 preferred). If omitted, uses POSTALS_DEFAULT_CARD_IMAGE.
idempotency_keyNoUnique key to prevent duplicate sends on retries. If a prior send with this key succeeded, returns the original result without sending a new card. Recommended for autonomous agents.
handwriting_styleNoHandwriting font name. Default: "Joyful Jennifer". Other options depend on your Postals account.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses cost ($4.99 per card), delivery time (3-5 business days), and the physical action (robotic handwriting, USPS). It also explains the default-card fallback. Since the annotations only offer basic hints (openWorldHint, non-idempotent, non-destructive), the description carries the behavioral burden well. It does not mention irreversibility or error handling, but the cost and physical nature imply permanence.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four sentences with no redundancy. It front-loads the main purpose, then gives operational details (delivery time, cost), then explicit instructions and fallback behavior. Every sentence contributes essential information, making it both concise and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With 11 parameters and no output schema, the description covers the core flow and key defaults but leaves out optional parameters like idempotency_key, return_address, and handwriting_style. These are fully documented in the schema, so the description doesn't need to repeat them. However, it does not mention what the tool returns (e.g., a tracking ID), which is a minor gap given the lack of an output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds little beyond a summary: it reminds the agent to provide address, message, and optionally an image, and repeats the 400-char limit already in the schema. It does not enrich parameter meaning beyond the schema's own descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Send a physical handwritten postcard to a US mailing address.' It clearly distinguishes itself from the sibling tool generate_message, which is for creating message content, not sending physical mail. The inclusion of 'physical,' 'robotic handwriting,' and 'USPS' leaves no ambiguity about what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use the tool: when you need to send a physical postcard to a US address. It implies the alternative (generate_message) by focusing on the send action, but it does not explicitly compare against it or state exclusions. The context is unambiguous, but explicit 'when not to use' guidance is missing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv1.0.0
    • First observedgenerate_message
    • First observedsend_postcard

TDQS

A4.1/5.0

Scored across 2 tools

Disambiguation5/5

The two tools are clearly distinct: one sends a postcard, the other generates a message. No overlap in purpose or functionality, making selection unambiguous.

Naming Consistency5/5

Both tool names follow a consistent verb_noun pattern: 'send_postcard' and 'generate_message'. This is uniform and predictable.

Tool Count3/5

With only two tools, the server feels minimal. This might be acceptable for a focused niche, but it is on the thin side compared to typical MCP servers that provide more operations like status checking or address validation.

Completeness3/5

The core functionality of sending a postcard and generating a message is covered, but there's no way to track delivery, cancel, or manage addresses. This leaves notable gaps for a complete lifecycle, though the server might intentionally be minimal.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables AI assistants to create, design, and send physical postcards directly through the PostcardAI platform. It provides comprehensive tools for managing contacts, generating postcard designs from prompts, and tracking mailing delivery metrics.
    -
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to send physical letters and postcards to over 200 countries using Solana cryptocurrency for payment. It provides tools for generating mail quotes, managing wallet balances, and automating physical correspondence directly through the Model Context Protocol.
    4
    37 npm
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Send real physical postcards worldwide via AI agents. Supports single and bulk send (up to 500 recipients), balance checking, delivery tracking, and volume pricing from $0.72/card.
    5
    4 npm
    5
    MIT