The machine economy runs a tab, not a signup.
Handsel unbundles your signup form into six protocol acts, carried in the OAuth 2.1 / MCP authorization slots agent clients already speak. An unmodified client reaches first value, and a co-signed receipt, with zero forms — the relationship materializes from use, not before it.
Protocol trace — illustrative, from SPEC §21's arc; the live in-browser run activates when the core enables browser CORS.
→ define("serendipity") returns the real bundled definition — no model, no fake inference.
The signup form is not friction to be reduced. It is a wall to be removed.
A signup form is six services a vendor sells to itself and bills your users, six minutes at a time.
Handsel doesn't shrink the form — it unbundles it. Each job becomes a just-in-time step-up on one grant that climbs tiers without ever re-onboarding, paid the instant it's needed and not before. Underneath, every vendor sees a pairwise subject: a per-vendor identifier that can't be joined across vendors. The seventh job — making the user fund the vendor's risk up front — Handsel simply drops.
| The form sold the vendor | Handsel re-provides it as | Paid when |
|---|---|---|
| Credential issuance | Rung 0 · guest a grant, on sight | on first call |
| Identity + contact capture | Rung 1 · claimed verified OIDC / email, same grant | quota runs out |
| Pricing consent + payment path | Rung 2 · mandated user-signed, spend-capped mandate — no card at the vendor | at commit |
| Risk filtering | Rung 3 · assured org attestation / higher cap | the floor demands it |
| A durable principal | always pairwise subject, stable bottom-to-top | always, silently |
| Making the user fund the vendor's risk | — | never |
Six jobs, one grant — each re-provided just in time, none demanded up front.
Six acts, in order. Each carried in a slot your client already speaks.
Software stops being sold by persuasion and starts being bought on evidence.
- IOpena grant, on sight
- IITrybudgeted, TTL-bounded trials
- IIIProveone blinded workload, signed
- IVCommitpromote the winner in place
- VEvaporatelosers garbage-collect
- VISettleco-signed receipts, never arrears
~20 lines of config. Keep your tool handler.
Mount the SDK over your existing endpoint. It serves the well-knowns, signs your tier schedule, emits the standard RFC 9728 401 challenge and the step-up 403 unchanged, meters guest quota locally with no phone-home, and writes a vendor-signed receipt per call.
The vendor edge
/**
* Word Count — a Handsel MCP vendor over @handsel/mcp. One REAL deterministic tool,
* word_count (a pure function of its input — no dataset, no inference, no network on
* the answer path), sits behind the Handsel ladder: @handsel/sdk enforces guest/claimed
* quota, the 401/403 challenges, and receipts. Configure via env (see .env.example).
*/
import { serveMcpVendor } from '@handsel/mcp'
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'
const env = z
.object({
HANDSEL_CORE: z.string().url(),
VENDOR_RESOURCE: z.string().url(),
VENDOR_SIGNING_SEED: z
.string()
.regex(/^[0-9a-fA-F]{64}$/, 'expected a 64-hex Ed25519 seed from `handsel keygen`')
.optional(),
VENDOR_SECRET: z.string().optional(),
CORE_SECRET: z.string().optional(),
PORT: z.coerce.number().int().positive().optional(),
})
.parse(process.env)
await serveMcpVendor({
product: 'word-count',
name: 'Word Count',
resource: env.VENDOR_RESOURCE,
handselCore: env.HANDSEL_CORE,
...(env.VENDOR_SIGNING_SEED ? { signingSeed: env.VENDOR_SIGNING_SEED } : {}),
...(env.VENDOR_SECRET ? { vendorSecret: env.VENDOR_SECRET } : {}),
...(env.CORE_SECRET ? { coreSecret: env.CORE_SECRET } : {}),
// Pricing models — the receipt bills price_usd_micros * units (patterns: the
// @handsel/sdk README, "Pricing models"). Beyond per-call flat:
// metered — unit: 'token' + onCall: (body) => ({ units: yourCount(body) })
// success-gated — bill_on_miss: false + onCall: (body) => ({ miss: wasEmpty(body) })
// freemium — price_usd_micros: 0 (calls co-sign price-0 proof-of-use receipts)
pricing: { unit: 'call', price_usd_micros: 500 },
tiers: { guest: { calls_per_day: 5 }, claimed: { calls_per_day: 500 } },
terms: 'Word Count demo vendor. Calls are metered per the vendor-signed schedule.',
port: env.PORT ?? 8787,
// Return a fresh MCP server per request; @handsel/mcp owns the transport. Register
// your real tools here — word_count is pure + deterministic (Covenant 3).
buildServer: () => {
const server = new McpServer({ name: 'word-count', version: '0.1.0' })
server.registerTool(
'word_count',
{
title: 'Count words',
description:
'Count the whitespace-separated words in a string. Deterministic; no dataset, no inference.',
inputSchema: { text: z.string().describe('The text to count words in.') },
},
({ text }) => {
const trimmed = text.trim()
const count = trimmed === '' ? 0 : trimmed.split(/\s+/).length
return { content: [{ type: 'text' as const, text: String(count) }] }
},
)
return server
},
})
Byte-identical to what npm create @handsel/vendor --product word-count
scaffolds — the one synced hello-world across the SDK docs and this site (docs:sync).
The ladder, one command at a time
None of these are on npm yet — this is the shape of the arc.
npm create @handsel/vendor scaffoldnpx handsel keygen Ed25519 seednpx handsel register claim a tenantnpx handsel verify self-grading examnpx handsel claim connect Stripenpx handsel connect go live0 forms · 0 sales calls · 0 humans. KYC is Stripe's, caps are published, promotion is code.
Three depths, one gradient: SDK (20 lines) · proxy (zero code) ·
your agent (/handsel-integrate).
Your agent reads /llms.txt and opens the PR.
A reference implementation that runs end to end today — you just can't npm-install it yet.
What's real, stated plainly:
the rung-0 zero-form MCP e2e (pnpm e2e:mcp-guest) — a stock client reaching a
receipted first call, over real Postgres.
SPEC §17's inviolable set — no negative balance, bounded overspend, idempotent settlement, no cash-out — asserted by fuzz and conformance against real Postgres.
no withdrawal, transfer, or resale-liquid path anywhere in the protocol; refunds only to the funding source.
Spec, SDK, and conformance suite are private today — by choice, until the design partners have shaped them. Stripe is in test mode by design: no real money moves until counsel signs off, and money-path deploys are human-gated.
# the whole arc, from a clone — design partners get the clone
pnpm i && pnpm db:up && pnpm e2e:mcp-guest
1.75% of Handsel-settled flow. $0 until money moves. The sandbox is free.
The rails' cut rides as an application_fee_amount on the vendor's own Stripe Connect
account — on the primary rail, Handsel is not merchant of record and holds $0.
Test mode by design — no live payment moves until counsel signs off; money-path deploys are human-gated.
We're hand-picking 3–5 design partners before the spec goes public.
If you sell a usage-priced API, the pitch is one sentence: we'll show you your API running behind Handsel — an unmodified agent client reaching your tool with zero forms, every call receipted, settlement in Stripe test mode — before you commit to anything.
One partner per category. First movers shape the tier schedule and the spec while both can still move, and get the handsel-enabled mark first when the registry flag ships.
We don't do forms — not even ours. Email is the whole flow:
The register stays honest here: reference implementation, Stripe test mode, no external vendor live yet. You'd be the first external vendor — that's the design-partner offer, not a gap.
Playground — paste your API's base URL and watch an unmodified agent climb it behind an ephemeral Handsel proxy: 401 challenge, zero-form guest grant, your real response, a co-signed receipt — sandbox tier, gone in an hour. Preview the playground → (the hosted run is still landing; until then the page degrades honestly and shows the local-run recipe.)
The signup form was the checkout counter of software sold to humans by persuasion.
Handsel is where software is bought by machines on evidence:
open ·
try ·
prove ·
commit ·
evaporate ·
settle
Your API behind Handsel — an unmodified agent reaching your tool with zero forms — before you commit to anything.
Show me my API behind Handsel