Securing API Credentials in MCP Server Configurations: A Complete Guide
Use environment variables injected via Docker -e flags or Docker secrets mounted at runtime to keep API credentials out of source control and image layers, accessing them securely within FastMCP tools using os.getenv() or file reads from /run/secrets/.
The Model Context Protocol (MCP) enables AI assistants to interact with external data sources through lightweight server implementations. In the cr2007/mcp-wordle-python repository—a FastMCP-based Wordle solution server—sensitive credentials must be injected at runtime rather than embedded in code or Docker images. This guide demonstrates production-ready patterns for securing API keys across Docker, uvx, and local development environments.
Understanding the MCP Server Architecture
The Wordle MCP (Python) server follows a minimal micro-service pattern built on the FastMCP framework. Understanding where configuration resides is essential before implementing credential security.
FastMCP Entry Point and Tool Registration
The server initialization occurs in src/mcp_wordle/main.py, where the FastMCP object is instantiated and tools are registered:
mcp = FastMCP("WordleMCP") # ← FastMCP registration
This object exposes the get_wordle_data tool, which fetches Wordle data. While the current implementation accesses public endpoints, extending it to authenticated APIs requires secure credential handling within this entry point.
Docker Runtime Configuration
The multi-stage Dockerfile builds a minimal runtime image where the application resides in /app. The ENTRYPOINT executes the mcp-wordle console script defined in pyproject.toml. Critically, the image contains no embedded credentials—they must be supplied at container startup via environment variables or secret mounts.
Configuration Entry Points for Credentials
MCP servers receive configuration through the client registration file (typically Claude Desktop's ~/.config/claude/servers.json) and runtime environment injection.
MCP Client Registration File
The README demonstrates the standard registration pattern for Docker-based deployment:
{
"mcpServers": {
"Wordle MCP (Python)": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"--init",
"-e",
"DOCKER_CONTAINER=true",
"ghcr.io/cr2007/mcp-wordle-python:latest"
]
}
}
}
This JSON structure is the only place where server commands and runtime arguments are defined. Adding -e KEY=VALUE flags here injects credentials without touching source code.
uvx-Based Registration
For Python-centric deployments using uvx, the registration supports environment injection:
{
"Wordle MCP (Python)": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/cr2007/mcp-wordle-python",
"mcp-wordle",
"--env",
"WORDLE_API_KEY=${WORDLE_API_KEY}"
]
}
}
The --env flag (or --env-file .env) ensures variables are set before the FastMCP server initializes.
Secure Credential Injection Patterns
Production MCP servers should implement defense-in-depth for secret management, keeping credentials out of image layers and source control.
Environment Variables via Docker Flags
The simplest pattern adds -e flags to the Docker args array in the MCP config:
"-e",
"WORDLE_API_KEY=${WORDLE_API_KEY}"
The host shell expands ${WORDLE_API_KEY} at runtime, keeping the raw value out of the JSON file. Inside src/mcp_wordle/main.py, access the key via:
import os
api_key = os.getenv("WORDLE_API_KEY")
if not api_key:
raise RuntimeError("Missing WORDLE_API_KEY")
Docker Secret Files
For Docker Swarm or Compose deployments, mount secrets as files:
# docker-compose.yml
services:
wordle-mcp:
image: ghcr.io/cr2007/mcp-wordle-python:latest
environment:
- DOCKER_CONTAINER=true
secrets:
- wordle_api_key
secrets:
wordle_api_key:
file: ./secrets/wordle_api_key.txt
Inside the container, read from /run/secrets/wordle_api_key:
from pathlib import Path
secret_path = Path("/run/secrets/wordle_api_key")
if secret_path.is_file():
api_key = secret_path.read_text().strip()
Local Development with .env Files
The repository includes a .env.example file. For local testing, copy it to .env and populate values:
# .env
WORDLE_API_KEY=s3cr3t-k3y-123
Load it via uvx:
uvx --from git+https://github.com/cr2007/mcp-wordle-python \
--env-file .env mcp-wordle
This keeps credentials out of shell history and source control while providing a convenient developer experience.
Implementing Secure Credentials in the Tool
Below is a complete example modifying src/mcp_wordle/main.py to securely handle a Wordle API key using multiple fallback methods:
import os
from pathlib import Path
from fastmcp import FastMCP
mcp = FastMCP("WordleMCP")
def get_api_key() -> str:
"""Retrieve API key from environment or Docker secrets."""
# Priority 1: Environment variable
api_key = os.getenv("WORDLE_API_KEY")
# Priority 2: Docker secret file
if not api_key:
secret_path = Path("/run/secrets/wordle_api_key")
if secret_path.is_file():
api_key = secret_path.read_text().strip()
if not api_key:
raise RuntimeError("Wordle API key not provided via environment or secrets")
return api_key
@mcp.tool(
name="get_wordle_solution",
description="Fetches the Wordle solution for a given date",
)
async def get_wordle_solution(target_date: str):
api_key = get_api_key()
headers = {"Authorization": f"Bearer {api_key}"}
# Implementation continues...
Summary
- Never hard-code credentials in
src/mcp_wordle/main.pyor theDockerfile. - Use environment variables injected via
-eflags in the MCP client config JSON for Docker deployments. - Leverage Docker secrets mounted at
/run/secrets/for production Swarm or Compose stacks. - Develop locally with
.envfiles loaded throughuvx --env-fileto keep secrets out of git history. - Validate at startup by checking for required environment variables or secret files in your FastMCP entry point to ensure fail-fast behavior.
Frequently Asked Questions
How do I pass API credentials to an MCP server running in Docker?
Add environment variable flags to the args array in your MCP client configuration. Use the syntax -e VARIABLE_NAME=${VARIABLE_NAME} so the host shell expands the value at runtime, keeping the raw secret out of the JSON file. Inside the container, access the value using os.getenv("VARIABLE_NAME") in your Python code.
Can I use Docker secrets instead of environment variables for MCP servers?
Yes. Configure your MCP server to read from files mounted at /run/secrets/secret_name. In Docker Compose, define the secret in a top-level secrets: section and reference it in your service configuration. In Python, use Path("/run/secrets/secret_name").read_text() to retrieve the credential. This method keeps secrets out of environment variables and process listings.
What is the safest way to handle credentials during local development of MCP servers?
Use a .env file that is excluded from version control via .gitignore. Load this file using uvx --env-file .env when running your MCP server locally. This prevents credentials from appearing in shell history, terminal logs, or source control while providing a convenient developer workflow. Always commit a .env.example template with dummy values rather than the actual .env file.
Where should I validate that API credentials are present in an MCP server?
Perform validation at server startup within your main entry point (e.g., src/mcp_wordle/main.py) before registering tools with FastMCP. Check for the presence of required environment variables or secret files and raise a RuntimeError with a descriptive message if they are missing. This fail-fast approach ensures that credential issues are caught immediately when the server starts rather than during tool execution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →