Skip to content

Commit 98cfb3c

Browse files
committed
refactor: restructure as proper Python package with type-safe configuration
- Moved server.py to src/server.py and created proper package structure - Added src/__init__.py and src/__main__.py for module execution - Implemented type-safe configuration management with pydantic-settings - Added comprehensive logging with uvicorn logger integration - Updated Dockerfile to use new package structure and non-root user - Enhanced HTTP client configuration with timeout and HTTP/2 support - Fixed httpx.Timeout configuration to include all required parameters - Added /ping health check endpoint - Updated documentation and development setup instructions - Improved .gitignore with comprehensive Python exclusions
1 parent b2c1838 commit 98cfb3c

File tree

11 files changed

+422
-823
lines changed

11 files changed

+422
-823
lines changed

.gitignore

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
.env
2-
3-
tests/
4-
5-
examples/
6-
7-
.venv/
8-
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
.pytest_cache
9+
.mypy_cache/
910
.ruff_cache/
1011

11-
.pytest_cache/
12+
# Virtual environments
13+
.venv
1214

13-
__pycache__/
15+
# macos
16+
.DS_Store
17+
18+
# Environment
19+
.env*
20+
!.env.example
1421

15-
.gitignore
22+
# LLM
23+
.claude

CLAUDE.md

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ This is a FastMCP (Model Context Protocol) server for Metabase integration. It p
1010

1111
### Core Components
1212

13-
- **`server.py`**: Main FastMCP server with all tool implementations
13+
- **`src/server.py`**: Main FastMCP server with all tool implementations using `mcp.server.fastmcp`
14+
- **`src/config.py`**: Type-safe configuration management using pydantic-settings
1415
- **`MetabaseClient`**: HTTP client class handling Metabase API authentication and requests
15-
- **Authentication**: Supports both API Key (preferred) and email/password session authentication
16-
- **Transport Methods**: STDIO (default for IDE integration), SSE, HTTP
16+
- **Authentication**: API Key authentication with `X-API-KEY` header
17+
- **Transport Methods**: HTTP (default), STDIO (for IDE integration)
1718

1819
### Key Tool Categories
1920

@@ -22,9 +23,13 @@ This is a FastMCP (Model Context Protocol) server for Metabase integration. It p
2223
3. **Collection Management**: `list_collections`, `create_collection`, `list_cards_by_collection`
2324
4. **Smart Search**: `search_metabase`, `find_candidate_collections`, `search_cards_in_collections`
2425

25-
### Authentication
26+
### Configuration Management
2627

27-
The server uses API Key authentication with the `X-API-KEY` header.
28+
The server uses `config.py` with pydantic-settings for type-safe configuration:
29+
- Automatic `.env` file loading
30+
- Environment variable validation
31+
- Type-safe access to all settings
32+
- Centralized configuration management
2833

2934
## Development Commands
3035

@@ -39,17 +44,14 @@ uv sync --group dev
3944

4045
### Running the Server
4146
```bash
42-
# STDIO transport (default for IDE integration)
43-
uv run python server.py
47+
# Default transport (streamable-http)
48+
uv run python -m src
4449

45-
# SSE transport for web applications
46-
uv run python server.py --sse
47-
48-
# HTTP transport
49-
uv run python server.py --http
50+
# STDIO transport (for IDE integration)
51+
MCP_TRANSPORT=stdio uv run python -m src
5052

5153
# Custom host/port via environment variables
52-
HOST=localhost PORT=9000 uv run python server.py --sse
54+
HOST=localhost PORT=9000 uv run python -m src
5355
```
5456

5557
### Code Quality & Testing
@@ -65,22 +67,32 @@ uv run black .
6567
uv run isort .
6668

6769
# Type checking
68-
uv run mypy server.py
70+
uv run mypy src/
6971

