MCP Proxy
by MikaBeee
README.md
# MCP Proxy
A self-hosted proxy that gives each user their own credentials for upstream MCP (Model Context Protocol) servers. Users connect services like GitHub and Confluence by visiting a one-time secure link, the LLM never sees the credentials.
## How it works
```
OpenWebUI → MCP Proxy → Upstream MCP servers → GitHub / Confluence / ...
(per-user (user's own
credential credentials
injection) forwarded)
```
The proxy sits between OpenWebUI and upstream MCP servers. When the LLM calls a tool, the proxy intercepts the request, looks up the calling user's stored credential, and forwards the call to the upstream server with that credential attached. Each user's keys are stored encrypted with two independent layers (AES-256-GCM with a master key + a per-user key derived from the JWT secret), so a single compromised secret is not enough to decrypt stored credentials.
For a full explanation see [docs/how-it-works.md](docs/how-it-works.md).
## Quick start
### Prerequisites
- Docker and Docker Compose
- A running OpenWebUI instance
- A running Redis instance
There are two compose files depending on your setup:
| File | Use when |
|---|---|
| `docker-compose.yaml` | You already run GitHub MCP and Confluence MCP separately, just deploys the proxy |
| `dev-stack-proxy/docker-compose.yaml` | Full local stack, deploys proxy + GitHub MCP + Confluence MCP together |
### 1. Configure environment
```bash
cp .env.example .env
```
Edit `.env` and fill in all values. Required:
| Variable | Description |
|---|---|
| `WEBUI_SECRET_KEY` | Must match your OpenWebUI `WEBUI_SECRET_KEY` |
| `MASTER_ENCRYPTION_KEY` | Base64-encoded 32-byte AES key - see instruction below |
| `REDIS_URL_PROXY` | Redis connection string, e.g. `redis://localhost:6379/2` |
| `PROXY_BASE_URL` | Public-facing URL of this proxy, no trailing slash |
| `MCP_SERVERS` | JSON map of service name to upstream MCP URL, e.g. `{"github":"http://github-mcp:8082/mcp","confluence":"http://confluence-mcp:8000/mcp"}`|
Generate a master encryption key:
```bash
python -c "import os,base64; print(base64.b64encode(os.urandom(32)).decode())"
```
### 2. Start the proxy
```bash
docker compose up -d
```
Or with the full dev stack (includes GitHub MCP and Confluence MCP):
```bash
docker compose -f dev-stack-proxy/docker-compose.yaml up -d
```
### 3. Register in OpenWebUI
Settings → Connections → MCP Servers → Add:
```
TYPE: MCP Streamable HTTP
URL: http://localhost:8000/mcp
AUTH: Session (Will forward user JWT(cookie) to the proxy so it knows who its talking to)
```
If OpenWebUI runs in Docker on the same host, use `http://host.docker.internal:8000/mcp` or the container name.
## Connecting services
Users connect services through the chat interface - no admin action needed per user.
### GitHub
1. In chat: *"Connect my GitHub account"*
2. Open the one-time link the assistant provides
3. Paste a GitHub PAT (Personal Access Token) with `repo` scope
4. Return to chat
For GitHub Enterprise, set `GITHUB_HOST` in `.env` to your enterprise hostname.
### Confluence
1. In chat: *"Connect my Confluence account"*
2. Open the one-time link
3. Enter your Atlassian email and an API token (generate at `https://id.atlassian.com/manage-profile/security/api-tokens`)
4. Return to chat
Requires `CONFLUENCE_URL` and `ATLASSIAN_OAUTH_CLOUD_ID` in `.env`. See [docs/atlassian-oauth-setup.md](docs/atlassian-oauth-setup.md).
Once connected, the LLM can search pages, read content, create and update pages, and more - full tool execution works end-to-end using Basic auth with the user's API token.
### Adding any other MCP server
Add the service to `MCP_SERVERS` in `.env` and a container entry to `docker-compose.yaml`. Tool discovery is automatic - no code changes needed for services that use standard `Bearer` auth.
See [docs/configuration.md](docs/configuration.md) for auth header scheme configuration.
## Available tools (exposed to the LLM)
| Tool | Description |
|---|---|
| `get_auth_url` | Generate a one-time link to connect a service |
| `list_available_tools` | List all tools from authenticated upstream services |
| `call_tool` | Call a tool on an upstream service |
| `list_authenticated_services` | Show which services the user has connected |
| `revoke_service` | Remove a stored credential |
## Multiple credentials per service
Users can store multiple credentials for the same service using aliases, useful for different GitHub organisations or scoped tokens:
```
"Connect my GitHub account for org my org"
→ get_auth_url('github', alias='my org')
"Connect my GitHub personal account"
→ get_auth_url('github', alias='')
```
The LLM uses the alias when calling tools:
```
call_tool('list_repositories', {}, alias='my org')
```
## Security
- Credentials are encrypted at rest with two independent AES-256-GCM layers
- Outer layer: `MASTER_ENCRYPTION_KEY`
- Inner layer: HKDF-derived from `WEBUI_SECRET_KEY` + `user_id`
- Auth links are one-time use with a 5-minute TTL
- User identity is verified on every request via JWT signed with `WEBUI_SECRET_KEY`
- Credentials expire after a user-selected TTL (30 / 90 / 180 / 365 days)
## Documentation
| File | Contents |
|---|---|
| [docs/how-it-works.md](docs/technical/how-it-works.md) | Conceptual and technical explanation |
| [docs/architecture.md](docs/technical/architecture.md) | Request flow, component design, design decisions |
| [docs/getting-started.md](docs/getting-started.md) | Operator setup guide |
| [docs/configuration.md](docs/configuration.md) | All environment variables |
| [docs/tools-reference.md](docs/tools-reference.md) | LLM-facing tools reference |
| [docs/atlassian-oauth-setup.md](docs/atlassian-oauth-setup.md) | Confluence Cloud setup |
## Project structure
```
MCP_Proxy/
├── main.py # FastMCP app, tool definitions, routing
├── auth.py # JWT verification
├── auth_flow.py # One-time auth URL and credential form
├── credentials.py # Encrypted credential storage (Redis)
├── registry.py # Upstream MCP server discovery and tool dispatch
├── Dockerfile
├── docker-compose.yaml # Proxy only - bring your own upstream MCP servers
├── .env.example
├── dev-stack-proxy/
│ ├── docker-compose.yaml # Full stack - proxy + GitHub MCP + Confluence MCP
│ └── .env.example
└── docs/
```
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues