# Clubber A toolset for managing members of non-profit societies (Vereine) with AI-powered administration. ## Overview Clubber provides a GraphQL API for member management combined with an MCP (Model Context Protocol) server that enables AI assistants to help with society administration tasks. **Current Status**: Early development - OpenSpec documentation and project structure in place. ## Architecture The system consists of two main components: 1. **GraphQL API** - Core backend for member data management - Built with FastAPI and Strawberry GraphQL - Database-agnostic design (SQLite for development, PostgreSQL for production) - SQLAlchemy 2.0 ORM with async support 2. **MCP Server** - AI assistant integration layer - Connects to the GraphQL API - Provides tools for AI agents to manage society operations - Service account authentication ## Tech Stack - **Python 3.11+** - Primary language - **uv** - Fast package and project manager - **FastAPI** - Async web framework - **Strawberry GraphQL** - Type-safe GraphQL with Python type hints - **SQLAlchemy 2.0** - Database ORM - **Alembic** - Database migrations ## Project Structure ``` clubber/ ├── openspec/ # Specification-driven development │ ├── project.md # Project conventions and context │ ├── specs/ # Current specifications │ └── changes/ # Change proposals ├── CLAUDE.md # AI assistant instructions └── README.md # This file ``` ## Getting Started ### Prerequisites - Python 3.11 or higher - [uv](https://github.com/astral-sh/uv) package manager ### Setup ```bash # Clone the repository git clone ssh://git@codeberg.org/gurix/clubber.git cd clubber # Install uv if not already installed curl -LsSf https://astral.sh/uv/install.sh | sh # Install dependencies uv sync # Run database migrations uv run alembic upgrade head # (Optional) Seed the database with sample data PYTHONPATH=. uv run python scripts/seed.py ``` ### Running the Server Start the development server: ```bash uv run uvicorn src.main:app --host 127.0.0.1 --port 8000 --reload ``` The `--reload` flag enables auto-restart when code changes are detected. Access the API: - **GraphQL Playground**: http://127.0.0.1:8000/graphql - **API Documentation**: http://127.0.0.1:8000/docs - **Root Endpoint**: http://127.0.0.1:8000/ ## API Usage Examples ### Member Data Model Members have the following fields: - `firstName` (required) - Member's first name - `lastName` (optional) - Member's last name - `street` (optional) - Street address - `apartmentNumber` (optional) - Apartment or unit number - `zip` (optional) - Postal code - `city` (optional) - City - `country` (optional) - Country - `email` (optional) - Email address (validated when provided) - `phone` (optional) - Phone number in E.164 format (validated when provided) **Key Feature**: Only `firstName` is required. All other fields are optional and can be populated later. Email and phone are validated only when provided (not when null/empty). ### GraphQL Queries List all members: ```graphql { members { id firstName lastName email phone city } } ``` Get a single member: ```graphql { member(id: 1) { id firstName lastName email street city country } } ``` ### GraphQL Mutations Create a member (minimal - only firstName): ```graphql mutation { createMember(input: { firstName: "Alice" }) { id firstName email } } ``` Create a member with all fields: ```graphql mutation { createMember(input: { firstName: "Bob" lastName: "Johnson" street: "123 Main St" apartmentNumber: "4B" zip: "12345" city: "Springfield" country: "USA" email: "bob.johnson@example.com" phone: "+14155551234" }) { id firstName lastName email phone } } ``` Update a member: ```graphql mutation { updateMember(input: { id: 1 email: "updated@example.com" phone: "+14155559999" }) { id firstName email phone } } ``` Delete a member: ```graphql mutation { deleteMember(id: 2) } ``` ### Using curl Query members: ```bash curl -X POST http://127.0.0.1:8000/graphql \ -H "Content-Type: application/json" \ -d '{"query": "{ members { id firstName lastName email } }"}' ``` Create a member: ```bash curl -X POST http://127.0.0.1:8000/graphql \ -H "Content-Type: application/json" \ -d '{"query": "mutation { createMember(input: { firstName: \"Charlie\" }) { id firstName } }"}' ``` ## MCP Server The MCP (Model Context Protocol) server enables AI assistants like Claude to manage society members through natural language interactions. ### What is MCP? The Model Context Protocol (MCP) is a standard protocol that allows AI assistants to use tools and access external systems. The Clubber MCP server provides 4 tools that connect to the GraphQL API: - **list_members** - List all members with their complete information - **get_member** - Get detailed information about a specific member by ID - **create_member** - Create a new member (only firstName required) - **update_member** - Update an existing member's information ### Running the MCP Server The MCP server requires the GraphQL API to be running first: ```bash # Terminal 1: Start the GraphQL API uv run uvicorn src.main:app --host 127.0.0.1 --port 8000 # Terminal 2: Run the MCP server python -m src.mcp_server ``` ### Configuration The MCP server can be configured via environment variables: ```bash # Use a custom API URL (default: http://127.0.0.1:8000/graphql) export CLUBBER_API_URL="http://localhost:3000/graphql" python -m src.mcp_server ``` ### Integrating with Claude Code To use the MCP server with Claude Code, add it to your MCP configuration: **For macOS/Linux** (`~/Library/Application Support/Claude/claude_desktop_config.json` or `~/.config/claude/config.json`): ```json { "mcpServers": { "clubber": { "command": "python", "args": ["-m", "src.mcp_server"], "cwd": "/absolute/path/to/clubber", "env": { "CLUBBER_API_URL": "http://127.0.0.1:8000/graphql" } } } } ``` After adding the configuration, restart Claude Code. You can then use natural language to manage members: - "List all members in the society" - "Create a new member named Alice" - "Update member 5's email to alice@example.com" - "Show me details for member 3" ### MCP Server Usage Examples Once configured, you can interact with the MCP server through Claude Code: **Example 1: Create a member** ``` User: Create a new member named Bob Smith with email bob@example.com Claude uses create_member tool: - firstName: "Bob" - lastName: "Smith" - email: "bob@example.com" Result: Member created with ID 1 ``` **Example 2: Update member information** ``` User: Update member 1's phone number to +41791234567 Claude uses update_member tool: - id: 1 - phone: "+41791234567" Result: Member updated successfully ``` **Example 3: List all members** ``` User: Show me all members in the society Claude uses list_members tool and displays formatted results: - Bob Smith (bob@example.com, +41791234567) - Alice Johnson (alice@example.com) ... ``` ## Development Workflow This project uses [OpenSpec](https://openspec.dev) for specification-driven development: 1. **Review existing specs** in `openspec/specs/` 2. **Create change proposals** in `openspec/changes/` before implementing features 3. **Validate proposals** with `openspec validate --strict` 4. **Implement changes** following the proposal 5. **Archive completed changes** after deployment See `openspec/AGENTS.md` for detailed workflow instructions. ### Working with Git Worktrees We use git worktrees for parallel development: ```bash # Create a worktree for a feature branch git worktree add ../clubber-feature-name feature/feature-name # Work in the worktree cd ../clubber-feature-name # Remove worktree after merge git worktree remove ../clubber-feature-name ``` ## Contributing 1. Read `openspec/project.md` for project conventions 2. For new features, create an OpenSpec proposal first 3. Follow the coding style (Black, isort, ruff) 4. Write tests for new functionality 5. Use conventional commits (`feat:`, `fix:`, `docs:`, etc.) ## License The MIT License ## Contact Markus Graf - [info@markusgraf.ch](mailto:info@markusgraf.ch) ## Acknowledgments Built with support from: - [Strawberry GraphQL](https://strawberry.rocks) - [FastAPI](https://fastapi.tiangolo.com) - [OpenSpec](https://openspec.dev)