# Directory Structure
```
├── .github
│ └── workflows
│ └── npm-publish.yml
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
.idea/
node_modules/
dist/
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Keycloak MCP Server
[](https://smithery.ai/server/keycloak-model-context-protocol)
A Model Context Protocol server for Keycloak administration, providing tools to manage users and realms.
## Features
- Create new users in specific realms
- Delete users from realms
- List available realms
- List users in specific realms
## Installation
### Installing via Smithery
To install Keycloak for Claude Desktop automatically via [Smithery](https://smithery.ai/server/keycloak-model-context-protocol):
```bash
npx -y @smithery/cli install keycloak-model-context-protocol --client claude
```
### Via NPM (Recommended)
The server is available as an NPM package:
```bash
# Direct usage with npx
npx -y keycloak-model-context-protocol
# Or global installation
npm install -g keycloak-model-context-protocol
```
### Local Development Setup
If you want to develop or modify the server:
```bash
git clone <repository-url>
cd keycloak-model-context-protocol
npm install
npm run build
```
## Configuration
### Using NPM Package (Recommended)
Configure the server in your Claude Desktop configuration file:
```json
{
"mcpServers": {
"keycloak": {
"command": "npx",
"args": ["-y", "keycloak-model-context-protocol"],
"env": {
"KEYCLOAK_URL": "http://localhost:8080",
"KEYCLOAK_ADMIN": "admin",
"KEYCLOAK_ADMIN_PASSWORD": "admin"
}
}
}
}
```
### For Local Development
```json
{
"mcpServers": {
"keycloak": {
"command": "node",
"args": ["path/to/dist/index.js"],
"env": {
"KEYCLOAK_URL": "http://localhost:8080",
"KEYCLOAK_ADMIN": "admin",
"KEYCLOAK_ADMIN_PASSWORD": "admin"
}
}
}
}
```
## Available Tools
### create-user
Creates a new user in a specified realm.
**Inputs**:
- `realm`: The realm name
- `username`: Username for the new user
- `email`: Email address for the user
- `firstName`: User's first name
- `lastName`: User's last name
### delete-user
Deletes a user from a specified realm.
**Inputs**:
- `realm`: The realm name
- `userId`: The ID of the user to delete
### list-realms
Lists all available realms.
### list-users
Lists all users in a specified realm.
**Inputs**:
- `realm`: The realm name
## Development
```bash
npm run watch
```
## Testing
To test the server using MCP Inspector:
```bash
npx -y @modelcontextprotocol/inspector npx -y keycloak-model-context-protocol
```
## Deployment
### NPM Package
This project is automatically published to [NPM](https://www.npmjs.com/package/keycloak-model-context-protocol) via GitHub Actions when a new release is published on GitHub.
#### Setup Requirements for Deployment
1. Create NPM account and get access token
2. Add NPM_TOKEN secret to GitHub repository
- Go to repository Settings > Secrets
- Add new secret named `NPM_TOKEN`
- Paste your NPM access token as the value
## Prerequisites
- Node.js 18 or higher
- Running Keycloak instance
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "keycloak-model-context-protocol",
"version": "0.0.2",
"description": "MCP server for Keycloak administration",
"license": "MIT",
"author": "Christoph Englisch",
"type": "module",
"bin": {
"keycloak-model-context-protocol": "./dist/index.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && shx chmod +x dist/*.js",
"prepare": "npm run build",
"watch": "tsc --watch"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.5.0",
"@keycloak/keycloak-admin-client": "^22.0.5",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/node": "^22",
"shx": "^0.3.4",
"typescript": "^5.3.3"
}
}
```
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
```yaml
name: NPM Publish
on:
push:
branches:
- main
pull_request:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build package
run: npm run build
publish:
runs-on: ubuntu-latest
needs: [build]
if: github.event_name == 'release'
environment: release
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
registry-url: "https://registry.npmjs.org"
- name: Install dependencies
run: npm ci
- name: Publish package
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import KcAdminClient from '@keycloak/keycloak-admin-client';
import { z } from 'zod';
const server = new Server(
{
name: "keycloak-admin",
version: "0.0.1",
},
{
capabilities: {
tools: {},
},
}
);
// Initialize Keycloak client
const kcAdminClient = new KcAdminClient({
baseUrl: process.env.KEYCLOAK_URL || 'http://localhost:8080',
realmName: 'master'
});
// Tool schemas
const CreateUserSchema = z.object({
realm: z.string(),
username: z.string(),
email: z.string().email(),
firstName: z.string(),
lastName: z.string()
});
const DeleteUserSchema = z.object({
realm: z.string(),
userId: z.string()
});
const ListUsersSchema = z.object({
realm: z.string()
});
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "create-user",
description: "Create a new user in a specific realm",
inputSchema: {
type: "object",
properties: {
realm: { type: "string" },
username: { type: "string" },
email: { type: "string", format: "email" },
firstName: { type: "string" },
lastName: { type: "string" }
},
required: ["realm", "username", "email", "firstName", "lastName"]
}
},
{
name: "delete-user",
description: "Delete a user from a specific realm",
inputSchema: {
type: "object",
properties: {
realm: { type: "string" },
userId: { type: "string" }
},
required: ["realm", "userId"]
}
},
{
name: "list-realms",
description: "List all available realms",
inputSchema: {
type: "object",
properties: {},
required: []
}
},
{
name: "list-users",
description: "List users in a specific realm",
inputSchema: {
type: "object",
properties: {
realm: { type: "string" }
},
required: ["realm"]
}
}
]
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
// Authenticate before each request
await kcAdminClient.auth({
username: process.env.KEYCLOAK_ADMIN || 'admin',
password: process.env.KEYCLOAK_ADMIN_PASSWORD || 'admin',
grantType: 'password',
clientId: 'admin-cli',
});
const { name, arguments: args } = request.params;
try {
switch (name) {
case "create-user": {
const { realm, username, email, firstName, lastName } = CreateUserSchema.parse(args);
kcAdminClient.setConfig({
realmName: realm
});
const user = await kcAdminClient.users.create({
realm,
username,
email,
firstName,
lastName,
enabled: true
});
return {
content: [{
type: "text",
text: `User created successfully. User ID: ${user.id}`
}]
};
}
case "delete-user": {
const { realm, userId } = DeleteUserSchema.parse(args);
kcAdminClient.setConfig({
realmName: realm
});
await kcAdminClient.users.del({
id: userId,
realm
});
return {
content: [{
type: "text",
text: `User ${userId} deleted successfully from realm ${realm}`
}]
};
}
case "list-realms": {
const realms = await kcAdminClient.realms.find();
return {
content: [{
type: "text",
text: `Available realms:\n${realms.map(r => `- ${r.realm}`).join('\n')}`
}]
};
}
case "list-users": {
const { realm } = ListUsersSchema.parse(args);
kcAdminClient.setConfig({
realmName: realm
});
const users = await kcAdminClient.users.find();
return {
content: [{
type: "text",
text: `Users in realm ${realm}:\n${users.map(u => `- ${u.username} (${u.id})`).join('\n')}`
}]
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
if (error instanceof z.ZodError) {
return {
isError: true,
content: [{
type: "text",
text: `Invalid arguments: ${error.errors.map(e => `${e.path.join(".")}: ${e.message}`).join(", ")}`
}]
};
}
throw error;
}
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Keycloak MCP Server running on stdio");
```