Skip to main content
Glama
CLAUDE.md17.5 kB
# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview This is an **Amazing Marvin MCP Server** (v2.0) - a Model Context Protocol server that connects AI assistants to Amazing Marvin's task management system. Built with **FastMCP** and **deployed on Smithery** for hosted, install-free access. **Key Technologies:** - FastMCP (Python MCP SDK) - Smithery for hosted deployment - Pydantic V2 for validation - httpx for async HTTP - Amazing Marvin Limited Access API **Deployment Model:** - **Primary**: Smithery (hosted, HTTP/SSE transport) - **Legacy**: Local STDIO deployment (deprecated in favor of Smithery) ## Development Commands ### Setup ```bash # Create virtual environment and install dependencies uv venv uv pip install -r requirements.txt ``` ### Testing ```bash # Test API connection (requires AMAZING_MARVIN_API_TOKEN env var) export AMAZING_MARVIN_API_TOKEN="your-token-here" .venv/bin/python test_server.py # Validate Python syntax python -m py_compile amazing_marvin_server.py ``` ### Running the Server ```bash # Run directly (will check for API token and exit if not set) .venv/bin/python amazing_marvin_server.py # The server is meant to be run by Claude Desktop via stdio transport # See claude_desktop_config.json or README.md for configuration ``` ## Architecture ### Single-File MCP Server Design The entire MCP server is contained in `amazing_marvin_server.py` (1,309 lines) with a clear hierarchical structure: ``` amazing_marvin_server.py ├── FastMCP initialization (line 19) ├── Constants (API_BASE_URL, CHARACTER_LIMIT, etc.) ├── Enums & Base Models (ResponseFormat, BaseTaskInput) ├── Shared Utility Functions (5 functions) │ ├── _get_headers() - API authentication │ ├── _make_api_request() - Async HTTP client wrapper │ ├── _handle_api_error() - Error formatting with guidance │ ├── _format_timestamp() - Unix timestamp to YYYY-MM-DD │ └── _truncate_response() - Character limit enforcement ├── Pydantic Input Models (8 models) │ └── All inherit from BaseTaskInput with strict validation └── Tool Implementations (9 @mcp.tool decorated functions) ├── Tier 1: Task Management (4 tools) ├── Tier 2: Organization (3 tools) └── Tier 3: Time Tracking (2 tools) ``` ### Key Architectural Patterns **1. Shared Utilities Pattern** - All API calls go through `_make_api_request()` - single source of truth - All errors formatted by `_handle_api_error()` - consistent messaging - All timestamps formatted by `_format_timestamp()` - human-readable dates - Follows DRY principle strictly **2. Pydantic V2 Validation Pattern** - Every tool has a dedicated input model (e.g., `AddTaskInput`, `GetTasksInput`) - All models inherit from `BaseTaskInput` for common configuration - Use `Field()` with detailed constraints (min_length, max_length, pattern, ge, le) - `model_config` sets: `str_strip_whitespace=True`, `validate_assignment=True`, `extra='forbid'` **3. Tool Registration Pattern** ```python @mcp.tool( name="marvin_add_task", # Always prefixed with "marvin_" annotations={ "title": "Human-Readable Title", "readOnlyHint": False, "destructiveHint": False, "idempotentHint": False, "openWorldHint": True } ) async def marvin_add_task(params: AddTaskInput) -> str: """Comprehensive docstring with Args, Returns, Examples, Error Handling""" ``` **4. Dual Response Format Pattern** - Most tools accept `response_format` parameter (ResponseFormat enum) - **Markdown** (default): Human-readable with headers, emojis, formatting - **JSON**: Structured data with json.dumps() for programmatic use - All responses check CHARACTER_LIMIT (25,000) and truncate with guidance **5. Error Handling Strategy** - Try/except wraps all tool logic - HTTP errors mapped to actionable messages (401→token help, 404→ID verification, 429→rate limit guidance) - Errors return as strings (not exceptions) so LLM sees them - Every error message includes next steps ## Amazing Marvin API Integration ### Authentication - Uses `X-API-Token` header with Limited Access API (recommended) - Optional `X-Full-Access-Token` for advanced operations (not currently used) - Token loaded from `AMAZING_MARVIN_API_TOKEN` environment variable ### API Endpoints Used ``` GET /api/categories - List all categories/projects GET /api/labels - List all labels GET /api/children - Get items in category (params: parentId) GET /api/todayItems - Get scheduled tasks (params: date) GET /api/dueItems - Get due/overdue tasks (params: by) POST /api/addTask - Create task (supports shortcuts) POST /api/markDone - Complete task (params: itemId) POST /api/track - Start/stop timer (params: itemId, action) ``` ### API Quirks - Timestamps are in **milliseconds** (not seconds) - `parentId="unassigned"` gets tasks without a category - Amazing Marvin shortcuts work in titles: `#Project @label ~60 +2024-03-20 ^1` - Auto-completion enabled by default unless `X-Auto-Complete: false` header ## Tool Implementation Guidelines ### Adding a New Tool 1. **Create Pydantic Input Model** ```python class NewToolInput(BaseTaskInput): param: str = Field(..., description="Detailed description with examples") ``` 2. **Define Tool with Decorator** ```python @mcp.tool(name="marvin_new_tool", annotations={...}) async def marvin_new_tool(params: NewToolInput) -> str: """Comprehensive docstring...""" ``` 3. **Implement with Shared Utilities** ```python try: data = await _make_api_request("/endpoint", method="POST", data={...}) # Format response (Markdown or JSON based on params.response_format) return _truncate_response(result, item_count) except Exception as e: return _handle_api_error(e) ``` ### Docstring Requirements Every tool must document: - **Purpose**: What the tool does (1-2 sentences) - **Args**: Full Pydantic model structure with types - **Returns**: Response format with example structure for both Markdown and JSON - **Examples**: "Use when" and "Don't use when" scenarios - **Error Handling**: Specific errors and HTTP status codes with guidance ### Tool Naming Convention - Always prefix with `marvin_` to avoid conflicts with other MCP servers - Use snake_case: `marvin_add_task`, not `marvinAddTask` - Action-oriented: `marvin_get_*`, `marvin_start_*`, `marvin_mark_*` ## Testing Strategy ### Connection Testing (`test_server.py`) - Validates API token format - Tests 3 endpoints: categories, labels, todayItems - Reports counts to verify connectivity - Use this before deploying changes ### Manual Testing via Claude Desktop 1. Update config with absolute paths to `.venv/bin/python` and server file 2. Restart Claude Desktop completely 3. Test each tool through natural language 4. Verify both Markdown and JSON response formats ## Important Constraints ### Character Limit Enforcement - All responses must check against `CHARACTER_LIMIT = 25000` - Use `_truncate_response()` for large result sets - Truncation adds guidance on using pagination/filters ### Response Format Consistency - Default to `ResponseFormat.MARKDOWN` unless specified - Markdown: Use headers (##), lists (-), emojis (✅⬜📁) - JSON: Use `json.dumps(response, indent=2)` for readability - Both formats must be truncated if needed ### Async/Await Requirement - **All I/O operations must be async**: `await _make_api_request()`, `async with httpx.AsyncClient()` - All tool functions are `async def` - MCP protocol requires async for proper stdio transport ### Pydantic V2 Specifics - Use `max_length` not deprecated `max_items` for lists - Use `@field_validator` not deprecated `@validator` - Use `model_dump()` not deprecated `dict()` - All validators require `@classmethod` decorator ## Configuration Files ### `requirements.txt` - `mcp[cli]>=1.0.0` - FastMCP support - `httpx>=0.27.0` - Async HTTP client - `pydantic>=2.0.0` - Validation (v2 required) ### `claude_desktop_config.json` (Example, not committed) Must use **absolute paths**: ```json { "mcpServers": { "amazing-marvin": { "command": "/absolute/path/.venv/bin/python", "args": ["/absolute/path/amazing_marvin_server.py"], "env": {"AMAZING_MARVIN_API_TOKEN": "token-here"} } } } ``` ## Security Notes - API tokens in `.gitignore` (never commit `claude_desktop_config.json`) - Limited Access API provides safety layer vs Full Access - All API communication over HTTPS - Input validation prevents injection attacks ## Future Enhancement Areas Documented in README.md, potential additions: - Document operations (`/api/doc/update`, `/api/doc/delete`) - Project creation (`/api/addProject`) - Goals integration (`/api/goals`) - Habit tracking (`/api/habits`, `/api/updateHabit`) - Reminders (`/api/reminder/set`) - Time blocks (`/api/todayTimeBlocks`) - Reward points system When adding these, follow the same patterns: Pydantic model → @mcp.tool decorator → shared utilities → comprehensive docstring. ## Smithery Deployment Architecture ### Package Structure for Smithery The project follows Python package conventions for Smithery deployment: ``` amazing-marvin-mcp/ ├── src/ │ └── amazing_marvin_mcp/ # Python package │ ├── __init__.py # Package initialization (__version__) │ └── server.py # Main server with @smithery.server() ├── pyproject.toml # Package config + [tool.smithery] ├── smithery.yaml # Runtime: "python" ├── requirements.txt # Local dev dependencies ├── README.md # Smithery-focused documentation ├── SMITHERY_DEPLOYMENT.md # Deployment guide └── CLAUDE.md # This file Legacy files (deprecated): ├── amazing_marvin_server.py # Old STDIO version └── test_server.py # Old env var based testing ``` ### Smithery-Specific Code Patterns **1. Server Creation with @smithery.server() Decorator** ```python from smithery.decorators import smithery from mcp.server.fastmcp import FastMCP, Context @smithery.server(config_schema=AmazingMarvinConfig) def create_server(): """ Entry point called by Smithery to create the MCP server. The config_schema generates a UI form for users to provide their API token. """ mcp = FastMCP("amazing_marvin_mcp") # Register all tools here using @mcp.tool() decorators return mcp ``` **2. Configuration Schema Pattern** ```python class AmazingMarvinConfig(BaseModel): """User configuration collected via Smithery UI.""" model_config = ConfigDict(str_strip_whitespace=True) api_token: str = Field( ..., description="Your Amazing Marvin API token. Get it from: https://app.amazingmarvin.com/pre?api=", min_length=10 ) ``` This schema: - Generates a configuration form in Smithery UI - Validates user input (min_length=10) - Provides help text (description field) - Stored encrypted per user session **3. Context Parameter Flow** Every tool must accept `ctx: Context` to access session configuration: ```python @mcp.tool(name="marvin_add_task", annotations={...}) async def marvin_add_task(params: AddTaskInput, ctx: Context) -> str: """ Args: params: Validated Pydantic input model ctx: Smithery context with session_config """ # Pass ctx to API request function result = await _make_api_request("/addTask", ctx, method="POST", data=data) return result ``` **4. Context-Aware API Authentication** ```python def _get_headers(ctx: Context, full_access: bool = False) -> Dict[str, str]: """Extract API token from session config instead of environment variable.""" config: AmazingMarvinConfig = ctx.session_config return {"X-API-Token": config.api_token} async def _make_api_request(endpoint: str, ctx: Context, ...): """All API requests require ctx parameter.""" headers = _get_headers(ctx) # ... make request with authenticated headers ``` ### Critical Smithery Differences vs STDIO | Aspect | STDIO (old) | Smithery (new) | |--------|-------------|----------------| | **Imports** | `from mcp.server.fastmcp import FastMCP` | `+ from smithery.decorators import smithery`<br>`+ from mcp.server.fastmcp import Context` | | **Server Init** | `mcp = FastMCP("...")` at module level | `@smithery.server()` wrapping `create_server()` function | | **API Token** | `os.getenv("AMAZING_MARVIN_API_TOKEN")` | `ctx.session_config.api_token` | | **Tool Signature** | `async def tool(params: Model) -> str` | `async def tool(params: Model, ctx: Context) -> str` | | **Entry Point** | `if __name__ == "__main__": mcp.run()` | `create_server()` function (NO main block) | | **Transport** | STDIO | HTTP/SSE | | **Configuration** | Environment variables | Session config via Pydantic schema | | **Testing** | `test_server.py` with env vars | `uv run playground` or `uv run dev` | ### Deployment Process **1. Local Testing** ```bash # Install dependencies including smithery uv venv uv pip install -r requirements.txt uv pip install smithery # Test with Smithery playground (ngrok tunneling) uv run playground # Or run in development mode uv run dev ``` **2. Deploy to Smithery** ```bash # Commit all changes git add . git commit -m "Update for Smithery deployment" git push origin main # Then in Smithery web UI: # 1. Go to https://smithery.ai/new # 2. Click "Continue with GitHub" # 3. Select repository: LucaDeLeo/amazing-marvin-mcp # 4. Click "Deploy" ``` **3. Auto-Deployment** Once connected, every `git push` to `main` triggers automatic redeployment. ### pyproject.toml Configuration ```toml [tool.smithery] server = "amazing_marvin_mcp.server:create_server" ``` This tells Smithery: - Package is `amazing_marvin_mcp` (in `src/`) - Module is `server.py` - Function is `create_server()` ### smithery.yaml Configuration ```yaml runtime: "python" ``` Minimal configuration specifying Python 3.12+ runtime. ## Adding New Tools (Smithery Version) When adding new tools to the Smithery deployment: 1. **Define Pydantic Input Model** ```python class NewToolInput(BaseTaskInput): param: str = Field(..., description="...") ``` 2. **Add Tool Inside create_server() Function** ```python @smithery.server(config_schema=AmazingMarvinConfig) def create_server(): mcp = FastMCP("amazing_marvin_mcp") @mcp.tool(name="marvin_new_tool", annotations={...}) async def marvin_new_tool(params: NewToolInput, ctx: Context) -> str: """Full docstring with Args including ctx...""" result = await _make_api_request("/endpoint", ctx, ...) return result return mcp ``` 3. **Always Include ctx: Context Parameter** - First parameter after `params` - Required for authentication - Pass to all `_make_api_request()` calls 4. **Update Docstring** - Include `ctx (Context): Smithery context with session configuration` in Args - Document session config usage if relevant 5. **Test with Playground** ```bash uv run playground # Configure API token in web UI # Test tool through interface ``` 6. **Deploy** ```bash git add src/amazing_marvin_mcp/server.py git commit -m "Add marvin_new_tool" git push origin main # Auto-deploys to Smithery ``` ## Troubleshooting Smithery Deployment ### Build Failures **Error**: `ModuleNotFoundError: No module named 'smithery'` - **Fix**: Add `smithery>=0.4.2` to dependencies in `pyproject.toml` **Error**: `ModuleNotFoundError: No module named 'amazing_marvin_mcp'` - **Fix**: Ensure package structure: `src/amazing_marvin_mcp/__init__.py` exists **Error**: `ImportError: cannot import name 'create_server'` - **Fix**: Check `[tool.smithery]` server path matches actual function name ### Runtime Failures **Error**: `AttributeError: 'NoneType' object has no attribute 'api_token'` - **Cause**: User hasn't configured API token in Smithery UI - **Fix**: User must fill out configuration form in Smithery **Error**: `401 Unauthorized` from Amazing Marvin - **Cause**: Invalid or expired API token - **Fix**: User needs to regenerate token at https://app.amazingmarvin.com/pre?api= ### Testing Issues **Local playground won't start** - Check `uv pip install smithery` was run - Verify port 8000 isn't already in use - Try `uv run dev` instead **Tools not receiving ctx parameter** - Ensure function signature includes `ctx: Context` - Check import: `from mcp.server.fastmcp import Context` - Verify tool is inside `create_server()` function ## Migration Guide ### From STDIO to Smithery If maintaining both versions: 1. **Keep old file**: `amazing_marvin_server.py` (STDIO) 2. **New file**: `src/amazing_marvin_mcp/server.py` (Smithery) 3. **Update README**: Guide users to Smithery as primary method 4. **Add deprecation notice**: To old STDIO instructions If fully migrating to Smithery only: 1. **Remove/Archive**: `amazing_marvin_server.py`, `test_server.py` 2. **Update all docs**: Remove STDIO instructions 3. **Update Claude Desktop instructions**: Point to Smithery connection link 4. **Commit migration**: `git commit -m "Complete migration to Smithery"` Current status: **Fully migrated to Smithery** (v2.0.0)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LucaDeLeo/amazing-marvin-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server