Expand the project structure section to include all directories and files with detailed descriptions. The updated structure now shows: - Complete src/ layout with all modules - Test suite organization (unit, integration, e2e, mcp) - Migration and script directories - OpenSpec structure with all spec categories - Configuration files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
526 lines
14 KiB
Markdown
526 lines
14 KiB
Markdown
# 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/
|
|
├── 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.py # MCP server implementation
|
|
│ └── validation.py # Input validation logic
|
|
├── 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
|
|
|
|
### 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": "uv",
|
|
"args": ["run", "python", "-m", "src.mcp_server"],
|
|
"env": {
|
|
"CLUBBER_API_URL": "http://127.0.0.1:8000/graphql"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Note**: The `uv run` command ensures the virtual environment and dependencies are properly activated. The MCP server will use the current working directory where Claude Code is running, so make sure to open Claude Code from the clubber project directory.
|
|
|
|
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)
|