mcpField
W
willitsend

willitsend

本地服务
github.com/abryfs通信协作115 周前更新

关于 willitsend

willitsend 是面向会发送短信的 AI Agent 的确定性预检工具,可在发送前检查 SMS/iMessage 草稿。它计算分段并标示文档规定的合规风险,但不会发送或存储消息。

主要功能

  1. 01检查首条消息的合规项
  2. 02计算编码与短信分段
  3. 03标示 iMessage 功能回退风险
  4. 04返回判定、依据与修正建议
  5. 05提供 MCP 服务、CLI 和库

适用场景

  1. 01发送首条短信前检查退订说明
  2. 02发现 Unicode 字符造成的分段增加
  3. 03检查 iMessage 转为 SMS 时丢失的功能

MCP 相关

MCP 服务向客户端提供 preflight_message 工具,用于检查短信草稿,并返回判定、发现项和发送追踪信息。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"abryfs-willitsend": {
"command": "npx",
"args": [
"-y",
"willitsend"
],
"env": {
"AGENTPHONE_API_KEY": "<AGENTPHONE_API_KEY>"
}
}
}
}
启动命令npx -y willitsend
需要配置的环境变量
AGENTPHONE_API_KEY密钥Optional API key for live carrier/capabilities lookup. The linter runs fully offline without it.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmwillitsend0.1.0

工具

1

收录的工具列表 · 更新于 2 周前

参数类型必填说明
bodystring是要预检的确切消息文本。仅发送媒体时请传入空字符串。此文本仅用于本地分析——工具不会在任何地方存储或转发该文本。
to_numberstring否单个目标:E.164 电话号码(例如 +15551234567)、电子邮件地址(仅限 iMessage)、5–6 位短代码,或 grp_ 群组 ID。使用 `recipients` 指定多收件人群组时请省略。
brand_namestring否首条消息中应能识别出的发送方品牌或公司名称。若未提供,品牌识别检查将降级为仅提供建议。
media_urlsarray否媒体附件 URL。包含 2–20 个条目时会触发 iMessage 轮播;在其他渠道上,请检查返回的发现项中是否有轮播大小警告。
recipientsarray否用于创建新的 iMessage 群组的 2 个或更多目标(仅限 iMessage,绝不会通过 SMS 投递)。不要与 to_number 同时使用。
send_stylestring否仅限 iMessage 的视觉发送效果(例如 "confetti"、"slam")。在 iMessage 以外的渠道会被静默丢弃;除非已确认渠道为 iMessage,否则会标记此项。
campaign_typeenum否已知时可传入 10DLC campaign tier,以便在跟踪信息中附加每日发送上限的静态示例。sole_proprietor | low_volume | high_volume_low_trust | high_volume_standard | high_volume_high_trust | high_volume_highest_trust
response_formatenum否输出长度。"detailed"(默认):完整消息、修复方法和引用 URL。"concise":每个发现项占一行(严重性、规则、修复方法),令牌数约减少 10 倍——使用相同引擎和判定,并包含所有必要的操作信息;适用于高频循环。detailed | concise
reply_to_message_idstring否仅限 iMessage 的线程回复目标消息 ID。
destination_line_typeenum否通过外部查询获知的目标电话号码线路类型(如有)。不得根据号码本身猜测——VoIP 线路的 iMessage/SMS 投递情况通常不可靠。mobile | voip | landline | unknown
destination_capabilitiesobject否如果已查询目标的已知投递能力,可在此传入。显式传入此值始终优先于此服务自身的可选查询,并会跳过该查询。
destination_capabilities.smsboolean是
destination_capabilities.imessageboolean是
is_first_message_to_contactboolean否这是否是首次向此联系人发送外发消息。确定时设置为 true 或 false——首条消息有更严格的合规要求(退订措辞、品牌标识、选择加入措辞)。只有确实未知时才留空;此时工具会将受影响的检查结果标记为有条件,而非直接断言(判定结果为 needs_context)。

详细介绍

willitsend

ci license: MIT

你的 AI Agent 会发送短信。运营商会悄悄丢弃不合规的短信,而 API 从不告知你。willitsend 是模型与运营商之间缺失的检查环节:它是一项确定性的发送前预检,可在你付费发送前发现静默过滤、分段暴增以及被丢弃的 iMessage 功能。

在浏览器中试用:在客户端运行,不会有任何内容离开当前页面。

