Skip to main content
RapidDev - Software Development Agency
mcp-tutorial

How to fix MCP authentication and permission errors

MCP authentication errors like '401 Unauthorized', 'Invalid API key', and 'Permission denied' happen when the server cannot validate credentials. The fix depends on the cause: check that the env block in your host config has the correct variable names and values, verify the API key is still valid and not expired, and ensure the key has the required permissions. For remote Streamable HTTP servers, OAuth 2.1 authentication issues require checking token expiry and redirect URIs.

What you'll learn

  • Common MCP authentication error messages and their causes
  • How to verify environment variables are reaching the server
  • How to troubleshoot invalid or expired API keys
  • How to debug OAuth 2.1 authentication for remote servers
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate7 min read10-15 minAll MCP hosts and serversMarch 2026RapidDev Engineering Team
TL;DR

MCP authentication errors like '401 Unauthorized', 'Invalid API key', and 'Permission denied' happen when the server cannot validate credentials. The fix depends on the cause: check that the env block in your host config has the correct variable names and values, verify the API key is still valid and not expired, and ensure the key has the required permissions. For remote Streamable HTTP servers, OAuth 2.1 authentication issues require checking token expiry and redirect URIs.

Fixing MCP Authentication Errors

MCP servers that connect to external services (GitHub, Slack, databases) need API keys or tokens passed as environment variables. When these credentials are missing, expired, or misconfigured, the server returns authentication errors. This tutorial covers every common auth error pattern and walks you through diagnosing and fixing each one.

Prerequisites

  • An MCP server showing authentication errors
  • Access to the API key or token the server needs
  • Access to your MCP host configuration file

Step-by-step guide

1

Identify the exact authentication error

Authentication errors appear in different forms depending on the server and the external service it connects to. Check your MCP host logs and the server's stderr output for the specific error message. The error message tells you whether the credential is missing entirely, present but invalid, or valid but lacking required permissions.

typescript
1# Common authentication error messages:
2
3# Missing credentials:
4# "Error: API_KEY environment variable is not set"
5# "Missing required configuration: GITHUB_PERSONAL_ACCESS_TOKEN"
6
7# Invalid credentials:
8# "401 Unauthorized"
9# "Invalid API key"
10# "Bad credentials"
11# "Authentication failed"
12
13# Insufficient permissions:
14# "403 Forbidden"
15# "Permission denied"
16# "Insufficient scopes. Required: repo, read:org"
17# "Resource not accessible by personal access token"
18
19# Check logs:
20tail -n 30 ~/Library/Logs/Claude/mcp*.log | grep -i -E "auth|401|403|key|token|permission"

Expected result: You have identified the specific authentication error and can determine if the issue is missing, invalid, or insufficient credentials.

2

Verify the env block in your host config

The most common cause is a missing or misnamed environment variable in the host config. Open your claude_desktop_config.json or .cursor/mcp.json and check the env block for the failing server. Verify the variable name matches exactly what the server expects (names are case-sensitive). Check the server's README or documentation for the correct variable names.

typescript
1// Check your config matches the server's expected variable names
2
3// GitHub server expects:
4{
5 "env": {
6 "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
7 }
8}
9// NOT: "GITHUB_TOKEN" or "GH_TOKEN" — the name must match exactly
10
11// Slack server expects:
12{
13 "env": {
14 "SLACK_BOT_TOKEN": "xoxb-xxxxxxxxxxxx",
15 "SLACK_TEAM_ID": "T01234567"
16 }
17}
18
19// Common pattern — check the server's documentation:
20// @modelcontextprotocol/server-github → GITHUB_PERSONAL_ACCESS_TOKEN
21// @modelcontextprotocol/server-slack → SLACK_BOT_TOKEN + SLACK_TEAM_ID
22// @modelcontextprotocol/server-brave-search → BRAVE_API_KEY

Expected result: Your env block has the correct variable names and values for the server you are configuring.

3

Validate the API key is still active

API keys and tokens can expire, be revoked, or reach usage limits. Test the key independently of MCP by making a direct API call with curl or your browser. For GitHub tokens, check the token's status on github.com/settings/tokens. For other services, check their respective dashboards. If the key is expired, generate a new one and update your MCP config.

typescript
1# Test a GitHub token directly:
2curl -H "Authorization: token ghp_your_token_here" \
3 https://api.github.com/user
4# Should return your user profile JSON. 401 = invalid token.
5
6# Test a Brave Search API key:
7curl "https://api.search.brave.com/res/v1/web/search?q=test" \
8 -H "X-Subscription-Token: your-brave-key"
9# Should return search results. 401 = invalid key.
10
11# Check GitHub token permissions:
12curl -H "Authorization: token ghp_your_token_here" \
13 https://api.github.com/rate_limit
14# Shows rate limit info if token is valid

Expected result: The API call succeeds with the key, confirming it is valid, or fails with a clear error indicating it needs to be regenerated.

4

Check token permissions and scopes

Even a valid token can fail if it lacks the required permissions. GitHub Personal Access Tokens need specific scopes (like repo for repository access, read:org for organization data). The error message often tells you which scope is missing. Generate a new token with the correct scopes, or edit the existing token's permissions in the service's dashboard.

typescript
1# GitHub token scopes needed for the MCP GitHub server:
2# - repo (full repository access)
3# - read:org (read organization data)
4# - gist (optional, for gist operations)
5
6# To create a new GitHub token with correct scopes:
7# 1. Go to github.com/settings/tokens
8# 2. Click 'Generate new token (classic)'
9# 3. Select scopes: repo, read:org
10# 4. Generate and copy the token
11# 5. Update your MCP config env block
12
13# The error message often tells you what is needed:
14# "Insufficient scopes. Required: repo, read:org"
15# Your token is missing those scopes

