Initial commit: MCP Hub Community Edition v3.0.0
Community edition generated from private repo via sync pipeline. Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with ~587 tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
357
plugins/directus/handlers/users.py
Normal file
357
plugins/directus/handlers/users.py
Normal file
@@ -0,0 +1,357 @@
|
||||
"""
|
||||
Users Handler - User management
|
||||
|
||||
Phase J.2: 10 tools
|
||||
- list_users, get_user, get_current_user
|
||||
- create_user, update_user, delete_user, delete_users
|
||||
- invite_user, accept_invite, update_current_user
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from plugins.directus.client import DirectusClient
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator (10 tools)"""
|
||||
return [
|
||||
{
|
||||
"name": "list_users",
|
||||
"method_name": "list_users",
|
||||
"description": "List all users in Directus with filtering options.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"filter": {
|
||||
"anyOf": [{"type": "object"}, {"type": "null"}],
|
||||
"description": 'Filter object (e.g., {"status": {"_eq": "active"}})',
|
||||
},
|
||||
"sort": {
|
||||
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
||||
"description": "Sort fields (e.g., ['email'])",
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description": "Maximum users to return",
|
||||
"default": 100,
|
||||
},
|
||||
"offset": {"type": "integer", "description": "Users to skip", "default": 0},
|
||||
"search": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Search term",
|
||||
},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "get_user",
|
||||
"method_name": "get_user",
|
||||
"description": "Get user details by ID.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"id": {"type": "string", "description": "User UUID"}},
|
||||
"required": ["id"],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "get_current_user",
|
||||
"method_name": "get_current_user",
|
||||
"description": "Get the currently authenticated user.",
|
||||
"schema": {"type": "object", "properties": {}, "required": []},
|
||||
"scope": "read",
|
||||
},
|
||||
{
|
||||
"name": "create_user",
|
||||
"method_name": "create_user",
|
||||
"description": "Create a new user.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"description": "User email address",
|
||||
},
|
||||
"password": {"type": "string", "minLength": 8, "description": "User password"},
|
||||
"role": {"type": "string", "description": "Role UUID"},
|
||||
"first_name": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "First name",
|
||||
},
|
||||
"last_name": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Last name",
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["draft", "invited", "active", "suspended", "archived"],
|
||||
"default": "active",
|
||||
"description": "User status",
|
||||
},
|
||||
},
|
||||
"required": ["email", "password", "role"],
|
||||
},
|
||||
"scope": "admin",
|
||||
},
|
||||
{
|
||||
"name": "update_user",
|
||||
"method_name": "update_user",
|
||||
"description": "Update user details.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {"type": "string", "description": "User UUID"},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Fields to update (email, first_name, last_name, role, status, etc.)",
|
||||
},
|
||||
},
|
||||
"required": ["id", "data"],
|
||||
},
|
||||
"scope": "admin",
|
||||
},
|
||||
{
|
||||
"name": "delete_user",
|
||||
"method_name": "delete_user",
|
||||
"description": "Delete a user. This action is irreversible.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"id": {"type": "string", "description": "User UUID to delete"}},
|
||||
"required": ["id"],
|
||||
},
|
||||
"scope": "admin",
|
||||
},
|
||||
{
|
||||
"name": "delete_users",
|
||||
"method_name": "delete_users",
|
||||
"description": "Delete multiple users. This action is irreversible.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ids": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Array of user UUIDs to delete",
|
||||
}
|
||||
},
|
||||
"required": ["ids"],
|
||||
},
|
||||
"scope": "admin",
|
||||
},
|
||||
{
|
||||
"name": "invite_user",
|
||||
"method_name": "invite_user",
|
||||
"description": "Invite a user by email.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"description": "Email to invite",
|
||||
},
|
||||
"role": {"type": "string", "description": "Role UUID for the invited user"},
|
||||
"invite_url": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Custom invite URL",
|
||||
},
|
||||
},
|
||||
"required": ["email", "role"],
|
||||
},
|
||||
"scope": "admin",
|
||||
},
|
||||
{
|
||||
"name": "update_current_user",
|
||||
"method_name": "update_current_user",
|
||||
"description": "Update the currently authenticated user's profile.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Fields to update (first_name, last_name, language, theme, etc.)",
|
||||
}
|
||||
},
|
||||
"required": ["data"],
|
||||
},
|
||||
"scope": "write",
|
||||
},
|
||||
{
|
||||
"name": "get_user_role",
|
||||
"method_name": "get_user_role",
|
||||
"description": "Get the role of a specific user.",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"id": {"type": "string", "description": "User UUID"}},
|
||||
"required": ["id"],
|
||||
},
|
||||
"scope": "read",
|
||||
},
|
||||
]
|
||||
|
||||
# =====================
|
||||
# HANDLER FUNCTIONS
|
||||
# =====================
|
||||
|
||||
async def list_users(
|
||||
client: DirectusClient,
|
||||
filter: dict | None = None,
|
||||
sort: list[str] | None = None,
|
||||
limit: int = 100,
|
||||
offset: int = 0,
|
||||
search: str | None = None,
|
||||
) -> str:
|
||||
"""List users."""
|
||||
try:
|
||||
result = await client.list_users(
|
||||
filter=filter, sort=sort, limit=limit, offset=offset, search=search
|
||||
)
|
||||
users = result.get("data", [])
|
||||
return json.dumps(
|
||||
{"success": True, "total": len(users), "users": users}, indent=2, ensure_ascii=False
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def get_user(client: DirectusClient, id: str) -> str:
|
||||
"""Get user by ID."""
|
||||
try:
|
||||
result = await client.get_user(id)
|
||||
return json.dumps(
|
||||
{"success": True, "user": result.get("data")}, indent=2, ensure_ascii=False
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def get_current_user(client: DirectusClient) -> str:
|
||||
"""Get current user."""
|
||||
try:
|
||||
result = await client.get_current_user()
|
||||
return json.dumps(
|
||||
{"success": True, "user": result.get("data")}, indent=2, ensure_ascii=False
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def create_user(
|
||||
client: DirectusClient,
|
||||
email: str,
|
||||
password: str,
|
||||
role: str,
|
||||
first_name: str | None = None,
|
||||
last_name: str | None = None,
|
||||
status: str = "active",
|
||||
) -> str:
|
||||
"""Create a new user."""
|
||||
try:
|
||||
result = await client.create_user(
|
||||
email=email,
|
||||
password=password,
|
||||
role=role,
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
status=status,
|
||||
)
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"User {email} created", "user": result.get("data")},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def update_user(client: DirectusClient, id: str, data: dict[str, Any]) -> str:
|
||||
"""Update user."""
|
||||
try:
|
||||
result = await client.update_user(id, data)
|
||||
return json.dumps(
|
||||
{"success": True, "message": "User updated", "user": result.get("data")},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def delete_user(client: DirectusClient, id: str) -> str:
|
||||
"""Delete a user."""
|
||||
try:
|
||||
await client.delete_user(id)
|
||||
return json.dumps({"success": True, "message": f"User {id} deleted"}, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def delete_users(client: DirectusClient, ids: list[str]) -> str:
|
||||
"""Delete multiple users."""
|
||||
try:
|
||||
await client.delete_users(ids)
|
||||
return json.dumps({"success": True, "message": f"Deleted {len(ids)} users"}, indent=2)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def invite_user(
|
||||
client: DirectusClient, email: str, role: str, invite_url: str | None = None
|
||||
) -> str:
|
||||
"""Invite a user."""
|
||||
try:
|
||||
result = await client.invite_user(email, role, invite_url)
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"message": f"Invitation sent to {email}",
|
||||
"result": result.get("data"),
|
||||
},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def update_current_user(client: DirectusClient, data: dict[str, Any]) -> str:
|
||||
"""Update current user profile."""
|
||||
try:
|
||||
# Get current user first to get ID
|
||||
current = await client.get_current_user()
|
||||
user_id = current.get("data", {}).get("id")
|
||||
if not user_id:
|
||||
return json.dumps(
|
||||
{"success": False, "error": "Cannot determine current user"}, indent=2
|
||||
)
|
||||
|
||||
result = await client.update_user(user_id, data)
|
||||
return json.dumps(
|
||||
{"success": True, "message": "Profile updated", "user": result.get("data")},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
|
||||
async def get_user_role(client: DirectusClient, id: str) -> str:
|
||||
"""Get user's role."""
|
||||
try:
|
||||
result = await client.get_user(id)
|
||||
user = result.get("data", {})
|
||||
role_id = user.get("role")
|
||||
|
||||
if role_id:
|
||||
role_result = await client.get_role(role_id)
|
||||
return json.dumps(
|
||||
{"success": True, "user_id": id, "role": role_result.get("data")},
|
||||
indent=2,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
else:
|
||||
return json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"user_id": id,
|
||||
"role": None,
|
||||
"message": "User has no role assigned",
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
except Exception as e:
|
||||
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
||||
Reference in New Issue
Block a user