mcpField
T
tollbooth-sample

tollbooth-sample

远程服务
github.com/lonniev开发工具0今天更新
代码仓库

关于 tollbooth-sample

这是一个天气 MCP 服务参考实现,也包含用于构建 Tollbooth DPYC 付费 API Operator 的 Claude Code 插件。它封装 Open-Meteo,并展示付费工具调用、积分处理和可复用的 Operator 配置方式。

主要功能

  1. 01当前、预报和历史天气工具
  2. 02通过 Tollbooth 积分门控付费调用
  3. 03自动扣款回滚与余额提醒
  4. 04动态定价约束与费用预览
  5. 05用于构建付费 Operator 的引导技能

适用场景

  1. 01围绕现有 API 构建付费 MCP 服务
  2. 02学习 Tollbooth 积分与定价模式
  3. 03生成可部署的 Operator 项目
  4. 04查询天气并预览调用费用

MCP 相关

提供天气工具以及余额、购买、定价、Secure Courier、Oracle 和约束等标准 DPYC 工具。该项目是基于 FastMCP 的 MCP 服务;其引导技能也可将现有 REST API 或 MCP 服务封装为付费 Operator。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://tollbooth-sample.fastmcp.app/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

工具

53
参数类型必填说明
npubstring是要查询余额的 Nostr 公钥(npub1...)。
dpop_tokenstring是证明调用方持有 npub 的凭据:来自 receive_npub_proof 的授权信封,或由 npub 签名的 kind-27235 Nostr 事件原始 JSON,其中 `u` 标签的值为此工具的名称。

详细介绍

tollbooth-sample

教育用途的天气统计 MCP 服务——使用 Bitcoin Lightning 微支付构建 Tollbooth DPYC 变现 API 服务的参考实现。

此服务封装了免费的 Open-Meteo 天气 API,并通过 @runtime.paid_tool() 装饰器,使用 Tollbooth 积分系统为付费工具调用设限。领域工具只包含业务逻辑;扣款、回滚、余额警告和约束评估均由 OperatorRuntime 自动处理。标准 DPYC 工具(余额、购买、Secure Courier、Oracle、定价、约束)通过 register_standard_tools() 委托给 wheel。

构建你自己的 operator — bootstrap-dpyc-operator 技能

此仓库同时也是一个 Claude Code 插件。bootstrap-dpyc-operator 技能可以将你的现有 REST API、stdio MCP 或 HTTP MCP 转换为变现的 DPYC Operator MCP:它会实时克隆此模板、封装你的领域逻辑,并生成可部署的项目。你只需继续编写业务逻辑——SDK 会处理支付、身份、保险库、审计和定价。

在 Claude Code 中安装:

/plugin marketplace add lonniev/tollbooth-sample
/plugin install bootstrap-dpyc-operator@tollbooth-dpyc

然后让 Claude “将我的 API 转换为付费 DPYC operator”——该技能会根据其描述自动激活。它绝不会改动你的原始代码(而是生成一个同级的 <slug>-mcp/ 项目),并在每次运行时读取此仓库当前的 wheel 版本固定信息,因此不会过时。

请参阅 skills/bootstrap-dpyc-operator/,了解该技能及其参考指南(规范模式、源适配器、会话与保险库、入门检查清单)。

DPYC 经济体系

DPYC 是 Don't Pester Your Customer(不要打扰你的客户) 的缩写。这是一种 API 变现理念和协议,旨在消除会话中途的支付弹窗、订阅提醒页面和 KYC 流程带来的阻碍。

