The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Tollbooth Sample listing page.
Educational Weather Stats MCP Service — the reference implementation for building Tollbooth DPYC monetized API services with Bitcoin Lightning micropayments.
This service wraps the free Open-Meteo weather API
and gates paid tool calls through the Tollbooth
credit system using the @runtime.paid_tool() decorator. Domain tools contain
only business logic; debit, rollback, balance warnings, and constraint evaluation
are handled automatically by the OperatorRuntime. Standard DPYC tools
(balance, purchase, Secure Courier, Oracle, pricing, constraints) are delegated
to the wheel via register_standard_tools().
Version: 0.4.2
bootstrap-dpyc-operator skillThis repo doubles as a Claude Code plugin. The bootstrap-dpyc-operator skill turns your
existing REST API, stdio MCP, or HTTP MCP into a monetized DPYC Operator MCP: it clones this
template live, wraps your domain logic, and generates a deploy-ready project. You keep writing
business logic — the SDK handles payments, identity, vault, audit, and pricing.
Install it in Claude Code:
Then ask Claude to "make my API a paid DPYC operator" — the skill activates automatically by
its description. It never touches your original code (it emits a sibling <slug>-mcp/ project)
and reads this repo's live wheel pin on every run, so it can't go stale.
See skills/bootstrap-dpyc-operator/ for the skill and its
reference guides (canonical pattern, source adapters, sessions & vaults, onboarding checklist).
DPYC stands for Don't Pester Your Customer. It's a philosophy and protocol for API monetization that eliminates mid-session payment popups, subscription nag screens, and KYC friction.
Pre-funded balances — Users buy credits via Bitcoin Lightning before using tools. Each tool call silently debits from their balance. No interruptions, no "please upgrade" modals.
Nostr keypair identity — Users are identified by a Nostr public key
(npub), not an email or password. One keypair per role, managed by the
user. No account creation forms.
UUID-keyed tool identity — Every tool is a ToolIdentity object with
a deterministic UUID v5 derived from a capability name. Pricing hints come
from the category field:
| Category | Pricing hint | Use case |
|---|---|---|
free | 0 sats | Balance checks, status |
read | 1 sat | Simple lookups |
write | 5 sats | Multi-step operations |
heavy | 10 sats | Expensive queries |
Actual prices are set dynamically by the operator's pricing model in Neon.
Rollback on failure — If the downstream API fails after a debit, credits are automatically rolled back via a compensating tranche. The user never pays for a failed call.
Social Contract — The DPYC ecosystem is a voluntary community bound by transparent, auditable economic rules, with a Certification Chain that cascades trust from the root:
tool_idEach domain tool is registered as a ToolIdentity with a frozen tool_id
(an opaque UUID), a capability name, a category (pricing hint), and an intent
description. Mint the UUID once at the tool's birth — run
capability_uuid("get_current_weather") at a REPL (or uuid.uuid4()), then
paste the result as a literal constant and never change it again. Freezing the
literal is what lets you rename a capability later without orphaning its pricing
rows in Neon. Do not call capability_uuid(...) at runtime; the identity
must live in exactly one place:
@runtime.paid_tool() decoratorEvery paid tool is a single decorator away from full DPYC monetization.
The decorator takes the tool's frozen tool_id constant and handles debit,
balance checks, constraint evaluation, rollback on failure, and low-balance
warnings automatically. Your tool function contains only domain logic:
That is the complete paid tool. No manual debit calls, no try/except rollback blocks, no balance-warning plumbing. The decorator:
ToolIdentity registry by UUIDnpub from the function arguments for billingdpop_token for operator proof verificationregister_standard_tools(mcp, "weather", runtime, …) — Registers all
standard DPYC tools (balance, purchase, payment, pricing, Secure Courier,
Oracle, constraints) from the tollbooth-dpyc wheel, mounts oracle
delegations under <slug>_oracle_*, and returns the slug-prefixed
@tool decorator. Capture the return so you can use the same decorator
for your own paid tools — every wire-exposed name on this operator then
shares one slug prefix.
validate_btcpay_creds — Credential validator that checks BTCPay
credentials at receive time, not at first use. Invalid credentials are
rejected immediately during the Secure Courier exchange.
CredentialTemplate — Declares the operator's required secrets
(BTCPay host, API key, store ID) so the Secure Courier flow can prompt
for the right fields and validate them on delivery.
npub and dpop_token parametersEvery paid tool must accept npub and dpop_token keyword arguments. The
npub tells the runtime which patron to bill; dpop_token carries the
operator proof for verification:
The defaults of "" keep both parameters optional in STDIO/dev mode.
The ConstraintGate is an opt-in dynamic pricing layer. Enable it by setting:
Common constraint types (the SDK registry holds more — weather_list_constraint_types
enumerates the full set live):
| Type | Effect |
|---|---|
free_trial | First N calls are free |
happy_hour | Discount during specific hours |
temporal_window | Allow calls only during a time window |
finite_supply | Cap total invocations globally |
loyalty_discount | Discount after spending N sats |
bulk_bonus | Discount after N invocations |
surge_pricing | Demand-elastic multiplier during high demand |
Use weather_check_price to preview constraint effects without spending credits.
See constraints/example_basic.json,
constraints/example_advanced.json, and
constraints/example_surge.json
for configuration examples.
New to Tollbooth? See GETTING-STARTED.md for a step-by-step guide covering Nostr keypair setup, Authority enrollment, BTCPay configuration, and deploying your first monetized MCP service.
In STDIO mode, all tools work without credits — great for development.
The hosting platform is Prefect Horizon (FastMCP is the runtime/framework the server is built on).
TOLLBOOTH_NOSTR_OPERATOR_NSEC — Nostr key for identity bootstrap
(the only env var required to boot; all other secrets are delivered
via Secure Courier credential templates)CONSTRAINTS_ENABLED=true + CONSTRAINTS_CONFIG=...Heads-up for operators with long-running tools. By default, claim-check / async jobs run in-memory (
async_jobs.backend: "memory"), which means they do not survive a Horizon recycle (durable_across_recycles: false). That's fine for this reference sample, which has no long-runners — but if you add a tool that defers work to a background job, pin the[prefect]extra and deliver the durable-executor secrets (prefect_api_url/prefect_api_key/closure_seal_key, theLONGRUNNER_CREDENTIAL_FIELDS) via Secure Courier so jobs settle across redeploys. Check your live state anytime withservice_status.async_jobs.
| MCP tool name | Cost | Description |
|---|---|---|
weather_current | read | Current weather for lat/lon |
weather_forecast | write | Multi-day forecast (1-16 days) |
weather_historical | heavy | Historical weather for a date range |
weather_check_balance | free | Check credit balance |
weather_purchase_credits | free | Buy credits via Lightning |
weather_check_payment | free | Check invoice status |
weather_request_adoption | free | Request adoption by an Authority (deferred-courtship onboarding) |
weather_check_price | free | Preview cost (shows constraint effects) |
weather_service_status | free | Health + constraint config summary |
weather_oracle_how_to_join | free | DPYC onboarding instructions |
weather_oracle_get_tax_rate | free | Current certification tax rate |
weather_oracle_lookup_member | free | Look up a DPYC member |
weather_oracle_about | free | DPYC ecosystem description |
weather_oracle_network_advisory | free | Active network advisories |
Core
Operators
Advocates & utilities
Apache-2.0