New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

replylayer-mcp

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

replylayer-mcp

MCP server for ReplyLayer — safe email for AI agents

latest
Source
npmnpm
Version
0.15.1
Version published
Weekly downloads
547
-43.14%
Maintainers
1
Weekly downloads
 
Created
Source

replylayer-mcp

The MCP server for ReplyLayer — safe email for AI agents. It exposes ReplyLayer's email surface (send, reply, read, monitor, release) as MCP tools so an MCP client (Claude Desktop, Cursor, …) can drive transactional and operational email with built-in security scanning. Not a bulk or marketing tool.

Server identity: replylayer v0.15.1. 44 tools. The hosted server (POST /v1/mcp on api.replylayer.ai) reports the same identity and injects the same server-level workflow primer as this binary — both read the version from this package, so neither can drift from it. The hosted sign-in endpoint (POST /v1/mcp/oauth) lists all of them except the four in AGENT_UNAVAILABLE_TOOL_NAMES: a sign-in connection is an agent key, which can never use those four, and its primer describes the connection instead of an API key. Its tools also carry OAuth linking metadata (securitySchemes, top-level and in _meta, and _meta["mcp/www_authenticate"] on a tool error caused by a revoked or expired token).

Install

Published to npm as replylayer-mcp. Most MCP clients launch it via npx (no global install needed) — see Client config. To run or install it directly:

npx -y replylayer-mcp        # or: npm install -g replylayer-mcp

Credentials