运作方式
  1. 预充值余额 — 用户在使用工具之前通过 Bitcoin Lightning 购买积分。每次工具调用都会从其余额中静默扣款。没有中断,也没有“请升级”弹窗。

  2. Nostr 密钥对身份 — 用户通过 Nostr 公钥(npub)而非电子邮件或密码进行身份识别。每个角色使用一个密钥对,由用户管理。无需填写账户创建表单。

  3. 以 UUID 为键的工具身份 — 每个工具都是一个 ToolIdentity 对象,其确定性 UUID v5 根据能力名称生成。定价提示来自 category 字段:

    类别定价提示用例
    free0 sats余额查询、状态
    read1 sat简单查询
    write5 sats多步骤操作
    heavy10 sats高成本查询

    实际价格由 operator 的定价模型在 Neon 中动态设置。

  4. 失败时回滚 — 如果扣款后下游 API 调用失败,积分会通过补偿批次自动回滚。用户无需为失败的调用付费。

  5. 社会契约 — DPYC 生态系统是一个自愿社区,成员遵守透明、可审计的经济规则;认证链会从根节点逐级传递信任:

    • 公民 — 使用 API 服务的用户
    • Operator — 运行 MCP 服务的开发者(例如本服务)
    • Authority — 认证 operator,并从购买中收取少量税款
    • First Curator — 链的根节点,负责铸造初始 cert-sat 供应量

Tollbooth 变现机制

ToolIdentity 与冻结的 tool_id

每个领域工具都会注册为一个 ToolIdentity,其中包含一个冻结的 tool_id(不透明 UUID)、能力名称、类别(定价提示)和意图描述。在工具诞生时只生成一次 UUID —— 在 REPL 中运行 capability_uuid("get_current_weather")(或 uuid.uuid4()),然后将结果粘贴为字面量常量,此后永不更改。冻结这个字面量,才能让你日后重命名能力时,不会导致其在 Neon 中的定价记录失去关联。不要在运行时调用 capability_uuid(...);该标识必须只保存在一个位置:

from tollbooth.tool_identity import ToolIdentity, STANDARD_IDENTITIES
from tollbooth.runtime import OperatorRuntime, register_standard_tools
from tollbooth.credential_templates import CredentialTemplate, FieldSpec
from tollbooth.credential_validators import validate_btcpay_creds

# Frozen UUIDs — minted once at tool birth, never recomputed.
GET_CURRENT_WEATHER_UUID    = "b7327eb8-92b4-5252-84e0-ba3f437a16ed"
GET_WEATHER_FORECAST_UUID   = "b6d0e596-3aec-5a62-980b-7875aa04d079"
GET_HISTORICAL_WEATHER_UUID = "5608f3e9-44c4-5b28-9744-704af6d701f0"

# 1. Define domain tool identities
_DOMAIN_TOOLS = [
    ToolIdentity(
        tool_id=GET_CURRENT_WEATHER_UUID,
        capability="get_current_weather",
        category="read",
        intent="Get current weather conditions",
    ),
    ToolIdentity(
        tool_id=GET_WEATHER_FORECAST_UUID,
        capability="get_weather_forecast",
        category="write",
        intent="Get weather forecast",
    ),
    ToolIdentity(
        tool_id=GET_HISTORICAL_WEATHER_UUID,
        capability="get_historical_weather",
        category="heavy",
        intent="Get historical weather data",
    ),
]

TOOL_REGISTRY: dict[str, ToolIdentity] = {ti.tool_id: ti for ti in _DOMAIN_TOOLS}
@runtime.paid_tool() 装饰器

每个付费工具只需一个装饰器,即可获得完整的 DPYC 变现功能。该装饰器接收工具冻结的 tool_id 常量,并自动处理扣款、余额检查、约束评估、失败时回滚和低余额警告。你的工具函数只包含领域逻辑:

from typing import Any
from fastmcp import FastMCP
from tollbooth.params import PatronNpub

mcp = FastMCP("tollbooth-sample", ...)

# Create the runtime with merged standard + domain identities
runtime = OperatorRuntime(
    tool_registry={**STANDARD_IDENTITIES, **TOOL_REGISTRY},
    operator_credential_template=CredentialTemplate(
        service="tollbooth-sample-operator",
        version=2,
        description="Operator credentials for BTCPay Lightning payments",
        fields={
            "btcpay_host": FieldSpec(required=True, sensitive=True, ...),
            "btcpay_api_key": FieldSpec(required=True, sensitive=True, ...),
            "btcpay_store_id": FieldSpec(required=True, sensitive=True, ...),
        },
    ),
    credential_validator=validate_btcpay_creds,
    # Connections prove the patron's npub (NIP-98 signed requests or an
    # OAuth holder connection) against this host.
    holder_auth_url="https://tollbooth-sample.fastmcp.app",
    ...
)

