Reader Sign-In Flow

Your readers are not Leed users and never will be. They have no account, no password, no invitation and no seat on your team. The sign-in flow behind your Docs MCP exists to prove exactly one thing: that the person on the other end owns the email address they typed. Everything else — the profile fields, the lead capture, the access decision — is built on that single proof.

Every screen in the flow is served from your domain, carries your logo and your company name, and posts back to itself. The reader never sees Leed.

StepWhat your reader doesWhat they seeLimits
1Nothing — their client does itNothing5 client registrations per minute per IP
2Types a work email, passes a human checkEmail screen on your domainTurnstile fails closed
3Types the 6-digit code from their inboxCode screen, same tab10-minute code, 5 attempts, 3 sends per minute per address
4Fills in whatever you do not already knowProfile screen, only the missing fieldsFirst and last name required
5NothingTheir client, connected — or the no-access screenDecided by your two access toggles
sequenceDiagram
    autonumber
    participant C as Reader's MCP client
    participant B as Reader's browser
    participant S as Your domain
    participant M as Their inbox
    C->>S: POST /mcp/oauth/register
    S-->>C: client_id (public, no secret)
    C->>B: Open /mcp/oauth/authorize with a PKCE challenge
    B->>S: GET /mcp/oauth/authorize
    S-->>B: Email screen + human check
    B->>S: POST step=request_otp
    S->>M: 6-digit code
    M-->>B: Reader reads the code
    B->>S: POST step=verify_otp
    Note over S: Arms the 5-minute email-verified marker
    S-->>B: Profile screen (missing fields only)
    B->>S: POST step=profile
    Note over S: Consumes the marker, then runs the access gate
    S-->>B: 302 back to the client with a single-use code
    B->>C: Authorization code
    C->>S: POST /mcp/oauth/token with the PKCE verifier
    S-->>C: 24-hour access token + refresh token

Step 1 — the client registers itself

Before anything is shown to a human, the reader’s MCP client registers itself with your domain using Dynamic Client Registration. It posts the redirect URIs it wants to POST /mcp/oauth/register and gets back a client_id.

There is no client secret. Your authorization server advertises token_endpoint_auth_method: "none" and issues public PKCE clients only, which is what lets a desktop app or a CLI complete the flow without anything pre-shared. A registration is refused if it names more than five redirect URIs, or if any of them is not https — http is accepted only for loopback addresses (localhost, 127.0.0.1, ::1), which is how native and desktop clients receive their callback.

Registrations are rate-limited to five per minute per source IP. Over that, the client gets 429 with invalid_client_metadata and the description registration rate limit exceeded.

Your reader sees none of this. If it fails, what they see is their client reporting that it could not connect.

Step 2 — the email screen

The client opens a browser at GET /mcp/oauth/authorize on your domain, and Leed renders a server-side page: your logo, a heading naming both the client and your company (“Connect Claude to Acme documentation” — or “your AI tool” when the client did not send a name), a single Work email field, and a Cloudflare Turnstile widget.

Under the field sits the opt-in disclaimer, verbatim:

By submitting your details you are opting in to engage via email with .

Turnstile runs before anything else and fails closed: a missing token, a missing key or a rejected challenge all stop the request before a code is generated, and the reader is returned to the same screen with “Verification failed. Please try again.” This is the only thing standing between your OTP sender and an email-bombing script, so there is no way to switch it off.

What happens next depends on the capture leads from non-customers toggle described in Configuring the Docs MCP. With it on — the default — the access gate is deferred to the very end and everyone who clears Turnstile gets a code. With it off, the gate runs right here: a reader whose domain does not match a current customer is refused on this screen, with the denial message in the error banner, and no code is ever sent.

Step 3 — the one-time code

Leed emails a six-digit code and re-renders the same page with a Login code field. The email is co-branded — your logo, your name, a link back to your site — with a “Powered by Leed” footer.

The code lives for 10 minutes and tolerates 5 wrong guesses. The fifth wrong guess destroys the challenge rather than locking the address, so the reader can request a fresh code immediately. Either failure shows the same message: “That code is incorrect or expired.”

The one-time code step on a documentation site, showing the six-digit code field

A correct code proves email ownership. Leed records that proof as a short-lived server-side marker — five minutes — which the next step consumes. The reader never sees it, and it is what stops a forged profile submission from minting an authorization code without a real verification behind it.

Step 4 — progressive profiling

Only the fields Leed does not already have are shown. A reader who signed in six months ago and whose record is complete skips this screen entirely and is granted on the spot.

FieldPrompted whenRequired
First nameNot on fileYes
Last nameNot on fileYes
PhoneNot on fileNo
Job titleNot on fileNo
CompanyTheir company is not already knownNo

The distinction between the last two columns is worth reading twice. A missing phone number or job title is enough to show the form; it is not enough to block the submission. Only first and last name are enforced — submitting without them re-renders the screen with “First and last name are required.” The company field appears only when the reader is not already linked to an account and their email domain matches no current-customer account, so a known customer is never asked to re-type their own employer.

