Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.7.0
current_version = 0.6.0
commit = False
tag = False
sign-tags = True
Expand Down
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ log/

# Certificates and secrets
certs/
jwt/
*.pem
*.key
*.crt
Expand Down
25 changes: 9 additions & 16 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ PROTOCOL_VERSION=2025-03-26
BASIC_AUTH_USER=admin
BASIC_AUTH_PASSWORD=changeme
AUTH_REQUIRED=true


# Secret used to sign JWTs (use long random value in prod)

# Content type for outgoing requests to Forge
FORGE_CONTENT_TYPE=application/json

Expand All @@ -100,29 +104,18 @@ JWT_ALGORITHM=HS256

# === HMAC (Symmetric) Configuration - Default for Development ===
# Secret used to sign JWTs (required for HMAC algorithms: HS256, HS384, HS512)

# PRODUCTION: Use a strong, random secret (minimum 32 characters)
# Generate with: openssl rand -base64 32
JWT_SECRET_KEY=my-test-key

# === RSA/ECDSA (Asymmetric) Configuration - Recommended for Production ===
# Public and private key paths (required for asymmetric algorithms: RS*, ES*)
# Generate RSA keys with: make certs-jwt
# (creates certs/jwt/private.pem and certs/jwt/public.pem with proper permissions)
# Generate ECDSA keys with: make certs-jwt-ecdsa
# (creates certs/jwt/ec_private.pem and certs/jwt/ec_public.pem with proper permissions)
# Generate both SSL and JWT keys: make certs-all
#JWT_PUBLIC_KEY_PATH=certs/jwt/public.pem
#JWT_PRIVATE_KEY_PATH=certs/jwt/private.pem

# JWT Claims Configuration
# Algorithm used to sign JWTs (e.g., HS256)
JWT_ALGORITHM=HS256

# JWT Audience and Issuer claims for token validation
# PRODUCTION: Set these to your service-specific values
JWT_AUDIENCE=mcpgateway-api
JWT_ISSUER=mcpgateway

# JWT Validation Options
# Set to false for Dynamic Client Registration (DCR) scenarios where audience varies
JWT_AUDIENCE_VERIFICATION=true

# Expiry time for generated JWT tokens (in minutes; e.g. 7 days)
TOKEN_EXPIRY=10080
REQUIRE_TOKEN_EXPIRATION=false
Expand Down
1 change: 0 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[flake8]
max-line-length = 600
exclude = mcp-servers
extend-ignore =
E203,
W503
Expand Down
2 changes: 1 addition & 1 deletion .github/tools/cleanup-ghcr-versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fi
##############################################################################
ORG="ibm"
PKG="mcp-context-forge"
KEEP_TAGS=( "0.1.0" "v0.1.0" "0.1.1" "v0.1.1" "0.2.0" "v0.2.0" "0.3.0" "v0.3.0" "0.4.0" "v0.4.0" "0.5.0" "v0.5.0" "0.6.0" "v0.6.0" "0.7.0" "v0.7.0" "latest" )
KEEP_TAGS=( "0.1.0" "v0.1.0" "0.1.1" "v0.1.1" "0.2.0" "v0.2.0" "0.3.0" "v0.3.0" "0.4.0" "v0.4.0" "0.5.0" "v0.5.0" "0.6.0" "v0.6.0" "latest" )
PER_PAGE=100

DRY_RUN=${DRY_RUN:-true} # default safe
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
#
# This workflow re-tags a Docker image (built by a previous workflow)
# when a GitHub Release is published, giving it a semantic version tag
# like `v0.7.0`. It assumes the CI build has already pushed an image
# like `v0.6.0`. It assumes the CI build has already pushed an image
# tagged with the commit SHA, and that all checks on that commit passed.
#
# ➀ Trigger: Release published (e.g. from GitHub UI or `gh release` CLI)
# ➀ Assumes: Existing image tagged with the commit SHA is available
# ➀ Result: Image re-tagged as `ghcr.io/OWNER/REPO:v0.7.0`
# ➀ Result: Image re-tagged as `ghcr.io/OWNER/REPO:v0.6.0`
#
# ======================================================================

Expand All @@ -25,7 +25,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.7.0)'
description: 'Release tag (e.g., v0.6.0)'
required: true
type: string

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-chart.yml.inactive
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: Release Helm Chart
on:
release:
types: [published] # tag repo, ex: v0.7.0 to trigger
types: [published] # tag repo, ex: v0.6.0 to trigger
permissions:
contents: read
packages: write
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ node_modules/
.tmp*
mcp.db-journal
certs/
jwt/
FIXMEs
*.old
logs/
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# report issues (linters). Modified files will need to be staged again.
# -----------------------------------------------------------------------------