# Delegate all standard DPYC tools to the wheel.
# register_standard_tools returns the slug-prefixed @tool decorator —
# use it for the operator's own paid tools below.
tool = register_standard_tools(mcp, "weather", runtime, ...)

# Decorate each paid domain tool
@tool
@runtime.paid_tool(GET_CURRENT_WEATHER_UUID)
async def current(
    latitude: float,
    longitude: float,
    npub: PatronNpub = "",
) -> dict[str, Any]:
    """Get current weather conditions for a location.

    Returns temperature, wind speed, and weather code from Open-Meteo.
    """
    return await weather.get_current(latitude, longitude)

这就是完整的付费工具。无需手动调用扣款、编写 try/except 回滚代码块,也无需处理余额警告。该装饰器会:

  • 根据 UUID,从 ToolIdentity 注册表中查找工具定价
  • 从函数参数中提取 npub,用于计费
  • 检查连接是否能证明该 npub 属于调用者(通过签名请求,或持有该 npub 的 OAuth 连接)
  • 在调用函数前扣款(根据定价模型遍历工具的约束链)
  • 如果函数引发异常,则自动回滚
  • 资金不足时,在响应中附加低余额警告
关键模式

register_standard_tools(mcp, "weather", runtime, …) —— 从 tollbooth-dpyc 轮子注册所有标准 DPYC 工具(余额、购买、付款、定价、Secure Courier、Oracle、约束),将 oracle 委托挂载为 <slug>_oracle_*,并返回带 slug 前缀的 @tool 装饰器。请保存其返回值,以便为自定义付费工具使用同一个装饰器 —— 此操作器上所有通过协议暴露的名称都会共用一个 slug 前缀。

validate_btcpay_creds —— 凭据验证器,在接收时而非首次使用时检查 BTCPay 凭据。无效凭据会在 Secure Courier 交换期间立即被拒绝。

CredentialTemplate —— 声明操作器所需的密钥(BTCPay 主机、API 密钥、商店 ID),以便 Secure Courier 流程提示填写正确字段,并在交付时进行验证。

npub 参数

每个付费工具都必须接受一个 npub 关键字参数。它会告知运行时向哪位赞助者收费;连接则负责证明调用者持有该值。请使用 SDK 的 PatronNpub 类型,以便每个操作器都以相同方式描述它:

from tollbooth.params import PatronNpub

npub: PatronNpub = ""

如果使用空的或无效的 npub 调用付费工具,请求会在函数运行前被拒绝(npub is required)。

运行时在底层处理的内容
Tool call arrives
    |
    v
@runtime.paid_tool(GET_CURRENT_WEATHER_UUID)
    |
    +-- UUID lookup in tool_registry -> ToolIdentity + pricing
    +-- npub extraction from kwargs; the connection proves it
    |
    +-- Constraint chain from the pricing model (discounts, surge, supply caps, coupons)
    +-- Balance check + debit
    |       |
    |       insufficient --> Return error (no function call)
    |
    +-- Call your function
    |       |
    |       exception --> Automatic rollback, return error
    |
    +-- Append low-balance warning if needed
    |
    v
Return result to caller

约束引擎

约束是动态定价步骤,存在于运营方的定价模型中: 模型中每个工具的 chain 都会列出 ConstraintGate 每次调用时遍历的步骤,绑定到工具的优惠券则会在这些步骤之后添加。请在 Pricing Studio 中编写约束,或使用 weather_set_pricing_model;不会通过环境变量启用任何功能。

常见约束类型(SDK 注册表中还有更多类型,可通过 weather_list_constraint_types 实时枚举完整列表):

