Resources
1Install
npx skillscat add mctlhq/mctl-gitops/platform-gitops-platform-skills-catalog-mcp-troubleshooting Install via the SkillsCat registry.
MCP Connection & OAuth PKCE Troubleshooting
This skill documents the diagnosis and recovery procedures for Model Context Protocol (MCP) servers, with specific focus on remote OAuth 2.0 PKCE authentication flows, transport selection, and token lifecycle management.
Architecture & Configuration Files
MCP servers can be configured in two main formats depending on the client runtime:
Claude Code / Standalone Schema (
~/.claude.jsonormcp.json):{ "mcpServers": { "mctl": { "type": "http", "url": "https://api.mctl.ai/mcp" }, "upwork": { "type": "stdio", "command": "uv", "args": ["run", "upwork-mcp"] } } }Antigravity / Gemini CLI Schema (
~/.gemini/config/mcp_config.json):- Remote SSE / HTTP:
{ "mcpServers": { "mctl": { "serverUrl": "https://api.mctl.ai/mcp" } } } - Local Stdio Wrapper:
{ "mcpServers": { "my-tool": { "command": "/path/to/wrapper-script" } } }
- Remote SSE / HTTP:
Step-by-Step Diagnostic & Recovery Flow
1. Handling Unauthorized [Auth Needed] & OAuth 2.0 PKCE Flow
When a remote MCP endpoint requires OAuth 2.0 with strict PKCE (code_challenge + code_challenge_method=S256), manual URL links missing PKCE params will fail with:{"error":"invalid_request","error_description":"PKCE S256 is required"}.
Procedure for Automated PKCE Authentication:
Register OAuth Client Dynamically:
import requests reg = requests.post("https://<server-domain>/oauth/register", json={ "client_name": "mcp-client", "redirect_uris": ["http://localhost:12105/callback"] }).json() client_id = reg["client_id"]Generate PKCE S256 Pair:
import os, base64, hashlib verifier = base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8').replace('=', '') challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode('utf-8')).digest()).decode('utf-8').replace('=', '')Construct Clean Authorization URL:
https://<server-domain>/oauth/authorize?response_type=code&client_id=<client_id>&redirect_uri=http%3A%2F%2Flocalhost%3A12105%2Fcallback&scope=<scopes>&code_challenge=<challenge>&code_challenge_method=S256Launch One-Shot Callback Listener & Exchange Code:
Start a temporary local HTTP server onhttp://localhost:12105/callback. When the browser redirects back with?code=..., perform thePOST /oauth/tokenexchange withcode_verifier=verifier.Store Tokens in Client Token Repositories:
Save the resulting tokens into BOTH client token locations to support all runtimes and surface✓ [Authed]and[Sign Out]in the CLI:Antigravity CLI Token Repository (
~/.gemini/antigravity-cli/mcp_oauth_tokens.json) - Map keyed by serverUrl:{ "https://<server-domain>/mcp": { "client_id": "<client_id>", "token": { "access_token": "<access_token>", "token_type": "Bearer", "refresh_token": "<refresh_token>", "expiry": "2030-01-01T00:00:00Z" }, "token_url": "https://<server-domain>/oauth/token" } }Legacy / Standalone Token Repository (
~/.gemini/mcp-oauth-tokens.json) - Array format:[ { "serverName": "<server_name>", "token": { "accessToken": "<access_token>", "tokenType": "Bearer", "scope": "<scopes>", "expiresAt": 1790000000000 }, "clientId": "<client_id>", "tokenUrl": "https://<server-domain>/oauth/token", "mcpServerUrl": "https://<server-domain>/mcp", "updatedAt": 1786051830000 } ]
[!IMPORTANT]
Native HTTP vs Stdio Command: Configure remote HTTP servers inmcp_config.jsonwith"serverUrl": "https://<server-domain>/mcp"(NOT"command"wrapper). When paired with an entry in~/.gemini/antigravity-cli/mcp_oauth_tokens.json, Antigravity CLI sends the Bearer token natively, recognizes the OAuth session, and displays✓ [Authed]and[Sign Out].
2. Resolving Lingering Process Locks (context deadline exceeded)
If a remote proxy process (e.g. mcp-remote) crashes or hangs, it holds open local sockets and ports, causing new CLI connection attempts to fail with context deadline exceeded.
Recovery:
- Check for stale processes:
ps aux | grep mcp-remote - Kill stale instances:
kill -9 <PID> - Check and free occupied callback ports:
lsof -i :<PORT> | awk 'NR>1 {print $2}' | xargs kill -9
3. Transport Selection & Diagnostic Log Redirection
Avoid piping raw unbuffered logs to stderr in stdio wrappers, as unhandled log lines interfere with the initial JSON-RPC handshake causing EOF or client is closing.
Best Practice Wrapper Pattern:
#!/bin/bash
exec /opt/homebrew/bin/mcp-remote "https://<server-domain>/mcp" 2>/tmp/mcp-<server>.log4. Telegram Preview & WWW-Authenticate Header Requirements
When configuring remote MCP endpoints (such as https://tg-preview.mctl.ai/mcp), clients rely on standard HTTP 401 response headers to trigger interactive login flows (surfacing [Sign In] buttons instead of connection failures):
- 401 Unauthorized Response: Unauthenticated requests to
/mcpmust return HTTP401 Unauthorizedwith the header:WWW-Authenticate: Bearer realm="mctl-telegram", error="invalid_token" - Dynamic Preview Auth Routing: In preview environments (
tg-preview.mctl.ai), ensure JWT issuer validation dynamically respects the preview hostname and scope list.
5. Context7 MCP & CLI Setup
Context7 provides documentation context via CLI and MCP stdio integration:
CLI Authentication:
ctx7 login # Stores API key (ctx7sk-*) in macOS Keychain under service 'context7'MCP Configuration in
~/.gemini/settings.json:{ "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } }