> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended**:
> ```bash
> npx @scalekit-inc/cli setup
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for 40+ agents.
> Features: full-stack-auth, agent-auth, mcp-auth, modular-sso, modular-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Troubleshooting MCP auth

Troubleshooting guide for common errors while adding auth for MCP Servers
Use this guide when you use Scalekit as the authentication server for your MCP servers and a client fails to connect, a request stalls, or the authentication handshake breaks. Start with the category that matches your symptom, then open the matching scenario.

Most issues fall into configuration and setup, CORS and network access, or client-specific behavior. Each scenario explains the cause and the fix.

## Configuration and setup

## `/auth-requests/` returns a 404 or "invalid ID" error

You may be passing the MCP server's resource ID instead of the connection ID in the URL path.

These are two different identifiers with different purposes:

| Identifier | Format | Purpose |
|---|---|---|
| `resource_id` | `res_xxx` | Identifies the MCP server; used in token audiences and client registration |
| `connection_id` | `conn_xxx` | Identifies your BYOA auth connection; required in `/auth-requests/` endpoints |

The correct endpoint uses `connection_id`:

```txt frame="none"
/api/v1/connections/<connection_id>/auth-requests/<login_request_id>/user
```

To find your `connection_id`, open **Dashboard > MCP Servers > [your server] > Advanced Configurations > Connection ID**.

## Access token issued but no refresh token

Add the `offline_access` scope to your authorization request. Without it, Scalekit does not issue a refresh token alongside the access token.

Include it with your other scopes:

```
openid profile email offline_access
```

After you add it, subsequent logins return both an access token and a refresh token.

## Client fails with an `invalid_client_metadata_url` error

This error means Scalekit rejected the OAuth client that tried to connect, because the client sent a `client_id` that Scalekit never issued or a client metadata URL it can't resolve. The connection often succeeds for some users and fails for others on the same MCP server, which points to client-side configuration rather than a problem with your Scalekit setup.

Two causes account for most occurrences.

**Cause 1: The connecting application has stale client credentials configured**

Some applications (for example, Glean) let you paste a `client_id` and `client_secret` for the MCP server. When those fields hold credentials that Scalekit didn't issue, the auth server rejects the connection.

To resolve it:

1. Open the connecting application's integration settings for your MCP server.
2. Clear the `client_id` and `client_secret` fields.
3. Reconnect. Applications that support Dynamic Client Registration (DCR) obtain valid credentials automatically once the fields are empty.
4. If the application cannot use DCR, generate a static pre-registered client in **Dashboard > MCP Servers > [your server]** and share those credentials instead.

**Cause 2: `mcp-remote` is sending a `client_id` it did not register**

`mcp-remote` can reuse a `client_id` from a different MCP server session or send a non-Scalekit value. Scalekit issues DCR clients only in the `m2m_xxx` format, so it rejects any other value. This affects only the users who route the connection through `mcp-remote`.

`mcp-remote` was a temporary bridge for clients that couldn't reach remote MCP servers directly. Claude Desktop, Claude Code, and Cursor now support remote HTTP MCP servers natively, so you no longer need it. Connect with the native HTTP transport instead:

- **Claude Desktop**: open **Settings > Connectors > Add custom connector** and enter your MCP server URL.
- **Claude Code**: run the following in your terminal.

  ```sh frame="terminal"
  claude mcp add --transport http <server-name> https://your-mcp-server.example.com/mcp
  ```

> note: The error points to the client, not Scalekit
>
> Because this error originates from the client's registration data, it reproduces per user. When one user fails while others succeed against the same MCP server, inspect that user's client configuration first.

## What is the difference between DCR and CIMD?

MCP clients identify themselves to Scalekit's authorization server using one of two mechanisms:

- **Dynamic Client Registration (DCR)**: The client registers itself with Scalekit and receives a `client_id` in the `m2m_xxx` format. See [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591).
- **Client ID Metadata Document (CIMD)**: The client presents a URL that hosts its client metadata document, and Scalekit fetches that URL to identify the client.

The `invalid_client_metadata_url` error belongs to the CIMD path: Scalekit couldn't resolve or validate the metadata URL the client sent. A client that completes DCR correctly doesn't trigger this error. Follow the resolution steps in the previous scenario.

## View authentication logs to debug a failed connection

Open **Dashboard > Authentication > Logs** to inspect the authorization and token requests for your environment. Each entry shows the OAuth client, the requested scopes, and the outcome of each step in the flow.

When you debug a failed connection, change the log filter to show all events rather than successful events only. Failed authorization and token requests appear only when you include error events, so a success-only view can hide the request you need to diagnose.

> note: Per-client MCP connection logs
>
> Connection logs for individual MCP clients aren't yet available for self-serve viewing. When the authentication logs don't explain a failure, contact Scalekit support with the failing `client_id` and the approximate time of the attempt.

## MCP server does not connect to the MCP Inspector

A failed connection to the MCP Inspector usually points to a problem with the authentication handshake or metadata configuration. Work through these checks.

**Verify the MCP server is responding correctly:**

1. Open your browser's developer tools (Network tab)
2. Navigate to your MCP server URL (for example, `http://localhost:3002/`)
3. Confirm the response returns a `401` status code
4. Check the response headers for `www-authenticate` containing `resource_metadata="<metadata-url>"`

