Las configuraciones del servidor MCP almacenan claves API en archivos JSON simples como .mcp.json o claude_desktop_config.json. Cuando una clave caduca, se ve comprometida o alcanza límites de velocidad, todos los agentes que utilizan ese servidor MCP fallan. La edición manual de archivos de configuración entre máquinas es lenta y propensa a errores. Este tutorial crea una utilidad de Python que lee las configuraciones de MCP, rota las claves de API desde un almacén seguro, valida la nueva clave con la API de destino y escribe la configuración actualizada de forma atómica. El enfoque funciona para cualquier servidor MCP que pase credenciales mediante variables de entorno o argumentos de línea de comandos.
Requisitos previos
- Python 3.8 o superior instalado
- solicita biblioteca instalada
- Un archivo de configuración MCP existente (.mcp.json o equivalente)
- Una clave API de Scavio para pruebas de validación
Guia paso a paso
Paso 1: Leer la configuración actual de MCP
Cargue el archivo de configuración de MCP e identifique todos los servidores que utilizan variables de entorno clave API. El script busca claves env que contengan API_KEY o TOKEN.
import json
import os
def load_mcp_config(path: str) -> dict:
with open(path, "r") as f:
return json.load(f)
config_path = os.path.expanduser("~/.mcp.json")
config = load_mcp_config(config_path)
for name, server in config.get("mcpServers", {}).items():
env = server.get("env", {})
key_vars = [k for k in env if "API_KEY" in k or "TOKEN" in k]
print(f"{name}: {key_vars}")Paso 2: Busca la clave rotada de tu tienda secreta
Extraiga la nueva clave API de una variable de entorno, un administrador de secretos o un archivo cifrado local. Este ejemplo lee variables de entorno con una convención de sufijo _NEW.
def get_rotated_key(var_name: str) -> str | None:
new_var = f"{var_name}_NEW"
rotated = os.environ.get(new_var)
if rotated:
print(f"Rotation candidate found: {new_var}")
return rotatedPaso 3: Valide la nueva clave antes de escribir
Realice una llamada API ligera con la nueva clave para confirmar que funciona. Para las claves Scavio, PUBLICAR una consulta de búsqueda mínima. Si la validación falla, omita la rotación de esa clave.
import requests
def validate_scavio_key(api_key: str) -> bool:
try:
r = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": api_key},
json={"query": "test", "country_code": "us"},
timeout=10
)
return r.status_code == 200
except Exception:
return False
new_key = get_rotated_key("SCAVIO_API_KEY")
if new_key and validate_scavio_key(new_key):
print("New key validated successfully")
else:
print("Validation failed, skipping rotation")Paso 4: Escriba la configuración actualizada de forma atómica
Primero escriba en un archivo temporal y luego cámbiele el nombre al original. Esto evita que las escrituras parciales dañen la configuración si se interrumpe el proceso.
import tempfile
import shutil
def rotate_config(config_path: str, config: dict, server_name: str, var_name: str, new_key: str) -> None:
config["mcpServers"][server_name]["env"][var_name] = new_key
tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
json.dump(config, tmp, indent=2)
tmp.close()
shutil.move(tmp.name, config_path)
print(f"Rotated {var_name} for {server_name}")Ejemplo en Python
import json
import os
import tempfile
import shutil
import requests
def load_config(path: str) -> dict:
with open(path, "r") as f:
return json.load(f)
def validate_key(api_key: str) -> bool:
try:
r = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": api_key},
json={"query": "test", "country_code": "us"},
timeout=10
)
return r.status_code == 200
except Exception:
return False
def atomic_write(path: str, data: dict) -> None:
tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False)
json.dump(data, tmp, indent=2)
tmp.close()
shutil.move(tmp.name, path)
def rotate_all(config_path: str) -> None:
config = load_config(config_path)
rotated = 0
for name, server in config.get("mcpServers", {}).items():
env = server.get("env", {})
for var in list(env.keys()):
if "API_KEY" not in var and "TOKEN" not in var:
continue
new_key = os.environ.get(f"{var}_NEW")
if not new_key:
continue
if validate_key(new_key):
env[var] = new_key
rotated += 1
print(f"Rotated {var} for {name}")
else:
print(f"Validation failed for {var}, skipping")
if rotated > 0:
atomic_write(config_path, config)
print(f"Config updated with {rotated} rotated keys")
else:
print("No keys rotated")
if __name__ == "__main__":
rotate_all(os.path.expanduser("~/.mcp.json"))Ejemplo en JavaScript
const fs = require("fs");
const os = require("os");
const path = require("path");
async function validateKey(apiKey) {
try {
const res = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": apiKey, "Content-Type": "application/json" },
body: JSON.stringify({ query: "test", country_code: "us" })
});
return res.ok;
} catch {
return false;
}
}
async function rotateAll(configPath) {
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
let rotated = 0;
for (const [name, server] of Object.entries(config.mcpServers || {})) {
const env = server.env || {};
for (const varName of Object.keys(env)) {
if (!varName.includes("API_KEY") && !varName.includes("TOKEN")) continue;
const newKey = process.env[`${varName}_NEW`];
if (!newKey) continue;
if (await validateKey(newKey)) {
env[varName] = newKey;
rotated++;
console.log(`Rotated ${varName} for ${name}`);
} else {
console.log(`Validation failed for ${varName}, skipping`);
}
}
}
if (rotated > 0) {
const tmp = path.join(os.tmpdir(), `mcp-${Date.now()}.json`);
fs.writeFileSync(tmp, JSON.stringify(config, null, 2));
fs.renameSync(tmp, configPath);
console.log(`Config updated with ${rotated} rotated keys`);
}
}
rotateAll(path.join(os.homedir(), ".mcp.json")).catch(console.error);Salida esperada
SCAVIO_API_KEY: rotation candidate found
New key validated successfully
Rotated SCAVIO_API_KEY for scavio-search
Config updated with 1 rotated keys