Add two new MCP tools for flexible GraphQL operations: - get_graphql_schema: Expose complete schema via introspection - execute_graphql_query: Execute arbitrary queries and mutations These tools complement existing dedicated member tools by enabling AI agents to: - Discover the GraphQL schema dynamically - Construct custom queries with specific field selection - Handle complex queries without requiring new dedicated tools - Work with query variables for parameterized operations The implementation reuses the existing GraphQLClient class and adds proper error handling for API unavailability and validation errors. Schema introspection is formatted as human-readable text while query results are returned as formatted JSON. Updated README.md with: - Documentation for both new tools - Usage examples for schema discovery and query execution - Guidance on when to use general vs dedicated tools 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
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:
-
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
-
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 package manager
Setup
# 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:
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 namelastName(optional) - Member's last namestreet(optional) - Street addressapartmentNumber(optional) - Apartment or unit numberzip(optional) - Postal codecity(optional) - Citycountry(optional) - Countryemail(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:
{
members {
id
firstName
lastName
email
phone
city
}
}
Get a single member:
{
member(id: 1) {
id
firstName
lastName
email
street
city
country
}
}
GraphQL Mutations
Create a member (minimal - only firstName):
mutation {
createMember(input: {
firstName: "Alice"
}) {
id
firstName
email
}
}
Create a member with all fields:
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:
mutation {
updateMember(input: {
id: 1
email: "updated@example.com"
phone: "+14155559999"
}) {
id
firstName
email
phone
}
}
Delete a member:
mutation {
deleteMember(id: 2)
}
Using curl
Query members:
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:
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:
# 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:
# 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):
{
"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
Development Workflow
This project uses OpenSpec for specification-driven development:
- Review existing specs in
openspec/specs/ - Create change proposals in
openspec/changes/before implementing features - Validate proposals with
openspec validate --strict - Implement changes following the proposal
- Archive completed changes after deployment
See openspec/AGENTS.md for detailed workflow instructions.
Working with Git Worktrees
We use git worktrees for parallel development:
# 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
- Read
openspec/project.mdfor project conventions - For new features, create an OpenSpec proposal first
- Follow the coding style (Black, isort, ruff)
- Write tests for new functionality
- Use conventional commits (
feat:,fix:,docs:, etc.)
License
The MIT License
Contact
Markus Graf - info@markusgraf.ch
Acknowledgments
Built with support from: