π Before claiming this issue
Two quick steps before you open a PR:
- β Star the repository β low-friction signal that you'll follow through
- π¬ Comment
I'll take this (or similar) below β prevents two contributors racing on the same issue
Full policy: CONTRIBUTING.md β How to claim. If a claim is older than 7 days with no PR, you can reclaim it politely.
Tracks follow-up #3 from PR #17 security review. Blocked on mcp-server-v1 M3 (~Jul 28, 2026).
What
The MCPPairingPanel handshake currently flows in a loose imperative sequence (enter token β submit β spinner β success-or-error). Replace this with an explicit state machine so invalid transitions are unreachable.
Why
The current code has implicit states (idle, validating, paired, error) tracked across multiple useState hooks. This creates classes of bugs that are hard to catch:
- User clicks Submit during the spinner β request fires twice
- Race between "paired" success and a subsequent re-validate
- Error state can be reached without going through validating
- Reset button works in some states but not others
An explicit state machine with 'idle' | 'validating' | 'paired' | 'error' makes invalid transitions a TypeScript error, not a runtime bug.
Acceptance criteria
Implementation hint
type PairingState =
| { kind: 'idle' }
| { kind: 'validating'; token: string }
| { kind: 'paired'; token: string; pairedAt: number }
| { kind: 'error'; reason: 'invalid_format' | 'network' | 'rejected'; token: string };
type PairingEvent =
| { type: 'submit'; token: string }
| { type: 'validation_succeeded'; token: string }
| { type: 'validation_failed'; reason: PairingState extends { kind: 'error' } ? ... : never }
| { type: 'reset' };
function pairingReducer(state: PairingState, event: PairingEvent): PairingState {
// Idle β validating on submit; everything else from idle is rejected
if (state.kind === 'idle' && event.type === 'submit') {
return { kind: 'validating', token: event.token };
}
// Validating β paired on success
if (state.kind === 'validating' && event.type === 'validation_succeeded') {
return { kind: 'paired', token: event.token, pairedAt: Date.now() };
}
// etc...
return state; // invalid transition: no-op
}
Context
Tracks follow-up #3 from PR #17 security review. Blocked on
mcp-server-v1M3 (~Jul 28, 2026).What
The MCPPairingPanel handshake currently flows in a loose imperative sequence (enter token β submit β spinner β success-or-error). Replace this with an explicit state machine so invalid transitions are unreachable.
Why
The current code has implicit states (idle, validating, paired, error) tracked across multiple
useStatehooks. This creates classes of bugs that are hard to catch:An explicit state machine with
'idle' | 'validating' | 'paired' | 'error'makes invalid transitions a TypeScript error, not a runtime bug.Acceptance criteria
MCPPairingPanel.tsxhas a single state variablepairingState: PairingState(the union above) instead of multiple booleansuseReducerso each event has a documented effect per statevalidatingstate is a no-op (no duplicate requests)pairedfromerrorrequires going throughvalidatingfirstImplementation hint
Context