The screen repeats the opt-in disclaimer and adds one more line: “Once connected, your access stays valid for 365 days before you’ll need to sign in again.” A hidden field captures the browser’s IANA time zone.

The progressive-profile step, showing first name, last name, phone, job title and company fields

If the five-minute verified marker has lapsed by the time they submit, the flow restarts at the email screen with “Your session expired. Please sign in again.”

Step 5 — the decision

With a verified email and a complete-enough profile, the access gate runs — or runs again, if it already ran before the code was sent. Whether a verified reader is granted or denied is decided entirely by your two toggles, which are documented in Configuring the Docs MCP.

Granted is invisible: a 302 back to the reader’s client carrying a single-use authorization code, and the client exchanges it for tokens without the human seeing anything.

Denied takes one of two shapes. With capture leads on, the reader has already verified and submitted their details, so those details are captured first and then a terminal screen appears — “No access yet”, with no retry and no form. With capture leads off, the refusal happened back on the email screen and reads:

This documentation is available to current customers. We could not match your email domain.

The terminal no-access screen shown to a reader whose company is not a current customer

The gate also fails closed on an email address whose domain cannot be resolved to a registrable domain, such as a bare public suffix. That reader is denied rather than passed through with an empty domain.

The exact wording of every screen

Email screen

  • Heading: Connect {client name} to {company} documentation — “your AI tool” replaces the client name when the client registered without one.
  • Body: Sign in with your work email to access the {host} documentation.
  • Access is reserved for active customers of {company}.
  • Field: Work email, placeholder you@company.com
  • By submitting your details you are opting in to engage via email with {company}.
  • Button: Send code

Code screen

  • Heading: Enter your code
  • Body: We emailed a 6-digit code to {email}. Enter it below.
  • Field: Login code
  • Button: Verify and continue

Profile screen

  • Heading: Almost there
  • Body: Tell us a little about yourself to finish connecting to {company} documentation.
  • Helper: Fields with * are required.
  • By submitting your details you are opting in to engage via email with {company}.
  • Once connected, your access stays valid for 365 days before you’ll need to sign in again.
  • Button: Continue

No-access screen

  • Heading: No access yet
  • Body: Thanks for verifying your email. Your company does not currently have access to the {host} documentation.
  • Access to {company}'s documentation is reserved for active customers. Reach out to your account team to request access.

How long access lasts

The token the client receives is good for 24 hours. Its refresh token is single-use and rotates on every renewal, and the whole family of tokens descending from one sign-in shares a single fixed 365-day window set at the original grant.

ThingLifetimeWhat happens at the end
One-time code10 minutes, 5 attemptsChallenge destroyed; the reader requests a new code
Email-verified marker5 minutesProfile submit is refused; the flow restarts at the email screen
Authorization code5 minutes, single useExchange fails; the client restarts the flow
Access token24 hoursThe client renews it silently
Refresh family365 days from the original sign-inRenewal fails; the reader does email + code again

Two other things end access early. Every renewal re-runs your access gate, so a customer who churns loses MCP access at their next renewal — within 24 hours, with nothing for you to do. And if a rotated refresh token is ever presented a second time, that is treated as theft: the entire token family from that sign-in is revoked and the reader starts over.

What is stored about a reader

A granted sign-in is recorded through the same machinery as any form fill on your site. The capture rides a protected system form named Documentation MCP, which the Forms UI cannot edit or delete because the service owns it, and it carries first name, last name, email, phone, title and company.

That means a Docs MCP reader appears among your contacts and in form submissions alongside everyone who filled in a form on your marketing site. When a reader had already been browsing your site anonymously, their prior activity is stitched onto the identified record. No confirmation email is sent — the one-time code already verified the address.

Also stored:

  • The marketing opt-in, recorded with the capture. The consent screens state it before the reader submits, which is why the disclaimer is on two of the three screens.
  • One request row per tool call. Tool name, the inputs including the search text, how many records came back, and which verified reader made it. The Docs MCP Tool Reference covers what you can learn from it.
  • One row per denied attempt, carrying the attempted email address and its domain even though no contact was created. Under capture-first the denial row is tied to the captured lead, so the demand signal and the lead are one record.

Marking a reader’s employer a current customer is what lets their whole domain through the gate, and that flag lives on the account record.

The rate limits your readers can hit

Three limits protect the unauthenticated part of the surface. All three are per-minute burst ceilings.

LimitCapWhat the reader sees
Code requests per email address3 per minute“Too many requests. Please wait and try again.” on the email screen
Code requests per source IP10 per minuteThe same message
Client registrations per source IP5 per minuteTheir client reports a failed connection; no screen is reached

The per-IP code limit is the one to know about if a group of readers shares an office egress address: ten sign-in attempts a minute from one IP is generous for humans and hostile to a script, but a training session where thirty people connect at once will queue.

If a code request fails to send for any other reason, the stored code is discarded so the reader can request a clean one, and they see “We couldn’t send your code right now. Please try again.” Their remaining allowance is unaffected by the retry. Failures on the client side of the connection — the ones where the reader gets in but their tools do not work — are collected in MCP Troubleshooting.

ESC