Metadata-Version: 2.4
Name: bibliocommons-mcp-keycloak
Version: 1.2.0
Summary: MCP server for Keycloak Identity and Access Management
Author-email: Cody Lusk <cody.lusk@bibliocommons.com>
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: fastmcp[code-mode]>=3.2.4
Requires-Dist: httpx>=0.28.1
Requires-Dist: fastapi>=0.136.1
Requires-Dist: uvicorn>=0.46.0
Requires-Dist: python-multipart>=0.0.27
Provides-Extra: dev
Requires-Dist: pytest>=9.0.3; extra == "dev"
Requires-Dist: pytest-asyncio>=1.3.0; extra == "dev"
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"

# Keycloak MCP Server

MCP server for Keycloak Identity and Access Management instances. Auto-generates 401 tools from the Keycloak OpenAPI specification using `FastMCP.from_openapi()`. Includes [CodeMode](https://gofastmcp.com/servers/transforms/code-mode#code-mode) for efficient AI interaction with large tool sets, and supports dual authentication modes (OAuth2 HTTP transport for user sessions, client_credentials for stdio service accounts).

## Configuration

**Authentication**: Keycloak supports two modes depending on transport:

- **stdio (service account)**: Uses `client_credentials` or `password` grant to obtain tokens automatically
- **HTTP (user OAuth)**: Uses OIDC proxy to authenticate end-users via browser flow

### Option 1: config.json

```bash
cp config.json.example config.json
# Edit config.json with your Keycloak credentials
```

### Option 2: Environment Variables

```bash
export KEYCLOAK_HOST="keycloak.example.com"
export KEYCLOAK_REALM="master"
export KEYCLOAK_CLIENT_ID="admin-cli"
export KEYCLOAK_CLIENT_SECRET="your-client-secret"
# Or use password grant:
export KEYCLOAK_USERNAME="admin"
export KEYCLOAK_PASSWORD="admin-password"
export KEYCLOAK_VERIFY_SSL="true"
export KEYCLOAK_CA_CERT_PATH=""
export KEYCLOAK_TIMEOUT="30"
```

## Authentication Modes

### stdio Mode (Service Account)

For AI assistants connecting via stdio transport. The server acquires tokens automatically using either:

- **Client credentials grant**: Set `client_id` + `client_secret`. Best for service-to-service.
- **Password grant**: Set `username` + `password`. Falls back to `admin-cli` as client_id.

Tokens are cached and refreshed automatically before expiry.

### HTTP Mode (User OAuth Flow)

For web-based access where end-users authenticate via browser:

```bash
python -m bibliocommons_mcp_keycloak --transport http --port 8000
```

Uses OIDC proxy with your Keycloak realm's well-known configuration. Users authenticate through the standard Keycloak login page.

## Installation

### Option 1: Using uv (Recommended)

[uv](https://github.com/astral-sh/uv) is a fast Python package manager. Install it first:

```bash
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
```

No additional installation needed - `uvx` will handle dependencies automatically.

### Option 2: Using pip

```bash
pip install bibliocommons-mcp-keycloak
# or for development
pip install -e ".[dev]"
```

## AI Client Setup

### VS Code (with MCP Extension)

```json
{
  "mcp.servers": {
    "keycloak": {
      "command": "uvx",
      "args": ["--from", "/absolute/path/to/keycloak", "bibliocommons-mcp-keycloak"]
    }
  }
}
```

### Claude Desktop

Configuration file:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "keycloak": {
      "command": "uvx",
      "args": ["--from", "/absolute/path/to/keycloak", "bibliocommons-mcp-keycloak"]
    }
  }
}
```

### Kiro IDE

```json
{
  "mcpServers": {
    "keycloak": {
      "command": "uvx",
      "args": ["--from", "/absolute/path/to/keycloak", "bibliocommons-mcp-keycloak"]
    }
  }
}
```

### Kiro CLI

Create or edit `~/.kiro/settings/mcp.json`:

```json
{
  "mcpServers": {
    "keycloak": {
      "command": "uvx",
      "args": ["--from", "/absolute/path/to/keycloak", "bibliocommons-mcp-keycloak"]
    }
  }
}
```

### Configuration Notes

- Replace `/absolute/path/to/keycloak/` with the actual path to your server directory
- Add `--config /path/to/config.json` to the args array to use a specific config file
- Add `--read-only` to the args array to disable destructive tools
- After adding the configuration, restart your AI client for changes to take effect

For other AI clients (Amazon Q, GitHub Copilot, Cline, Zed, Cursor), see [docs/ai-clients.md](docs/ai-clients.md).

## Docker

```bash
# Pull from Docker Hub (private)
docker pull bibliocommons/mcp-keycloak:latest

# Run in stdio mode
docker run -i --rm \
  -v /path/to/config.json:/config.json:ro \
  bibliocommons/mcp-keycloak:latest \
  --config /config.json

# Run in HTTP mode
docker run -d --rm \
  -v /path/to/config.json:/config.json:ro \
  -p 8000:8000 \
  bibliocommons/mcp-keycloak:latest \
  --config /config.json --transport http --port 8000

# Run in expanded mode
docker run -i --rm \
  -v /path/to/config.json:/config.json:ro \
  bibliocommons/mcp-keycloak:latest \
  --config /config.json --expanded
```

## Standalone MCP Server

```bash
python -m bibliocommons_mcp_keycloak
```

### CLI Flags

| Flag | Env Var | Description |
|------|---------|-------------|
| `--config PATH` | `KEYCLOAK_CONFIG` | Path to config.json |
| `--read-only` | `KEYCLOAK_READ_ONLY` | Exclude destructive tools (POST, PUT, DELETE, PATCH) |
| `--expanded` | `KEYCLOAK_EXPANDED` | Register all tools individually instead of gateway mode |
| `--transport stdio\|http` | `KEYCLOAK_TRANSPORT` | Transport mode (default: stdio) |
| `--port PORT` | `KEYCLOAK_PORT` | HTTP port (default: 8000) |
| `--version` | — | Show version and exit |

## Gateway Mode (Default)

By default, the server exposes 2 tools instead of 401 individual tools:

| Tool | Purpose |
|------|---------|
| `keycloak_api` | Execute any Keycloak action by name with a params dict |
| `keycloak_help` | Search available actions, parameters, and descriptions |

The AI assistant calls `keycloak_help` to discover available actions, then calls `keycloak_api(action="get_users", params={"realm": "master"})` to execute them.

To register all individual tools (previous behavior), use `--expanded`:

```bash
python -m bibliocommons_mcp_keycloak --expanded
```

## Read-Only Mode

Disable all write operations (POST, PUT, DELETE, PATCH) for safe, audit-only operation:

```bash
# CLI flag
python -m bibliocommons_mcp_keycloak --read-only

# Environment variable
export KEYCLOAK_READ_ONLY=true
```

In read-only mode, only GET-based tools are exposed. All mutating operations are excluded.

## OpenAPI Spec Caching

The server downloads the Keycloak OpenAPI spec on first run and caches it locally as `openapi.json`. To use a custom cache location:

```bash
export KEYCLOAK_SPEC_CACHE="/path/to/openapi.json"
```

To refresh the cache, delete the file and restart the server.

## Security Notes

- Create a **dedicated service account** in Keycloak for MCP access with minimal required roles
- **Never commit `config.json`** with real credentials (excluded by `.gitignore`)
- Use `config.json.example` as a template
- Prefer environment variables for production and CI environments
- Use `--read-only` mode when write access is not needed
- Tokens are cached in memory only and never written to disk
- Set `verify_ssl=true` in production; only disable for development with self-signed certificates