7072
# Run tests
7173
uv run pytest
7274
```
7375

7476
## Configuration
7577

78+
Configuration is managed through `config.py` using pydantic-settings. All settings can be configured via environment variables or a `.env` file.
79+
7680
### Environment Variables
77-
Required in `.env` file:
81+
**Required:**
7882
- `METABASE_URL`: Metabase instance URL
7983
- `METABASE_API_KEY`: Metabase API key
8084

81-
Optional:
82-
- `HOST`: Server host (default: 0.0.0.0 for SSE/HTTP)
83-
- `PORT`: Server port (default: 8000 for SSE/HTTP)
85+
**Optional (with defaults):**
86+
- `HOST`: Server host (default: "0.0.0.0")
87+
- `PORT`: Server port (default: 8080)
88+
- `MCP_TRANSPORT`: Transport method - "streamable-http" or "stdio" (default: "streamable-http")
89+
- `LOG_LEVEL`: Logging level (default: "INFO")
90+
- `MCP_SERVER_NAME`: Server name (default: "metabase-mcp")
91+
92+
**HTTP Client Configuration:**
93+
- `HTTP_CONNECT_TIMEOUT`: Connection timeout in seconds (default: 10.0, range: 1.0-60.0)
94+
- `HTTP_READ_TIMEOUT`: Read timeout in seconds (default: 30.0, range: 5.0-300.0)
95+
- `HTTP_ENABLE_HTTP2`: Enable HTTP/2 support for better performance (default: False)
8496

8597
### Tool Configuration
8698
- Line length: 100 characters (Black, Ruff, isort)
@@ -91,11 +103,12 @@ Optional:
91103
## Common Development Patterns
92104

93105
### Adding New Tools
94-
1. Use `@mcp.tool` decorator
95-
2. Include proper type hints and docstrings
96-
3. Handle errors with try/except and logger.error
97-
4. Use `metabase_client.request()` for API calls
98-
5. Return structured dict responses
106+
1. Use `@mcp.tool()` decorator
107+
2. Add `ctx: Context` as first parameter to access lifespan context
108+
3. Include proper type hints and docstrings
109+
4. Access MetabaseClient via `ctx.request_context.lifespan_context.metabase_client`
110+
5. Handle errors with try/except and logger.error
111+
6. Return structured responses
99112

100113
### Error Handling
101114
- Log errors with `logger.error()`
@@ -111,5 +124,8 @@ Optional:
111124

112125
- The `list_cards()` tool returns 1700+ cards and may timeout - use `list_cards_paginated()` instead
113126
- Search tools are optimized: use `find_candidate_collections()` first, then `search_cards_in_collections()`
114-
- SSE/HTTP transports require starting the server before IDE integration
115-
- Uses per-request HTTP clients for automatic connection management
127+
- Server uses `mcp.server.fastmcp` with type-safe context management
128+
- Configuration uses pydantic-settings for validation and type safety
129+
- Default transport is `streamable-http` - set `MCP_TRANSPORT=stdio` for IDE integration
130+
- Uses per-request HTTP clients for automatic connection management
131+
- Custom `/ping` endpoint available for health checks

Dockerfile

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
1-
FROM python:3.12-slim
1+
FROM ghcr.io/astral-sh/uv:0.8-python3.12-bookworm-slim
22

3-
# Set working directory
4-
WORKDIR /app
3+
# Set environment variables
4+
ENV PYTHONUNBUFFERED=1 \
5+
PYTHONDONTWRITEBYTECODE=1
56

6-
# Install system dependencies and uv
7-
RUN apt-get update && apt-get install -y \
8-
gcc \
9-
curl \
10-
&& curl -LsSf https://astral.sh/uv/install.sh | sh \
11-
&& rm -rf /var/lib/apt/lists/*
7+
# Create non-root user
8+
RUN groupadd -r appuser && useradd -r -g appuser appuser
129

13-
# Add uv to PATH
14-
ENV PATH="/root/.local/bin:$PATH"
10+
# Set working directory
11+
WORKDIR /app
1512

16-
# Copy project files
17-
COPY pyproject.toml .
18-
COPY requirements.txt .
13+
# Copy workspace files for dependency resolution
14+
COPY pyproject.toml uv.lock ./
1915

20-
# Install Python dependencies with uv
21-
RUN uv sync --frozen
16+
# Install dependencies
17+
RUN uv sync --locked
2218

2319
# Copy application code
24-
COPY server.py .
25-
COPY .env* ./
20+
COPY src/ src/
2621

27-
# Create non-root user
28-
RUN useradd --create-home --shell /bin/bash app
29-
USER app
22+
# Create cache directory and change ownership
23+
RUN mkdir -p /home/appuser/.cache/uv && \
24+
chown -R appuser:appuser /app /home/appuser/.cache
25+
26+
# Switch to non-root user
27+
USER appuser
3028

31-
# Expose port (for HTTP/SSE transport)
32-
EXPOSE 8000
29+
# Expose port (updated to match our config default)
30+
EXPOSE 8080
3331

34-
# Default command
35-
CMD ["uv", "run", "python", "server.py"]
32+
# Run the application using our new package structure
33+
CMD ["uv", "run", "python", "-m", "src"]

README.md

Lines changed: 44 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,76 @@
1-
# Metabase FastMCP Server
1+
# Metabase MCP Server
22

3-
A FastMCP (Model Context Protocol) server for Metabase, built with Python. This server provides tools to interact with Metabase databases, execute queries, manage cards, and work with collections.
4-
5-
## Features
6-
7-
- List and manage Metabase databases
8-
- Execute SQL queries and saved questions/cards
9-
- Create and manage cards (questions)
10-
- Work with collections
11-
- List tables and fields
12-
- API Key authentication
3+
A FastMCP server for Metabase integration that provides tools to interact with Metabase databases, execute queries, manage cards (questions), and work with collections.
134

145
## Installation
156

16-
### Quick Start with uv (Recommended)
17-
18-
1. **Install uv** if not already installed:
19-
```bash
20-
curl -LsSf https://astral.sh/uv/install.sh | sh
21-
```
22-
23-
2. **Clone and setup**:
247
```bash
8+
# Clone the repository
259
git clone <repository-url>
2610
cd metabase-mcp
27-
uv sync # Install dependencies and create virtual environment
28-
```
2911

30-
3. **Configure environment**:
31-
```bash
32-
# Create .env file with your Metabase configuration
33-
echo "METABASE_URL=http://localhost:3000" > .env
34-
echo "METABASE_API_KEY=your-api-key-here" >> .env
12+
# Install dependencies
13+
uv sync
3514
```
3615

3716
## Configuration
3817

39-
Set the following environment variables in your `.env` file:
40-
41-
- `METABASE_URL`: Your Metabase instance URL
42-
- `METABASE_API_KEY`: Your Metabase API key
43-
44-
## Usage
45-
46-
### Run the Server
18+
Set required environment variables in a `.env` file:
4719

4820
```bash
49-
# STDIO transport (default for MCP integration)
50-
uv run server.py
51-
52-
# SSE transport (for web applications)
53-
uv run server.py --sse
54-
55-
# HTTP transport
56-
uv run server.py --http
21+
METABASE_URL=https://your-metabase-instance.com
22+
METABASE_API_KEY=your-api-key-here
23+
24+
# Optional settings (defaults shown)
25+
MCP_TRANSPORT=streamable-http # or "stdio" for IDE integration
26+
HOST=0.0.0.0
27+
PORT=8080
28+
LOG_LEVEL=INFO
29+
30+
# HTTP Client Configuration (optional)
31+
HTTP_CONNECT_TIMEOUT=10.0 # Connection timeout in seconds (1.0-60.0)
32+
HTTP_READ_TIMEOUT=30.0 # Read timeout in seconds (5.0-300.0)
33+
HTTP_ENABLE_HTTP2=false # Enable HTTP/2 support
5734
```
5835

59-
### Claude Desktop Integration
36+
## Running
6037

61-
To integrate with Claude Desktop, add the configuration to `~/Library/Application\ Support/Claude/claude_desktop_config.json`:
38+
```bash
39+
# Default (HTTP transport)
40+
uv run python -m src
6241