类型效果
free_trial前 N 次调用免费
happy_hour在特定时段提供折扣
temporal_window仅允许在指定时间窗口内调用
finite_supply全局限制调用总次数
loyalty_discount消费 N sats 后提供折扣
bulk_bonus调用 N 次后提供折扣
surge_pricing高需求期间按需求弹性调整乘数

使用 weather_check_price 可在不消耗额度的情况下预览约束效果—— 它会遍历与扣款完全相同的步骤链。

成为运营方

初次接触 Tollbooth?请参阅 GETTING-STARTED.md,了解 逐步指南,其中涵盖 Nostr 密钥对设置、Authority 注册、 BTCPay 配置,以及部署首个收费 MCP 服务。

快速开始

本地开发
git clone https://github.com/lonniev/tollbooth-sample.git
cd tollbooth-sample
pip install -e ".[dev]"
python -m tollbooth_sample.server

此服务通过 STDIO 提供。没有开发环境绕过机制:付费工具的访问门控 与生产环境完全一致,并且赞助者的 npub 通过 HTTP 连接进行证明(NIP-98 签名请求或 OAuth 持有者 Bearer),因此通过 STDIO 调用付费工具会被拒绝。请针对已部署的实例测试付费工具。

在 Prefect Horizon 上部署

托管平台是 Prefect Horizon(FastMCP 是构建该服务所用的运行时/框架)。

  1. 推送到 GitHub
  2. 在 Prefect Horizon 上连接此仓库
  3. 设置环境变量:
    • TOLLBOOTH_NOSTR_OPERATOR_NSEC — 用于身份初始化的 Nostr 密钥 (运营方唯一需要的环境变量;Neon 数据库 URL 由你的 Authority 通过 Nostr 提供, relay 集合来自共享的 dpyc-community 注册表,而所有其他密钥都通过 Secure Courier 凭据模板交付)

Horizon 会从 fastmcp.json 读取入口点和依赖项。

长期运行工具的运营方请注意。 默认情况下,claim-check/ 异步任务在内存中运行(async_jobs.backend: "memory (default)"),这意味着 它们无法在 Horizon 重启后继续运行(durable_across_recycles: false)。这 对此参考示例来说没问题,因为它没有长时间运行的任务;但如果你添加了一个 将工作延后到后台任务中的工具,请固定 tollbooth-dpyc [modal] 附加组件版本,将 LONGRUNNER_CREDENTIAL_FIELDS 合并到运营方凭据模板中,并通过 Secure Courier 提供 持久执行器密钥(modal_token_id/modal_token_secret/ modal_app_name),以便任务在重新部署期间继续完成。 随时通过 service_status.async_jobs 检查实时状态。

运行测试
pip install -e ".[dev]"
pytest -v

工具参考

MCP 工具名称费用描述
weather_currentread指定纬度/经度的当前天气
weather_forecastwrite多日天气预报(1-16 天)
weather_historicalheavy指定日期范围内的历史天气
weather_check_balancefree查看额度余额
weather_purchase_creditsfree通过 Lightning 购买额度
weather_check_paymentfree查看发票状态
weather_request_adoptionfree请求 Authority 收养(延迟求偶式入驻)
weather_check_pricefree预览费用(显示约束条件的影响)
weather_service_statusfree健康状态与约束配置摘要
weather_oracle_how_to_joinfreeDPYC 入驻说明
weather_oracle_get_tax_ratefree当前认证税率
weather_oracle_lookup_memberfree查找 DPYC 成员
weather_oracle_aboutfreeDPYC 生态系统介绍
weather_oracle_network_advisoryfree当前网络公告

三个天气工具由此运营方提供;其余工具来自 register_standard_tools(),包括身份验证工具(weather_request_npub_proof、 weather_receive_npub_proof)、Secure Courier、定价、优惠券和公证工具。 weather_get_operator_onboarding_status 会报告仍缺少哪些内容。

DPYC 生态系统

核心

运营方

倡导者与实用工具

许可证

Apache-2.0

评论

支持 Markdown

第一次接触 MCP?

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

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