Fork intervals-mcp-server: native OAuth + streamable-HTTP, no monkeypatch
build-image / build (push) Failing after 18s
build-image / build (push) Failing after 18s
- Bump mcp[cli] 1.22 -> 1.28.1 (negotiates MCP protocol 2025-11-25, matching current Claude clients; the old 2025-06-18 server never got a tools/list on the connector surface). - Bake transport config into code: stateless_http + json_response for HTTP (single JSON body instead of a 34KB SSE stream, which the connector pipeline handles far more reliably). - Bake Authentik OAuth (AuthSettings + JWT TokenVerifier) into intervals_mcp_server.auth, configured from MCP_ISSUER/MCP_RESOURCE/MCP_JWKS_URI/MCP_CLIENT_ID — removes the runtime FastMCP.__init__ monkeypatch from the k8s deployment command. - Accept token audience with/without trailing slash (RFC 8707 clients use the slash-normalised resource metadata value). - Dockerfile CMD runs the module (transport via MCP_TRANSPORT); add .gitea CI to build+push the image to git.farh.net/farhoodlabs/intervalsicu-mcp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# API Communication Patterns
|
||||
|
||||
## Intervals.icu API Integration
|
||||
|
||||
This project communicates with the Intervals.icu API using consistent patterns defined in [src/intervals_mcp_server/server.py](mdc:src/intervals_mcp_server/server.py).
|
||||
|
||||
## Core API Function
|
||||
|
||||
All API communication goes through `make_intervals_request()`:
|
||||
|
||||
```python
|
||||
async def make_intervals_request(
|
||||
url: str,
|
||||
api_key: str | None = None,
|
||||
params: dict[str, Any] | None = None
|
||||
) -> dict[str, Any] | list[dict[str, Any]]
|
||||
```
|
||||
|
||||
### Usage Pattern
|
||||
- **URL Format**: Relative paths like `/athlete/{id}/activities`
|
||||
- **Authentication**: HTTP Basic Auth with username "API_KEY" and password as the API key
|
||||
- **Headers**: Includes User-Agent and Accept: application/json
|
||||
- **Timeout**: 30 seconds for all requests
|
||||
|
||||
## Error Handling Strategy
|
||||
|
||||
### HTTP Status Code Mapping
|
||||
The system provides user-friendly messages for common HTTP errors:
|
||||
- `401 Unauthorized` - Invalid API key
|
||||
- `403 Forbidden` - Permission denied
|
||||
- `404 Not Found` - Resource doesn't exist
|
||||
- `422 Unprocessable Entity` - Invalid parameters
|
||||
- `429 Too Many Requests` - Rate limiting
|
||||
- `500 Internal Server Error` - Server issues
|
||||
- `503 Service Unavailable` - Maintenance/downtime
|
||||
|
||||
### Error Response Format
|
||||
All errors return a consistent structure:
|
||||
```python
|
||||
{
|
||||
"error": True,
|
||||
"status_code": int, # HTTP status code
|
||||
"message": str # User-friendly error message
|
||||
}
|
||||
```
|
||||
|
||||
## MCP Tool Implementation Pattern
|
||||
|
||||
1. **Parameter Validation**: Check required parameters and provide defaults
|
||||
2. **API Key Resolution**: Use provided key or fall back to global `API_KEY`
|
||||
3. **Athlete ID Handling**: Support both numeric and i-prefixed formats
|
||||
4. **Date Validation**: Parse and validate date strings
|
||||
5. **API Request**: Call `make_intervals_request()` with appropriate parameters
|
||||
6. **Error Checking**: Return formatted error messages for API failures
|
||||
7. **Data Formatting**: Use utilities from [src/intervals_mcp_server/utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Required configuration (validated on startup):
|
||||
- `API_KEY` - Cannot be empty
|
||||
- `ATHLETE_ID` - Must match pattern `r"i?\d+"` (digits or i-prefixed digits)
|
||||
|
||||
Optional configuration:
|
||||
- `INTERVALS_API_BASE_URL` - Defaults to `https://intervals.icu/api/v1`
|
||||
|
||||
## Shared HTTP Client
|
||||
|
||||
Uses a single `httpx.AsyncClient` instance (`httpx_client`) managed by the FastMCP lifespan context manager to:
|
||||
- Reuse connections for better performance
|
||||
- Ensure proper cleanup when server stops
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Development Workflow Guide
|
||||
|
||||
## Environment Setup
|
||||
|
||||
1. **Use uv for package management**: This project uses [uv](mdc:https:/github.com/astral-sh/uv) instead of pip
|
||||
```bash
|
||||
uv venv --python 3.12
|
||||
source .venv/bin/activate
|
||||
uv sync --all-extras
|
||||
```
|
||||
|
||||
2. **Environment Configuration**: Copy [.env.example](mdc:.env.example) to `.env` and configure:
|
||||
- `API_KEY` - Your Intervals.icu API key
|
||||
- `ATHLETE_ID` - Your athlete ID (digits or i-prefixed)
|
||||
|
||||
## Running the Server
|
||||
|
||||
- **Manual Testing**: `mcp run src/intervals_mcp_server/server.py`
|
||||
- **Claude Desktop Integration**: Use `mcp install` command as documented in [README.md](mdc:README.md)
|
||||
|
||||
## Code Quality Checks
|
||||
|
||||
Before committing, ensure all three checks pass:
|
||||
|
||||
1. **Linting**: `ruff .` - Uses default ruff rules, config in [pyproject.toml](mdc:pyproject.toml)
|
||||
2. **Type Checking**: `mypy src tests` - Static type analysis
|
||||
3. **Testing**: `pytest` - Unit tests in [tests/](mdc:tests) directory
|
||||
|
||||
## Code Organization
|
||||
|
||||
- **Main Logic**: All MCP tools are implemented in [src/intervals_mcp_server/server.py](mdc:src/intervals_mcp_server/server.py)
|
||||
- **Utilities**: Helper functions in [src/intervals_mcp_server/utils/](mdc:src/intervals_mcp_server/utils)
|
||||
- **API Communication**: `make_intervals_request()` function handles all Intervals.icu API calls
|
||||
- **Error Handling**: Comprehensive HTTP error handling with user-friendly messages
|
||||
|
||||
## Adding New MCP Tools
|
||||
|
||||
1. Create async function decorated with `@mcp.tool()`
|
||||
2. Add proper type hints and docstrings
|
||||
3. Use `make_intervals_request()` for API calls
|
||||
4. Add formatting utilities to [src/intervals_mcp_server/utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py) if needed
|
||||
5. Write unit tests in [tests/](mdc:tests)
|
||||
|
||||
## Commit Guidelines
|
||||
|
||||
- Use concise commit messages
|
||||
- Title PRs as `[intervals-mcp-server] <brief description>`
|
||||
- Ensure `ruff`, `mypy`, and `pytest` all pass
|
||||
- Document manual testing steps in PR description
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Intervals.icu MCP Server Project Overview
|
||||
|
||||
This is a Model Context Protocol (MCP) server for connecting Claude with the Intervals.icu API. The project enables Claude to retrieve and analyze athlete data including activities, events, workouts, and wellness metrics.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- **Main Entry Point**: [src/intervals_mcp_server/server.py](mdc:src/intervals_mcp_server/server.py) - Contains the FastMCP server implementation with all MCP tools
|
||||
- **Configuration**: [pyproject.toml](mdc:pyproject.toml) - Project configuration, dependencies, and build settings
|
||||
- **Environment Setup**: [.env.example](mdc:.env.example) - Template for environment variables (API_KEY, ATHLETE_ID)
|
||||
- **Documentation**: [README.md](mdc:README.md) - Comprehensive setup and usage guide
|
||||
- **Developer Guide**: [AGENTS.md](mdc:AGENTS.md) - Contributor and development instructions
|
||||
|
||||
## Core Components
|
||||
|
||||
### MCP Tools (in server.py)
|
||||
- `get_activities` - Retrieve athlete activities with filtering options
|
||||
- `get_activity_details` - Get detailed information for specific activities
|
||||
- `get_activity_intervals` - Get detailed interval data for activities
|
||||
- `get_events` - Retrieve upcoming events (workouts, races, etc.)
|
||||
- `get_event_by_id` - Get detailed information for specific events
|
||||
- `get_wellness_data` - Fetch wellness metrics and data
|
||||
|
||||
### Utilities
|
||||
- **Formatting**: [src/intervals_mcp_server/utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py) - Data formatting utilities for MCP responses
|
||||
|
||||
### Testing
|
||||
- **Tests Directory**: [tests/](mdc:tests) - Unit tests for server functionality and utilities
|
||||
- **Sample Data**: [tests/sample_data.py](mdc:tests/sample_data.py) - Test data for development
|
||||
|
||||
## Key Technologies
|
||||
- **Python 3.12+** - Required runtime version
|
||||
- **FastMCP** - MCP server framework
|
||||
- **httpx** - Async HTTP client for API calls
|
||||
- **uv** - Package manager and virtual environment tool
|
||||
- **pytest** - Testing framework
|
||||
- **ruff** - Linting and code formatting
|
||||
- **mypy** - Static type checking
|
||||
|
||||
## Environment Variables
|
||||
- `API_KEY` - Intervals.icu API key (required)
|
||||
- `ATHLETE_ID` - Target athlete ID (required)
|
||||
- `INTERVALS_API_BASE_URL` - API base URL (optional, defaults to intervals.icu)
|
||||
- `LOG_LEVEL` - Logging level (optional, defaults to INFO)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Python Best Practices for Intervals MCP Server
|
||||
|
||||
## Code Style and Readability
|
||||
|
||||
- Follow **PEP 8** for formatting, indentation, and naming conventions
|
||||
- Write readable, maintainable code:
|
||||
- Use descriptive names for variables, functions, and classes
|
||||
- Keep functions short and single-purpose (see MCP tools in [server.py](mdc:src/intervals_mcp_server/server.py))
|
||||
- Keep indentation and spacing consistent
|
||||
- Embrace Pythonic idioms:
|
||||
- Use list/dict comprehensions and generators where appropriate
|
||||
- Prefer built-in functions and stdlib modules
|
||||
- Use context managers (like the `lifespan` manager for httpx client)
|
||||
- Avoid global variables except for validated constants (API_KEY, ATHLETE_ID)
|
||||
- Use lazy **`%`** formatting in logging calls: `logger.debug("val=%s", val)`
|
||||
- Represent datetimes as timezone-aware UTC objects when possible
|
||||
|
||||
## Type Annotations and Documentation
|
||||
|
||||
- Add **type hints** to all function signatures (see examples in [server.py](mdc:src/intervals_mcp_server/server.py))
|
||||
- Use built-in collection types (`list`, `dict`, `set`, `tuple`) instead of `typing.List`, etc.
|
||||
- Provide clear **docstrings** for:
|
||||
- All MCP tool functions (required by FastMCP)
|
||||
- Public utility functions in [utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
|
||||
- The main module docstring explaining the server's purpose
|
||||
- Use inline comments only for non-obvious logic
|
||||
|
||||
## Error Handling and Validation
|
||||
|
||||
- Handle errors gracefully with explicit `try/except` blocks (see `make_intervals_request()`)
|
||||
- Catch specific exceptions: `httpx.HTTPStatusError`, `httpx.RequestError`, etc.
|
||||
- Return consistent error structures with user-friendly messages
|
||||
- Validate inputs:
|
||||
- Check API key and athlete ID on startup
|
||||
- Validate date formats in MCP tools
|
||||
- Use regex pattern `r"i?\d+"` for athlete ID validation
|
||||
|
||||
## Async Programming Patterns
|
||||
|
||||
- Use `async/await` consistently for all MCP tools and API calls
|
||||
- Share a single `httpx.AsyncClient` instance across requests
|
||||
- Properly close async resources using lifespan context manager
|
||||
- Follow FastMCP's async patterns for tool implementations
|
||||
|
||||
## Testing Standards
|
||||
|
||||
- Write unit tests for all MCP tools and utilities
|
||||
- Use **pytest** with `pytest-asyncio` for async function testing
|
||||
- Use **pytest-mock** (`MockerFixture`) for mocking HTTP requests
|
||||
- Test both success and error paths
|
||||
- Mock external API calls to avoid dependencies in tests
|
||||
|
||||
## Development Environment
|
||||
|
||||
- Target **Python 3.12+** as specified in [pyproject.toml](mdc:pyproject.toml)
|
||||
- Use **uv** for package management: `uv sync --all-extras`
|
||||
- Manage dependencies in `pyproject.toml` with lock file (`uv.lock`)
|
||||
- Always use virtual environments (`.venv/`)
|
||||
- Run quality checks before commits:
|
||||
- `ruff .` for linting
|
||||
- `mypy src tests` for type checking
|
||||
- `pytest` for tests
|
||||
|
||||
## Security Practices
|
||||
|
||||
- Never hard-code secrets - use environment variables via `.env` file
|
||||
- Load sensitive data (API_KEY, ATHLETE_ID) from environment
|
||||
- Use HTTP Basic Auth for API authentication
|
||||
- Validate all external inputs before processing
|
||||
- Follow least-privilege principles for API access
|
||||
|
||||
## Project-Specific Patterns
|
||||
|
||||
- All API communication through `make_intervals_request()` function
|
||||
- Consistent error response format: `{"error": True, "status_code": int, "message": str}`
|
||||
- Use formatting utilities from [utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
|
||||
- Follow MCP tool naming conventions: `get_*` for retrieval operations
|
||||
- Support both numeric and i-prefixed athlete IDs
|
||||
|
||||
## Code Organization
|
||||
|
||||
- Keep all MCP tools in [server.py](mdc:src/intervals_mcp_server/server.py)
|
||||
- Place formatting utilities in [utils/formatting.py](mdc:src/intervals_mcp_server/utils/formatting.py)
|
||||
- Organize tests by functionality in [tests/](mdc:tests) directory
|
||||
- Use `__init__.py` files to mark Python packages
|
||||
- Include `py.typed` for type checking support
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Testing Patterns and Practices
|
||||
|
||||
## Test Organization
|
||||
|
||||
Tests are organized in the [tests/](mdc:tests) directory with the following structure:
|
||||
|
||||
- **[tests/test_server.py](mdc:tests/test_server.py)** - Main MCP tool testing
|
||||
- **[tests/test_formatting.py](mdc:tests/test_formatting.py)** - Utility function tests
|
||||
- **[tests/test_make_intervals_request.py](mdc:tests/test_make_intervals_request.py)** - API communication tests
|
||||
- **[tests/sample_data.py](mdc:tests/sample_data.py)** - Mock data for testing
|
||||
|
||||
## Testing Framework Setup
|
||||
|
||||
- **Framework**: pytest with async support (`pytest-asyncio`)
|
||||
- **Mocking**: pytest-mock for HTTP request mocking
|
||||
- **Configuration**: Test settings in [pyproject.toml](mdc:pyproject.toml) under `[tool.pytest.ini_options]`
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Async Testing
|
||||
All MCP tools are async functions, so tests use:
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_function_name():
|
||||
# Test async MCP tools
|
||||
```
|
||||
|
||||
### Mock API Responses
|
||||
HTTP requests are mocked using `pytest-mock`:
|
||||
```python
|
||||
def test_api_call(mocker):
|
||||
mock_response = mocker.Mock()
|
||||
mock_response.json.return_value = {"test": "data"}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
|
||||
mocker.patch("httpx.AsyncClient.get", return_value=mock_response)
|
||||
```
|
||||
|
||||
### Test Data Management
|
||||
- **Sample Data**: [tests/sample_data.py](mdc:tests/sample_data.py) contains realistic mock data
|
||||
- **Isolation**: Each test uses fresh mock data to avoid side effects
|
||||
- **Coverage**: Tests cover both success and error scenarios
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest
|
||||
|
||||
# Run with verbose output
|
||||
pytest -v
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/test_server.py
|
||||
|
||||
# Run with coverage (if installed)
|
||||
pytest --cov=src/intervals_mcp_server
|
||||
```
|
||||
|
||||
## Test Requirements
|
||||
|
||||
Before committing code, ensure:
|
||||
1. **All tests pass**: `pytest` returns exit code 0
|
||||
2. **No new linting errors**: `ruff .` passes
|
||||
3. **Type checking passes**: `mypy src tests` succeeds
|
||||
|
||||
## Writing New Tests
|
||||
|
||||
When adding new MCP tools or utilities:
|
||||
|
||||
1. **Create test cases** in appropriate test file
|
||||
2. **Mock external dependencies** (HTTP requests, file I/O)
|
||||
3. **Test both success and error paths**
|
||||
4. **Use realistic test data** from [tests/sample_data.py](mdc:tests/sample_data.py)
|
||||
5. **Follow async testing patterns** for MCP tools
|
||||
6. **Verify error message formatting** for user-facing errors
|
||||
|
||||
## Mock Strategy
|
||||
|
||||
The project uses comprehensive mocking to:
|
||||
- **Avoid real API calls** during testing
|
||||
- **Test error handling** by simulating various HTTP error responses
|
||||
- **Ensure deterministic results** with controlled test data
|
||||
- **Speed up test execution** by eliminating network requests
|
||||
Reference in New Issue
Block a user