MCPJungle Gateway - MCP-Enabled AI Platform
Describes an MCP gateway with auth, RBAC, rate limiting, and OpenTelemetry tracing for connecting to GitHub, Notion, and filesystem servers.
What this file does
Describes an MCP gateway with auth, RBAC, rate limiting, and OpenTelemetry tracing for connecting to GitHub, Notion, and filesystem servers.
When to use it
- Building a unified API gateway for multiple MCP servers
- Adding authentication and role-based access to MCP tools
- Integrating OpenTelemetry tracing into an MCP-based platform
- Setting up a multi-tenant AI agent gateway with rate limits
Assumes this stack
MCPJungle Gateway - MCP-Enabled AI Platform
Overview
MCPJungle Gateway is an enhanced AI platform gateway that integrates the Model Context Protocol (MCP) with authentication, OpenTelemetry tracing, and role-based access control (RBAC). It provides a unified interface to multiple MCP servers while maintaining security and observability.
Features
Core Capabilities
- MCP Server Management: Connect to multiple MCP servers (GitHub, Notion, Filesystem)
- Authentication & Authorization: API key-based authentication with role-based access control
- OpenTelemetry Tracing: Distributed tracing with Jaeger integration
- Unified
/mcpEndpoint: Single endpoint for all MCP operations - ACL Configuration: Fine-grained permissions for agents and users
- Rate Limiting: Per-role rate limits with Redis-backed tracking
- Memory & RAG Integration: Seamless integration with existing services
Supported MCP Servers
- mcp-github: GitHub repositories, issues, PRs, and code search
- mcp-notion: Notion databases, pages, and blocks
- mcp-filesystem: File system operations with sandboxing
Quick Start
Prerequisites
- Docker and Docker Compose
- Node.js 18+ (for MCP servers)
- GitHub Personal Access Token (optional, for GitHub MCP)
- Notion Integration Token (optional, for Notion MCP)
Setup
- Configure Environment Variables:
cp .env.mcpjungle.example .env.mcpjungle
# Edit .env.mcpjungle and set:
# - GITHUB_TOKEN (for GitHub MCP server)
# - NOTION_API_KEY (for Notion MCP server)
# - API_KEYS (customize your API keys)
-
Configure MCP Servers (optional): Edit
configs/mcp-servers.yamlto enable/disable servers or modify settings. -
Configure Access Control (optional): Edit
configs/mcp-acl.yamlto customize roles and permissions. -
Start Services:
# Start all services including MCPJungle Gateway
docker compose up -d mcpjungle-gateway
# Or start all services
docker compose up -d
- Verify Deployment:
# Check health
curl http://localhost:9100/health
# Check available servers (requires API key)
curl -H "Authorization: Bearer sk-project-agent-key-321" http://localhost:9100/mcp/servers
Architecture
┌─────────────────────────────────────────────────────────┐
│ Client Layer │
│ (HTTP/REST with Bearer Token Auth) │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ MCPJungle Gateway (Port 9100) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Authentication & Authorization (ACL) │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ OpenTelemetry Tracing (Jaeger) │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ MCP Client Manager │ │
│ │ - Connection Pooling │ │
│ │ - Request Routing │ │
│ │ - Error Handling │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────┬──────────────────────────────────────┘
│
┌─────────┴─────────┬─────────────┐
│ │ │
┌────────▼────────┐ ┌───────▼──────┐ ┌──▼──────────┐
│ MCP GitHub │ │ MCP Notion │ │ MCP │
│ Server │ │ Server │ │ Filesystem │
│ (npx) │ │ (npx) │ │ (npx) │
└─────────────────┘ └──────────────┘ └─────────────┘
API Reference
Authentication
All MCP endpoints require authentication using Bearer tokens:
curl -H "Authorization: Bearer sk-test-key-123" <endpoint>
Endpoints
POST /mcp
Unified MCP endpoint for core operations in CI/staging.
Request (list tools):
{
"action": "list_tools",
"server_id": "mcp-github"
}
Request (call tool):
{
"action": "call_tool",
"server_id": "mcp-github",
"tool_name": "github_search_repositories",
"arguments": {"query": "language:python stars:>1000"}
}
Supported actions: list_tools, call_tool, list_resources, read_resource.
GET /mcp/servers
List all available MCP servers (filtered by user role).
Response:
{
"servers": [
{
"id": "mcp-github",
"name": "GitHub MCP Server",
"description": "Access GitHub repositories, issues, PRs, and code",
"connected": true,
"capabilities": ["resources", "tools", "prompts"]
}
],
"total": 3,
"role": "developer"
}
POST /mcp/resources/list
List resources from an MCP server.
Request:
{
"server_id": "mcp-github"
}
Response:
{
"server_id": "mcp-github",
"resources": [
{
"uri": "github://repos/user/repo",
"name": "repository",
"description": "GitHub repository resource"
}
],
"count": 10
}
POST /mcp/resources/read
Read a specific resource.
Request:
{
"server_id": "mcp-github",
"uri": "github://repos/octocat/hello-world"
}
POST /mcp/tools/list
List available tools from an MCP server.
Request:
{
"server_id": "mcp-github"
}
Response:
{
"server_id": "mcp-github",
"tools": [
{
"name": "github_search_repositories",
"description": "Search GitHub repositories",
"inputSchema": {...}
}
],
"count": 8
}
POST /mcp/tools/call
Call a tool on an MCP server.
Request:
{
"server_id": "mcp-github",
"tool_name": "github_search_repositories",
"arguments": {
"query": "language:python stars:>1000",
"per_page": 10
}
}
Response:
{
"server_id": "mcp-github",
"tool_name": "github_search_repositories",
"result": {
"content": [
{
"type": "text",
"text": "Found 10 repositories matching query..."
}
],
"isError": false
}
}
GET /mcp/acl/role
Get ACL role information for authenticated user.
Response:
{
"role": "developer",
"description": "Read/write access to code repositories and filesystem",
"servers": ["mcp-github", "mcp-notion", "mcp-filesystem"],
"rate_limits": {
"requests_per_minute": 300,
"requests_per_hour": 5000
},
"permissions": {
"mcp-github": {
"operations": ["read", "write"],
"resources_count": 5,
"tools_count": 9
}
}
}
Role-Based Access Control
Predefined Roles
Admin
- Description: Full access to all MCP servers and operations
- Permissions: All resources, tools, operations (read/write/delete)
- Rate Limits: 1000 req/min, 10000 req/hour
Project-Agent
- Description: Scoped read-only access for project automation agents
- Permissions: Read-only GitHub + Notion tools for test repositories/workspaces
- Rate Limits: 120 req/min, 1800 req/hour
Developer
- Description: Read/write access to code repositories and filesystem
- Permissions:
- GitHub: All resources and tools (read/write)
- Notion: Read-only access
- Filesystem: Read/write access
- Rate Limits: 300 req/min, 5000 req/hour
Analyst
- Description: Read-only access to data sources
- Permissions:
- GitHub: Repository browsing, issue viewing (read)
- Notion: Database and page reading (read)
- Filesystem: File reading and listing (read)
- Rate Limits: 100 req/min, 2000 req/hour
Writer
- Description: Write access to Notion and limited GitHub access
- Permissions:
- GitHub: Issue management (read/write)
- Notion: Full access (read/write)
- Filesystem: File read/write
- Rate Limits: 200 req/min, 3000 req/hour
Readonly (Default)
- Description: Read-only access to all sources
- Permissions: Read-only across all servers
- Rate Limits: 60 req/min, 1000 req/hour
Customizing Roles
Edit configs/mcp-acl.yaml to customize roles and permissions:
roles:
custom_role:
description: "Custom role description"
permissions:
mcp-github:
resources:
- "repositories"
- "issues"
tools:
- "github_search_repositories"
- "github_list_issues"
operations: ["read"]
rate_limits:
requests_per_minute: 120
requests_per_hour: 2000
OpenTelemetry Tracing
Jaeger UI
Access the Jaeger UI to view distributed traces:
http://localhost:16686
Trace Information
Traces include:
- Request authentication and authorization
- MCP server connection establishment
- Tool/resource invocations
- Response times and errors
Example Trace Flow
mcpjungle-gateway: POST /mcp/tools/call
├─ verify_api_key (100ms)
├─ validate_acl_permissions (50ms)
├─ call_mcp_tool (1200ms)
│ ├─ connect_to_server (200ms)
│ ├─ call_tool (900ms)
│ └─ parse_response (100ms)
└─ format_response (50ms)
Total: 1400ms
Configuration Files
configs/mcp-servers.yaml
Defines MCP servers and their connection parameters:
servers:
mcp-github:
name: "GitHub MCP Server"
type: "stdio"
command: "npx"
args:
- "-y"
- "@modelcontextprotocol/server-github"
env:
GITHUB_TOKEN: "${GITHUB_TOKEN}"
enabled: true
timeout: 30
configs/mcp-acl.yaml
Defines roles, permissions, and rate limits:
roles:
developer:
description: "Read/write access to code repositories"
permissions:
mcp-github:
resources: ["repositories", "issues", "pull_requests"]
tools: ["github_search_repositories", "github_create_issue"]
operations: ["read", "write"]
rate_limits:
requests_per_minute: 300
requests_per_hour: 5000
api_key_roles:
sk-dev-key-789: developer
Example Usage
Python Client Example
import httpx
API_KEY = "sk-dev-key-789"
BASE_URL = "http://localhost:9100"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
async def main():
async with httpx.AsyncClient() as client:
# List available servers
response = await client.get(
f"{BASE_URL}/mcp/servers",
headers=HEADERS
)
print("Servers:", response.json())
# Search GitHub repositories
response = await client.post(
f"{BASE_URL}/mcp/tools/call",
headers=HEADERS,
json={
"server_id": "mcp-github",
"tool_name": "github_search_repositories",
"arguments": {
"query": "language:python",
"per_page": 5
}
}
)
print("Search Results:", response.json())
if __name__ == "__main__":
import asyncio
asyncio.run(main())
cURL Examples
# List MCP servers
curl -H "Authorization: Bearer sk-test-key-123" \
http://localhost:9100/mcp/servers
# List GitHub tools
curl -X POST \
-H "Authorization: Bearer sk-test-key-123" \
-H "Content-Type: application/json" \
-d '{"server_id": "mcp-github"}' \
http://localhost:9100/mcp/tools/list
# Search repositories
curl -X POST \
-H "Authorization: Bearer sk-dev-key-789" \
-H "Content-Type: application/json" \
-d '{
"server_id": "mcp-github",
"tool_name": "github_search_repositories",
"arguments": {
"query": "language:python stars:>1000",
"per_page": 10
}
}' \
http://localhost:9100/mcp/tools/call
# Read a file (filesystem MCP)
curl -X POST \
-H "Authorization: Bearer sk-dev-key-789" \
-H "Content-Type: application/json" \
-d '{
"server_id": "mcp-filesystem",
"tool_name": "read_file",
"arguments": {
"path": "/workspace/example.txt"
}
}' \
http://localhost:9100/mcp/tools/call
Security Best Practices
-
API Keys:
- Use strong, randomly generated API keys
- Rotate keys regularly
- Store keys in secrets manager (not .env files) in production
-
MCP Server Credentials:
- Use tokens with minimal required permissions
- Rotate GitHub/Notion tokens regularly
- Use separate tokens for different environments
-
Network Security:
- Deploy behind HTTPS reverse proxy
- Use firewall rules to restrict access
- Enable CORS only for trusted origins
-
Rate Limiting:
- Enable rate limiting in production
- Monitor for abuse patterns
- Adjust limits based on usage patterns
-
Filesystem Security:
- Configure
allowed_pathsin mcp-servers.yaml - Block sensitive directories (.git, .env)
- Set appropriate
max_file_size_mblimits
- Configure
Monitoring & Debugging
Logs
# View gateway logs
docker compose logs -f mcpjungle-gateway
# View Jaeger logs
docker compose logs -f jaeger
Metrics
# Get performance metrics (public in CI/staging)
curl http://localhost:9100/metrics
Example response fields include mcp_requests, mcp_errors, and mcp_error_rate.
Health Checks
# Check gateway health
curl http://localhost:9100/health
Troubleshooting
MCP Server Not Connecting
Issue: Server shows as not connected Solutions:
- Check Node.js is installed:
docker compose exec mcpjungle-gateway node --version - Verify credentials (GITHUB_TOKEN, NOTION_API_KEY) are set
- Check server logs for connection errors
- Ensure npx can reach npm registry
Authentication Errors
Issue: 401 Unauthorized errors Solutions:
- Verify API key is correct
- Check Authorization header format:
Bearer <key> - Ensure API key is defined in .env.mcpjungle
Permission Denied Errors
Issue: 403 Forbidden errors Solutions:
- Check user role:
GET /mcp/acl/role - Verify role has required permissions in mcp-acl.yaml
- Check rate limits haven't been exceeded
Tracing Not Working
Issue: No traces appearing in Jaeger Solutions:
- Verify
ENABLE_TELEMETRY=true - Check Jaeger is running:
docker compose ps jaeger - Verify OTLP_ENDPOINT is correct
- Check network connectivity between services
Development
Local Development Setup
# Install dependencies
pip install -r requirements.txt
npm install -g @modelcontextprotocol/server-github
npm install -g @modelcontextprotocol/server-notion
npm install -g @modelcontextprotocol/server-filesystem
# Set environment variables
export MAX_SERVE_URL=http://localhost:8080
export QDRANT_HOST=localhost
export REDIS_HOST=localhost
export GITHUB_TOKEN=your_token
export NOTION_API_KEY=your_key
# Run gateway
python gateway_service_mcp.py
Testing
# Run health check
curl http://localhost:9100/health
# Test MCP endpoints
python test_mcpjungle.py
Contributing
When adding new MCP servers:
- Add server configuration to
configs/mcp-servers.yaml - Define permissions in
configs/mcp-acl.yaml - Update documentation
- Test with different roles
- Add example usage
License
[Your License Here]
Support
For issues and questions:
- GitHub Issues: [Your Repo URL]
- Documentation: [Your Docs URL]
- Slack/Discord: [Your Community URL]
Built with: FastAPI, MCP SDK, OpenTelemetry, Redis, Qdrant, Jaeger
What's inside
14 sections, 6 predefined roles, 8 API endpoints, 6 code examples, 2 config file snippets
Change this for your project
- Replace
sk-project-agent-key-321andsk-test-key-123with your own API keys - Replace
sk-dev-key-789with your own developer API key - Replace
lamallamadel/brainegowith your own repository URL - Replace
http://localhost:9100with your deployment URL
Where it goes
Save as AGENTS.md in your repository root. Read by Codex, Cursor and other agents that follow the AGENTS.md convention.
Worth borrowing
- Separating ACL config from server config into two YAML files for maintainability
- Using a single
/mcpendpoint with anactionfield to route to different server operations - Predefining roles (admin, developer, analyst, writer, readonly) with explicit rate limits and permissions
Related Documents
Browser-only development
Guides AI assistants on an Electron + React + TypeScript desktop app for browsing and organizing AI-generated images locally.
Claude Agents — Reference & Recommendations
Catalogues 40+ Claude agents and marketing skills for building a cat adoption charity landing page, with a ready-to-paste prompt and backend API reference.
Golden DKG Prototype -- Master Plan
Defines an 8-phase implementation plan for a Rust prototype of the Golden non-interactive DKG protocol using BLS12-381 and tokio.
Swarms Examples Index
Lists 60+ example scripts for building single and multi-agent systems with the Swarms framework, organized by category and use case.