npx -y -p github:abryfs/willitsend willitsend-cli "Hey! Your appointment is tomorrow at 2pm." --first-message
Verdict: BLOCK
Segments: 1 (gsm7, 41 septets)
Channel: unknown
[BLOCK] first-message.opt-out: First message doesn't include opt-out instructions (e.g. "Reply STOP
to unsubscribe"). Carriers may silently filter first messages that lack one, with no API error.
  Fix: Append "Reply STOP to unsubscribe." to the message body.
  Source: https://docs.agentphone.ai/documentation/reference/messaging-rate-limits#first-message-requirements
[WARN] first-message.opt-in: No opt-in acknowledgment language found …
[INFO] first-message.brand: No brand_name was provided …

问题

消息 API 在消息到达下游运营商时就会报告 "sent",而不是在消息到达手机时。AgentPhone 的速率限制文档明确说明了后果:首次发送的消息如果缺少品牌标识、选择加入确认或退订说明,“可能会被运营商静默过滤。API 不会返回错误。”

如今,AI Agent 会自主发送短信,而模型与运营商之间没有任何环节进行检查。Agent 如果漏掉退订说明,不会收到错误,也无法送达。Agent 如果加上一个表情符号,就会把一条 160 个字符的消息变成三个计费分段,却毫无察觉。

willitsend 是缺失的检查环节:针对草稿消息的确定性、无状态 lint 工具。输入文本和上下文,输出判定和依据。它不会发送或存储任何内容。

sequenceDiagram
    participant A as AI agent
    participant W as willitsend
    participant API as Messaging API
    participant C as Carrier
    participant P as Phone

    rect rgba(255,69,58,0.08)
    note over A,P: without preflight
    A->>API: send_message (no opt-out language)
    API-->>A: "sent" ✓
    C--xP: silently filtered. no error, no delivery
    end

    rect rgba(52,199,89,0.08)
    note over A,P: with preflight
    A->>W: preflight_message(draft, context)
    W-->>A: BLOCK · missing opt-out · fix + citation
    A->>W: preflight_message(fixed draft)
    W-->>A: PASS
    A->>API: send_message
    API-->>A: "sent" ✓
    C->>P: delivered
    end

为什么不直接把规则作为提示词告诉 Agent?

这是个合理的问题:AgentPhone 的文档可供 Agent 读取,因此你可以在每次生成调用时都把文档提供给模型,并要求模型遵守。两个问题会随之出现,其中一个还会带来高昂成本:

  1. 生成是概率性的,验证是确定性的。 当规则出现在上下文中时,模型 通常 会遵守。本检查器每次都会返回相同的判定,出错时会给出明确的检查结果,而不是让消息被静默过滤。撰写时的指导与发送前的验证相辅相成——此仓库两者都提供(Agent 技能是撰写时的部分)。
  2. 模型无法计算分段。 七位组计数、GSM-7 扩展字符、一个智能引号导致的编码切换、考虑位置的分段打包——基于词元的模型在结构上就不擅长这类计算。再多提示词也无法解决;300 行的确定性函数却可以。
  3. 实测词元成本(基于实际页面的字符数除以 4 的估算方法,2026 年 7 月):
方案每条消息的词元数每天 10,000 条消息,输入价格为每百万词元 3 美元
每次生成调用都提供 AgentPhone 的两个文档页面~7,700~$231/天(开启 90% 提示词缓存后约 ~$23/天)
preflight_message 调用结果(默认)~250(另需每个会话一次,约 ~1,450 词元用于工具定义)~$7.50/天
preflight_message 搭配 response_format: "concise"通过时约 ~4,拦截时约 ~44(实测)~$0.35/天,按 90% 通过率计算

默认模式下,每条消息的词元数大约减少 30 倍,精简模式下则减少数百倍;检查器自身的计算成本可忽略不计(本地约 4µs,无需 API)。如果你把规则压缩到系统提示词中——我们的技能文件只需约 950 个词元——就能保留撰写时的好处,但仍然没有验证器,也无法计算分段。批量发送者对此感受最深:这个工具的存在,正是为了服务大量发送短信的 AI Agent。

快速入门

作为 MCP 工具(Claude Code):

claude mcp add willitsend -- npx -y -p github:abryfs/willitsend willitsend-mcp

或在任何 MCP 客户端配置中:

{
  "mcpServers": {
    "willitsend": {
      "command": "npx",
      "args": ["-y", "-p", "github:abryfs/willitsend", "willitsend-mcp"]
    }
  }
}

供 AI Agent 使用 — 将此代码块粘贴到你的 Agent 中(或让它查看此 README):

