Enterprise MCP Gateway
Provides tools for interacting with Jira via the Jira REST API, enabling read and write operations on Jira issues and related project data.
Provides tools for interacting with a Neo4j knowledge graph, including schema retrieval, Cypher query validation, and execution of read-only queries with enforced limits. Also exposes the graph schema as a resource and Cypher composition guidance as a prompt.
Provides tools for interacting with Teradata databases through an MCP sidecar, enabling query and data operations against Teradata.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Enterprise MCP Gatewaylist my open Jira issues assigned to me"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Enterprise MCP Gateway
Multi-Tenant Model Context Protocol Gateway with Dynamic RBAC, Multi-Backend Integration, and Defense-in-Depth Observability
An enterprise-grade MCP server that mediates AI agent and IDE copilot access to backend systems through a unified, governed interface with authentication, authorization, rate limiting, audit logging, and encryption.
Key Metrics
Metric | Value |
MCP Tools | 15 registered (1 built-in, 4 Jira, 4 BRE stubs, 3 Neo4j, 3 Teradata) |
Backend Systems | 4 backend modules (Jira, BRE/RDS, Neo4j, Teradata) plus DocumentDB as the config/audit store |
Authentication Modes | 3 (API Key + JWT, Kiro RBAC, Legacy opt-in loopback only) |
Security Guardrails | 7 composable checks per request |
Audit Pipelines | 3 parallel (Jira, Generic, Access) |
Rate Limiting | 2-layer sliding window (per-consumer + per-group) |
Encryption | AES-256-GCM with AWS KMS envelope encryption |
RBAC Roles | 5 AD-group-mapped tiers |
Related MCP server: mcp-gateway
Architecture Overview
Client (AI Agent / Kiro IDE / REST)
|
v
AuthMiddleware (3-mode: API Key+JWT, Kiro RBAC, Legacy)
|
v
Guardrail Pipeline (7 composable security checks)
|
v
Two-Layer Rate Limiter (per-consumer + per-tool-group)
|
v
Backend Dispatcher
|
+--- Jira REST API / LLM Orchestrator
+--- Business Rule Engine (RDS API)
+--- Neo4j (Bolt driver)
+--- Teradata (MCP Sidecar)
|
v
Audit Pipelines (Jira | Generic | Access) --> DocumentDBMCP Capabilities
Exactly what the server advertises over /mcp, and nothing more:
Capability | Served | Methods |
Tools | 15 |
|
Resources | 1 ( |
|
Prompts | 1 ( |
|
graph://neo4j/schema(application/json) returns node labels, relationship types, property keys and connected(:Label)-[:REL]->(:Label)relationship patterns read fromCALL db.schema.visualization(). When that procedure is unavailable the pattern list is empty and anotesfield explains why; patterns are never fabricated.cypher_query_guidance(question)returns Cypher composition guidance that embeds the live schema and the constraints the gateway actually enforces (read-only keyword blocking and the auto-injectedLIMIT, capped at 500 rows).Both are registered only when
NEO4J_ENABLED=true. With the backend disabledresources/listandprompts/listreturn empty and aresources/readofgraph://neo4j/schemafails withResource not found. Theresourcesandpromptscapability flags themselves are advertised unconditionally: the FastMCP layer builds theinitializecapability object from its own server features, not from what the backends registered. The registration guard controls the lists, which is what a discovering client enumerates.Both pass through the same guardrail pipeline as the tools before any database work: the resource authorizes against
neo4j_get_schemaand the prompt againstneo4j_validate_query, and the prompt withholds the live schema unless the caller is also authorized forneo4j_get_schema. A caller denied the tool cannot read the data through the resource or the prompt, and a caller with no identity is denied outright.A guardrail denial is delivered in the body, not as a JSON-RPC error, which follows the gateway convention that handler errors are returned at HTTP 200: a denied
resources/readreturnscontents[0].textholding the{"error": "forbidden", "guardrail": "tool_authorization", ...}document, and a deniedprompts/getreturns that same document as the prompt text. A client has to inspect the payload; it gets no protocol-level signal.No other resources or prompts are registered.
Quick Start
Prerequisites
Python 3.11+
Docker and Docker Compose (for local infrastructure)
Local Development
# Clone the repository
git clone <repo-url> && cd enterprise-mcp-gateway-core
# Copy environment config
cp .env.example .env
# Start infrastructure (MongoDB + Redis)
docker compose up -d mongodb redis
# Create a virtual environment
python -m venv .venv
source .venv/bin/activate # Linux/Mac
# .venv\Scripts\Activate.ps1 # Windows PowerShell
# Install dependencies
pip install -r requirements.txt
pip install -e ".[dev]"
# Run the gateway
python src/gateway/app.py --transport=streamable-http --port=8100
# In another terminal, run tests
pytest tests/ -vDocker Compose (Full Stack)
docker compose up -d
# Gateway at http://localhost:8100
# MongoDB at localhost:27017
# Redis at localhost:6379Project Structure
enterprise-mcp-gateway-core/
src/gateway/
app.py # ASGI entry point (Starlette + FastMCP)
config/
settings.py # All env-var configuration with defaults
db.py # DocumentDB/MongoDB connection
middleware/
auth.py # AuthMiddleware (3-mode authentication)
security/
jwt_validator.py # IdP-agnostic JWT validation
consumer_registry.py # Consumer profile management
kms_envelope.py # AES-256-GCM envelope encryption
guardrails/
pipeline.py # 7-step composable security pipeline
ratelimit/
limiter.py # Two-layer sliding-window rate limiter
audit/
loggers.py # 3 audit pipelines + sanitization
backends/
jira_backend.py # Jira tools (read + write)
bre_backend.py # Business Rule Engine tools (stubs, not implemented)
neo4j_backend.py # Neo4j Knowledge Graph tools
teradata_backend.py # Teradata via MCP sidecar
tests/ # Unit test suite
docs/ # Architecture docs and whitepaper
Dockerfile # Production container image
docker-compose.yml # Local dev environment
pyproject.toml # Build configuration
requirements.txt # Pinned dependenciesEnvironment Variables
Server
Variable | Default | Description |
|
| Gateway HTTP port |
|
| Logging level |
Authentication
Variable | Default | Description |
|
| Enable JWT bearer token validation |
| (empty) | Shared secret for JWT signing/validation |
|
| Accepted JWT signing algorithms. Must stay within one key family (HMAC, RSA or EC); a mixed list is refused |
|
| Verify the |
| (empty) | Expected audience; required when |
| (empty) | Expected issuer; always required, see the fail-closed note below |
|
| Comma-separated candidate claim keys for user ID |
|
| Candidate claim keys for group membership |
|
| Require a gateway-signed token for IDE consumers |
|
| Opt-in escape hatch for the legacy auto-whitelist. Also requires |
|
| Grant a local stdio caller (no AuthMiddleware, therefore no identity) unrestricted tool and project scope. Keep false for any HTTP deployment |
|
| Kiro token expiry in hours |
The token validator is fail-closed and the configuration check runs on every
validation, independently of JWT_AUTH_ENABLED. With KIRO_RBAC_ENABLED=true
(the default) the Mode 2 gateway-signed token is validated by the same code
path, so that mode requires JWT_SECRET, JWT_ISSUER, and either
JWT_AUDIENCE or JWT_VERIFY_AUD=false. Any other combination makes the
gateway refuse to validate tokens at all. This documents today's effective
behaviour rather than changing it: validation already failed on the empty
JWT_SECRET default.
Rate Limiting
Variable | Default | Description |
|
| Sliding window duration |
|
| Global Jira calls per window |
|
| Default group limit, enforced per pod (not a global cap) |
|
| Max writes per consumer per write window |
Backends
Variable | Default | Description |
|
| Jira REST API base URL |
|
| Backend mode: |
|
| Enable Neo4j backend |
|
| Enable Teradata sidecar |
|
| Teradata sidecar URL |
Encryption
Variable | Default | Description |
|
| Enable KMS envelope encryption |
|
| AWS KMS CMK ARN or alias |
DocumentDB
Variable | Default | Description |
|
| MongoDB/DocumentDB host |
|
| Database name |
|
| Connect with TLS and full certificate validation. The DocumentDB/RDS CA bundle must be in the host trust store |
Authentication Modes
Mode 1: API Key + JWT Bearer Token
For service-to-service consumers (AI agents, REST clients):
X-MCP-API-Key: <consumer-api-key>
Authorization: Bearer <jwt-token>Mode 2: Kiro IDE RBAC (Gateway-Signed Token)
For IDE copilots authenticated via browser-based flow:
X-MCP-App-Name: kiro-username
X-MCP-Auth-Token: <gateway-signed-jwt>Mode 3: Legacy (Auto-Whitelist) - local development only, off by default
Disabled unless all three conditions hold: GATEWAY_ALLOW_INSECURE_LOCAL=true,
KIRO_RBAC_ENABLED=false, and the request arrives from a loopback peer with no
forwarding headers (X-Forwarded-For, X-Forwarded-Host, X-Real-IP,
Forwarded). The peer address is the immediate TCP peer, so in a deployment
where a reverse proxy or sidecar on the same host fronts the gateway and strips
forwarding headers, a remote caller would appear loopback - keep this flag off
anywhere behind a proxy. Any other request without a credential receives
HTTP 401.
X-MCP-App-Name: kiro-localstdio transport
AuthMiddleware only runs on the HTTP transport, so a tool call arriving over
stdio carries no caller identity. The absence of an identity is not a grant:
such calls are denied by the tool authorization guardrail (forbidden /
tool_authorization) unless GATEWAY_STDIO_TRUST_LOCAL=true is set, which
grants the local caller unrestricted tool and project scope and logs a warning
on every call.
JSON-RPC error codes
An authentication failure on the MCP JSON-RPC endpoint (/mcp and
/mcp/<...>, excluding the public /mcp/portal, /mcp/dashboard and
/mcp/api/ prefixes) is returned as HTTP 401 with a well-formed JSON-RPC 2.0
error object. The inbound request id is echoed when it is readable and is
null otherwise (batch array, notification, or an unreadable, oversized or
non-JSON body). The pre-existing machine-readable error key is preserved at
error.data.error, so consumers that already branch on it keep working.
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "An API key is required. Set the X-MCP-API-Key header.",
"data": {
"error": "unauthorized",
"message": "An API key is required. Set the X-MCP-API-Key header."
}
}
}Gateway-defined codes, from the -32000 to -32099 range the specification
reserves for server-defined errors:
Code | Condition |
|
| Authentication required: no credential presented |
|
| Authentication failed: credential presented but rejected |
|
| Authorization precondition not met |
|
-32002 overlaps a code MCP itself defines: the FastMCP layer returns
-32002 with the message Resource not found on this same /mcp endpoint,
for example on a resources/read of an unregistered URI. The gateway keeps
its three codes in one contiguous block rather than renumbering around the
overlap, so do not branch on the code alone. The discriminators for a gateway
authentication failure are the HTTP 401 status, which the protocol-level
errors never carry, and the error.data.error key.
The standard codes -32700 (parse error), -32600 (invalid request),
-32601 (method not found), -32602 (invalid params) and -32603 (internal
error) are produced by the FastMCP JSON-RPC layer, as are MCP-defined codes
such as the -32002 above, so that list is not exhaustive; the gateway does
not override any of them and does not invent codes for conditions it never
detects.
Requests to any other path keep their plain {"error": ..., "message": ...}
body: wrapping a health check or a dashboard API response in a jsonrpc
object would misrepresent the protocol in use on that path. No error body
ever carries a stack trace, exception text, token or key material.
Security Guardrails
Every tool invocation passes through 7 composable checks:
# | Guardrail | Fail Mode | Description |
0 | Tool Active | Fail-open | Registry lookup; disabled tools blocked |
1 | Tool Authorization | Fail-closed | Consumer's allowed_tools whitelist |
2 | User Write Auth | Fail-closed | AD group gate for write operations |
3 | Write Attribution | Fail-closed | Personal PAT required for writes |
4 | JQL Validation | Fail-closed | Injection pattern blocking |
5 | SQL Read-Only | Fail-closed | DDL/DML blocking for Teradata |
6 | Project Scope | Fail-closed | Consumer project whitelist |
7 | Write Rate Limit | Fail-closed | Per-consumer mutation throttle |
Testing
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=gateway --cov-report=term-missing
# Run specific test module
pytest tests/test_guardrails.py -vrequirements.txt declares starlette>=1.0,<2 and the suite is exercised
locally against starlette 1.0.0, where it reports zero warnings. A clean CI
install resolves a newer release in the same 1.x line, which emits one
StarletteDeprecationWarning from starlette.testclient about its httpx
dependency. The warning comes from the test client, not from gateway code, and
no gate turns warnings into errors, so the suite passes either way; the
zero-warning property is a property of the local pin, not of the declared
range. The range is kept deliberately: the pure-ASGI middleware is written
against stable ASGI primitives and is expected to hold across 1.x, and
pinning an upper bound the project has never needed would claim more than the
evidence supports.
License
MIT
Documentation
Technical Whitepaper - Full architecture paper with threat model and academic mapping
Architecture Baseline - Detailed discovery baseline with inventory tables
This server cannot be deployed
Maintenance
Related MCP Connectors
Zero-trust gateway for AI agents: score tool calls, verify agent cards, enforce policy, audit.
- ElaichiOAuthai.elaichi
Governed AI agents for the software your company already runs. 400+ connectors, one endpoint.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables autonomous AI agents to securely access and execute external tools, such as GitHub REST API operations, with per-user authentication, authorization, audit logging, and observability.MIT
- AlicenseNot gradedqualityBmaintenanceEnables secure exposure of internal tools to LLM clients with API-key authentication, tenant isolation, per-tool guardrails, PII scrubbing, rate limiting, audit logging, and telemetry over stdio and HTTP/SSE.MIT
- AlicenseNot gradedqualityCmaintenanceEnables secure enterprise integration by bridging SQL, financial, SCADA, and document backends to LLM agents with RBAC, DLP redaction, human-in-the-loop approvals, and OpenTelemetry tracing.MIT
- AlicenseNot gradedqualityCmaintenanceEnables LLM agents to access third-party APIs through a least-privilege set of audited tools with allowlists, per-caller scopes, label gates, write confirmation, limits, and PII-masked logging. It restricts reads to labeled data, prevents sending or destructive actions, and records every allowed or denied call.MIT