This guide explains how to use this MCP-Forge-Python project as a template for developing your own MCP servers.
This project serves as a production-ready template for building MCP (Model Context Protocol) servers with OAuth support. The current tools are demo examples - replace them with your own functionality.
- Fork or clone this repository
- Configure placeholders (see below)
- Replace demo tools with your own
- Customize configuration for your needs
src/mcp_app/
├── main.py # FastAPI app, MCP server setup, middlewares
├── config.py # Pydantic configuration models
├── handlers/ # OAuth endpoint handlers
├── middlewares/ # JWT validation, access logs
└── tools/router.py # MCP tools registration
tests/ # Comprehensive test suite
chart/ # Kubernetes deployment
config.toml # Configuration example
The current tools (hello_world, whoami) are examples. Here's how to replace them:
Create src/mcp_app/tools/my_tools.py:
def my_business_logic_tool(param1: str, param2: int) -> dict:
"""Execute your business logic.
Args:
param1: Description of parameter 1
param2: Description of parameter 2
Returns:
Dictionary with results
"""
# Your tool implementation here
result = {
"status": "success",
"data": f"Processed {param1} with {param2}"
}
return resultEdit src/mcp_app/tools/router.py:
from mcp.server import FastMCP
# Remove demo imports
# from mcp_app.mcp_components.tools.hello_world import hello_world
# from mcp_app.mcp_components.tools.whoami import whoami
# Add your tools
from my_app.tools.my_tools import my_business_logic_tool
def register_tools(mcp: FastMCP) -> None:
"""Register MCP tools with the server."""
# Remove demo tools
# mcp.tool()(hello_world)
# mcp.tool()(whoami)
# Register your tools
mcp.tool()(my_business_logic_tool)Modify tests/test_tools.py to test your new tools instead of the demo ones.
Update README.md and DEVELOPMENT.md to document your tools instead of the demo ones.
The project includes JWT validation middleware for securing tools. By default, it's configured for local validation using a JWKS endpoint.
To enable JWT validation with Keycloak:
- Run Keycloak locally (e.g., via Docker:
docker run -p 8080:8080 quay.io/keycloak/keycloak:latest start-dev). - Create a realm and client in Keycloak admin console.
- Update
config.toml:- Set
jwks_uri = "http://localhost:8080/realms/your-realm/protocol/openid-connect/certs" - Adjust
allow_conditionsto match your email domain, e.g.,payload.email.endswith("@yourdomain.com")
- Set
- Enable OAuth endpoints if needed by setting
oauth_authorization_server.enabled = trueandoauth_protected_resource.enabled = true, updating issuer_uri and auth_servers accordingly.
To use Auth0 as your identity provider:
- Get your Auth0 tenant details (tenant name, client ID, etc.).
- Update
config.toml:- Set
jwks_uri = "https://your-tenant.auth0.com/.well-known/jwks.json" - Set
allow_conditionsto validate claims, e.g.,payload.iss == "https://your-tenant.auth0.com/" and payload.aud == "your-client-id"
- Set
- Ensure Auth0 is configured to issue JWTs with the required claims.
For external validation (e.g., via a proxy), set strategy = "external" and configure your proxy to forward validated JWTs in the X-Validated-Jwt header.
As a template, this project allows configuring which JWT claims are exposed to MCP tools via the context module. This is crucial for security, as JWT payloads may contain sensitive information (PII) that should not be accessible to tools.
- Default:
jwt_exposed_claims = "all"- Exposes all claims in the JWT payload. - Secure Option:
jwt_exposed_claims = ["user_id", "roles"]- Only exposes specific claims.
Important: The permissions claim is always included automatically for authorization purposes, regardless of the jwt_exposed_claims configuration. Review your JWT structure and set jwt_exposed_claims to only the claims your tools need. Avoid exposing sensitive data like emails, personal info, or internal IDs unless necessary. Update this in config.toml and test that tools receive only expected data.
Example in config.toml:
jwt_exposed_claims = ["user_id", "roles"]Note: permissions is always included automatically for authorization.
This project implements a basic permission-based access control system using OAuth permissions to restrict tool execution. Permissions are issued by the identity provider (e.g., Keycloak, Auth0, or other OAuth providers) and validated in tools at runtime.
You can define custom permissions based on your application's needs. Permissions control access to specific tools or features.
Example Permissions (define in your OAuth provider's API/client configuration):
tool:read: Access to read-only tools.tool:write: Access to tools that modify data.tool:admin: Administrative access to sensitive tools.
Permissions are included in JWT claims as a permissions array via your OAuth provider's rules/actions/policies. Configure your provider to include the appropriate permissions in tokens based on user roles.
To restrict a tool based on permissions, add authorization checks inside the tool function. Use get_jwt_payload() from mcp_app.context to access claims. Note that permissions is always included in the filtered payload for authorization purposes.
Example for a tool requiring tool:user permission:
from mcp_app.context import get_jwt_payload
def my_tool(param: str) -> str:
"""My tool description. Requires tool:user permission.
Args:
param: Parameter description.
Returns:
Result description.
Raises:
PermissionError: If user lacks required permission.
"""
payload = get_jwt_payload()
if payload: # Only check in HTTP mode (with JWT)
permissions = payload.get("permissions", [])
if "tool:user" not in permissions:
raise PermissionError("Insufficient permissions: tool:user permission required")
# Tool logic here
return f"Processed: {param}"- In HTTP mode: Validates permissions from JWT; raises
PermissionErrorif unauthorized. - In stdio mode: Allows execution without checks (for development).
- Always include permission checks for sensitive tools to prevent unauthorized access.
Configure your OAuth provider to issue tokens with the appropriate permissions based on user roles.
Before using this template, you must replace all placeholders with your actual values:
-
pyproject.toml:- Change
YOUR_NAMEandyour.email@example.comto your real name and email
- Change
-
chart/values.yaml:- Change
ghcr.io/YOUR_USERNAME/YOUR_REPOto your real container repository - Update all example URLs (
your-keycloak.example.com,your-mcp-domain.example.com) according to your infrastructure
- Change
-
config.toml:- Configure Keycloak URLs, JWKS, etc. for your specific environment
- Change
your-keycloak.example.comandyourdomain.comto your real values - Set environment variables for secrets:
MCP_CLIENT_IDandMCP_CLIENT_SECRET
-
README files:
- Replace
bercianor/mcp-forge-pythonin badges with your actual GitHub username/repo
- Replace
-
.github/CODEOWNERS:- Change
@YOUR_USERNAMEto your GitHub username
- Change
-
.github/dependabot.yml:- Change
YOUR_USERNAMEto your GitHub username
- Change
YOUR_NAME(pyproject.toml)your.email@example.com(pyproject.toml)ghcr.io/YOUR_USERNAME/YOUR_REPO(chart/values.yaml)your-keycloak.example.com(config.toml, chart/values.yaml)your-mcp-domain.example.com(chart/values.yaml)yourdomain.com(config.toml)your-realm(config.toml, chart/values.yaml)PLACEHOLDER(chart/values.yaml - secret key)your-kv/applications/mcp-forge-python/credentials(chart/values.yaml - Vault path)@YOUR_USERNAME(.github/CODEOWNERS)YOUR_USERNAME(.github/dependabot.yml)YOUR_CLIENT_ID(config.toml)YOUR_CLIENT_SECRET(config.toml)
your-keycloak.example.com→ Your real Keycloak serveryour-mcp-domain.example.com→ Your real domain@yourdomain.com→ Your email domain
Add custom middlewares in src/mcp_app/middlewares/ and register them in main.py.
The OAuth implementation follows RFC 8414 and RFC 9728. Modify handlers in src/mcp_app/handlers/ if needed.
Add database connections, ORMs, or other data sources as needed for your tools.
Create multiple config.*.toml files for different environments (dev, staging, prod).
# Build image
docker build -t your-mcp-server .
# Run container
docker run -p 8080:8080 -v $(pwd)/config.toml:/data/config.toml your-mcp-serverUse the provided Helm chart in chart/ directory. Update chart/values.yaml with your configuration.
# Install dependencies
uv sync
# Run in development mode
uv run http # or uv run stdio# Run all tests
just test
# Run with coverage
just cov
# Run all quality checks
just check-all- Replace demo tools with your business logic
- Configure OAuth providers (Keycloak, Auth0, etc.)
- Set up your deployment infrastructure
- Add comprehensive tests for your tools
- Update documentation for your specific use case
For more information, see Contributing for contribution guidelines.