ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Implement MCP Auth Key Rotation
Tutorial

How to Implement MCP Auth Key Rotation

Rotate MCP API keys without downtime. Implement dual-key validation, graceful deprecation, and automated rotation for production MCP servers.

Get Free API KeyAPI Docs

API keys get compromised. Team members leave. Security policies require regular rotation. Rotating keys on a live MCP server without downtime requires a dual-key validation window where both the old and new keys work simultaneously during the transition period. This tutorial implements key rotation for an MCP server that wraps the Scavio API, with graceful deprecation warnings, configurable overlap periods, and automated rotation scheduling.

Prerequisites

  • Python 3.9+ installed
  • requests library installed
  • A Scavio API key from scavio.dev
  • An existing MCP server deployment

Walkthrough

Step 1: Design the key rotation state machine

Define key states (active, rotating, deprecated, revoked) and the transition rules between them. During rotation, both old and new keys must work.

Python
import os, requests, json, time, secrets
from datetime import datetime, timedelta

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}
KEY_FILE = 'mcp_keys.json'

def generate_key() -> str:
    return f'mcp_{secrets.token_hex(16)}'

def load_keys() -> dict:
    if os.path.exists(KEY_FILE):
        with open(KEY_FILE) as f:
            return json.load(f)
    return {'keys': {}}

def save_keys(data: dict):
    with open(KEY_FILE, 'w') as f:
        json.dump(data, f, indent=2)

# Key states: active -> rotating -> deprecated -> revoked
VALID_TRANSITIONS = {
    'active': ['rotating'],
    'rotating': ['deprecated'],
    'deprecated': ['revoked'],
    'revoked': [],
}

print('Key states:', list(VALID_TRANSITIONS.keys()))

Step 2: Implement the rotation workflow

Create functions to initiate rotation (generate new key, mark old as rotating), complete rotation (mark old as deprecated), and revoke old keys.

Python
def create_key(name: str) -> str:
    data = load_keys()
    key = generate_key()
    data['keys'][key] = {
        'name': name,
        'state': 'active',
        'created': datetime.now().isoformat(),
        'rotated_at': None,
        'deprecated_at': None,
    }
    save_keys(data)
    print(f'Created key: {name} ({key[:12]}...)')
    return key

def initiate_rotation(old_key: str) -> str:
    data = load_keys()
    if old_key not in data['keys'] or data['keys'][old_key]['state'] != 'active':
        raise ValueError('Can only rotate active keys')
    # Mark old key as rotating
    data['keys'][old_key]['state'] = 'rotating'
    data['keys'][old_key]['rotated_at'] = datetime.now().isoformat()
    # Create replacement key
    new_key = generate_key()
    data['keys'][new_key] = {
        'name': data['keys'][old_key]['name'],
        'state': 'active',
        'created': datetime.now().isoformat(),
        'replaces': old_key[:12],
    }
    save_keys(data)
    print(f'Rotation started: {old_key[:12]}... -> {new_key[:12]}...')
    print(f'Both keys are now valid. Update clients to use the new key.')
    return new_key

def complete_rotation(old_key: str):
    data = load_keys()
    if data['keys'].get(old_key, {}).get('state') != 'rotating':
        raise ValueError('Key is not in rotating state')
    data['keys'][old_key]['state'] = 'deprecated'
    data['keys'][old_key]['deprecated_at'] = datetime.now().isoformat()
    save_keys(data)
    print(f'Rotation complete: {old_key[:12]}... is now deprecated')

# Demo
key1 = create_key('production-v1')
key2 = initiate_rotation(key1)
complete_rotation(key1)

Step 3: Validate keys with rotation awareness

Build a validation function that accepts active and rotating keys, warns on deprecated keys, and rejects revoked keys.

Python
def validate_with_rotation(api_key: str) -> dict:
    data = load_keys()
    key_config = data['keys'].get(api_key)
    if not key_config:
        return {'valid': False, 'error': 'Unknown key'}
    state = key_config['state']
    if state == 'active':
        return {'valid': True, 'warning': None}
    elif state == 'rotating':
        return {'valid': True, 'warning': 'This key is being rotated. Please switch to the new key.'}
    elif state == 'deprecated':
        return {'valid': False, 'error': 'Key deprecated. Use the replacement key.', 'deprecated_at': key_config.get('deprecated_at')}
    elif state == 'revoked':
        return {'valid': False, 'error': 'Key revoked.'}
    return {'valid': False, 'error': 'Invalid key state'}

def secure_search_with_rotation(api_key: str, query: str) -> dict:
    check = validate_with_rotation(api_key)
    if not check['valid']:
        return {'error': check['error']}
    resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
        json={'query': query, 'country_code': 'us', 'num_results': 3})
    result = {'results': resp.json().get('organic_results', [])}
    if check.get('warning'):
        result['warning'] = check['warning']
    return result

# Test with the active key
result = secure_search_with_rotation(key2, 'test query')
print(f'Results: {len(result.get("results", []))}, Warning: {result.get("warning", "none")}')

Python Example

Python
import os, requests, json, secrets
from datetime import datetime

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'}

keys = {}

def create_key(name):
    key = f'mcp_{secrets.token_hex(16)}'
    keys[key] = {'name': name, 'state': 'active', 'created': datetime.now().isoformat()}
    return key

def rotate_key(old_key):
    keys[old_key]['state'] = 'rotating'
    new_key = create_key(keys[old_key]['name'])
    print(f'Rotated: {old_key[:12]}... -> {new_key[:12]}...')
    return new_key

def validate(key):
    k = keys.get(key, {})
    if k.get('state') in ('active', 'rotating'):
        resp = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
            json={'query': 'test', 'country_code': 'us', 'num_results': 1})
        print(f'[{k["state"]}] Search OK')
    else:
        print(f'Key rejected: {k.get("state", "unknown")}')

k1 = create_key('prod')
k2 = rotate_key(k1)
validate(k1)  # Still works (rotating)
validate(k2)  # Works (active)

JavaScript Example

JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const crypto = require('crypto');

const keys = {};

function createKey(name) {
  const key = `mcp_${crypto.randomBytes(16).toString('hex')}`;
  keys[key] = { name, state: 'active', created: new Date().toISOString() };
  return key;
}

function rotateKey(oldKey) {
  keys[oldKey].state = 'rotating';
  const newKey = createKey(keys[oldKey].name);
  console.log(`Rotated: ${oldKey.slice(0, 12)}... -> ${newKey.slice(0, 12)}...`);
  return newKey;
}

async function validate(key) {
  const k = keys[key];
  if (!k || !['active', 'rotating'].includes(k.state)) {
    console.log(`Key rejected: ${k?.state || 'unknown'}`);
    return;
  }
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query: 'test', country_code: 'us', num_results: 1 })
  });
  console.log(`[${k.state}] Search OK`);
}

const k1 = createKey('prod');
const k2 = rotateKey(k1);
validate(k1).then(() => validate(k2));

Expected Output

JSON
Key states: ['active', 'rotating', 'deprecated', 'revoked']
Created key: production-v1 (mcp_a1b2c3d4...)
Rotation started: mcp_a1b2c3d4... -> mcp_e5f6g7h8...
Both keys are now valid. Update clients to use the new key.
Rotation complete: mcp_a1b2c3d4... is now deprecated
Results: 3, Warning: none

[rotating] Search OK
[active] Search OK

Related Tutorials

  • How to Secure MCP Data Server Deployment
  • How to Add Real-Time Search to Claude via MCP

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.9+ installed. requests library installed. A Scavio API key from scavio.dev. An existing MCP server deployment. A Scavio API key gives you 50 free credits on signup.

Yes. The free tier includes 50 credits on signup, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Related Resources

Best Of

Best MCP Server Management and Optimization Tools in May 2026

Read more
Solution

Rotate MCP Server Credentials Without Downtime

Read more
Best Of

Best MCP Credential Management Tools in 2026

Read more
Solution

MCP Auth and Secret Management

Read more
Use Case

MCP Credential Management

Read more
Use Case

MCP Custom Search Server

Read more

Start Building

Rotate MCP API keys without downtime. Implement dual-key validation, graceful deprecation, and automated rotation for production MCP servers.

Get Free API KeyRead the Docs
ScavioScavio

Real-time search API for AI agents. Search every platform, not just Google.

Product

  • Features
  • Pricing
  • Dashboard
  • Affiliates

Developers

  • Documentation
  • API Reference
  • Quickstart
  • MCP Integration
  • Python SDK

Alternatives

  • Tavily Alternative
  • SerpAPI Alternative
  • Firecrawl Alternative
  • Exa Alternative

Tools

  • JSON Formatter
  • cURL to Code
  • Token Counter
  • All Tools

© 2026 Scavio. All rights reserved.

Featured on TAAFT
Terms of ServicePrivacy Policy