Skip to content

Require email verification before MCP setup and preserve media results - #734

Merged
kody-bot merged 4 commits into
mainfrom
cursor/smooth-onboarding-images-f304
Jul 12, 2026
Merged

kody-bot merged 4 commits into
mainfrom
cursor/smooth-onboarding-images-f304

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add a verification-first signup and onboarding experience that blocks MCP OAuth grants until the account email is verified.
  • Preserve safe OAuth resume targets consistently across signup, resend, verification, server redirects, and SPA navigation.
  • Pass protocol-valid downstream MCP image, audio, and resource blocks through execute with bounded media-specific limits.
  • Harden stale verification state, open redirects, marker collisions, malformed payload handling, and durable invocation persistence.

Validation

  • npm run validate passes: format, lint, typecheck, 825 unit/worker tests, 14 Playwright tests, and 2 MCP E2E tests.
  • CI Validate, preview deployment, CodeRabbit, and Cursor Bugbot checks pass on 602eb2af.
  • All substantive automated review findings are addressed; one optional test-encoding nit remains intentionally unchanged because it does not affect production behavior.
  • Manual browser walkthrough confirms unverified onboarding is blocked, no MCP URL is exposed, resend/continue controls work, and /onboarding redirects to verification.

verification-first-onboarding.mp4

Verification-first pending page

System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ 025ea00a · Head: 602eb2af

Classification: extends — changes email-verification gates, MCP OAuth authorization, and downstream MCP result transport without adding a primitive.

Primitives touched

Primitive Group Impact
app-ui surfaces extends — adds pending-verification and redirect-safe onboarding states
app-sessions auth extends — preserves safe post-verification redirect state
mcp-oauth auth extends — rejects grant approval until email verification
mcp-server surfaces extends — emits bounded non-text protocol content through execute
capability-registry assistant extends — synthesized downstream tools retain MCP content blocks
mcp-client-servers assistant extends — validates and wraps downstream tool content
remote-connectors assistant extends — validates and wraps connector tool content
package-runtime runtime extends — bounds persisted raw invocation content

System map

Email verification gates onboarding and OAuth grants while downstream media flows through trusted markers into MCP responses and bounded persistence.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	appUi["app-ui<br/>Browser app"]:::extended
	appSessions["app-sessions<br/>Browser sessions"]:::extended
	mcpOauth["mcp-oauth<br/>MCP OAuth"]:::extended
	mcpClients["mcp-client-servers<br/>MCP client servers"]:::extended
	remoteConnectors["remote-connectors<br/>Remote connectors"]:::extended
	capabilityRegistry["capability-registry<br/>Capability registry"]:::extended
	mcpServer["mcp-server<br/>MCP endpoint"]:::extended
	packageRuntime["package-runtime<br/>Package runtime"]:::extended
	appUi -->|"pending-verification + safe redirectTo"| appSessions
	appSessions -->|"verified-email guard before grant"| mcpOauth
	mcpClients -->|"validated CallToolResult content"| capabilityRegistry
	remoteConnectors -->|"validated tool content"| capabilityRegistry
	capabilityRegistry -->|"trusted bounded content markers"| mcpServer
	mcpServer -->|"bounded raw invocation artifacts"| packageRuntime
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Invariants

  • Every verification, grant, connector, and persisted invocation path remains scoped to the authenticated userId.
  • Unverified accounts retain browser sessions for resend and recovery, but cannot receive MCP OAuth grants.
  • Verification resume targets remain same-origin across server and client navigation.
  • Kody does not fetch downstream image URLs or normalize malformed content blocks.
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Added a dedicated pending email verification experience with Resend and Continue actions, including redirect preservation.
    • Email verification now gates onboarding, OAuth approval, and MCP availability.
    • Enhanced MCP tool/execute handling for non-text (images/audio/resources) and structured content, with clearer output sizing.
  • Bug Fixes
    • Improved protection against unsafe redirects and malformed/oversized MCP payloads.
  • Documentation
    • Updated auth and MCP content protocol docs for verification gating and passthrough behavior.
  • Tests
    • Expanded unit and end-to-end coverage for verification redirects, OAuth gating, and MCP passthrough validation.

cursoragent and others added 2 commits July 12, 2026 18:23
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds pending email verification with preserved redirects, gates onboarding and MCP OAuth access until verification, centralizes verification prompts, and introduces bounded passthrough handling for non-text MCP content across execution and persistence.

Changes

Email verification flow

