From 6d1af46ce6fad909fff56c70039b13e2ed0e84ea Mon Sep 17 00:00:00 2001 From: insleker Date: Mon, 19 Jan 2026 13:52:05 +0800 Subject: [PATCH] feat: add MCP function to list_user --- .claude/skills/list_user/SKILL.md | 81 ++++++++ .claude/skills/list_user/mcp_server.py | 244 +++++++++++++++++++++++++ .claude/skills/list_user/test_mcp.py | 68 +++++++ pyproject.toml | 9 +- 4 files changed, 400 insertions(+), 2 deletions(-) create mode 100644 .claude/skills/list_user/mcp_server.py create mode 100644 .claude/skills/list_user/test_mcp.py diff --git a/.claude/skills/list_user/SKILL.md b/.claude/skills/list_user/SKILL.md index 6c9a6b3..8cf76ff 100644 --- a/.claude/skills/list_user/SKILL.md +++ b/.claude/skills/list_user/SKILL.md @@ -28,6 +28,8 @@ Use this skill when: - **User Validation**: Check if users exist and identify missing entries - **Filtering**: Query users by team or department - **Reloadable**: Update mappings without restarting the application +- **MCP Integration**: Share user data with other AI systems via Model Context Protocol +- **Multi-Interface**: Access as Python library OR via MCP server ## File Structure @@ -301,8 +303,75 @@ except ValueError: print("User mapping file has invalid JSON!") ``` +## MCP Server Integration + +### Overview + +The skill includes an MCP (Model Context Protocol) server that exposes user management functionality to other AI systems. This allows seamless sharing of user data across multiple Claude instances and AI tools. + +### Quick Start + +```bash +# Install MCP SDK +pip install mcp +# or +uv add mcp + +# Run the MCP server +python mcp_server.py +``` + +### Configure for Claude Desktop + +Add to `~/.claude/claude.json`: + +```json +{ + "mcpServers": { + "user-manager": { + "command": "python", + "args": ["/absolute/path/to/.claude/skills/list_user/mcp_server.py"], + "type": "stdio" + } + } +} +``` + +### MCP Tools Available + +The server exposes 7 tools: + +1. **`get_formal_name`** - Map a single nickname to formal name +2. **`map_names`** - Map multiple nicknames in batch +3. **`get_user_info`** - Get complete user record +4. **`get_users_by_team`** - Find all users in a team +5. **`get_users_by_department`** - Find all users in a department +6. **`validate_users`** - Check if users exist +7. **`list_all_users`** - Get all users in system + +### Example MCP Usage + +Once configured, other AI systems can use the tools: + +``` +You: "What's the formal name for ben sung and get their team info?" +AI: [Uses get_user_info tool via MCP] +AI: "Ben sung's formal name is 宋柏昆 and they are in the 智能控制組 team." +``` + +### Full Documentation + +See [MCP_README.md](MCP_README.md) for: +- Detailed tool specifications +- Response formats +- Configuration options +- Integration examples +- Troubleshooting + ## Testing +### Test the Python Module + Run the module directly to test basic functionality: ```bash @@ -315,6 +384,16 @@ This will display: - User information retrieval - Team filtering +### Test the MCP Server + +```bash +# Run the server (will wait for client connections) +python mcp_server.py + +# In another terminal, test with curl or MCP client +# The server communicates via stdio transport +``` + ## Future Enhancements Potential improvements: @@ -325,6 +404,8 @@ Potential improvements: - User status tracking (active, inactive, etc.) - Export/import utilities for user data - Audit logging for user lookup operations +- HTTP REST API wrapper for web integration +- User management tools (add, update, delete) in MCP server ## License diff --git a/.claude/skills/list_user/mcp_server.py b/.claude/skills/list_user/mcp_server.py new file mode 100644 index 0000000..33eb239 --- /dev/null +++ b/.claude/skills/list_user/mcp_server.py @@ -0,0 +1,244 @@ +""" +MCP Server for User Manager + +Exposes user management and name mapping functionality via Model Context Protocol. +Allows other AI systems to query and manage user information. + +Usage: + python mcp_server.py + +Environment: + Communicates via stdio transport. +""" + +import sys +from typing import Any + +# Try importing from the installed MCP SDK +try: + from mcp.server import Server + from mcp.server.stdio import stdio_server + MCP_AVAILABLE = True +except ImportError as e: + MCP_AVAILABLE = False + MCP_ERROR = str(e) + +from user_manager import UserManager + + +def create_mcp_server() -> Any: + """ + Create and configure the MCP server with user management tools. + + Returns: + Server instance if available, otherwise None + """ + if not MCP_AVAILABLE: + return None + + # Initialize MCP server + server = Server(name="user-manager") + + # Initialize user manager + try: + user_manager = UserManager() + except Exception as e: + print(f"Error initializing UserManager: {e}", file=sys.stderr) + raise + + # ============================================================================ + # Tool 1: Get Formal Name + # ============================================================================ + @server.call_tool() + def get_formal_name(nickname: str, fallback: str = "keep_original") -> dict: + """Get the formal name for a given nickname.""" + try: + formal_name = user_manager.get_formal_name(nickname, fallback=fallback) + return { + "success": True, + "nickname": nickname, + "formal_name": formal_name, + "message": f"Successfully mapped '{nickname}' to '{formal_name}'" + } + except ValueError as e: + return { + "success": False, + "nickname": nickname, + "error": str(e), + "message": f"Failed to map nickname: {e}" + } + + # ============================================================================ + # Tool 2: Map Multiple Names + # ============================================================================ + @server.call_tool() + def map_names(nicknames: list, fallback: str = "keep_original") -> dict: + """Map multiple nicknames to their formal names.""" + try: + mapped = user_manager.map_names(nicknames, fallback=fallback) + return { + "success": True, + "input_count": len(nicknames), + "mappings": [ + {"nickname": nick, "formal_name": formal} + for nick, formal in zip(nicknames, mapped) + ], + "message": f"Successfully mapped {len(nicknames)} nickname(s)" + } + except Exception as e: + return { + "success": False, + "error": str(e), + "message": f"Failed to map names: {e}" + } + + # ============================================================================ + # Tool 3: Get User Information + # ============================================================================ + @server.call_tool() + def get_user_info(nickname: str) -> dict: + """Get complete user information.""" + user_info = user_manager.get_user_info(nickname) + + if user_info: + return { + "success": True, + "user": user_info, + "message": f"Found user: {user_info.get('formal_name', 'Unknown')}" + } + else: + return { + "success": False, + "nickname": nickname, + "error": f"User not found: {nickname}", + "message": f"No user found with nickname '{nickname}'" + } + + # ============================================================================ + # Tool 4: Get Users by Team + # ============================================================================ + @server.call_tool() + def get_users_by_team(team: str) -> dict: + """Get all users in a specific team.""" + try: + users = user_manager.get_users_by_team(team) + return { + "success": True, + "team": team, + "user_count": len(users), + "users": users, + "message": f"Found {len(users)} user(s) in team '{team}'" + } + except Exception as e: + return { + "success": False, + "team": team, + "error": str(e), + "message": f"Failed to retrieve team members: {e}" + } + + # ============================================================================ + # Tool 5: Get Users by Department + # ============================================================================ + @server.call_tool() + def get_users_by_department(department: str) -> dict: + """Get all users in a specific department.""" + try: + users = user_manager.get_users_by_department(department) + return { + "success": True, + "department": department, + "user_count": len(users), + "users": users, + "message": f"Found {len(users)} user(s) in department '{department}'" + } + except Exception as e: + return { + "success": False, + "department": department, + "error": str(e), + "message": f"Failed to retrieve department users: {e}" + } + + # ============================================================================ + # Tool 6: Validate Users + # ============================================================================ + @server.call_tool() + def validate_users(nicknames: list) -> dict: + """Validate that nicknames exist in the user mapping.""" + try: + found, missing = user_manager.validate_users(nicknames) + return { + "success": True, + "total": len(nicknames), + "found_count": len(found), + "missing_count": len(missing), + "found": found, + "missing": missing, + "message": f"Validation complete: {len(found)} found, {len(missing)} missing" + } + except Exception as e: + return { + "success": False, + "error": str(e), + "message": f"Validation failed: {e}" + } + + # ============================================================================ + # Tool 7: List All Users + # ============================================================================ + @server.call_tool() + def list_all_users() -> dict: + """Get all users in the system.""" + try: + users = user_manager.get_all_users() + return { + "success": True, + "user_count": len(users), + "users": users, + "message": f"Retrieved {len(users)} user(s) from the system" + } + except Exception as e: + return { + "success": False, + "error": str(e), + "message": f"Failed to retrieve user list: {e}" + } + + return server + + +def main(): + """Run the MCP server.""" + if not MCP_AVAILABLE: + print( + "ERROR: MCP SDK is not installed. Install with:\n" + " pip install mcp\n" + "or\n" + " uv add mcp", + file=sys.stderr + ) + print(f"Installation error: {MCP_ERROR}", file=sys.stderr) + sys.exit(1) + + try: + server = create_mcp_server() + if server: + print("Starting User Manager MCP Server...", file=sys.stderr) + with stdio_server(server) as session: + session.wait() + else: + print("Failed to create MCP server", file=sys.stderr) + sys.exit(1) + except KeyboardInterrupt: + print("\nServer stopped", file=sys.stderr) + sys.exit(0) + except Exception as e: + print(f"Server error: {e}", file=sys.stderr) + import traceback + traceback.print_exc() + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.claude/skills/list_user/test_mcp.py b/.claude/skills/list_user/test_mcp.py new file mode 100644 index 0000000..6036dc7 --- /dev/null +++ b/.claude/skills/list_user/test_mcp.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +""" +Quick test to verify MCP server initialization +""" + +import sys +import os + +# Add list_user to path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +try: + from mcp_server import create_mcp_server + from user_manager import UserManager + + print("✓ Successfully imported MCP server and UserManager") + print() + + # Test UserManager + print("Testing UserManager:") + print("-" * 60) + manager = UserManager() + print(f"✓ UserManager initialized with {len(manager.users)} users") + + # Test name mapping + test_name = "ben sung" + formal = manager.get_formal_name(test_name) + print(f"✓ Name mapping: '{test_name}' → '{formal}'") + print() + + # Test MCP server creation + print("Testing MCP Server:") + print("-" * 60) + try: + server = create_mcp_server() + if server: + print("✓ MCP server created successfully") + print("✓ Server is ready to accept connections on stdio transport") + print() + print("Available Tools:") + # List tools by checking method names + tool_methods = ['get_formal_name', 'map_names', 'get_user_info', + 'get_users_by_team', 'get_users_by_department', + 'validate_users', 'list_all_users'] + for tool in tool_methods: + print(f" - {tool}") + else: + print("✗ Failed to create MCP server - returned None") + sys.exit(1) + except Exception as mcp_error: + print(f"✗ MCP server creation error: {mcp_error}") + import traceback + traceback.print_exc() + sys.exit(1) + + print() + print("=" * 60) + print("✓ All tests passed!") + print("=" * 60) + print() + print("To run the MCP server:") + print(" python mcp_server.py") + +except Exception as e: + print(f"✗ Error: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/pyproject.toml b/pyproject.toml index 1d77c6f..0073a6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,10 +4,15 @@ license = { text = "MIT" } name = "report-skill-expm" readme = "README.md" - requires-python = ">=3.9" + requires-python = ">=3.10" version = "1.0.0" - dependencies = ["openpyxl>=3.1.0", "pandas>=2.0.0", "xlrd>=2.0.0"] + dependencies = [ + "openpyxl>=3.1.0", + "pandas>=2.0.0", + "xlrd>=2.0.0", + "mcp>=0.1.0", + ] [project.optional-dependencies] dev = ["pytest>=7.0.0", "black>=23.0.0", "flake8>=6.0.0"]