2025-11-19 22:26:28 +01:00
# 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
2025-12-01 20:46:50 +01:00
- **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
2025-11-19 22:26:28 +01:00
- **FastAPI** - Async web framework
- **Strawberry GraphQL** - Type-safe GraphQL with Python type hints
- **SQLAlchemy 2.0** - Database ORM
- **Alembic** - Database migrations
## Project Structure
```
clubber/
2025-11-21 20:38:08 +01:00
├── 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
2025-12-01 20:46:50 +01:00
│ ├── mcp_server.ts # MCP server implementation (TypeScript)
2025-11-21 20:38:08 +01:00
│ └── validation.py # Input validation logic
2025-12-01 20:46:50 +01:00
├── dist/ # Compiled TypeScript code
├── node_modules/ # Node.js dependencies
├── package.json # Node.js project configuration
├── tsconfig.json # TypeScript configuration
2025-11-21 20:38:08 +01:00
├── 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
2025-11-19 22:26:28 +01:00
```
## 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
2025-11-20 12:53:09 +01:00
# 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 } }"}'
2025-11-19 22:26:28 +01:00
```
2025-11-20 13:54:52 +01:00
## MCP Server
The MCP (Model Context Protocol) server enables AI assistants like Claude to manage society members through natural language interactions.
### What is MCP?
2025-11-21 11:15:22 +01:00
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:
2025-11-20 13:54:52 +01:00
2025-11-21 11:15:22 +01:00
**Dedicated Member Tools** (simple, focused operations):
2025-11-20 13:54:52 +01:00
- **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
2025-11-21 11:15:22 +01:00
**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
2025-12-01 20:46:50 +01:00
### Setup & Build
2025-11-20 13:54:52 +01:00
2025-12-01 20:46:50 +01:00
The MCP server connects to the GraphQL API, so ensure the API is running:
2025-11-20 13:54:52 +01:00
```bash
2025-12-01 20:46:50 +01:00
# Start the GraphQL API (Backend)
2025-11-20 13:54:52 +01:00
uv run uvicorn src.main:app --host 127.0.0.1 --port 8000
```
2025-12-01 20:46:50 +01:00
Then, install dependencies and build the MCP server:
2025-11-20 13:54:52 +01:00
```bash
2025-12-01 20:46:50 +01:00
npm install
npm run build
2025-11-20 13:54:52 +01:00
```
2025-12-01 20:46:50 +01:00
*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.*
2025-11-20 13:54:52 +01:00
### 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" : {
2025-12-01 20:46:50 +01:00
"command" : "node" ,
"args" : [ "/absolute/path/to/clubber/dist/mcp_server.js" ],
2025-11-20 13:54:52 +01:00
"env" : {
"CLUBBER_API_URL" : "http://127.0.0.1:8000/graphql"
}
}
}
}
```
2025-12-01 20:46:50 +01:00
**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.
2025-11-20 14:26:27 +01:00
2025-11-20 13:54:52 +01:00
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)
...
```
2025-11-21 11:15:22 +01:00
**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
2025-11-21 19:56:03 +01:00
## 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
2025-11-19 22:26:28 +01:00
## 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 )