When connecting to an MCP server that requires OAuth (tested with Miro's MCP server, https://mcp.miro.com/), the authorization flow started by the Copilot language server's LoopbackAuthServer always fails with:
CopilotAuthError: Missing required parameters, at least one from code, state, nonce is needed.
Root cause (from logs)
The state parameter sent in the /authorize request is the entire local callback URL including its own query string (containing the nonce), instead of an opaque URL-safe token:
state=http://127.0.0.1:33428/callback?nonce=<redacted>
Miro's authorization server validates state strictly against A-Za-z0-9_-+=/.: % (no ? allowed) and rejects the request as invalid_request before it can redirect back with a code. The loopback server then reports the callback as missing required parameters, because the browser redirect it receives is Miro's error redirect, not a successful auth code.
Environment
- Plugin: GitHub Copilot for JetBrains (
github-copilot-intellij) version 1.18.0-261
- IDE: PhpStorm 2026.1 (macOS)
- MCP server: Miro (
https://mcp.miro.com/)
- Underlying language server:
copilot-language-server-internal (see stack trace below)
Log excerpt (idea.log)
INFO - [LoopbackAuthServer] OAuth server started and state configured { port: 33428, redirectUri: 'http://127.0.0.1:33428/callback' }
INFO - [default] Auth URL created and opening browser for sign-in
INFO - [LoopbackAuthServer] Redirecting to auth server
WARN - [LoopbackAuthServer] OAuth callback missing required parameters {
originalUrl: 'https://mcp.miro.com/authorize?client_id=<redacted>&response_type=code&scope=boards%3Aread+boards%3Awrite+email+openid&code_challenge=<redacted>&code_challenge_method=S256&resource=https%3A%2F%2Fmcp.miro.com%2F&state=http%3A%2F%2F127.0.0.1%3A33428%2Fcallback%3Fnonce%3Dbd5505e8c9683bed174cce861744ce5b&redirect_uri=http%3A%2F%2F127.0.0.1%3A33428%2Fcallback',
callbackUrl: '/callback?error=invalid_request&error_description=Invalid+authorization+parameters%3A+state%3A+Value+error%2C+Invalid+state+format.+Must+be+1-2048+URL-safe+characters+%28A-Za-z0-9_-%2B%3D%2F.%3A%25%29&state=http%3A%2F%2F127.0.0.1%3A33428%2Fcallback%3Fnonce%3Dbd5505e8c9683bed174cce861744ce5b'
}
WARN - [default] OAuth flow error CopilotAuthError: Missing required parameters, at least one from code, state, nonce is needed.
at Server.<anonymous> (/snapshot/copilot-language-server-internal/dist/main.js:4281:1582)
at Server.emit (node:events:519:28)
at parserOnIncoming (node:_http_server:1186:12)
at HTTPParser.parserOnHeadersComplete (node:_http_common:125:17)
WARN - [mcpGateway] Interactive OAuth failed for https://mcp.miro.com/ CopilotAuthError: Missing required parameters, at least one from code, state, nonce is needed.
WARN - [mcpGateway.manager] server 'miro' requires authorization
Steps to reproduce
- In PhpStorm, add an MCP server config for Miro:
{ "url": "https://mcp.miro.com/" }.
- Trigger the "Connect"/authorization flow for that server.
- Browser opens Miro's authorize page; it immediately redirects back with
error=invalid_request instead of a code.
- Copilot reports "server 'miro' requires authorization" indefinitely — the flow can never succeed.
Expected behavior
The state parameter should be an opaque, URL-safe random token (e.g. just the nonce, or a base64url-encoded value), with the nonce/return-path tracked server-side in the loopback server's own session state — not encoded as a full URL with its own query string. This would satisfy strict OAuth servers like Miro's that validate state format per RFC recommendations.
Impact
Any MCP server that validates the state parameter format strictly (rejecting non-URL-safe characters like ?) cannot be authorized via the JetBrains Copilot plugin's built-in OAuth loopback flow. Confirmed with Miro's official MCP server.
When connecting to an MCP server that requires OAuth (tested with Miro's MCP server,
https://mcp.miro.com/), the authorization flow started by the Copilot language server'sLoopbackAuthServeralways fails with:Root cause (from logs)
The
stateparameter sent in the/authorizerequest is the entire local callback URL including its own query string (containing the nonce), instead of an opaque URL-safe token:Miro's authorization server validates
statestrictly againstA-Za-z0-9_-+=/.: %(no?allowed) and rejects the request asinvalid_requestbefore it can redirect back with acode. The loopback server then reports the callback as missing required parameters, because the browser redirect it receives is Miro's error redirect, not a successful auth code.Environment
github-copilot-intellij) version1.18.0-261https://mcp.miro.com/)copilot-language-server-internal(see stack trace below)Log excerpt (
idea.log)Steps to reproduce
{ "url": "https://mcp.miro.com/" }.error=invalid_requestinstead of acode.Expected behavior
The
stateparameter should be an opaque, URL-safe random token (e.g. just the nonce, or a base64url-encoded value), with the nonce/return-path tracked server-side in the loopback server's own session state — not encoded as a full URL with its own query string. This would satisfy strict OAuth servers like Miro's that validatestateformat per RFC recommendations.Impact
Any MCP server that validates the
stateparameter format strictly (rejecting non-URL-safe characters like?) cannot be authorized via the JetBrains Copilot plugin's built-in OAuth loopback flow. Confirmed with Miro's official MCP server.