# 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 for GraphQL API - **TypeScript** - Language for MCP Server - **Node.js** - Runtime for MCP Server - **uv** - Fast package and project manager for Python - **npm** - Package manager for TypeScript - **FastAPI** - Async web framework - **Strawberry GraphQL** - Type-safe GraphQL with Python type hints - **SQLAlchemy 2.0** - Database ORM - **Alembic** - Database migrations ## Project Structure ``` clubber/ ├── src/ # Application source code │ ├── models/ # SQLAlchemy database models │ ├── resolvers/ # GraphQL resolvers (queries and mutations) │ ├── schemas/ # GraphQL schema definitions │ ├── config.py # Application configuration │ ├── database.py # Database setup and session management │ ├── main.py # FastAPI application entry point │ ├── mcp_server.ts # MCP server implementation (TypeScript) │ └── validation.py # Input validation logic ├── dist/ # Compiled TypeScript code ├── node_modules/ # Node.js dependencies ├── package.json # Node.js project configuration ├── tsconfig.json # TypeScript configuration ├── tests/ # Test suite │ ├── unit/ # Unit tests (validation logic) │ ├── integration/ # Integration tests (database, resolvers) │ ├── e2e/ # End-to-end tests (API flows) │ ├── mcp/ # MCP server tests │ └── conftest.py # Shared test fixtures ├── migrations/ # Alembic database migrations │ └── versions/ # Migration version files ├── scripts/ # Utility scripts │ └── seed.py # Database seeding script ├── openspec/ # Specification-driven development │ ├── specs/ # Current specifications │ │ ├── database-layer/ │ │ ├── graphql-api/ │ │ ├── mcp-integration/ │ │ ├── member-crud/ │ │ ├── project-documentation/ │ │ └── project-setup/ │ └── changes/ # Change proposals │ └── archive/ # Archived completed changes ├── pyproject.toml # Project dependencies and configuration ├── alembic.ini # Alembic migration configuration ├── uv.lock # Locked dependency versions ├── 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 6 tools that connect to the GraphQL API: **Dedicated Member Tools** (simple, focused operations): - **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 **General GraphQL Tools** (flexible, for complex queries): - **get_graphql_schema** - Get the complete GraphQL schema via introspection - **execute_graphql_query** - Execute arbitrary GraphQL queries and mutations ### Setup & Build The MCP server connects to the GraphQL API, so ensure the API is running: ```bash # Start the GraphQL API (Backend) uv run uvicorn src.main:app --host 127.0.0.1 --port 8000 ``` Then, install dependencies and build the MCP server: ```bash npm install npm run build ``` *Note: You don't need to run the MCP server manually in a separate terminal. It will be started automatically by your AI client (Claude) based on the configuration below.* ### 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": "node", "args": ["/absolute/path/to/clubber/dist/mcp_server.js"], "env": { "CLUBBER_API_URL": "http://127.0.0.1:8000/graphql" } } } } ``` **Configuration Options**: - `CLUBBER_API_URL`: URL of the GraphQL API (default: `http://127.0.0.1:8000/graphql`) **Note**: Make sure to provide the absolute path to the `dist/mcp_server.js` file. 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) ... ``` **Example 4: Discover GraphQL schema** ``` User: What fields are available in the Member type? Claude uses get_graphql_schema tool to discover: - The complete schema structure - All available types (Query, Mutation, Member, etc.) - Field definitions with types and descriptions - Available queries and mutations Result: Shows Member type with all fields (id, firstName, lastName, email, etc.) ``` **Example 5: Execute custom GraphQL query** ``` User: Get only the first names and emails of all members Claude uses execute_graphql_query tool with: query: | query { members { firstName email } } Result: Returns JSON with only the requested fields ``` **Example 6: Execute query with variables** ``` User: Get member 5's contact information Claude uses execute_graphql_query tool with: query: | query GetMember($id: Int!) { member(id: $id) { firstName lastName email phone city } } variables: {"id": 5} Result: Returns member 5's contact details ``` ### When to Use Which Tools **Use dedicated member tools** when: - Performing simple, common operations - The tool matches your exact need - You want a formatted, human-readable response **Use general GraphQL tools** when: - Exploring what's possible (use `get_graphql_schema`) - Needing custom field selection - Combining multiple operations - Working with complex queries or filters (future) - You need the raw JSON response ## Running Tests This project uses pytest for automated testing. All tests are organized in the `tests/` directory. ### Run All Tests ```bash uv run pytest ``` ### Run Specific Test Categories ```bash # Unit tests only (validation logic) uv run pytest tests/unit # Integration tests only (database and resolvers) uv run pytest tests/integration # E2E tests only (complete API flows) uv run pytest tests/e2e # MCP server tests only uv run pytest tests/mcp ``` ### Run Tests with Verbose Output ```bash uv run pytest --verbose ``` ### Run Tests with Coverage ```bash uv run pytest --cov=src --cov-report=term-missing ``` ## Test Organization The test suite is organized into four categories: ### Unit Tests (`tests/unit/`) Pure logic tests for validation functions and utilities. - **`test_validation.py`** - Email, phone, and firstName validation ### Integration Tests (`tests/integration/`) Tests for database operations and GraphQL resolvers. - **`test_member_model.py`** - SQLAlchemy Member model - **`test_member_queries.py`** - GraphQL queries (member, members) - **`test_member_mutations.py`** - GraphQL mutations (create, update, delete) ### E2E Tests (`tests/e2e/`) Complete API request/response flows over HTTP. - **`test_graphql_api.py`** - GraphQL API endpoints, introspection ### MCP Tests (`tests/mcp/`) MCP server tools and functionality. - **`test_mcp_server.py`** - All 6 MCP tools (list_members, get_member, create_member, update_member, get_graphql_schema, execute_graphql_query) ### Test Fixtures Shared fixtures are defined in `tests/conftest.py`: - **`test_engine`** - In-memory SQLite database - **`async_session`** - Async database session - **`graphql_client`** - FastAPI test client for GraphQL ## 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)