#SchCP review brief
This brief is for a reviewer who has not seen the project before. It explains what exists, how it was built, which claims need checking, and where we already know there are weaknesses.
#What this is
SchCP (Scheduling Context Protocol) is a proposed protocol that lets AI agents arrange time between people on their behalf. That means booking a business's appointments (barbers, auto shops, clinics), or setting up a meeting between several people whose own agents keep their calendars. It is meant as a first step toward agents coordinating people's time. Since v0.4 it never moves money. A host may state a price, which is disclosed in the signed offer and paid to the host directly, but many meetings have none. It covers:
- Discovery: each host publishes
/.well-known/scheduling.json. - Holds: short-lived holds on a time slot, built so that agents cannot sit on slots they never book.
- Mandates: signed statements of what the human allowed the agent to do. These extend AP2's mandates to bookings.
- Lifecycle: an iCalendar/iTIP-style sequence of book, reschedule, cancel and no-show.
Since v0.14 of the paper it is positioned as its own protocol for scheduling people, at a
business and between people's own calendars. It borrows from Stripe's UCP Local Services, Wix's USP
and Google's AP2 where they fit, and keeps its envelope close to theirs so one endpoint could speak
both, but it is not offered as a profile of them (the author's decision, design/v0.4-notes.md
§8.19). Payment, where a host wants it, belongs to the payment protocols or to the host's own
checkout, never to SchCP. Meta and Sierra's PAP is covered as a platform risk.
The repo is /home/businessdesk/dev/schcp, also kept in a private GitHub repository; v0.4 is on the branch v0.4-hosts. It contains:
| Path | What it is |
|---|---|
whitepaper/index.html |
The whitepaper, "Arranging People's Time: delegated scheduling authority for AI agents" (v0.15, which reports the live testing in §10.21, the decided open questions in §10.22 and the second implementation in §10.23). It is an HTML fragment with SVG figures. It is also published privately at https://claude.ai/artifact/H7kv3Qvkgm9uTYK3cjjX4y |
whitepaper/schcp-whitepaper-v*.pdf |
PDF render of the paper |
spec/SPEC.md, spec/schemas/schcp.schema.json |
Normative spec v0.4 and its JSON Schema |
design/v0.4-notes.md |
Why v0.4 took the money out and left cancellations and no-shows to hosts, what it removed and added, what changed on the wire, and the decisions made in live testing (§8) |
src/ |
Reference implementation in TypeScript. host/ holds the engine, the SQLite store and the HTTP server; agent/ holds the client, the planner (book an appointment; arrange, finish and revise a meeting of several people, before and after it is confirmed), the key app and webhooks; protocol/ holds JOSE, mandates, meeting contracts, acceptances and rules (meeting.ts), attestations, stated prices and durations; sim/ is a simulated internet of five fictional businesses (one a café that holds tables for meetings), a booking platform, and a personal-calendar service (Daybook) with three people on it |
test/ |
330 tests: 329 pass, 1 skipped, 0 fail, 0 todo. test/scenarios/ holds the business-viewpoint scenarios; test/people.test.ts and test/open-end.test.ts cover people as hosts and open-ended bookings; test/meetings.test.ts covers meetings of several people, rules and counters; test/meeting-changes.test.ts covers changing a confirmed meeting and businesses as members |
conformance/ |
Wire-level test vectors (233) and a black-box runner (npm run conformance) |
implementations/python/ |
A second, independent implementation in Python written from the spec, schema and vectors only: signatures, mandates, offers, receipts, moves, meetings and a live client (python3 -m schcp.vectors). Its README lists the 24 places the spec text was not enough, all since settled (design notes §8.20) |
src/mcp/ |
An MCP server, so an existing assistant can book through SchCP. 16 tools: discover_host, find_slots (with with and daily), hold_slot (with with), approve_and_book, release_hold, get_booking, cancel_booking, change_booking, respond_to_change, join_waitlist, waitlist_status, leave_waitlist, arrange_meeting (with answers and in_person), meeting_status, revise_meeting, cancel_meeting |
examples/site |
Sample booking site and playground (npm run site, port 4600), including a playdate between two parents and a stranger refused by a contacts-only calendar. npm run site:check runs its scenarios headless: 22 runs, 0 BAD |
docs/diagrams/ |
Five SVG diagrams: booking states, the meeting flow, who says yes, a guest's move, and how the live testing was run |
video/ |
Remotion source for a 131-second explainer (1080p). npm run render there writes out/schcp-explainer.mp4; the output is not committed |
research/ |
Four research reports: AdCP, scheduling standards, agent-commerce delegation (AP2/ACP/UCP and others), booking platforms |
reviews/ |
Ten business-viewpoint reviews. reviews/README.md is the ranked synthesis |
LICENSE, LICENSE-docs, IPR_POLICY.md, NOTICE |
Licensing modelled on AdCP: Apache-2.0 for code, CC-BY-4.0 for docs, and a royalty-free licence under Necessary Claims |
Run it with Node ≥ 22.18; there is no build step, because Node strips the TypeScript types itself:
npm install
npm test # 330 tests (329 pass, 1 skipped)
npm run site:check # 22 headless scenario runs
npx tsc -p . # typecheck
npm run site # playground on http://localhost:4600
npm run demo # oil-change walkthrough
#How it was built (in passes)
The user's draft (v0.1). A problem statement and a sketch inspired by AdCP.
Research. Parallel research agents produced the four reports in
research/. They surveyed AdCP, iCalendar/iTIP/CalDAV/JSCalendar, AP2/ACP/UCP/PAP/Web Bot Auth/UCAN/Biscuit, and booking platforms (Square, Mindbody, Booksy, Fresha and others), with market numbers.First build (spec v0.1). The reference merchant endpoint, the agent SDK, signed offers on holds, booking-scoped management mandates, and 77 scenario tests. This produced whitepaper v0.3, which has a build report.
Adversarial review. An independent review found 18 defects. All were fixed, with regression tests R1–R18 in
test/review-regressions.test.ts, bringing the suite to 97 tests.Sample site. Added the booking site, no-show fees, alternatives offered on decline, and screenshots in the paper.
Second pass (spec v0.2). We built three items the paper had listed as open questions, bringing the suite to 108 tests:
- Closed mandates. The human signs the hash (
offer_sha256) of the exact offer the merchant issued. The merchant policyclosed_mandate: never | if_charged | alwayscontrols when this is required, and a mismatch returnsOFFER_APPROVAL_REQUIRED. The consent text is generated from the signed offer. - Attestations. Third-party attesters (for example, a phone check) vouch that a principal key
belongs to one person. Attested newcomers get their own pool of holds
(
max_active_per_attester), so a flood of throwaway identities can't crowd them out. An invalid attestation is ignored rather than rejected. - Reputation decay. Old hold expiries count less over time (half-life of 7 days by default). A suspended agent gets 60 s holds back after about 1.1 days and the full TTL after about 7 days. The "has booked here before" signal never decays.
- Closed mandates. The human signs the hash (
Third pass: ten business seats. Ten reviewers each took one viewpoint: small merchant, booking platform, healthcare, AI-assistant vendor, payments risk, multi-location chain, home services, consumer-protection regulator, scarce slots, and investor/standards. They wrote 47 scenario tests: passing tests show what already works, and
todotests mark gaps.Fixes from the third pass (whitepaper v0.5). The reviews found four real defects, all now fixed:
- A deposit pre-authorization was not bound to the payer. It now carries
principal, and a mismatch returnsPAYMENT_DECLINED. - Refund failures were swallowed. Refund state (
pending/refunded/failed) is now recorded on the booking, retried with backoff, and visible to the agent. Every retry uses the same idempotency key, so a lost response can't cause a double refund. This is a MUST in spec §5.5. - A no-show was silent. It now bumps the sequence, issues a signed receipt and sends a
booking.no_showwebhook. - The consent text showed raw ISO timestamps and an internal ID. It now gives local time in words and the business name. The key app shows the domain it verified during discovery, never a domain the offer merely claims.
Also in this pass:
- Schema migration for older databases (removed in v0.4: nothing has been released, so there is nothing to migrate).
- The licensing above.
- Paper changes: abstract rewritten around the gap, the money and the ask; market numbers in §1; new §10.13 "Third pass: ten seats at the table"; expanded §11 open questions; new §12 "Conclusion and ask".
- A deposit pre-authorization was not bound to the payer. It now carries
Deep review (whitepaper v0.6). An adversarial review reproduced 20 defects with 21 probes (
reviews/deep-review.md); each was fixed with a regression test (test/money-and-leases.test.ts, removed with money in v0.4,authorization-boundaries,accepted-terms,calendar-and-wire,standing).Protocol v0.3 (whitepaper v0.7). Built from
design/v0.3-notes.md: every acceptance names the SHA-256 of a merchant-signed offer; offers state an all-in total; late cancellations, late changes and no-shows are recorded rather than charged, and an enforcing merchant adds them to the next offer, which only the human's own signature accepts; a 60-second undo; a waitlist; two-party confirmation and reconfirmation; one refusal shape with registered reasons.Two v0.3 reviews.
reviews/v0.3-review.md(V01–V10) andreviews/follow-up-review.md(R01–R12) reproduced 22 distinct defects between them, mostly where the new waitlist and two-party paths met the older controls. Each is fixed with a regression test intest/v03-review.test.ts, and every fix was checked by reverting it.Closing the gaps. Every
todoscenario the ten seats wrote now passes (the 1 skipped test is arrival windows, a deliberate "won't do"). New in the engine and spec: resource capacity and combinable resources for parties, per-person prices, release schedules, a walk-in reserve, customer eligibility, a notes refusal for clinics, service locations and areas, surcharges, a deposit after a no-show, merchant extensions, displacement when a resource goes out, deposit waitlists, two-party bookings at approval merchants, pre-approved no-show ceilings, mandates for several merchants with a shared booking limit kept by the key app, operator-wide rate limits and reputation, brands, and calendar cold start. Each rule was mutation-checked (27 mutations, all caught). Built in parallel: crash tests (which found a stranded-capture race in the payment journal's release, now fixed), a conformance runner with test vectors, an MCP server, an AP2 profile (SD-JWT VC mandates with ES256 keys), and a sample site showing the v0.3 features.v0.4: arranging time, never money (
design/v0.4-notes.md). SchCP no longer moves money. Removed: deposits, the payment provider, captures, refunds, the payment journal, the confirm and approval leases, adjustment lines, and the crash and payment tests. Confirming a booking is now one database transaction. Prices are optional disclosures. "Merchant" became host (a business or a person) and "customer" became guest. New:- open-ended bookings, which have a start and no agreed end; the host sets time aside and ends the booking when it is over;
- places: at the guest's address, or a place the guest names;
- people as hosts, with contacts-only calendars whose contact list is never published;
arrangeMeeting, which books one meeting on two people's calendars with one consent.
The engine, tests, MCP server, conformance runner, sample site and spec were updated in parallel; every new rule was mutation-checked.
Meetings of several people, rules and counters (
design/v0.4-notes.md§4.5–4.7, spec §5.11, §5.12, §7.9). One organizer-signed contract names every member, their calendar and that calendar's signed offer; each calendar checks only its own entry; every member accepts the contract's hash, with their own key or through their agent under a rule they signed; nothing is confirmed anywhere until the organizer commits every acceptance to every calendar. A member's counter (another place) leads to a revised contract that everyone answers again. Bookings say whether the host, a person or a rule said yes, and a guest can ask for the person. 13 mutations, all caught.Cancellations and no-shows left to hosts (
design/v0.4-notes.md§5, spec §5.6, §7.6). With no money moving, recording late cancellations and no-shows made no sense. Removed: fee terms in offers and policies (cancellation tiers, late-change treatment, no-show fees), outcome records,max_no_show_fee, theif_no_show_feeclosed-mandate policy, the cancel quote and the 60-second undo. Cancelling is always free and records nothing. Kept: a host may mark a past bookingcompletedorno_showas a status only, because a kept appointment is what earns standing under the anti-sybil cap (max_active_unproven) and for "returning guest" eligibility. The conformance checkscancel.quoteandcancel.undobecamecancel.free.Live testing with AI assistants on both sides (
design/v0.4-notes.md§8). Claude Haiku assistants used the MCP tools through a local gateway, as guests (Noor, Sam, Kim, Alex) and as hosts (the front desk of Rosie's Nails, and the agents keeping Ellis's and Priya's calendars). Each round ran three scenarios. After each round the defects and misread wording were fixed, until three clean rounds in a row: 25 rounds, clean ones 17, 19, 21, 23, 24 and 25. The commits from "Fixes from the first live run" onward record each fix. What changed since the last brief:- Guests can move a booking (
change_booking). At ahost_approvalhost the move waits for the host's yes and the booking keeps its time meanwhile (proposal.awaiting_host,consent); a rule the person signed may say yes. New webhooksbooking.change_approvedandbooking.change_declined(with arefusal:host_declined:*, orhost_did_not_answer). A declined move stays on the booking asdeclined_changefor guests who poll (spec §5.7, §5.8, §6). - Per-member
answersin a meeting contract: the organizer can ask one member to answer in person and let a rule answer for another; the consent text names each group (spec §5.12, §7.9; MCPin_person). - Taking back a yes, and backing out. Until a meeting is final, a member may decline a
booking they or their rule already said yes to; a decline is always recorded as the
person's own answer, never a rule's. A member's calendar that cancels its part, before or
after the meeting is final, calls the meeting off, and the other calendars hear why in that
person's words.
cancel_meetingcalls a meeting off on every calendar at once (spec §5.12). - A business's own answer is
by: "host". A front desk's yes, no or counter used to be recorded asanswer.by: "person", which assistants read as a private individual."person"now means the person whose own calendar it is, and"rule"a rule they signed (spec §7.9). - A confirmed meeting is not revised, and the refusal now comes before anything is held
or asked: asking for a change offer on a confirmed meeting booking is
meeting_invalid. (Superseded in pass 17: a confirmed meeting can now change with every member's yes.) - Choosing a person.
discover_hostlistschoose_from;find_slotsandhold_slottakewith; offers carryresource_namenext toresource(spec §5.2). - A waitlist window must hold the whole appointment (
window_too_short, spec §5.9). - MCP wording.
approve_and_booksaysuser_signed("this exact offer" or "only the limits: service, time window, price ceiling");find_slotstakesdailyhours;discover_hostlists taxes and says when a host refuses notes; prices say "list price"; deadlines come with relative times (expires_in,held_for);meeting_statusgivesanswer_by; outcomes sayrequestedrather thanbooked: false. - Times on the wire are whole seconds; consent texts name attendees and who answers how.
- Guests can move a booking (
Deciding the open questions (paper §10.22,
design/v0.4-notes.md§8.10–§8.19). The author answered fourteen open questions, each put as a plain choice, and this pass built the answers:- Changing a confirmed meeting. The organizer revises it; every calendar keeps it
confirmedat its time with the revision inmeeting.pending_revision; once every member has said yes,commitmoves each calendar atomically (booking.change_approved); a no, a yes taken back or the deadline (never after the start) drops it, and the organizer withdraws it elsewhere (POST /bookings/{id}/meeting/withdraw, reasonorganizer_withdrew). - Businesses as meeting members (
business: true): the business's signed offer is its yes, or the host's yes where it approves each booking. Bellwoods Café joined the sample world. - The
meetingscapability in discovery; a calendar without it refuses a meeting contract. - Members shown by your own contact names, or as "someone you don't know, named …" (a SHOULD).
- Records retention stated by the host:
policies.retentionandretentionin every offer and receipt (required), and in the consent text. SchCP sets no number and checks nothing. - Receipts as evidence:
consent: {approved: "offer" | "contract" | "limits", mandate, mode?}replacesapproved_by; the SDK refuses a receipt whose retention or mandate disagrees. - Consent guidance (spec §5.3 SHOULDs) and
differs_from_requestin the MCP'shold_slot. - A cancel gives a booking back to a shared booking limit, retried while the key app is down.
- Renames: "wallet" became the key app and "budget" a shared booking limit
(
constraints.shared_limit: true, reasonshared_limit_reached), since neither holds money. - Decided without building: recurring meetings are not in v0.4; finding a place is out of scope; a cross-host reputation is left out; a host's silence still counts as attended; accessibility requests stay open. SchCP stands on its own rather than as a profile.
- Also: closed schemas for holds, waitlist entries and booking responses; the v0.2/v0.3 review
probes retired;
npm run check-docs, which fails when a count in the docs disagrees with the code; and the R2 cap test timed in CPU time, the likely cause of live testing's one unexplained failure.
- Changing a confirmed meeting. The organizer revises it; every calendar keeps it
#What we'd most like reviewed
In priority order:
Do the paper, spec and code agree? The paper makes many specific claims, such as recovery timelines, error codes, field names, test counts and the decay maths. Spot-check them against
spec/SPEC.mdandsrc/. Inconsistencies between these three have been our most common bug.Are the security and trust mechanisms sound? Look at mandate verification and chaining (
src/protocol/mandate.ts), closed-mandate enforcement (only the ROOT mandate'soffer_sha256counts, not an agent-signed child), attestation handling (src/protocol/attestation.ts), hold caps and reputation (src/host/engine.ts), HTTP message signatures (src/protocol/httpsig.ts), and contacts-only access (checkAccess, and whether a stranger can learn anything about a person's calendar). Try to break them. New in this pass, and worth the most attention:- Meeting contract checks (
checkMeeting,verifyMeetinginsrc/protocol/meeting.ts): each calendar checks only its own entry (signer is the booker, member is its owner, offer hash, time and place). Can a contract say something to one member that another member's offer does not? - Acceptances and rules (
verifyAcceptance,verifyRule,ruleCovers): another calendar verifies a rule acceptance it did not see given. Can a rule be stretched past what the person signed, or used by an agent it does not name? - Commit (
commitMeeting): nothing is confirmed without every other member's acceptance of the current hash. Atomicity across calendars is the organizer agent's job (finishMeetingcancels the rest if one commit fails); hosts never talk to each other. - Revision (
reviseMeeting): same meeting, same organizer, higher revision, and an offer the calendar actually made. Can an old contract or acceptance be replayed?
New in pass 17, and worth the most attention now:
- Changing a confirmed meeting (
reviseMeeting,commitMeeting,withdrawRevisioninsrc/host/engine.ts;finishMeetinginsrc/agent/planner.ts; spec §5.12). Can a waiting revision move one calendar without every yes, outlive a cancellation or the start, or leave calendars disagreeing without the organizer's agent noticing? - Business members: can a business member's booking be committed before its host said yes, or a meeting be final with the business still waiting?
- Receipt
consentandretention: can a receipt name a mandate the person never signed, or a retention that differs from the offer, and still pass the SDK? - Shared booking limit release on cancel (
src/host/server.ts,limit_claimsin the store): can a unit be given back twice, or given back for a booking that went ahead?
New in pass 16, from live testing:
- Host-approved guest moves (
acceptChange,approveChange,declineChangeand the sweeper insrc/host/engine.ts; spec §5.7 step 3). The booking keeps its old time, contract andsequencewhile the new time is held until the approval deadline. Can a waiting move be applied twice, outlive a cancellation, or move a booking without the host's or a rule's yes? Does a move by the owner, or one a rule covers, skip the wait as it should, and does an unanswered move stay out of the guest's reputation? declined_change: recorded on a decline or a lapse, cleared by the next change request. Is it ever left stale, or set for a move that did happen?- Per-member answers (
memberAnswersinsrc/protocol/meeting.ts, and where the engine uses it): a member's ownanswersoverrides the contract's. Can a rule answer for a member the organizer asked to answer in person? - Retraction (
hostDeclineon atentativemeeting booking): allowed only until the meeting is final. Can a confirmed meeting booking be declined, or a retraction race a commit? - Backing out (
hostCancelon a meeting booking, andfinishMeetinginsrc/agent/planner.ts): the meeting goes off, the cause is a no or a lapse before any cancellation, and the others are told in the member's words. Is the right person ever blamed wrongly, or a calendar left showing a meeting that is off?
- Meeting contract checks (
Are the external facts true? Dates and claims about other efforts came from research agents and have not been independently verified. Check these in particular:
- the UCP Local Services charter (filed 2026-10-09);
- Wix USP;
- AP2 v0.2;
- PAP (Meta + Sierra, 2026-10-06);
- AdCP's licensing and IPR (
research/adcp.md§2.4); - the market numbers in §1, which come from
research/booking-platforms.md§5: 84k employer and 838k non-employer US salons, ~176k US auto shops, ~23% healthcare no-shows, a 56% phone-booking share, Booksy's 30% and Fresha's 20% fees, and Treatwell's £1.6bn.
Is the paper persuasive? Does the abstract land with an executive in two minutes? Is the ask in §12 clear?
reviews/investor-standards.mdmade suggestions we did not take: retitling the paper, adding an author byline, a "who pays" section, and a unit-economics example.Are the tests real? We mutation-checked each feature: each was broken on purpose to confirm a test fails. The third pass showed that 108 tests written only from the protocol designer's point of view missed four bugs. What other viewpoints or attacks are still missing?
#Known gaps and limitations (already acknowledged)
- No
todoscenarios remain. Gaps named only in the narrative reviews are still open:- Arrival windows are a deliberate "won't do".
- Per-staff skills and travel time are out of scope on purpose (spec §11). Which staff member can do a service is the host's own matching: a guest only sees availability, and choosing a person is already covered by selectable resources. Getting there is the humans' business.
- Accessibility needs can't be requested.
- Regulated data is only partly handled: participants name an attendee separate from the signer, but there is no policy on clinical notes.
- Records retention is each host's own statement (spec §2.1): SchCP sets no number and cannot check it.
- No money, by design. A host that wants deposits takes them outside SchCP.
- Cancellation and no-show policies are the host's own, by design. SchCP carries no fee terms
and records nothing; cancelling through it is always free. A host marks attendance as a status
only. A guest cannot contest a
no_showmark through the protocol, and the mark costs them standing (spec §7.2). - There is no SDK beyond TypeScript. The conformance runner has only been run against the simulated world, not a third-party endpoint. A second implementation, written from the spec alone, is the next step.
- Live testing was narrow. It used one model family (Claude Haiku) on both sides, the simulated hosts, and a local gateway, with scenarios written by us. Three clean rounds in a row show the tools are usable in those scenarios, not that every assistant will read them the same way.
- Deliberate simplifications worth a reviewer's look:
- A waitlist offer does not re-check mandate revocation; the confirm does.
- "Returning guest" standing is inferred from past bookings not marked otherwise: a host's silence counts as attended (decided in pass 17).
- A participant answers an invitation with a mandate of any scope; its constraints still bind.
- Checking a contacts-only calendar's availability from the MCP server uses a mandate that can act
on nothing: its only scope is
cancel, limited to a booking that does not exist. An empty-scope "identify only" mandate would look cleaner, but under the rule above it could accept an invitation. - A meeting member's calendar checks only its own entry. The other members' names and principals are the organizer's word; a member's agent shows them by its own contact names (a SHOULD, not a check).
- Recurring meetings are not in v0.4: each occurrence is its own meeting.
- An acceptance has no expiry of its own; it is bounded by the terms it names (a contract's
exp, a booking's approval deadline) and must be dated between when those terms existed and now.
- The review probes are retired. The deep, follow-up and v0.3 reviews' probe files ran against
v0.2/v0.3 code. Each of their 46 findings is now a regression test (36) or went away with money and
records in v0.4 (10);
reviews/README.mdhas the table. - Holding paper and code consistent is mostly manual.
npm run check-docschecks the counts, the protocol version and the pictures the docs state; everything else is checked by hand. examples/traces/is regenerated by test runs and shows up as modified. This is not a real change: restore it withgit checkout examples/traces/and never commit it.
#Suggested output from the reviewer
A ranked list of findings. For each one, give the file and line, what is wrong, a concrete failure scenario or counter-evidence, and a suggested fix. Separate confirmed defects from opinions, and flag anything in the paper that overstates what the code does.