**Validate the metadata:**

1. Copy the metadata URL from the `www-authenticate` header
2. Open it in your browser
3. Confirm the JSON structure matches what you see in your Scalekit dashboard

> note
>
> If all checks pass but the connection still fails, see the CORS and network access scenarios below.

## `redirect_uri` mismatch during authorization

This error typically occurs when your MCP client has cached an old MCP server domain after you changed it. The client keeps sending requests to the old URL, which no longer matches your Scalekit configuration.

Clear cached authentication by client type.

**mcp-remote:**

1. Delete the cached configuration folder: `~/.mcp-auth/mcp-remote-<version>`
2. Reconnect to your MCP server

**VS Code:**

1. Open the Command Palette (Cmd/Ctrl + Shift + P)
2. Search for **Authentication: Remove Dynamic Authentication Provider**
3. Select and remove the cached entry
4. Reconnect to your MCP server

**Claude Desktop:**

> caution
>
> Claude Desktop does not currently support clearing cached authentication data. As a workaround, use a different domain or subdomain for your MCP server, or contact Claude support for assistance.

## GitHub Copilot CLI: stale cached credentials after an environment switch

GitHub Copilot CLI caches OAuth client credentials locally. If you switch your Scalekit environment (for example, from US to EU), the cached `client_id` no longer matches the new environment and login fails with `unable to retrieve client by id`.

1. Locate and delete the cached OAuth config files:
   ```sh
   rm -rf ~/.copilot/mcp-oauth-config
   ```
2. Reconnect your MCP server in GitHub Copilot CLI. It registers a fresh client against the correct environment.

> note
>
> If you cannot find the files using the path above, also check `~/.config/github-copilot/` for cached MCP auth files.

## CORS and network access

## CORS errors in the network logs when using MCP Inspector

CORS errors occur when your MCP client cannot make cross-origin requests to your Scalekit environment during the authentication handshake, which prevents the flow from completing.

1. Navigate to **Dashboard > Authentication > Redirect URLs > Allowed Callback URLs**
2. Add your MCP Inspector URL to the allowed list: `http://localhost:6274/`
3. Retry the connection

> tip: Development vs production URLs
>
> Add callback URLs for both your development (`http://localhost:6274/`) and production environments to avoid CORS errors in either environment.

## MCP client requests never reach the server

If requests from your MCP client silently fail to reach your server, a proxy or firewall may be blocking them. This often happens in corporate environments or when you use CDN services.

1. Check whether you're using a proxy (for example, Cloudflare, AWS WAF, or a corporate proxy)
2. Configure your proxy to allow or exempt requests from your MCP client to your server domain
3. Review proxy logs to confirm whether requests are being blocked
4. Test direct connectivity from your client machine to your MCP server, without the proxy if possible

> note
>
> Some corporate proxies require explicit allowlisting of authentication endpoints. Contact your network administrator if you suspect this is the case.

## Cloudflare bot protection is blocking MCP client requests

If your MCP server is behind Cloudflare and AI agents (such as Claude Desktop, Cursor, or other MCP clients) cannot reach it, Cloudflare's **Bot Fight Mode**, **Super Bot Fight Mode**, or **AI Crawl Control** settings may classify agent traffic as bot traffic and block it at the edge.

**Symptoms:**

- MCP client connections fail silently or return `403 Forbidden`
- The authentication handshake never completes
- Browser access to the MCP server works, but programmatic access from AI agents does not
- Cloudflare serves a JavaScript challenge or Turnstile page instead of your MCP response

**Diagnose which rule is blocking:**

1. Open the [Cloudflare dashboard](https://dash.cloudflare.com/) for your domain
2. Navigate to **Security > Events**
3. Filter for your MCP server path and look for blocked or challenged requests
4. Note the **rule name**. It tells you which Cloudflare feature caused the block (Bot Fight Mode, Super Bot Fight Mode, AI Crawl Control, or a managed rule)

**Resolution for the Cloudflare Free plan (Bot Fight Mode):**

On the Free plan, Bot Fight Mode runs before the WAF Ruleset Engine, so custom WAF skip rules have no effect on it. Your options are:

1. Open **Security Settings** in the Cloudflare dashboard (direct link: `https://dash.cloudflare.com/?to=/:account/:zone/security/settings`)
2. Under **Bot traffic**, turn **Bot Fight Mode** off
3. If **Block AI Scrapers and Crawlers** is also enabled, disable it
4. Retry the MCP client connection

**Resolution for Pro, Business, or Enterprise plans (Super Bot Fight Mode):**

On paid plans, you can create a WAF custom rule that skips bot protection only for MCP traffic while keeping the rest of your domain protected:

1. Navigate to **Security > WAF > Custom rules > Create rule**
2. Set the expression to match your MCP server path: `starts_with(http.request.uri.path, "/mcp")`. Adjust the path to match your MCP server's base path
3. Set the action to **Skip**, then select **Super Bot Fight Mode**
4. Move this rule to the top of your custom rules list so it evaluates first
5. Save and deploy, then retry the MCP client connection

Alternatively, navigate to **AI Crawl Control** in your bot settings and set Claude-related bots (`ClaudeBot`, `Claude-User`) to **Allow**.

> tip
>
> The WAF skip rule is recommended for paid plans because it disables bot checks only for MCP server traffic while keeping your other endpoints protected.

> note
>
> Some MCP client SDKs send empty or missing `User-Agent` headers, which can trigger separate WAF rules unrelated to bot protection. If the Security Events log shows a user-agent-based block rather than a bot rule, check your MCP client's request headers.

## Client-specific issues

## Claude Desktop ignores custom ports when connecting to MCP servers

Claude Desktop currently supports only standard HTTPS traffic on port `443`. If your MCP server runs on a custom port (for example, `https://mymcp.internal:8443/`), Claude Desktop still attempts to connect to port `443`, so the connection fails.

- Expose your MCP server on port `443` (requires a proxy or load balancer)
- Use a reverse proxy that listens on `443` and forwards requests to your custom port

> note
>
> Future versions of Claude Desktop may add custom port support. Check the Claude Desktop release notes for updates.

## Multiple authentication tabs open when using both mcp-remote and Claude Desktop

Recent versions of Claude Desktop include Connectors, which removes the need to run mcp-remote separately. The **Custom Connector** feature lets you configure MCP servers directly without additional tools.

- Use Claude Desktop's built-in Custom Connector feature for MCP server management
- Disable or stop mcp-remote if you're only using Claude Desktop
- If you have a specific use case that requires both, contact Claude's official support

> tip
>
> To avoid duplicate authentication flows, use only one MCP client at a time.

## OAuth popup closes immediately or shows "window closed too soon"

MCP clients that use a popup-based OAuth flow (such as Amazon Q) may report that the popup closed too soon or that authentication failed, even though the OAuth flow completed successfully in the popup window.

**Root cause:** Your application's login page returns a `Cross-Origin-Opener-Policy: same-origin` HTTP header. When a cross-origin MCP client (for example, `quick.aws.com`) opens a popup that navigates to a page with this header, the browser severs the opener's reference to the popup. The MCP client sees the popup as `closed` immediately, before the OAuth callback can complete.

**Diagnosis:**

Check the response headers on your login or redirect endpoint:

```sh frame="terminal"
curl -sI https://your-app.com/login | grep -i cross-origin-opener-policy
```

If the output includes `cross-origin-opener-policy: same-origin`, this is the cause.

**Resolution:**

Remove the `Cross-Origin-Opener-Policy` header from your login and OAuth callback endpoints, or change its value to `unsafe-none`:

```
Cross-Origin-Opener-Policy: unsafe-none
```

You can scope this change to only the endpoints involved in the OAuth flow rather than your entire application. After the change, retry the MCP client connection.

> note: This is an application-side fix
>
> This header is set by your application or its hosting platform, not by Scalekit. Common sources include framework defaults (for example, Next.js or Rails), CDN or reverse proxy settings, and security middleware.

## The browser does not open during authentication

Some MCP clients require permission to open your default browser during the authentication flow. If your browser doesn't launch, the authentication handshake may time out.

**macOS:**

1. Open **System Preferences > Security & Privacy > App Management**
2. Confirm the MCP client has permission to open applications
3. Restart your MCP client

**Windows:**

1. Navigate to **Settings > Privacy > App permissions**
2. Enable **Allow apps to manage your default app settings**
3. Restart your MCP client

**Linux:**

1. Confirm `xdg-open` or your default browser opener is installed: `which xdg-open`
2. Verify the command is accessible from your terminal
3. Restart your MCP client

> note
>
> After you update permissions, restart your MCP client so the changes take effect.

## Best practices

Follow these best practices to avoid common issues and maintain a robust MCP authentication setup:

1. **Use separate Scalekit environments** for development and production to prevent configuration conflicts
2. **Register MCP servers with environment-specific domains:**
   - Development: `https://mcp-dev.yourdomain.com/`
   - Production: `https://mcp.yourdomain.com/`
3. **Update your MCP client configuration** to point to the correct Scalekit environment for each deployment
4. **Test authentication independently** in each environment before you deploy to production
5. **Monitor authentication logs** in **Dashboard > Authentication > Logs** to identify and resolve issues quickly
6. **Keep callback URLs updated** whenever you change domains or ports

> tip: Environment management
>
> Maintain separate environment variables for your MCP server configuration (for example, `SCALEKIT_ENVIRONMENT_URL` and `MCP_SERVER_URL`) to switch between development and production environments.

## Get help

If none of the scenarios resolve the issue, open **Dashboard > Authentication > Logs** and review the authorization and token requests for the failing attempt.

When you contact [support](mailto:support@scalekit.com), include:

- The failing `client_id` and the MCP server `resource_id`
- The MCP client and transport (for example, a Claude Desktop custom connector or `mcp-remote`)
- The full error text and the approximate timestamp of the attempt
- The steps that reproduce the failure


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
