← Back to Blog
tutorialauthenticationsecuritypatterns

Authentication Patterns for MCP Tools

Secure your MCP tools with API keys, OAuth, environment variables, and token management strategies.

·3 min read·xapable

Authentication Patterns for MCP Tools

Most MCP tools need authentication — API keys, tokens, or OAuth. This guide covers secure patterns for every scenario.

Pattern 1: Environment Variables (Most Common)

Best for API keys and static tokens.

const API_KEY = process.env.MY_API_KEY;
if (!API_KEY) {
  console.error("MY_API_KEY is required");
  process.exit(1);
}

User configures in their MCP client:

{
  "mcpServers": {
    "my-tool": {
      "command": "npx",
      "args": ["-y", "my-tool"],
      "env": {
        "MY_API_KEY": "${MY_API_KEY}"
      }
    }
  }
}

Pros: Simple, secure, works everywhere
Cons: Tokens are static; rotation requires config update

Pattern 2: Config File

For tools with complex configuration.

import { readFile } from "fs/promises";
import { homedir } from "os";
import { join } from "path";

async function loadConfig() {
  const path = join(homedir(), ".my-tool", "config.json");
  try {
    return JSON.parse(await readFile(path, "utf-8"));
  } catch {
    console.error("Config not found. Run: my-tool setup");
    process.exit(1);
  }
}

const config = await loadConfig();

Pros: Supports multiple credentials, complex settings
Cons: Extra setup step; file permissions matter

Pattern 3: OAuth Device Flow

For tools that need user-specific access.

async function authenticate() {
  // Step 1: Request device code
  const { device_code, user_code, verification_uri } = await fetch(
    "https://auth.service.com/device/code",
    { method: "POST", body: JSON.stringify({ client_id: CLIENT_ID }) }
  ).then(r => r.json());

  // Step 2: Tell user to authenticate
  console.error(`Please visit: ${verification_uri}`);
  console.error(`Enter code: ${user_code}`);

  // Step 3: Poll for token
  for (let i = 0; i < 30; i++) {
    await sleep(2000);
    const resp = await fetch("https://auth.service.com/oauth/token", {
      method: "POST",
      body: JSON.stringify({
        client_id: CLIENT_ID,
        device_code,
        grant_type: "urn:ietf:params:oauth:grant-type:device_code"
      })
    });

    if (resp.ok) {
      const { access_token, refresh_token } = await resp.json();
      return { access_token, refresh_token };
    }
  }

  throw new Error("Authentication timed out");
}

Pros: User-specific access, refreshable tokens
Cons: Complex implementation; requires browser interaction

Pattern 4: Token Caching

Avoid re-authenticating on every tool call:

import { writeFile, readFile } from "fs/promises";

const CACHE_PATH = join(homedir(), ".my-tool", "token.json");

async function getToken() {
  // Try cache first
  try {
    const cached = JSON.parse(await readFile(CACHE_PATH, "utf-8"));
    if (cached.expires_at > Date.now()) {
      return cached.access_token;
    }
  } catch {}

  // Get fresh token
  const token = await authenticate();
  await writeFile(CACHE_PATH, JSON.stringify({
    access_token: token.access_token,
    expires_at: Date.now() + 3600 * 1000 // 1 hour
  }));

  return token.access_token;
}

Pattern 5: CI/CD Authentication

For tools used in automated pipelines:

// Accept token via env var OR config file
const TOKEN = process.env.MY_TOOL_TOKEN
  || (await loadConfig()).token;

if (!TOKEN) {
  console.error(
    "Authentication required. Set MY_TOOL_TOKEN env var or run setup."
  );
  process.exit(1);
}

Security Checklist

  • ✅ Never log tokens or API keys
  • ✅ Validate tokens on startup
  • ✅ Handle expired tokens with clear error messages
  • ✅ Use process.exit(1) when auth fails — don't start the server
  • ✅ Store cached tokens with restricted file permissions
  • ✅ Support token refresh when possible

Error Messages for Auth Failures

// ❌ Bad — exposes internals
throw new Error("401: invalid_grant");

// ✅ Good — actionable
return {
  content: [{
    type: "text",
    text: "❌ Authentication failed. Your API key may be expired. " +
          "Get a new key at https://service.com/keys and update your MCP config."
  }]
};

Choose the right pattern for your tool's security needs. Start simple, add complexity only when needed.

#MCP #Tutorial #Authentication #Security #mcpm