Auth is a single ReplyLayer API key. The server resolves it from the REPLYLAYER_API_KEY environment variable first, then from the stored credentials file at ~/.replylayer/credentials. Production (https://api.replylayer.ai) is the default endpoint; REPLYLAYER_API_URL is a testing-only override.

Set up the stored credential interactively (writes ~/.replylayer/credentials, mode 0600):

replylayer-mcp init        # or, without a global install: npx -y replylayer-mcp init

For CI / automation, skip init and pass REPLYLAYER_API_KEY in the environment.

A key only works once a human has finished the account's one-time signup verification: confirming the account's email address and then its mobile number (a 6-digit SMS code, texted once the email is verified). Web signup at https://app.replylayer.ai/signup walks through both before any key is created. A key returned by rly signup stays inert until rly auth verify and rly auth verify-phone succeed; until then calls fail with EMAIL_NOT_VERIFIED or PHONE_NOT_VERIFIED. The server runs with a key a human has already provisioned and verified.

Client config (copy-paste)

Claude Desktop (claude_desktop_config.json) or Cursor (mcpServers) — launch via npx so there's no global install or absolute path to manage:

{
  "mcpServers": {
    "replylayer": {
      "command": "npx",
      "args": ["-y", "replylayer-mcp"],
      "env": {
        "REPLYLAYER_API_KEY": "rly_live_k3m9p2qx7vn4hjd0.uZ8Qb1vK3mN0pR7sT2wX9yA4cF6gH8jL1nP3rT5vW7z"
      }
    }
  }
}

If you ran replylayer-mcp init to store the key, the env block is optional. With a global install (npm i -g replylayer-mcp), use "command": "replylayer-mcp" and drop the args.

The directional safety model

Every message is security-scanned, and scanning is directional:

  • Outbound text that resembles prompt-injection is your agent's own authored content — delivered clean. The secrets scanner is outbound-only.
  • Inbound prompt-injection is quarantined.

The send tools (send_email, reply_to_message, send_draft) return an outcome — branch on the outcome, not on success/failure. The discriminator is email_effect.effect_status: sent | held_for_review | held_infrastructure | blocked (the Governed Email Effect contract). Treat an effect_status you do not recognize as held, never as sent. Where email_effect is absent, fall back to the top-level status: sent | quarantined | pending_review | blocked. sent means ReplyLayer accepted the message for sending — it is not proof the recipient received it.

Those send responses also carry the why inline: scan (the scanner verdict — inspect scan.verdict, scan.findings, and scan.findings[].agent_instructions) and hold_context (the hold reason plus agent_instructions on held sends — a typed policy cause, a policy change to the scanner's decision, or a genuine scan-explained hold; null on sent/terminal outcomes and normally on infrastructure holds). A held send's hold_context.review_url is the dashboard page where the account owner approves or releases it (omitted when the server has no public dashboard URL); a held result leads with a HELD FOR REVIEW — not sent / HELD — not sent line telling the agent to tell the account owner (the person the agent acts for) where it waits — and by when, from hold_context.review_expires_at — and not to send it again. An infrastructure hold gets neither the link nor that line (retry later instead). Call read_message for full detail. A finding with failure_class: "inference_error" is an infrastructure error, not a content judgment — retry later, do not rewrite.

Recovery: inbound quarantined → release_quarantined_message; firewall_blocked → release_firewall_blocked_message; pending_review needs the account owner's approval in the dashboard (agent keys cannot approve — tell the account owner where, do not resend). RATE_LIMITED → if details.reason is failed_authentication, the API key is wrong or revoked: stop and ask a human to fix the key, do not retry; if details.reset_at is present it is the daily send quota (back off until then); otherwise it is a short-window limit — wait for details.retry_after (or the time in the error message), then retry. UNAUTHORIZED / API_KEY_REVOKED → the key is wrong or revoked: stop and ask a human, do not retry. RECIPIENT_SUPPRESSED / BILLING_* / EMAIL_NOT_VERIFIED → escalate, do not retry.

This server does no implicit retry (neither on 5xx nor on a 429 Retry-After): every tool call fails fast and returns the structured error, so the agent owns retry timing and a tool call is never held open by a silent server-side wait.

Idempotent sends

send_email and reply_to_message take an optional idempotency_key. Pick one stable key per send or reply intent and reuse that exact key for every retry of that intent: a replay returns the original outcome instead of sending a second message. The record is permanent. On IDEMPOTENT_REQUEST_IN_FLIGHT, retry the same key after the indicated delay. On IDEMPOTENT_REQUEST_NOT_PROVEN_SENT, stop and report it for investigation — the message was not re-sent, and minting a new key could duplicate it.

create_draft's idempotency_key is a different thing: it is valid only alongside send_at (scheduled sends) and replays for 24 h, not permanently.

Trusted instruction sources

By default every inbound read carries the standing untrusted-content contract: agent_safety_context.untrusted_content: true plus a guidance string telling the agent not to follow instructions embedded in the message body. Relaxing that on a given read is entirely server-side and operator-configured — this server has no client-side opt-in or env var for it. The server relaxes a read only when the customer has (a) put the mailbox into instruction_trust_mode: 'enabled', (b) enabled this API key's instruction-trust capability, and (c) designated that exact sender address (address-grain only — no domain-wide trust) as a trusted instruction source, and the message itself is verified_aligned (Mailgun-verified domain authenticity), clean-scanning, and available from that sender. When all of that lines up, guidance is replaced with trusted-instruction guidance (the agent may act on that verified sender's own explicit requests in this message) and a thin instruction_trust basis object is attached — untrusted_content stays true. This is read-relaxation only: it never changes how an outbound send is gated.

Default is off end to end: no operator configuration → no relaxation → byte-identical legacy reads. There is currently no MCP tool to grant, list, or revoke trusted sources — see the note at the end of Tool surface.

Tool surface (44 tools)

Reads (no side effects): list_messages (search + filters + paging), read_message, get_thread, list_threads, wait_for_message, list_mailboxes, list_recipients, list_suppressions, list_allowlist, list_allowlist_blocked_attempts, list_drafts, get_draft, list_inbound_blocklist, list_inbound_allowlist, list_inbound_firewall_blocked_attempts, get_account_usage, get_agent_quota, get_attachment_preview, get_link_scanning_status.

get_link_scanning_status reports whether malicious link scanning (URL reputation, backed by Google Web Risk) is active for the account. Turning it on is an admin action (it enables an account-wide sub-processor data flow), so the MCP surface is read-only here — enable it from the dashboard, the SDK, or rly account link-scanning enable.

Sends (dispatch outbound mail): send_email, reply_to_message, send_draft.

Structured results. Every tool except remove_suppression (which always returns an error) declares an outputSchema and returns structuredContent — a typed object you can consume without re-parsing the text blob — alongside the text block, on every transport. The text block is the same JSON. Each schema follows the API route's response schema and describes the result after the fields the tool leaves out (see Tool notes): objects accept fields the API adds later, enum-like values are plain strings, and only the top-level fields the route always returns are required. A tool error (isError: true) carries no structuredContent, except a blocked or infrastructure-held send, which keeps its send result.

Drafts: create_draft, update_draft, delete_draft (plus send_draft above).

Inbound triage: release_quarantined_message, block_quarantined_message, report_and_block (block + blocklist the sender, inbound-only), release_firewall_blocked_message. Human-in-the-loop (admin-only; agent keys 403): approve_review, deny_review.

Delete: delete_message — soft-deletes a message (any direction) and purges its raw MIME; agent keys require the account delete policy to be enabled (else 403).

Policy / contacts: add_recipient, add_suppression, add_inbound_blocklist, add_inbound_allowlist_entry, mark_message_read, mark_thread_read.

Star / unstar: star_message (toggle a message's starred flag), star_thread (toggle a thread's starred flag).

Bulk policy mutations: add_inbound_allowlist_bulk (up to 1000 sender addresses for a mailbox's inbound allowlist in one call), add_suppressions_bulk (up to 1000 addresses to the account-wide do-not-contact list), add_inbound_blocklist_bulk (up to 1000 addresses to the account-wide inbound blocklist).

Policy stub (intentionally unavailable): remove_suppression — always returns an isError result explaining that suppression removal is a per-human admin decision; use rly suppressions remove, an SDK admin key, or the dashboard instead.

Not yet a tool: trusted-instruction-source management (grant / list / revoke, plus the mailbox mode and per-key capability toggles — see above) has no MCP wrapper today. That includes the revoke endpoint, which — like the inbound-triage release tools — is unprivileged and agent-key-callable server-side; it just isn't exposed here yet. Manage trusted sources from the ReplyLayer dashboard (or the REST API directly) in the meantime.

Tool annotations

Every tool declares a title (mirrored in annotations.title, which Anthropic's directory reads) and explicit boolean readOnlyHint, destructiveHint and openWorldHint; every tool that changes something also declares idempotentHint. One tools/list serves every client, so where client guidelines differ the stricter reading wins. The hints are advisory for the host. The API enforces the permission gates.

GroupToolsread-onlydestructiveopen-worldidempotent
Readsthe 19 read tools listed abovetruefalsefalsenot declared
Sendssend_email, reply_to_message, send_draft, approve_reviewfalsetruetruefalse
Scheduled sendscreate_draft, update_draft (a send_at dispatches with no further call)falsetruetruefalse
Irreversible state changesdeny_review, block_quarantined_message, release_quarantined_message, release_firewall_blocked_messagefalsetruefalsefalse
Draft deletedelete_draft (deleting a scheduled draft sends a message.schedule_cancelled webhook to the account's own endpoint)falsetruetruefalse
Irreversible, safe to repeatdelete_message (a re-delete succeeds), report_and_block (a re-report changes nothing further)falsetruefalsetrue
List and marker writesadd_suppression, add_suppressions_bulk, add_inbound_blocklist, add_inbound_blocklist_bulk, add_inbound_allowlist_entry, add_inbound_allowlist_bulk, mark_message_read, mark_thread_read, star_message, star_threadfalsetruefalsetrue
Recipient writeadd_recipient (emails the recipient a confirmation link; with attest, sends nothing but makes the person sendable)falsetruetruefalse
Stubremove_suppression (always an error)truefalsefalsenot declared

Tool notes

  • add_recipient — wraps POST /v1/recipients (free Sandbox accounts only; other tiers get 403 FORBIDDEN, and on a paid plan the description tells the agent that a mailbox restricted to people the account owner approves needs the user to approve the person for that mailbox). Input: email, optional attest: boolean. Without attest the person is emailed a confirmation link and becomes sendable when they click it. With attest: true no email is sent and the person is sendable at once: the account vouches for them, spending one of its per-trial attestations (never refunded), and the result carries attestation: "account_attested" and a complaint_notice. The description tells the agent to attest only when the user has said they know the person. If someone added that way reports the account's email as spam, everyone added that way must confirm by link before they can be emailed again and the account loses its attestations; a report from a second person suspends the Sandbox account. An agent key, including every sign-in connection on /v1/mcp/oauth, is refused with INSUFFICIENT_SCOPE until the operator turns agent recipient writes on, and with RECIPIENT_WRITE_AGENT_CONTAINED when a bound mailbox only lets the agent email people the account owner approves; in both cases the hint says to ask the user to add the person (for this tool, INSUFFICIENT_SCOPE does not suggest an admin key). Attesting with none left is TIER_LIMIT with attestations_remaining: 0 (forfeited: true after a complaint), or attestations_limit: 0 while attestation is off. RECIPIENT_WRITE_UNAVAILABLE means retry once; if it fails again, stop and tell the user. A send refused with SANDBOX_RECIPIENT_NOT_VERIFIED gets a hint naming add_recipient.
  • star_message — wraps PATCH /v1/messages/:id/star. Input: message_id, starred: boolean. Use one tool call to star or unstar (matches the SDK/REST {starred} shape — there are no separate star/unstar verbs).
  • star_thread — wraps PATCH /v1/mailboxes/:id/threads/:thread_id/star. Input: mailbox, thread_id, starred: boolean.
  • get_attachment_preview — wraps GET /v1/messages/:id/attachments/:idx/preview. Returns derived text preview only (PDF, Office, text/plain, text/csv). Surfaces 404 ATTACHMENT_PREVIEW_NOT_AVAILABLE as an isError result when the derivative is not ready, failed, or unavailable.
  • add_inbound_allowlist_bulk — wraps POST /v1/mailboxes/:id/inbound-allowlist/bulk. Input: mailbox, emails: string[] (max 1000). Returns the full partial-success shape: added, already_existed, invalid (with per-entry reason), and counts.
  • add_suppressions_bulk — wraps POST /v1/suppressions/bulk. Input: emails: string[] (max 1000). Same partial-success shape.
  • add_inbound_blocklist_bulk — wraps POST /v1/inbound-blocklist/bulk. Input: emails: string[] (max 1000). Same partial-success shape.
  • remove_suppression — policy-explanation stub. Always returns isError with code SUPPRESSION_REMOVE_NOT_AVAILABLE. Improves agent UX by explaining why removal is not available via MCP rather than returning a generic unknown-tool error.
  • wait_for_message timeout — the tool reflects the real server cap: max 30 s per call (not 120 s). For longer waits, call the tool again.
  • Fields left out of results — on every transport the tools drop internal identifiers and bookkeeping from the API response before building the text block and structuredContent (src/lib/trim-response.ts): reviewed_by_id and six attachment bookkeeping fields (read_message, get_thread), r2_objects_failed (becomes r2_objects_failed_count, always present, delete_message), added_by_actor_id (the suppression, blocklist and allowlist tools), actor_id (list_allowlist_blocked_attempts), warmup.velocity_gate_mode (get_agent_quota), attachment.hash and preview.extractor (get_attachment_preview), and the scanner, PII-detector, instruction-trust and consent-bookkeeping fields (list_mailboxes). A vendor-named suppression source reads provider_webhook or provider_sync, and an unrecognised one reads system. A 400 validation error keeps only instancePath and message per details.validation item, and SELF_HOSTED_DECRYPT_FAILED drops details.domain_id. next_cursor is an opaque paging token; pass it back unchanged. The REST API, SDK and CLI are unchanged. The full table is in the MCP guide.
  • list_drafts — returns has_more beside drafts; pass the last draft's id as before for the next page.

Inbox search and full thread reads are first-class on MCP: list_messages filters by keyword search, sender, date range, direction, status (incl. the held-mail states quarantined / pending_review for triage) and starred, and pages older via before=<oldest message id from the previous page>. list_threads lists conversations (with starred / has_inbound), and get_thread reads a full thread — pass mailbox to disambiguate a thread key that resolves in more than one mailbox. list_messages also supports has_attachment (true/false) — it probes /v1/health for the messages.has_attachment_filter capability and returns a structured SEARCH_OPERATOR_UNSUPPORTED on older servers that don't advertise it.

Hosted vs local attachments

send_email, reply_to_message, create_draft and update_draft take an attachments array. What that array means depends on which server you are talking to.

Local server (this package, npx -y replylayer-mcp) — attachments are paths on your own machine. The server reads each file, uploads it, waits for its content scan, and references the resulting handles on the send. This is the normal way to attach files.

Hosted server (POST /v1/mcp on api.replylayer.ai) — the hosted server never reads files. It runs in ReplyLayer's own infrastructure, so a path in attachments would name a file on our server, not yours. Supplying a non-empty attachments array is refused before any file is opened, with a stable code:

{
  "error": "The hosted MCP server cannot read files from your machine. To attach files, run the local MCP server (npx -y replylayer-mcp) or upload through the REST API / SDK and send from there; hosted attachment handles are not yet available on MCP.",
  "code": "HOSTED_LOCAL_ATTACHMENTS_UNSUPPORTED",
  "details": { "attachments": 1 }
}

details reports only how many paths you sent; the paths themselves are never echoed back.

To attach a file on the hosted server, pick one of:

  • Run the local MCP server (npx -y replylayer-mcp) and use attachments as documented above; or
  • Upload through the REST API or an SDK — POST /v1/attachments returns a handle id, and POST /v1/messages/send accepts attachment_ids. Send from there.

Two details worth knowing:

  • An empty attachments: [] is not a rejection — it behaves exactly like omitting the field.
  • A same-key idempotent retry still replays. If an earlier send from the local server carried real paths and you retry it against the hosted server with the same idempotency_key, the replay probe runs first and returns the original result. The attachments parameter therefore stays in the hosted schema (so such a retry still parses) — only its description changes, and it is refused on a probe miss.

Hosted attachment handles on MCP are not available yet; when they land, they will be an explicit handle-id parameter rather than a path.

Changelog

  • 0.15.1 — Adds mcpName (ai.replylayer/replylayer) so the official MCP Registry can list this package. No tool, schema or behaviour change.
  • 0.15.0 — Moves to the MCP TypeScript SDK v2 (@modelcontextprotocol/server). The stdio binary and the hosted endpoints now serve the 2026-07-28 protocol revision (server/discover, which returns the same server instructions initialize does) alongside the 2025-era initialize handshake, from one server factory; a client that opens with initialize is served exactly as before. Tool names, descriptions, input and output schemas, annotations and results are unchanged (the tools stay on zod 3 through an adapter that reproduces the previous JSON Schema). Two SDK-generated error texts change: invalid tool arguments read Input validation error: Invalid arguments for tool <name>: <field>: Required (an isError result, as before), and calling a tool that is not registered is a JSON-RPC -32602 "Tool not found" error rather than an isError result. The replylayer-mcp/tools entry now exports buildReplyLayerMcpServer and a structural ToolServer in place of the SDK's McpServer type, so registerAllTools no longer depends on the SDK. Breaking for embedders of replylayer-mcp/tools; none for stdio users.
  • 0.14.4 — The top-level releasable field descriptions now say it is the draft-only flag (always false on sent or received messages) and point to email_effect for held messages; the server primer gains one sentence saying the same and that held outbound mail is released only by the account owner in the dashboard. No schema or behaviour change.
  • 0.14.3 — The create_draft, reply_to_message and send_email descriptions now link the ReplyLayer API reference they call (Anthropic directory requirement). No schema or behaviour change.
  • 0.14.2 — Every tool now sets annotations.title (Anthropic directory requirement). The top-level title is kept. No schema or behaviour change.
  • 0.14.1 — Corrected Sandbox complaint copy in the add_recipient description: a spam report from someone added with attest also revokes everyone added that way (they must confirm by link again), and only a report from a second person suspends the Sandbox account. No schema or behaviour change.
  • 0.14.0 — Output schemas on every tool (G7). Every tool that returned only a text block now also declares an outputSchema and returns matching structuredContent (the same JSON as the text block, which is unchanged); remove_suppression, which always returns an error, is the one tool without one, so every tool on the sign-in endpoint has one. Each schema follows the API route's response schema, after the fields the tool leaves out; objects are open, enum-like values are strings, and only the top-level fields the route always returns are required. The existing schemas gain the fields they were missing: hold_context.review_url, review_expires_at, review_causes and agent_instructions, the review fields and firewall_block on message reads, the trimmed attachment fields, and the mailbox fields list_mailboxes keeps. get_attachment_preview now requires attachment and preview. Also ships the held-send changes merged after 0.13.0 was published: the HELD line on a held send now tells the agent to tell the account owner where the send waits (hold_context.review_url) and by when (hold_context.review_expires_at), and not to send it again; send_draft now shapes its result like send_email and reply_to_message (a held draft send leads with the same line, and a blocked one is an isError result); the primer says the same. No tool name, input, annotation or description changes. Requires @replylayer/sdk 0.28.1.
  • 0.13.0 — Tool titles and complete annotations. Every tool declares a title and explicit boolean readOnlyHint, destructiveHint and openWorldHint, plus idempotentHint on every tool that changes something (see Tool annotations). create_draft, update_draft, both release tools, add_recipient, and the ten list and marker writes (add_suppression*, add_inbound_blocklist*, add_inbound_allowlist*, mark_*_read, star_*) are now destructive, so hosts that honour the hints ask before running them. remove_suppression is now read-only (it always returns an error). create_draft and update_draft say that no email is sent unless send_at is set. The masking guidance in the idempotency_key descriptions and the primer no longer names a specific host. delete_message and report_and_block declare idempotentHint: true. New grant registration option ('any' by default): the hosted sign-in endpoint registers with grant: 'agent', which leaves out AGENT_UNAVAILABLE_TOOL_NAMES (approve_review, deny_review, get_account_usage, remove_suppression; the other 40 remain) and ships OAUTH_SERVER_INSTRUCTIONS, the primer with its API-key wording replaced by connection wording. A grant: 'agent' registration also takes oauth: { scope, challenge } (required) and adds OAuth linking metadata: every tool carries securitySchemes ([{ type: 'oauth2', scopes: [scope] }]) in _meta and, through a wrapped tools/list handler, top-level too, and a tool error caused by a 401 from the API carries _meta["mcp/www_authenticate"]: [challenge], which a client that signs in per tool (ChatGPT) needs to re-link. The stdio server and /v1/mcp are unchanged by the grant. Apart from add_recipient's new attest (below), no parameter changes. Results leave out internal identifiers and bookkeeping on every transport (see Tool notes): reviewed_by_id, added_by_actor_id, actor_id, r2_objects_failed (now r2_objects_failed_count), warmup.velocity_gate_mode, preview.extractor, attachment hash and bookkeeping fields, and the mailbox scanner, PII-detector, instruction-trust and consent-bookkeeping fields; a vendor-named suppression source reads provider_webhook or provider_sync; 400 validation details keep only instancePath and message, and SELF_HOSTED_DECRYPT_FAILED drops details.domain_id. get_attachment_preview's output schema no longer lists attachment.hash or preview.extractor, and list_allowlist_blocked_attempts's description no longer names actor_id. list_drafts returns the API's has_more again. Sandbox recipient attestation: add_recipient gains an optional attest: boolean (sendable now, no confirmation email, spends one of the account's per-trial attestations) and a description that states the Sandbox-only scope, both branches, the complaint consequence and the restricted-mailbox refusal; it stays listed on the sign-in endpoint. It no longer says other accounts "can email anyone": they don't use this list, and on a paid plan a mailbox restricted to approved people still binds agent sends, so the agent asks the user to approve the person. The Sandbox-recipient hints likewise say pay-as-you-go credit removes the Sandbox recipient rule, not that it lets the agent email anyone. Tool errors gain hints for SANDBOX_RECIPIENT_NOT_VERIFIED, RECIPIENT_WRITE_AGENT_CONTAINED, RECIPIENT_WRITE_UNAVAILABLE and an attestation TIER_LIMIT. Requires @replylayer/sdk 0.28.0.
  • 0.12.3 — Idempotency keys must be literal. The idempotency_key descriptions on send_email, reply_to_message and create_draft, and the server-level primer, now say: pass the literal key in every call, and if your host masks fields ending in _key (OpenClaw does), retry from a new session with the full literal request instead of reusing a masked value. The API refuses a masked key (***, a value containing …, __OPENCLAW_REDACTED__, or a word such as [REDACTED]) with 400 IDEMPOTENCY_KEY_INVALID. The hosted server already carries this text. No tool or schema change. The release also produces the Claude Desktop extension bundle.
  • 0.12.2 — API-key authentication failures. The server-level workflow primer (SERVER_INSTRUCTIONS) now tells the agent that a RATE_LIMITED whose details.reason is failed_authentication means the API key is wrong or revoked: stop and ask a human to fix the key, do not retry. It also names UNAUTHORIZED / API_KEY_REVOKED as stop-and-ask-a-human codes. Tool errors gain matching friendly hints for the failed-authentication 429 (checked before the daily-quota test) and for API_KEY_REVOKED. No tool or schema change.
  • 0.12.1 — documentation only; no tool, schema, or runtime change. The auth note no longer describes signup as invite-gated: it states the email and mobile-number verification a new account's key waits on.
  • 0.12.0 — the hosted MCP server now refuses local attachments paths before any filesystem access (HOSTED_LOCAL_ATTACHMENTS_UNSUPPORTED); see Hosted vs local attachments. Additive public API: the tool registrars exported from replylayer-mcp/tools accept an optional third RegisterToolOptions argument ({ transport?: 'local' | 'hosted' }, default 'local'), so every existing two-argument call is unchanged. The hosted MCP server also injects the same server-level workflow primer as this binary and advertises the real package version; both transports read the version from package.json. The primer branches on email_effect.effect_status (with the legacy top-level status as a fallback for older servers) and carries the immediate-send idempotency rules. Additive public API: replylayer-mcp/tools also exports SERVER_INSTRUCTIONS and MCP_PACKAGE_VERSION.

See also

  • Public CLI repo (install & package trust): https://github.com/replylayer/rly
  • The agent SKILL.md, the CLI ↔ MCP ↔ REST verb-parity map, and ENDPOINTS.md (the REST API contract) will be published with the hosted docs site when it is available.

Keywords

email

FAQs

Package last updated on 01 Oct 2026

Related posts