runEmulatedOAuthPreflight to execute an emulated client’s authentication ladder against a real MCP server, headlessly. The runner compiles an evidence-backed OAuth profile into ordered attempts, executes each one over the hardened OAuth networking path, and returns a structured result with three independent dimensions: outcome, coverage, and comparison.
runEmulatedOAuthPreflight is Node-only. The redirect-planning utilities
(planCompletionSafeRedirects, isInvalidRedirectUriRejection) and their
types are also exported from the browser-safe entry point.Import
Quick start
runEmulatedOAuthPreflight
EmulatedOAuthPreflightConfig
EmulatedOAuthPreflightOutcome
EmulatedOAuthPreflightResult
EmulatedAuthAttemptResult
Attempt ladder
TheauthModel field of an OAuth profile compiles into an ordered attempt ladder. The runner executes each rung in sequence, with these fallback rules:
- Consecutive
oauth2-*entries collapse into one OAuth attempt carrying their relative order as a registration preference. A client that lists["oauth2-cimd", "oauth2-dcr"]performs a single OAuth dance preferring CIMD, not two separate dances. api-keyandnonestay in position as direct MCP probes, so a “static bearer first, OAuth on 401” client reproduces that sequence exactly.- A static credential falls through to OAuth only on a 401. Any other status stops the ladder.
- An OAuth attempt falls through to the next strategy only while nothing has been committed on the wire — once a registration request has been sent or authorization has begun, failure stops the ladder.
authModel has no evidence, the runner uses MCPJam’s own AUTO precedence: preregistered → cimd → dcr, filtered to what the negotiated protocol version supports.
Completion-safe redirects
The runner must replay the real client’s registration body while still finishing the dance with a real token. It resolves this withplanCompletionSafeRedirects:
- The registration body carries the captured
redirect_urisin captured order, with MCPJam’s callback appended. The appended entry is a declared divergence. - The authorization and token legs always use MCPJam’s callback, so the code comes back and can be exchanged.
- If the server rejects the registration with a structured RFC 7591
invalid_redirect_uri(exactly HTTP 400 with{ "error": "invalid_redirect_uri" }), the runner re-registers with MCPJam’s callback alone. That is the second and final registration a run will ever perform.
planCompletionSafeRedirects
Output (
CompletionSafeRedirectPlan):
isInvalidRedirectUriRejection
Returns true only when a registration response is exactly HTTP 400 with { "error": "invalid_redirect_uri" } in the body — the structured RFC 7591 error that authorizes a retry. Any other status or body shape returns false.
Diagnostics hygiene
Credentials are returned once, inresult.credentials, and are kept out of every other surface:
result.attempts[*].httpHistoryis redacted — no tokens, codes, secrets, or PKCE values.- A caller-supplied
staticCredentialis scrubbed by header name and value before the generic redactor runs, because no generic redactor can know an unconventional header name likeX-Acme-Tenant-Token. result.comparisonis always"not_compared"— a run never claims parity with a golden trace on its own.
Related
- OAuth Conformance SDK — conformance testing with
OAuthConformanceTest - CLI: OAuth Conformance — CLI recipes and CI integration