exclude: '(^|/)(\.pre-commit-config\.yaml|normalize_special_characters\.py|test_input_validation\.py)$|(^|/)mcp-servers/templates/|.*\.(jinja|j2)$' # ignore these files, all templates, and jinja files
exclude: '(^|/)(\.pre-commit-config\.yaml|normalize_special_characters\.py|test_input_validation\.py)$|.*\.(jinja|j2)$' # ignore these files and jinja templates

repos:
# -----------------------------------------------------------------------------
Expand Down
2 changes: 0 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Repository Guidelines

For specific tasks, see also: llms/api.md llms/helm.md llms/mcpgateway.md llms/mcp-server-go.md llms/mcp-server-python.md llms/mkdocs.md llms/plugins-llms.md llms/testing.md

## Project Structure & Module Organization
- `mcpgateway/`: FastAPI gateway source (entry `main.py`, `cli.py`, services, transports, templates/static, Alembic).
- Services: `mcpgateway/services/` (gateway, server, tool, resource, prompt logic).
Expand Down
2 changes: 1 addition & 1 deletion DEVELOPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Gateway & auth
export MCP_GATEWAY_BASE_URL=http://localhost:4444
export MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp
export MCP_AUTH="Bearer <your_bearer_token>"
export MCP_AUTH="<your_bearer_token>"
```

| Mode | Command | Notes |
Expand Down
24 changes: 11 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ npx -y @modelcontextprotocol/inspector
<summary><strong>πŸ–§ Using the stdio wrapper (mcpgateway-wrapper)</strong></summary>

```bash
export MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}"
export MCP_AUTH=$MCPGATEWAY_BEARER_TOKEN
export MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp
python3 -m mcpgateway.wrapper # Ctrl-C to exit
```
Expand All @@ -423,7 +423,7 @@ In MCP Inspector, define `MCP_AUTH` and `MCP_SERVER_URL` env variables, and sele
```bash
echo $PWD/.venv/bin/python3 # Using the Python3 full path ensures you have a working venv
export MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'
export MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}"
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
npx -y @modelcontextprotocol/inspector
```

Expand All @@ -446,7 +446,7 @@ When using a MCP Client such as Claude with stdio:
"command": "python",
"args": ["-m", "mcpgateway.wrapper"],
"env": {
"MCP_AUTH": "Bearer your-token-here",
"MCP_AUTH": "your-token-here",
"MCP_SERVER_URL": "http://localhost:4444/servers/UUID_OF_SERVER_1",
"MCP_TOOL_CALL_TIMEOUT": "120"
}
Expand Down Expand Up @@ -645,13 +645,13 @@ The `mcpgateway.wrapper` lets you connect to the gateway over **stdio** while ke
```bash
# Set environment variables
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username [email protected] --exp 10080 --secret my-test-key)
export MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}"
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_URL='http://localhost:4444/servers/UUID_OF_SERVER_1/mcp'
export MCP_TOOL_CALL_TIMEOUT=120
export MCP_WRAPPER_LOG_LEVEL=DEBUG # or OFF to disable logging

docker run --rm -i \
-e MCP_AUTH=$MCP_AUTH \
-e MCP_AUTH=$MCPGATEWAY_BEARER_TOKEN \
-e MCP_SERVER_URL=http://host.docker.internal:4444/servers/UUID_OF_SERVER_1/mcp \
-e MCP_TOOL_CALL_TIMEOUT=120 \
-e MCP_WRAPPER_LOG_LEVEL=DEBUG \
Expand All @@ -669,7 +669,7 @@ Because the wrapper speaks JSON-RPC over stdin/stdout, you can interact with it

```bash
# Start the MCP Gateway Wrapper
export MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}"
export MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_URL=http://localhost:4444/servers/YOUR_SERVER_UUID
python3 -m mcpgateway.wrapper
```
Expand Down Expand Up @@ -730,12 +730,10 @@ The `mcpgateway.wrapper` exposes everything your Gateway knows about over **stdi
<summary><strong>🐳 Docker / Podman</strong></summary>