Expected result: Your token has the required scopes and permissions for all operations the MCP server needs.

5

Debug credential passing with a startup check

If you are building your own MCP server and users report auth errors, add a startup check that verifies credentials are present and valid before the server begins accepting connections. This gives users an immediate, clear error instead of a confusing failure on the first tool call. If you are dealing with complex authentication setups involving OAuth, SSO, or multiple services, the RapidDev team can help design a secure credential management strategy for your MCP servers.

typescript
1// Add to your server's entry point
2async function validateCredentials() {
3 const apiKey = process.env.API_KEY;
4 if (!apiKey) {
5 console.error("ERROR: API_KEY environment variable is required.");
6 console.error("Add it to your MCP host config:");
7 console.error(' "env": { "API_KEY": "your-key-here" }');
8 process.exit(1);
9 }
10
11 // Optional: test the key with a lightweight API call
12 try {
13 const res = await fetch("https://api.example.com/ping", {
14 headers: { Authorization: `Bearer ${apiKey}` },
15 });
16 if (!res.ok) {
17 console.error(`ERROR: API key validation failed (${res.status})`);
18 console.error("Check that your API key is valid and not expired.");
19 process.exit(1);
20 }
21 console.error("API key validated successfully.");
22 } catch (error) {
23 console.error("WARNING: Could not validate API key:", error.message);
24 }
25}
26
27await validateCredentials();

Expected result: The server validates credentials at startup and provides clear error messages if they are missing or invalid.

Complete working example

src/auth-validated-server.ts
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3import { z } from "zod";
4
5// Validate credentials at startup
6const API_KEY = process.env.API_KEY;
7const BASE_URL = process.env.BASE_URL || "https://api.example.com";
8
9if (!API_KEY) {
10 console.error("FATAL: API_KEY environment variable is required.");
11 console.error("Configure it in your MCP host config:");
12 console.error('{"env": {"API_KEY": "your-key-here"}}');
13 process.exit(1);
14}
15
16console.error(`Server starting with API base: ${BASE_URL}`);
17console.error(`API_KEY: ${API_KEY.substring(0, 8)}...`);
18
19const server = new McpServer({
20 name: "auth-demo-server",
21 version: "1.0.0",
22});
23
24server.tool(
25 "fetch-data",
26 "Fetch data from the authenticated API",
27 { endpoint: z.string() },
28 async ({ endpoint }) => {
29 const response = await fetch(`${BASE_URL}/${endpoint}`, {
30 headers: {
31 Authorization: `Bearer ${API_KEY}`,
32 "Content-Type": "application/json",
33 },
34 });
35
36 if (response.status === 401) {
37 return {
38 content: [{
39 type: "text",
40 text: "Authentication failed. Your API key may be expired. Generate a new key and update your MCP host config.",
41 }],
42 isError: true,
43 };
44 }
45
46 if (!response.ok) {
47 return {
48 content: [{
49 type: "text",
50 text: `API error: ${response.status} ${response.statusText}`,
51 }],
52 isError: true,
53 };
54 }
55
56 const data = await response.json();
57 return {
58 content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
59 };
60 }
61);
62
63const transport = new StdioServerTransport();
64await server.connect(transport);

Common mistakes when fixing MCP authentication and permission errors

Why it's a problem: Using the wrong environment variable name (e.g., GITHUB_TOKEN instead of GITHUB_PERSONAL_ACCESS_TOKEN)

How to avoid: Check the server's documentation for the exact variable name. Names are case-sensitive and must match precisely.

Why it's a problem: Pasting the API key with leading/trailing whitespace

How to avoid: Trim any whitespace from the API key value. Some editors add invisible characters when pasting.

Why it's a problem: Using an expired or revoked API key

How to avoid: Test the key independently with curl. If it fails, generate a new one from the service's dashboard.

Why it's a problem: Creating a GitHub token without the required scopes

How to avoid: The MCP GitHub server needs at least repo and read:org scopes. Check the error message for the specific scopes required.

Best practices

  • Always validate credentials at server startup with clear error messages
  • Test API keys independently before adding them to MCP config
  • Use the minimum required permissions/scopes for API tokens
  • Rotate API keys periodically and update your MCP config when you do
  • Never log full API keys — log only the first few characters for identification
  • Document which environment variables your server requires in its README

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My MCP server is showing '[error message]' when trying to connect to [service]. I configured the API key in my [Claude Desktop / Cursor] config. Help me debug the authentication error and fix it.

MCP Prompt

My MCP GitHub server shows '401 Unauthorized'. I set GITHUB_PERSONAL_ACCESS_TOKEN in my config. Help me verify the token is valid, has the right scopes, and is configured correctly.

Frequently asked questions

Where should I store API keys for MCP servers?

In the env block of your MCP host configuration file (claude_desktop_config.json or .cursor/mcp.json). Never hardcode keys in server source code or commit them to version control.

My API key works in curl but not in the MCP server. Why?

The key might not be reaching the server process. Add a startup log (console.error) that prints whether the env var is set (without printing the actual value). Also check for whitespace or invisible characters in the config file.

How do I rotate API keys without downtime?

Generate the new key first, update the env block in your MCP config, then restart the MCP host. Finally, revoke the old key. The downtime is only the few seconds it takes to restart.

Can MCP servers use OAuth instead of API keys?

Yes, remote MCP servers using Streamable HTTP transport support OAuth 2.1 authentication. This is defined in the MCP specification and is the recommended auth method for shared remote servers.

What if the server needs multiple API keys for different services?

Add all required keys to the env block. Each key is a separate key-value pair. The server accesses them independently via process.env.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.