63-
```json
64-
{
65-
"mcpServers": {
66-
"metabase-mcp": {
67-
"command": "uv",
68-
"args": ["run", "python", "/path/to/metabase-mcp-kmlx/server.py"],
69-
"cwd": "/path/to/metabase-mcp-kmlx"
70-
}
71-
}
72-
}
42+
# STDIO transport (for IDE integration)
43+
MCP_TRANSPORT=stdio uv run python -m src
7344
```
7445

7546
## Available Tools
7647

7748
### Database & Schema Tools
78-
- `list_databases`: List all databases in Metabase
79-
- `list_tables`: List all tables in a database with formatted output
80-
- `get_table_fields`: Get all fields/columns in a table
49+
- **`list_databases`** - List all databases in Metabase
50+
- **`list_tables(database_id)`** - List tables in a database with formatted markdown output
51+
- **`get_table_fields(table_id, limit=20)`** - Get fields/columns in a table
8152

8253
### Card & Query Tools
83-
- `list_cards`: List all questions/cards in Metabase (WARNING: Large dataset)
84-
- `list_cards_paginated`: List cards with pagination to avoid timeout issues
85-
- `execute_card`: Execute a Metabase question/card and get results
86-
- `execute_query`: Execute a SQL query against a Metabase database
87-
- `create_card`: Create a new question/card in Metabase
54+
- **`list_cards`** - List all questions/cards (WARNING: Large dataset, may timeout)
55+
- **`list_cards_paginated(limit=50, offset=0, filter_type="all")`** - List cards with pagination
56+
- **`list_cards_by_collection(collection_id)`** - List cards in a specific collection
57+
- **`execute_card(card_id, parameters=None)`** - Execute a Metabase question/card
58+
- **`execute_query(database_id, query, native_parameters=None)`** - Execute SQL query
59+
- **`create_card(name, database_id, query, description=None, collection_id=None, visualization_settings=None)`** - Create new card
8860