Layer / File(s) Summary
Redirect and loader contracts
packages/worker/src/app/safe-redirect.ts, packages/worker/client/routes/*redirect*, packages/worker/src/app/loader-data.ts
Adds safe redirect normalization, pending-verification paths, authentication redirect resolution, and verification-aware loader contracts.
Server verification and OAuth gating
packages/worker/src/app/handlers/*, packages/worker/src/oauth-handlers.ts, packages/worker/src/app/onboarding-data.ts, packages/worker/src/app/routes.ts, packages/worker/src/app/router.ts
Routes unverified accounts to /pending-verification, suppresses MCP setup data, carries redirectTo through verification links, and rejects unverified OAuth approval before authorization completion.
Client verification flow
packages/worker/client/routes/*
Adds the pending-verification route, shared resend prompt, verification-aware onboarding navigation, OAuth verification UI, and post-verification CTAs.
Validation and documentation
e2e/*, packages/worker/**/*.test.ts, docs/use/*, docs/contributing/architecture/*
Tests verification redirects, OAuth gating, onboarding behavior, MCP access, and documents the updated flows.

MCP content passthrough

Layer / File(s) Summary
Downstream result handling
packages/worker/src/mcp/downstream-mcp-result.ts
Defines explicit markers, validates content blocks, preserves structured results and errors, isolates marker collisions, and bounds persisted raw content.
Execution and persistence integration
packages/worker/src/mcp/capabilities/*, packages/worker/src/mcp/tools/execute.ts, packages/worker/src/mcp/executor.ts, packages/worker/src/package-invocations/service.ts
Passes through non-text MCP blocks, applies separate content limits, extracts explicit passthrough results, and persists bounded artifacts.
MCP validation and documentation
packages/worker/src/mcp/*test.ts, docs/contributing/architecture/mcp-client-servers.md, docs/contributing/architecture/remote-connectors.md, docs/use/execute.md, docs/use/raw-content-blocks.md
Covers malformed blocks, unsafe URLs, marker collisions, size limits, extraction, and persistence, and documents the protocol behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: email verification gating before MCP setup and preserving media results.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/smooth-onboarding-images-f304

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kody-bot
kody-bot marked this pull request as ready for review July 12, 2026 18:42
@github-actions

github-actions Bot commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-734.kody-a99.workers.dev

Worker: kody-pr-734
D1: kody-pr-734-db
KV: kody-pr-734-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/worker/client/routes/login.tsx (1)

354-369: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify passkey redirect to a single resolvePasswordAuthRedirect call.

The two-branch pattern with an early return is unnecessary — resolvePasswordAuthRedirect already handles requiresTwoFactor internally. A single call mirrors the email/password flow on lines 269-277 and eliminates the conditional.

♻️ Proposed refactor
 		if (
 			!verificationResponse.ok ||
 			verificationPayload?.ok !== true
 		) {
 			const errorMessage =
 				typeof verificationPayload?.error === 'string'
 					? verificationPayload.error
 					: 'Passkey sign-in failed.'
 			setState('error', errorMessage)
 			return
 		}

-		if (verificationPayload.requiresTwoFactor === true) {
-			window.location.assign(
-				resolvePasswordAuthRedirect({
-					mode: 'login',
-					requiresTwoFactor: true,
-					redirectTo: getCurrentRedirectTo(handle),
-				}),
-			)
-			return
-		}
 		window.location.assign(
 			resolvePasswordAuthRedirect({
 				mode: 'login',
+				requiresTwoFactor: verificationPayload.requiresTwoFactor === true,
 				redirectTo: getCurrentRedirectTo(handle),
 			}),
 		)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/client/routes/login.tsx` around lines 354 - 369, Simplify the
passkey redirect flow by removing the requiresTwoFactor conditional and early
return, then invoke resolvePasswordAuthRedirect once with mode,
requiresTwoFactor, and redirectTo. Preserve the existing redirect target and let
resolvePasswordAuthRedirect handle the two-factor value internally, matching the
email/password flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/client/routes/oauth-authorize.tsx`:
- Around line 289-292: Update the email-verification handling in the
authorization flow so the refreshed result from fetchSessionInfo() is
authoritative after an email_verification_required response. In the logic around
the approval and verification prompt checks, including the paths corresponding
to info.emailVerified and related session-state handling, derive verification
status from the refreshed session rather than stale authorization metadata,
ensuring the prompt appears and approval is blocked when required.

In `@packages/worker/client/routes/pending-verification.tsx`:
- Around line 138-150: Update handleContinue to wrap fetchSessionInfo and the
subsequent verification flow in the same error-handling pattern used by
loadPending and handleResend. On failure, set the appropriate error tone and
user-facing message, then call handle.update() so rejected requests do not leave
the button without feedback.

In `@packages/worker/src/app/handlers/verify-email.ts`:
- Around line 73-76: Update the email verification flow around the verification
transaction/link and its success response to preserve the normalized OAuth
resume target from the original request. Carry that target through verification,
then derive the success CTA’s href from it instead of always using the literal
/onboarding path, while retaining the existing onboarding fallback and covering
the verify-to-CTA OAuth resume path.

---

Nitpick comments:
In `@packages/worker/client/routes/login.tsx`:
- Around line 354-369: Simplify the passkey redirect flow by removing the
requiresTwoFactor conditional and early return, then invoke
resolvePasswordAuthRedirect once with mode, requiresTwoFactor, and redirectTo.
Preserve the existing redirect target and let resolvePasswordAuthRedirect handle
the two-factor value internally, matching the email/password flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 343c99e0-3bf6-4f88-b732-6ebfbc470540

📥 Commits

Reviewing files that changed from the base of the PR and between 025ea00 and 07a2622.

📒 Files selected for processing (51)
  • docs/contributing/architecture/authentication.md
  • docs/contributing/architecture/mcp-client-servers.md
  • docs/contributing/architecture/primitives.yaml
  • docs/contributing/architecture/remote-connectors.md
  • docs/use/connect-your-agent.md
  • docs/use/execute.md
  • docs/use/raw-content-blocks.md
  • docs/use/troubleshooting.md
  • e2e/invite-signup-verification.spec.ts
  • packages/worker/client/routes/account.tsx
  • packages/worker/client/routes/email-verification-prompt.tsx
  • packages/worker/client/routes/home.tsx
  • packages/worker/client/routes/index.tsx
  • packages/worker/client/routes/login.tsx
  • packages/worker/client/routes/oauth-authorize.tsx
  • packages/worker/client/routes/onboarding-banner.tsx
  • packages/worker/client/routes/onboarding.tsx
  • packages/worker/client/routes/pending-verification-path.node.test.ts
  • packages/worker/client/routes/pending-verification-path.ts
  • packages/worker/client/routes/pending-verification.tsx
  • packages/worker/client/routes/resolve-password-auth-redirect.node.test.ts
  • packages/worker/client/routes/resolve-password-auth-redirect.ts
  • packages/worker/client/routes/verify-email.tsx
  • packages/worker/src/app/auth-redirect.ts
  • packages/worker/src/app/email-verification.ts
  • packages/worker/src/app/handlers/account.ts
  • packages/worker/src/app/handlers/home.ts
  • packages/worker/src/app/handlers/onboarding.node.test.ts
  • packages/worker/src/app/handlers/onboarding.ts
  • packages/worker/src/app/handlers/pending-verification.node.test.ts
  • packages/worker/src/app/handlers/pending-verification.ts
  • packages/worker/src/app/handlers/verify-email.ts
  • packages/worker/src/app/loader-data.ts
  • packages/worker/src/app/onboarding-data.node.test.ts
  • packages/worker/src/app/onboarding-data.ts
  • packages/worker/src/app/router.ts
  • packages/worker/src/app/routes.ts
  • packages/worker/src/app/safe-redirect.ts
  • packages/worker/src/app/ssr-render.node.test.ts
  • packages/worker/src/mcp/capabilities/mcp-server/index.ts
  • packages/worker/src/mcp/capabilities/remote-connector/index.ts
  • packages/worker/src/mcp/downstream-mcp-result.node.test.ts
  • packages/worker/src/mcp/downstream-mcp-result.ts
  • packages/worker/src/mcp/executor.node.test.ts
  • packages/worker/src/mcp/executor.ts
  • packages/worker/src/mcp/tools/execute.node.test.ts
  • packages/worker/src/mcp/tools/execute.ts
  • packages/worker/src/oauth-handlers.ts
  • packages/worker/src/oauth-handlers.workers.test.ts
  • packages/worker/src/package-invocations/service.ts
  • packages/worker/tsconfig-client.json

Comment thread packages/worker/client/routes/oauth-authorize.tsx
Comment thread packages/worker/client/routes/pending-verification.tsx
Comment thread packages/worker/src/app/handlers/verify-email.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d85b503. Configure here.

Comment thread packages/worker/client/routes/onboarding.tsx
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/worker/client/routes/onboarding-redirect.node.test.ts (1)

13-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test expectations use encodeURIComponent but implementation uses URLSearchParams.

The implementations of buildAuthLink and buildPendingVerificationPath (from safe-redirect.ts / pending-verification-path.ts) serialize query parameters with new URLSearchParams(...).toString(), which uses application/x-www-form-urlencoded encoding. The test computes expected values with encodeURIComponent. These two encoders differ for space (%20 vs +), !, ', (, ), and ~. The current test data (/oauth/authorize?client_id=demo&state=abc) contains none of these characters, so the tests pass — but if a future test case adds any of them, the expectations will silently break.

Consider building expected values with URLSearchParams to match the implementation exactly:

♻️ Optional refactor for encoding consistency
 test('onboarding redirect helpers preserve safe redirectTo and reject open redirects', () => {
 	const oauthResume = '/oauth/authorize?client_id=demo&state=abc'
+	const encode = (value: string) => new URLSearchParams({ redirectTo: value }).toString().slice('redirectTo='.length)
 
-	expect(buildOnboardingPath(null)).toBe(onboardingPath)
-	expect(buildOnboardingPath(oauthResume)).toBe(
-		`/onboarding?redirectTo=${encodeURIComponent(oauthResume)}`,
-	)
+	expect(buildOnboardingPath(null)).toBe(onboardingPath)
+	expect(buildOnboardingPath(oauthResume)).toBe(
+		`/onboarding?redirectTo=${encode(oauthResume)}`,
+	)
 	expect(buildOnboardingPath('https://evil.example')).toBe(onboardingPath)
 	expect(buildOnboardingPath('/\\evil.example')).toBe(onboardingPath)
 
-	expect(resolveOnboardingPendingVerificationPath(null)).toBe(
-		'/pending-verification',
-	)
-	expect(resolveOnboardingPendingVerificationPath(oauthResume)).toBe(
-		`/pending-verification?redirectTo=${encodeURIComponent(oauthResume)}`,
-	)
+	expect(resolveOnboardingPendingVerificationPath(null)).toBe(
+		'/pending-verification',
+	)
+	expect(resolveOnboardingPendingVerificationPath(oauthResume)).toBe(
+		`/pending-verification?redirectTo=${encode(oauthResume)}`,
+	)
 	expect(resolveOnboardingPendingVerificationPath('https://evil.example')).toBe(
 		'/pending-verification',
 	)
 	expect(resolveOnboardingPendingVerificationPath('/\\evil.example')).toBe(
 		'/pending-verification',
 	)
 
-	expect(resolveOnboardingLoginPath(null)).toBe(
-		'/login?redirectTo=%2Fonboarding',
-	)
-	expect(resolveOnboardingLoginPath(oauthResume)).toBe(
-		`/login?redirectTo=${encodeURIComponent(buildOnboardingPath(oauthResume))}`,
-	)
+	expect(resolveOnboardingLoginPath(null)).toBe(
+		'/login?redirectTo=%2Fonboarding',
+	)
+	expect(resolveOnboardingLoginPath(oauthResume)).toBe(
+		`/login?redirectTo=${encode(buildOnboardingPath(oauthResume))}`,
+	)
 	expect(resolveOnboardingLoginPath('https://evil.example')).toBe(
 		'/login?redirectTo=%2Fonboarding',
 	)
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/client/routes/onboarding-redirect.node.test.ts` around lines
13 - 37, Update the expectations in the onboarding redirect tests to serialize
redirectTo values with URLSearchParams, matching buildOnboardingPath,
resolveOnboardingPendingVerificationPath, and resolveOnboardingLoginPath.
Replace encodeURIComponent-based expected query strings while preserving the
existing redirect values and unsafe-redirect assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/worker/client/routes/onboarding-redirect.node.test.ts`:
- Around line 13-37: Update the expectations in the onboarding redirect tests to
serialize redirectTo values with URLSearchParams, matching buildOnboardingPath,
resolveOnboardingPendingVerificationPath, and resolveOnboardingLoginPath.
Replace encodeURIComponent-based expected query strings while preserving the
existing redirect values and unsafe-redirect assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: afb6d0ad-f84c-4be9-8d1d-b4a2c6788825

📥 Commits

Reviewing files that changed from the base of the PR and between d85b503 and 602eb2a.

📒 Files selected for processing (4)
  • e2e/invite-signup-verification.spec.ts
  • packages/worker/client/routes/onboarding-redirect.node.test.ts
  • packages/worker/client/routes/onboarding-redirect.ts
  • packages/worker/client/routes/onboarding.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • e2e/invite-signup-verification.spec.ts
  • packages/worker/client/routes/onboarding.tsx

@kody-bot
kody-bot merged commit e34c159 into main Jul 12, 2026
5 checks passed
@kody-bot
kody-bot deleted the cursor/smooth-onboarding-images-f304 branch July 12, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants