Right—the clone won’t have production request logs. I was considering a live Cloudflare log stream, but I don’t have that tooling set up here.
Instead, I can use a temporary localhost OAuth diagnostic proxy with Devin’s CLI. It will forward the exchange to kody.codes and report only the HTTP status and OAuth error fields—not codes, headers, or tokens. That should reveal the error Devin is currently hiding, without changing Kody or your existing MCP configuration.
The diagnostic proxy is running on localhost. I’ll start one more sign-in; please authorize it in the browser. This uses a separate temporary kody-diagnostic entry and leaves your existing kody entry unchanged.
The diagnostic attempt hit Cloudflare 403 / error 1010 (browser_signature_banned) before reaching Kody’s OAuth handler. That happened on the proxy’s registration request, so it does not yet prove the cause of Devin’s original token-exchange failure.
I’ll compare requests with and without a User-Agent using deliberately invalid, non-sensitive OAuth inputs. This needs no browser sign-in and may tell us whether Cloudflare’s browser checks are interfering.
The requests without a User-Agent reached Kody normally. The Cloudflare block was caused by Python’s default User-Agent in my proxy, which I’ve removed so it preserves Devin’s behavior. That was a diagnostic artifact, not an explanation of the original failure.
The proxy check now passes. I’ll retry the diagnostic sign-in.
The instrumented flow reproduced Devin’s error, but no request reached the token endpoint.
I found a plausible explanation: Kody advertises RFC 9207 issuer validation, and Rust MCP clients must preserve the callback’s iss parameter when handing it to the SDK. I’ll test that behavior with a fully local mock OAuth server. This requires no real credentials or further Kody sign-ins.
I have a deterministic client-side reproduction:
| Local mock server setting | Callback includes the correct iss |
Token requests sent |
|---|---|---|
Issuer validation advertised: true |
Yes | 0 |
Issuer validation advertised: false |
Yes | 1 |
Only that metadata flag changed. Kody advertises true.
This strongly points to Devin’s callback handling—likely dropping iss before passing the callback to the Rust MCP SDK. I can’t confirm that internal detail without Devin’s source, but the failing behavior is isolated. I won’t disable issuer validation on Kody as a workaround.
The connection is still blocked, but I isolated a reproducible Devin-side OAuth failure. Here’s a report you can forward.
- Devin bundled CLI:
3000.5.20 (2d902011) - macOS, ARM64, Darwin 25.6
- MCP endpoint:
https://kody.codes/mcp - Tested September 7, 2026
- Browser authorization completes.
- The localhost callback page displays “Authentication complete.”
- Devin remains in “Needs auth”, and MCP tools are unavailable.
- Running
devin mcp login kodydirectly reports:Error: Failed to exchange authorization code for tokens
Logout/login did not repair it. Logout initially reported no OAuth credentials stored for kody.
A standalone localhost OAuth mock implements discovery, public-client registration, and an authorization redirect containing:
- An authorization code
- The unchanged request
state - An
issvalue exactly matching the discovery document’sissuer
Two runs changed only authorization_response_iss_parameter_supported:
| Metadata flag | Registrations | Callbacks | Token endpoint requests |
|---|---|---|---|
true |
1 | 1 | 0 |
false |
1 | 1 | 1 |
With the flag enabled, Devin fails before sending the token request, despite receiving a correct issuer.
The mock deliberately rejects any token request with HTTP 400, so both runs end with the generic exchange error. The decisive difference is whether Devin reaches the token endpoint.
Kody’s live authorization-server metadata includes:
{
"issuer": "https://kody.codes",
"authorization_response_iss_parameter_supported": true
}Discovery endpoints return HTTP 200. The observed registration flow uses public-client authentication (none) and PKCE S256.
Please inspect whether Devin’s loopback callback handler preserves iss and passes it into the MCP SDK.
The Rust MCP SDK exposes issuer-aware methods:
handle_callback_with_issuer(code, state, issuer)handle_callback_url(callback_url)
Calling the issuer-less callback method, or dropping iss while parsing the redirect, would explain this result. The behavioral failure is reproduced; the exact internal cause remains unconfirmed.
SDK callback API documentation
- Preserve and validate the authorization-response issuer end to end.
- Test advertised issuer support with correct, missing, and mismatched callback issuers.
- Surface the underlying validation/exchange error instead of only the generic wrapper.
- Do not show browser authentication success until token exchange and credential persistence succeed—or label the page as “Authorization received” while those steps remain pending.
Attach these files:
- Mock OAuth server: <ref_file file="/tmp/kody-devin-oauth-diagnostic.Jc5mlR/mock_oauth.py" />
- Isolated MCP configuration: <ref_file file="/tmp/kody-devin-oauth-diagnostic.Jc5mlR/.devin/mcp_config.local.json" />
Start the mock with ISSUER_SUPPORTED=true, run devin mcp login mock-issuer-test from the configuration’s project directory, then inspect http://127.0.0.1:18466/stats. Restart with ISSUER_SUPPORTED=false and repeat. No real credentials are needed.
All diagnostic servers are stopped. Your repository is clean, existing MCP configuration is unchanged, and I did not weaken Kody’s issuer validation.