89-
### Collection Management Tools
90-
- `list_collections`: List all collections in Metabase
91-
- `list_cards_by_collection`: List cards in a specific collection (focused dataset)
92-
- `create_collection`: Create a new collection in Metabase
61+
### Collection Management
62+
- **`list_collections`** - List all collections
63+
- **`create_collection(name, description=None, color=None, parent_id=None)`** - Create new collection
9364

9465
### Smart Search Tools
95-
- `search_metabase`: Universal search using Metabase search API (cards, dashboards, collections)
96-
- `find_candidate_collections`: Find collections by name/description matching (fast)
97-
- `search_cards_in_collections`: Search for cards within specific collections (targeted)
66+
- **`search_metabase(query, limit=20, models=None, archived=False, search_native_query=None)`** - Search using Metabase API
67+
- **`find_candidate_collections(query, limit_collections=10)`** - Find collections matching query
68+
- **`search_cards_in_collections(query, collection_ids, limit=25, offset=0)`** - Search cards within specific collections
9869

99-
## Development
70+
## Health Check
10071

101-
### Development Setup
72+
The server provides a `/ping` endpoint for health checks when running with HTTP transport.
10273

103-
```bash
104-
# Install development dependencies (Python 3.12+)
105-
uv sync --group dev
106-
107-
# Run tests
108-
uv run pytest
74+
## Integration
10975

110-
# Format and lint code
111-
uv run ruff check . # Lint
112-
uv run ruff format . # Format
113-
uv run black . # Alternative formatter
114-
uv run isort . # Import sorting
115-
116-
# Type checking
117-
uv run mypy server.py
118-
```
76+
For Claude Desktop or Cursor IDE integration, set `MCP_TRANSPORT=stdio` in your environment.

0 commit comments

Comments
 (0)