```bash
export MCP_AUTH="Bearer $MCPGATEWAY_BEARER_TOKEN"

docker run -i --rm \
--network=host \
-e MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
-e MCP_AUTH=${MCP_AUTH} \
-e MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \
-e MCP_TOOL_CALL_TIMEOUT=120 \
ghcr.io/ibm/mcp-context-forge:0.7.0 \
python3 -m mcpgateway.wrapper
Expand All @@ -753,7 +751,7 @@ docker run -i --rm \
pipx install --include-deps mcp-contextforge-gateway

# Run the stdio wrapper
MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}" \
MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
python3 -m mcpgateway.wrapper
# Alternatively with uv
Expand All @@ -769,7 +767,7 @@ uv run --directory . -m mcpgateway.wrapper
"command": "python3",
"args": ["-m", "mcpgateway.wrapper"],
"env": {
"MCP_AUTH": "Bearer <your-token>",
"MCP_AUTH": "<your-token>",
"MCP_SERVER_URL": "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp",
"MCP_TOOL_CALL_TIMEOUT": "120"
}
Expand Down Expand Up @@ -806,7 +804,7 @@ source ~/.venv/mcpgateway/bin/activate
uv pip install mcp-contextforge-gateway

# Launch wrapper
MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}" \
MCP_AUTH=${MCPGATEWAY_BEARER_TOKEN} \
MCP_SERVER_URL=http://localhost:4444/servers/UUID_OF_SERVER_1/mcp \
uv run --directory . -m mcpgateway.wrapper # Use this just for testing, as the Client will run the uv command
```
Expand All @@ -826,7 +824,7 @@ uv run --directory . -m mcpgateway.wrapper # Use this just for testing, as the C
"mcpgateway.wrapper"
],
"env": {
"MCP_AUTH": "Bearer <your-token>",
"MCP_AUTH": "<your-token>",
"MCP_SERVER_URL": "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp"
}
}
Expand Down
44 changes: 44 additions & 0 deletions check_todays_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
import sqlite3
from datetime import datetime

def check_todays_metrics():
conn = sqlite3.connect('mcp.db')
cursor = conn.cursor()

print("=== TOOL METRICS FROM TODAY (2025-08-27) ===")
cursor.execute("""
SELECT t.name, tm.timestamp, tm.is_success, tm.response_time
FROM tool_metrics tm
JOIN tools t ON t.id = tm.tool_id
WHERE tm.timestamp LIKE '2025-08-27%'
ORDER BY tm.timestamp DESC
""")
results = cursor.fetchall()
if results:
for name, timestamp, success, response_time in results:
print(f" {name}: {timestamp} (success: {success}, {response_time:.3f}s)")
else:
print(" No tool metrics recorded today!")

print(f"\nTotal tool metrics today: {len(results)}")

print("\n=== CHECKING TOOLS THAT APPEAR IN UI ===")
cursor.execute("SELECT id, name, enabled FROM tools WHERE name LIKE 'json%' OR name LIKE 'test%' OR name LIKE 'book%' ORDER BY name")
tools = cursor.fetchall()
for tool_id, name, enabled in tools:
cursor.execute("SELECT COUNT(*) FROM tool_metrics WHERE tool_id = ?", (tool_id,))
metric_count = cursor.fetchone()[0]
cursor.execute("SELECT MAX(timestamp) FROM tool_metrics WHERE tool_id = ?", (tool_id,))
last_exec = cursor.fetchone()[0]
print(f" {name}:")
print(f" ID: {tool_id}")
print(f" Enabled: {enabled}")
print(f" Total metrics: {metric_count}")
print(f" Last execution: {last_exec}")
print()

conn.close()

if __name__ == "__main__":
check_todays_metrics()
4 changes: 2 additions & 2 deletions docs/docs/development/mcp-developer-guide-json-rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,8 @@ For command-line integration and desktop client compatibility, use the STDIO wra

```bash
# Configure environment variables
export MCP_AUTH="Bearer ${MCPGATEWAY_BEARER_TOKEN}"
export MCP_SERVER_URL="http://localhost:4444/servers/your-server-id"
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_CATALOG_URLS="http://localhost:4444/servers/your-server-id"
export MCP_TOOL_CALL_TIMEOUT=120
export MCP_WRAPPER_LOG_LEVEL=INFO

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/faq/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
"command": "python3",
"args": ["-m", "mcpgateway.wrapper"],
"env": {
"MCP_AUTH": "Bearer <your-token>",
"MCP_AUTH": "<your-token>",
"MCP_SERVER_URL": "http://localhost:4444/servers/UUID_OF_SERVER_1/mcp",
"MCP_TOOL_CALL_TIMEOUT": "120"
}
Expand Down
Loading
Loading