Install the willitsend MCP server (pre-send SMS/iMessage lint, no API key needed):
- Claude Code: run `claude mcp add willitsend -- npx -y -p github:abryfs/willitsend willitsend-mcp`
- Any other MCP client: add {"command": "npx", "args": ["-y", "-p", "github:abryfs/willitsend", "willitsend-mcp"]} to its MCP server config.
Then, before every send_message (or equivalent) call, call preflight_message with the
draft body, is_first_message_to_contact (true/false when known), and brand_name.
Treat verdict "block" as do-not-send: apply the returned fix strings and re-run until
pass. Treat "needs_context" as a signal to determine whether this is a first message
(check conversation history), never as permission to send. For high-volume loops pass
response_format: "concise" (~4 tokens on pass).

无需 API 密钥。如果你想查询电话号码目的地的实时 iMessage/SMS 功能,请设置 AGENTPHONE_API_KEY;不设置时,工具会离线运行。上述配置直接从此仓库安装(安装时会运行 prepare 脚本进行构建)——无需注册。willitsend 也会发布到 npm 和 MCP Registry,服务名称为 io.github.abryfs/willitsend;发布版本后,支持注册表的客户端可以按名称添加它,并通过 npx -y willitsend 直接启动服务。有关发布流程,请参阅 docs/publishing.md。

作为库使用(npm install github:abryfs/willitsend):

import { preflight } from "willitsend";

const report = preflight({
  body: "Acme: thanks for signing up. Your order shipped. Reply STOP to unsubscribe.",
  is_first_message_to_contact: true,
  brand_name: "Acme",
});

report.verdict; // "pass" | "warn" | "block" | "needs_context"
report.findings; // each with severity, fix, and a citation URL
report.trace.segments; // { encoding, units, segments, perSegment, ... }

作为 CLI 使用: npx -y -p github:abryfs/willitsend willitsend-cli --help(或从 npm 安装后运行 npx -y -p willitsend willitsend-cli)。退出代码:0 表示通过/警告,1 表示阻止,2 表示需要上下文,3 表示用法错误。可直接用于 CI 或 Shell 管道。

检查内容

规则严重级别来源
first-message.opt-out:发给联系人的第一条消息必须附带退订说明拦截AgentPhone 文档
first-message.brand:第一条消息必须标明品牌(仅根据你提供的 brand_name 检查,绝不猜测)拦截AgentPhone 文档
first-message.opt-in:第一条消息应说明联系人如何选择订阅警告AgentPhone 文档
first-message.media-only:合规文本不能只放在图片里警告AgentPhone 文档
segments.unicode-blowup:一个非 GSM 字符就会导致整条消息重新编码为 UCS-2警告AgentPhone 文档
imessage.feature-fallback:回退到 SMS 时,send_style、串线回复和轮播消息会悄然丢失警告AgentPhone 文档
imessage.invalid-send-style、imessage.carousel-count:无效的效果名称;轮播数量不在文档所述的 2-20 范围内拦截AgentPhone 文档
imessage.new-contact-cap:iMessage 对每条线路向新联系人发送消息的数量限制为每天 50 条信息AgentPhone 文档
destination.invalid、destination.voip:目标地址格式错误;已知的 VoIP 线路(绝不根据号码猜测线路类型)拦截 / 警告发送 API / AgentPhone 文档
content.shaft:运营商会过滤涉及性、酒精、枪支、烟草的词语(仇恨言论没有关键词规则:词表无法检测仇恨言论,我们不会假装可以)警告Twilio 指南
content.url-shortener:共用的公共短链接服务(bit.ly、tinyurl、……)与 CTIA 关于专用短链接的指导原则相冲突警告CTIA MP&BP(PDF)
content.spam-patterns:连续大写字母、$$$、!!!信息启发式规则

规则分为两个标注的层级。来源于 AgentPhone 自有文档的规则可以拦截发送。来自行业资料的规则最多发出警告,因为关键词启发式规则无权否决你的发送操作。

判定模型

如果某项检查依赖你未提供的上下文,就不会假装能确定结果。如果你没有说明这是否是发给某位联系人的第一条消息,退订规则会报告为有条件适用,判定结果为 needs_context。你不会得到虚假的通过或拦截结果。

flowchart LR
    F[findings] --> B{any block finding<br/>with known context?}
    B -- yes --> BLOCK
    B -- no --> C{any block finding<br/>waiting on context?}
    C -- yes --> NC[NEEDS_CONTEXT]
    C -- no --> W{any warning?}
    W -- yes --> WARN
    W -- no --> PASS

每份报告还会包含一条发送跟踪信息:目标地址分类、假定渠道(iMessage/SMS/MMS)、完整的分段计算(编码、septet 和码元数量、考虑位置的逐段打包),以及在提供 10DLC 活动等级时,这条消息会占用已公布的每日分段上限多少额度。

基准测试

以下所有数字都可以从仓库中复现;没有任何一项需要账户。

  • **分段计算一致性:**在一个固定的 126 个向量语料库中,编码和分段数量均与 Twilio 的参考分段计算器一致,涵盖 GSM-7/UCS-2 边界、扩展字符、表情符号、ZWJ 序列和确定性模糊测试。语料库和测试套件位于 test/parity.test.ts;运行 npm test。
  • **延迟:**每条消息的中位延迟约为 4µs,单线程每秒约处理 100,000 条消息(Node 22、Apple Silicon 笔记本电脑;采用单次遍历码点表,逐字符分配次数为零)。在你的设备上运行 npm run bench。对每条出站消息进行预检,比记录日志更省成本。
  • **成本示例,基于公开的上限:**独资经营者的 10DLC 营销活动每天可获得 1,000 个 T-Mobile 分段(按 AgentPhone 公布的估算,美国境内总计约 3,000 个)。一个多余的表情符号若将一条 2 个 GSM-7 分段的消息变为 4 个 UCS-2 分段,就会让该配额可发送的消息数量减半。文本相同,收件人相同,覆盖范围减半。

这里不会声称投递率有所提升。这种说法需要我们并不掌握的发送数据。

此工具无法做到的事

运营商使用的过滤系统规则并未公开。政策层面是公开的(行为准则、内容类别、发送量上限);运行时的过滤决策则刻意保持不透明,而且只能观察到部分结果(某些拦截会返回明确的过滤错误,某些消息则会被接受,但始终无法送达)。此工具仅涵盖确定且有文档说明的部分,不涉及其他内容:

  • pass 不代表保证送达。它只表示没有任何有文档说明的规则会拦截消息。
  • 自愿订阅检查会确认是否包含相关措辞,而不是确认是否确有同意。同意记录保存在你的记录中,而非消息文本里。
  • v1 仅检测英文(检测结果包含 locale 字段)。
  • 配额计算是根据公开上限进行的静态示例,不反映你账户的实时状态。
  • 此工具不会根据电话号码推断线路类型(VoIP/移动线路)。如果你有查询服务,请传入 destination_line_type;libphonenumber-js 可提供免费的近似判断,但对美国号码有已知限制。

相关先例

  • TwilioDevEd/message-segment-calculator 是分段计算的参考实现。此库会测试与其是否一致,而不是与之竞争。
  • sms_policy_checker 使用大模型层为 Ruby/Rails 提供类似的合规检查;willitsend 则是确定性、零网络请求的对应方案。
  • 网页检查工具(10dlccheck.com、Calilio)以面向用户的表单形式覆盖了部分相同规则。willitsend 让这些检查可嵌入使用:一个库,Agent 每秒可调用十万次,并提供引用依据。
  • 据我们所知,这是首个用于发送前 SMS 内容合规检查的 MCP 工具。欢迎指正。

隐私

不收集遥测数据。除非你使用 API 密钥启用了可选的能力查询,否则不会发起网络请求。此工具绝不会记录、存储或在输出中回显消息正文。交互式试用工具在你的浏览器中运行,不会向任何地方发送数据。

未来

  • 引擎最适合部署在服务端:在发送端点本身加入 dry_run 参数。该库采用即插即用的设计,是一个无状态的纯函数,核心部分不依赖任何其他组件。
  • 闭环:将预检判定与投递状态 Webhook 关联起来,根据实际结果而非假设来衡量规则的准确性。过滤过程只能部分观测到(某些拦截会明确报错,另一些则没有反馈),但这些信号足以诚实地评估一套确定性规则。要学习不透明层本身,需要汇总许多发送方的发送数据;这属于平台的职责,而不是无状态客户端工具的职责。
  • 通过 MCP Apps 扩展呈现投递能力报告卡。
  • 添加英语以外的语言包。

开发

git clone https://github.com/abryfs/willitsend && cd willitsend
npm install   # builds via prepare
npm test      # 191 tests: unit, Twilio parity, held-out acceptance
npm run bench

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。