#
tokens: 12295/50000 27/27 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── main.py
├── pyproject.toml
├── README.md
├── server
│   ├── __init__.py
│   ├── dtos
│   │   ├── create_card.py
│   │   ├── create_label.py
│   │   └── update_card.py
│   ├── models.py
│   ├── services
│   │   ├── __init__.py
│   │   ├── board.py
│   │   ├── card.py
│   │   ├── checklist.py
│   │   └── list.py
│   ├── tools
│   │   ├── __init__.py
│   │   ├── board.py
│   │   ├── card.py
│   │   ├── checklist.py
│   │   ├── list.py
│   │   └── tools.py
│   ├── trello.py
│   └── utils
│       ├── __init__.py
│       └── trello_api.py
├── TODO.md
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

.env
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
# for more detail on how to getting the api key and token please check README.md
TRELLO_API_KEY=your_trello_api_key
TRELLO_TOKEN=your_trello_token

# MCP Server Configuration
MCP_SERVER_NAME=Trello MCP Server
MCP_SERVER_PORT=8000
MCP_SERVER_HOST=0.0.0.0

# LLM Client Configuration
# Set to 'true' to use Claude, 'false' to use other LLMs via SSE
# Default is true to prefer Claude app integration
USE_CLAUDE_APP=true

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Trello MCP Server

A powerful MCP server for interacting with Trello boards, lists, and cards via AI Hosts.

## Table of Contents
- [Table of Contents](#table-of-contents)
- [Prerequisites](#prerequisites)
- [Pre-installation](#pre-installation)
- [Installation](#installation)
- [Server Modes](#server-modes)
- [Configuration](#configuration)
- [Client Integration](#client-integration)
- [Capabilities](#capabilities)
- [Detailed Capabilities](#detailed-capabilities)
    - [Board Operations](#board-operations)
    - [List Operations](#list-operations)
    - [Card Operations](#card-operations)
- [Usage](#usage)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)


## Prerequisites

1. Python 3.12 or higher, can easly managed by `uv`
2. [Claude for Desktop](https://claude.ai/download) installed
3. Trello account and API credentials
4. [uv](https://github.com/astral-sh/uv) package manager installed

## Pre-installation
1. Make sure you have installed Claude Desktop App
2. Make sure you have already logged in with your account into Claude.
3. Start Claude

## Installation



1. Set up Trello API credentials:
   - Go to [Trello Apps Administration](https://trello.com/power-ups/admin)
   - Create a new integration at [New Power-Up or Integration](https://trello.com/power-ups/admin/new)
   - Fill in your information (you can leave the Iframe connector URL empty) and make sure to select the correct Workspace
   - Click your app's icon and navigate to "API key" from left sidebar. 
   - Copy your "API key" and on the right side: "you can manually generate a Token." click the word token to get your Trello Token.

2. Rename the `.env.example` file in the project root with `.env` and set vairables you just got:
```bash
TRELLO_API_KEY=your_api_key_here
TRELLO_TOKEN=your_token_here
```

3. Install uv if you haven't already:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

4. Clone this repository:
```bash
git clone https://github.com/m0xai/trello-mcp-server.git
cd trello-mcp-server
```

5. Install dependencies and set server for Claude using uv::
```bash
uv run mcp install main.py
```

6. Restart Claude Desktop app

## Server Modes

This MCP server can run in two different modes:

### Claude App Mode

This mode integrates directly with the Claude Desktop application:

1. Set `USE_CLAUDE_APP=true` in your `.env` file (this is the default)
2. Run the server with:
```bash
uv run mcp install main.py
```
3. Restart the Claude Desktop application

### SSE Server Mode

This mode runs as a standalone SSE server that can be used with any MCP-compatible client, including Cursor:

1. Set `USE_CLAUDE_APP=false` in your `.env` file
2. Run the server with:
```bash
python main.py
```
3. The server will be available at `http://localhost:8000` by default (or your configured port)

### Docker Mode

You can also run the server using Docker Compose:

1. Make sure you have Docker and Docker Compose installed
2. Create your `.env` file with your configuration
3. Build and start the container:
```bash
docker-compose up -d
```
4. The server will run in SSE mode by default
5. To view logs:
```bash
docker-compose logs -f
```
6. To stop the server:
```bash
docker-compose down
```

## Configuration

The server can be configured using environment variables in the `.env` file:

| Variable | Description | Default |
|----------|-------------|---------|
| TRELLO_API_KEY | Your Trello API key | Required |
| TRELLO_TOKEN | Your Trello API token | Required |
| MCP_SERVER_NAME | The name of the MCP server | Trello MCP Server |
| MCP_SERVER_HOST | Host address for SSE mode | 0.0.0.0 |
| MCP_SERVER_PORT | Port for SSE mode | 8000 |
| USE_CLAUDE_APP | Whether to use Claude app mode | true |

You can customize the server by editing these values in your `.env` file.

## Client Integration

### Using with Claude Desktop

1. Run the server in Claude app mode (`USE_CLAUDE_APP=true`)
2. Start or restart Claude Desktop
3. Claude will automatically detect and connect to your MCP server

### Using with Cursor

To connect your MCP server to Cursor:

1. Run the server in SSE mode (`USE_CLAUDE_APP=false`)
2. In Cursor, go to Settings (gear icon) > AI > Model Context Protocol
3. Add a new server with URL `http://localhost:8000` (or your configured host/port)
4. Select the server when using Cursor's AI features

You can also add this configuration to your Cursor settings JSON file (typically at `~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "trello": {
      "url": "http://localhost:8000/sse"
    }
  }
}
```

### Using with Other MCP Clients

For other MCP-compatible clients, point them to the SSE endpoint at `http://localhost:8000`.

### Minimal Client Example

Here's a minimal Python example to connect to the SSE endpoint:

```python
import asyncio
import httpx

async def connect_to_mcp_server():
    url = "http://localhost:8000/sse"
    headers = {"Accept": "text/event-stream"}
    
    async with httpx.AsyncClient() as client:
        async with client.stream("GET", url, headers=headers) as response:
            # Get the session ID from the first SSE message
            session_id = None
            async for line in response.aiter_lines():
                if line.startswith("data:"):
                    data = line[5:].strip()
                    if "session_id=" in data and not session_id:
                        session_id = data.split("session_id=")[1]
                        
                        # Send a message using the session ID
                        message_url = f"http://localhost:8000/messages/?session_id={session_id}"
                        message = {
                            "role": "user",
                            "content": {
                                "type": "text",
                                "text": "Show me my Trello boards"
                            }
                        }
                        await client.post(message_url, json=message)

if __name__ == "__main__":
    asyncio.run(connect_to_mcp_server())
```

## Capabilities

| Operation | Board | List | Card | Checklist | Checklist Item |
|-----------|-------|------|------|-----------|----------------|
| Read      | ✅    | ✅    | ✅   | ✅        | ✅              |
| Write     | ❌    | ✅    | ✅   | ✅        | ✅              |
| Update    | ❌    | ✅    | ✅   | ✅        | ✅              |
| Delete    | ❌    | ✅    | ✅   | ✅        | ✅              |

### Detailed Capabilities

#### Board Operations
- ✅ Read all boards
- ✅ Read specific board details

#### List Operations
- ✅ Read all lists in a board
- ✅ Read specific list details
- ✅ Create new lists
- ✅ Update list name
- ✅ Archive (delete) lists

#### Card Operations
- ✅ Read all cards in a list
- ✅ Read specific card details
- ✅ Create new cards
- ✅ Update card attributes
- ✅ Delete cards

#### Checklist Operations
- ✅ Get a specific checklist
- ✅ List all checklists in a card
- ✅ Create a new checklist
- ✅ Update a checklist
- ✅ Delete a checklist
- ✅ Add checkitem to checklist
- ✅ Update checkitem
- ✅ Delete checkitem

## Usage

Once installed, you can interact with your Trello boards through Claude. Here are some example queries:

- "Show me all my boards"
- "What lists are in board [board_name]?"
- "Create a new card in list [list_name] with title [title]"
- "Update the description of card [card_name]"
- "Archive the list [list_name]"

Here are my example usages:

<img width="1277" alt="Example Usage of Trello MCP server: Asking to list all my cards in Guitar Board" src="https://github.com/user-attachments/assets/fef29dfc-04b2-4af9-92a6-f8db2320c860" />

<img width="1274" alt="Asking to add new song card into my project songs" src="https://github.com/user-attachments/assets/2d8406ca-1dde-41c0-a035-86d5271dd78f" />

<img width="1632" alt="Asking to add new card with checklist in it" src="https://github.com/user-attachments/assets/5a63f107-d135-402d-ab33-b9bf13eca751" />

## Troubleshooting

If you encounter issues:

1. Verify your Trello API credentials in the `.env` file
2. Check that you have proper permissions in your Trello workspace
3. Ensure Claude for Desktop is running the latest version
4. Check the logs for any error messages with `uv run mcp dev main.py` command.
5. Make sure uv is properly installed and in your PATH

## Contributing

Feel free to submit issues and enhancement requests!

```

--------------------------------------------------------------------------------
/server/__init__.py:
--------------------------------------------------------------------------------

```python

```

--------------------------------------------------------------------------------
/server/services/__init__.py:
--------------------------------------------------------------------------------

```python

```

--------------------------------------------------------------------------------
/server/utils/__init__.py:
--------------------------------------------------------------------------------

```python

```

--------------------------------------------------------------------------------
/server/tools/__init__.py:
--------------------------------------------------------------------------------

```python


```

--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------

```markdown
# Trello MCP Server TODO List

[ ] Add `pydantic` data classes for other update/create tool operations  
[ ] Move `models.py` into schemas

```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[project]
name = "trello-mcp"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "httpx>=0.28.1",
    "mcp[cli]>=1.5.0",
]

```

--------------------------------------------------------------------------------
/server/dtos/create_label.py:
--------------------------------------------------------------------------------

```python
from pydantic import BaseModel


class CreateLabelPayload(BaseModel):
    """
    Payload for creating a label.

    Attributes:
        name (str): The name of the label.
        color (str): The color of the label.
    """

    name: str
    color: str | None = None
```

--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------

```yaml
version: '3.8'

services:
  trello-mcp-server:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "${MCP_SERVER_PORT:-8000}:${MCP_SERVER_PORT:-8000}"
    volumes:
      - .:/app
    env_file:
      - .env
    environment:
      - PYTHONUNBUFFERED=1
      - USE_CLAUDE_APP=false  # Use SSE mode by default in Docker
    restart: unless-stopped 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
FROM python:3.12-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY pyproject.toml uv.lock ./
RUN pip install --no-cache-dir uv && \
    uv pip install --system -e .

# Copy application code
COPY . .

# Expose port
ENV MCP_SERVER_PORT=8000
EXPOSE ${MCP_SERVER_PORT}

# Run the application
CMD ["python", "main.py"] 
```

--------------------------------------------------------------------------------
/server/models.py:
--------------------------------------------------------------------------------

```python
from typing import List

from pydantic import BaseModel


class TrelloBoard(BaseModel):
    """Model representing a Trello board."""

    id: str
    name: str
    desc: str | None = None
    closed: bool = False
    idOrganization: str | None = None
    url: str


class TrelloList(BaseModel):
    """Model representing a Trello list."""

    id: str
    name: str
    closed: bool = False
    idBoard: str
    pos: float


class TrelloLabel(BaseModel):
    """Model representing a Trello label."""
    
    id: str
    name: str
    color: str | None = None


class TrelloCard(BaseModel):
    """Model representing a Trello card."""

    id: str
    name: str
    desc: str | None = None
    closed: bool = False
    idList: str
    idBoard: str
    url: str
    pos: float
    labels: List[TrelloLabel] = []
    due: str | None = None

```

--------------------------------------------------------------------------------
/server/tools/tools.py:
--------------------------------------------------------------------------------

```python
"""
This module contains tools for managing Trello boards, lists, and cards.
"""

from server.tools import board, card, checklist, list


def register_tools(mcp):
    """Register tools with the MCP server."""
    # Board Tools
    mcp.add_tool(board.get_board)
    mcp.add_tool(board.get_boards)
    mcp.add_tool(board.get_board_labels)
    mcp.add_tool(board.create_board_label)

    # List Tools
    mcp.add_tool(list.get_list)
    mcp.add_tool(list.get_lists)
    mcp.add_tool(list.create_list)
    mcp.add_tool(list.update_list)
    mcp.add_tool(list.delete_list)

    # Card Tools
    mcp.add_tool(card.get_card)
    mcp.add_tool(card.get_cards)
    mcp.add_tool(card.create_card)
    mcp.add_tool(card.update_card)
    mcp.add_tool(card.delete_card)

    # Checklist Tools
    mcp.add_tool(checklist.get_checklist)
    mcp.add_tool(checklist.get_card_checklists)
    mcp.add_tool(checklist.create_checklist)
    mcp.add_tool(checklist.update_checklist)
    mcp.add_tool(checklist.delete_checklist)
    mcp.add_tool(checklist.add_checkitem)
    mcp.add_tool(checklist.update_checkitem)
    mcp.add_tool(checklist.delete_checkitem)

```

--------------------------------------------------------------------------------
/server/dtos/create_card.py:
--------------------------------------------------------------------------------

```python
from pydantic import BaseModel


class CreateCardPayload(BaseModel):
    """
    Payload for creating a card.

    Attributes:
        name (str): The name of the card.
        desc (str): The description of the card.
        closed (bool): Whether the card is closed or not.
        idMembers (str): Comma-separated list of member IDs for the card.
        idList (str): The ID of the list the card is in.
        idLabels (str): Comma-separated list of label IDs for the card.
        idBoard (str): The ID of the board the card is in.
        pos (str | int): The position of the card.
        due (str): The due date of the card in ISO 8601 format.
        start (str): The start date of the card in ISO 8601 format.
        dueComplete (bool): Whether the card is due complete or not.
        subscribed (bool): Whether the card is subscribed or not.
    """

    name: str
    desc: str | None = None
    closed: bool | None = None
    idMembers: str | None = None
    idList: str
    idLabels: str | None = None
    idBoard: str | None = None
    pos: str | None = None
    due: str | None = None
    start: str | None = None
    dueComplete: bool | None = None
    subscribed: bool | None = None

```

--------------------------------------------------------------------------------
/server/dtos/update_card.py:
--------------------------------------------------------------------------------

```python
from pydantic import BaseModel


class UpdateCardPayload(BaseModel):
    """
    Payload for updating a card.

    Attributes:
        name (str): The name of the card.
        desc (str): The description of the card.
        closed (bool): Whether the card is closed or not.
        idMembers (str): Comma-separated list of member IDs for the card.
        idList (str): The ID of the list the card is in.
        idLabels (str): Comma-separated list of label IDs for the card.
        idBoard (str): The ID of the board the card is in.
        pos (str | int): The position of the card.
        due (str): The due date of the card in ISO 8601 format.
        start (str): The start date of the card in ISO 8601 format.
        dueComplete (bool): Whether the card is due complete or not.
        subscribed (bool): Whether the card is subscribed or not.
    """

    name: str | None = None
    desc: str | None = None
    closed: bool | None = None
    idMembers: str | None = None
    idList: str | None = None
    idLabels: str | None = None
    idBoard: str | None = None
    pos: str | None = None
    due: str | None = None
    start: str | None = None
    dueComplete: bool | None = None
    subscribed: bool | None = None

```

--------------------------------------------------------------------------------
/server/trello.py:
--------------------------------------------------------------------------------

```python
import logging
import os

from dotenv import load_dotenv

from server.utils.trello_api import TrelloClient

# Configure logging
logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()


# Initialize Trello client and service
try:
    api_key = os.getenv("TRELLO_API_KEY")
    token = os.getenv("TRELLO_TOKEN")
    if not api_key or not token:
        raise ValueError(
            "TRELLO_API_KEY and TRELLO_TOKEN must be set in environment variables"
        )
    client = TrelloClient(api_key=api_key, token=token)
    logger.info("Trello client and service initialized successfully")
except Exception as e:
    logger.error(f"Failed to initialize Trello client: {str(e)}")
    raise


# Add a prompt for common Trello operations
def trello_help() -> str:
    """Provides help information about available Trello operations."""
    return """
    Available Trello Operations:
    1. Board Operations:
       - Get a specific board
       - List all boards
       - Get board labels
       - Add label to a board
    2. List Operations:
       - Get a specific list
       - List all lists in a board
       - Create a new list
       - Update a list's name
       - Archive a list
    3. Card Operations:
       - Get a specific card
       - List all cards in a list
       - Create a new card
       - Update a card's attributes
       - Delete a card
    4. Checklist Operations:
       - Get a specific checklist
       - List all checklists in a card
       - Create a new checklist
       - Update a checklist
       - Delete a checklist
       - Add checkitem to checklist
       - Update checkitem
       - Delete checkitem
    """

```

--------------------------------------------------------------------------------
/server/services/board.py:
--------------------------------------------------------------------------------

```python
"""
Service for managing Trello boards in MCP server.
"""

from typing import List

from server.models import TrelloBoard, TrelloLabel
from server.utils.trello_api import TrelloClient


class BoardService:
    """
    Service class for managing Trello boards
    """

    def __init__(self, client: TrelloClient):
        self.client = client

    async def get_board(self, board_id: str) -> TrelloBoard:
        """Retrieves a specific board by its ID.

        Args:
            board_id (str): The ID of the board to retrieve.

        Returns:
            TrelloBoard: The board object containing board details.
        """
        response = await self.client.GET(f"/boards/{board_id}")
        return TrelloBoard(**response)

    async def get_boards(self, member_id: str = "me") -> List[TrelloBoard]:
        """Retrieves all boards for a given member.

        Args:
            member_id (str): The ID of the member whose boards to retrieve. Defaults to "me" for the authenticated user.

        Returns:
            List[TrelloBoard]: A list of board objects.
        """
        response = await self.client.GET(f"/members/{member_id}/boards")
        return [TrelloBoard(**board) for board in response]

    async def get_board_labels(self, board_id: str) -> List[TrelloLabel]:
        """Retrieves all labels for a specific board.

        Args:
            board_id (str): The ID of the board whose labels to retrieve.

        Returns:
            List[TrelloLabel]: A list of label objects for the board.
        """
        response = await self.client.GET(f"/boards/{board_id}/labels")
        return [TrelloLabel(**label) for label in response]

    async def create_board_label(self, board_id: str, **kwargs) -> TrelloLabel:
        """Create label for a specific board.

        Args:
            board_id (str): The ID of the board whose to add label.

        Returns:
            List[TrelloLabel]: A list of label objects for the board.
        """
        response = await self.client.POST(f"/boards/{board_id}/labels", data=kwargs)
        return TrelloLabel(**response)

```

--------------------------------------------------------------------------------
/server/services/card.py:
--------------------------------------------------------------------------------

```python
"""
Service for managing Trello cards in MCP server.
"""

from typing import Any, Dict, List

from server.models import TrelloCard
from server.utils.trello_api import TrelloClient


class CardService:
    """
    Service class for managing Trello cards.
    """

    def __init__(self, client: TrelloClient):
        self.client = client

    async def get_card(self, card_id: str) -> TrelloCard:
        """Retrieves a specific card by its ID.

        Args:
            card_id (str): The ID of the card to retrieve.

        Returns:
            TrelloCard: The card object containing card details.
        """
        response = await self.client.GET(f"/cards/{card_id}")
        return TrelloCard(**response)

    async def get_cards(self, list_id: str) -> List[TrelloCard]:
        """Retrieves all cards in a given list.

        Args:
            list_id (str): The ID of the list whose cards to retrieve.

        Returns:
            List[TrelloCard]: A list of card objects.
        """
        response = await self.client.GET(f"/lists/{list_id}/cards")
        return [TrelloCard(**card) for card in response]

    async def create_card(self, **kwargs) -> TrelloCard:
        """Creates a new card in a given list.

        Args
            list_id (str): The ID of the list to create the card in.
            name (str): The name of the new card.
            desc (str, optional): The description of the new card. Defaults to None.

        Returns:
            TrelloCard: The newly created card object.
        """
        response = await self.client.POST("/cards", data=kwargs)
        return TrelloCard(**response)

    async def update_card(self, card_id: str, **kwargs) -> TrelloCard:
        """Updates a card's attributes.

        Args:
            card_id (str): The ID of the card to update.
            **kwargs: Keyword arguments representing the attributes to update on the card.

        Returns:
            TrelloCard: The updated card object.
        """
        response = await self.client.PUT(f"/cards/{card_id}", data=kwargs)
        return TrelloCard(**response)

    async def delete_card(self, card_id: str) -> Dict[str, Any]:
        """Deletes a card.

        Args:
            card_id (str): The ID of the card to delete.

        Returns:
            Dict[str, Any]: The response from the delete operation.
        """
        return await self.client.DELETE(f"/cards/{card_id}")

```

--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------

```python
import logging
import os

import uvicorn
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

from server.tools.tools import register_tools

# Configure logging
logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()


# Initialize MCP server
mcp = FastMCP("Trello MCP Server")

# Register tools
register_tools(mcp)


def start_claude_server():
    """Start the MCP server in Claude app mode"""
    try:
        # Verify environment variables
        if not os.getenv("TRELLO_API_KEY") or not os.getenv("TRELLO_TOKEN"):
            raise ValueError(
                "TRELLO_API_KEY and TRELLO_TOKEN must be set in environment variables"
            )

        logger.info("Starting Trello MCP Server in Claude app mode...")
        mcp.run()
        logger.info("Trello MCP Server started successfully")
    except Exception as e:
        logger.error(f"Error starting Claude server: {str(e)}")
        raise


def start_sse_server():
    """Start the MCP server in SSE mode using uvicorn"""
    try:
        # Verify environment variables
        if not os.getenv("TRELLO_API_KEY") or not os.getenv("TRELLO_TOKEN"):
            raise ValueError(
                "TRELLO_API_KEY and TRELLO_TOKEN must be set in environment variables"
            )

        host = os.getenv("MCP_SERVER_HOST", "0.0.0.0")
        port = int(os.getenv("MCP_SERVER_PORT", "8000"))

        # Create Starlette app with MCP server mounted
        app = Starlette(
            routes=[
                Mount("/", app=mcp.sse_app()),
            ]
        )

        logger.info(
            f"Starting Trello MCP Server in SSE mode on http://{host}:{port}..."
        )
        uvicorn.run(app, host=host, port=port)
    except Exception as e:
        logger.error(f"Error starting SSE server: {str(e)}")
        raise


if __name__ == "__main__":
    try:
        # Check which mode to run in (default to true for Claude app mode)
        use_claude = os.getenv("USE_CLAUDE_APP", "true").lower() == "true"

        if use_claude:
            # Run in Claude app mode
            start_claude_server()
        else:
            # Run in SSE mode
            start_sse_server()
    except KeyboardInterrupt:
        logger.info("Shutting down server...")
    except Exception as e:
        logger.error(f"Server error: {str(e)}")
        raise

```

--------------------------------------------------------------------------------
/server/services/list.py:
--------------------------------------------------------------------------------

```python
from typing import List

from server.models import TrelloList
from server.utils.trello_api import TrelloClient


class ListService:
    """
    Service class for managing Trello lists.
    """

    def __init__(self, client: TrelloClient):
        self.client = client

    # Lists
    async def get_list(self, list_id: str) -> TrelloList:
        """Retrieves a specific list by its ID.

        Args:
            list_id (str): The ID of the list to retrieve.

        Returns:
            TrelloList: The list object containing list details.
        """
        response = await self.client.GET(f"/lists/{list_id}")
        return TrelloList(**response)

    async def get_lists(self, board_id: str) -> List[TrelloList]:
        """Retrieves all lists on a given board.

        Args:
            board_id (str): The ID of the board whose lists to retrieve.

        Returns:
            List[TrelloList]: A list of list objects.
        """
        response = await self.client.GET(f"/boards/{board_id}/lists")
        return [TrelloList(**list_data) for list_data in response]

    async def create_list(
        self, board_id: str, name: str, pos: str = "bottom"
    ) -> TrelloList:
        """Creates a new list on a given board.

        Args:
            board_id (str): The ID of the board to create the list in.
            name (str): The name of the new list.
            pos (str, optional): The position of the new list. Can be "top" or "bottom". Defaults to "bottom".

        Returns:
            TrelloList: The newly created list object.
        """
        data = {"name": name, "idBoard": board_id, "pos": pos}
        response = await self.client.POST("/lists", data=data)
        return TrelloList(**response)

    async def update_list(self, list_id: str, name: str) -> TrelloList:
        """Updates the name of a list.

        Args:
            list_id (str): The ID of the list to update.
            name (str): The new name for the list.

        Returns:
            TrelloList: The updated list object.
        """
        response = await self.client.PUT(f"/lists/{list_id}", data={"name": name})
        return TrelloList(**response)

    async def delete_list(self, list_id: str) -> TrelloList:
        """Archives a list.

        Args:
            list_id (str): The ID of the list to close.

        Returns:
            TrelloList: The archived list object.
        """
        response = await self.client.PUT(
            f"/lists/{list_id}/closed", data={"value": "true"}
        )
        return TrelloList(**response)

```

--------------------------------------------------------------------------------
/server/tools/board.py:
--------------------------------------------------------------------------------

```python
"""
This module contains tools for managing Trello boards.
"""

import logging
from typing import List

from mcp.server.fastmcp import Context

from server.models import TrelloBoard, TrelloLabel
from server.dtos.create_label import CreateLabelPayload
from server.services.board import BoardService
from server.trello import client

logger = logging.getLogger(__name__)

service = BoardService(client)


async def get_board(ctx: Context, board_id: str) -> TrelloBoard:
    """Retrieves a specific board by its ID.

    Args:
        board_id (str): The ID of the board to retrieve.

    Returns:
        TrelloBoard: The board object containing board details.
    """
    try:
        logger.info(f"Getting board with ID: {board_id}")
        result = await service.get_board(board_id)
        logger.info(f"Successfully retrieved board: {board_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get board: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def get_boards(ctx: Context) -> List[TrelloBoard]:
    """Retrieves all boards for the authenticated user.

    Returns:
        List[TrelloBoard]: A list of board objects.
    """
    try:
        logger.info("Getting all boards")
        result = await service.get_boards()
        logger.info(f"Successfully retrieved {len(result)} boards")
        return result
    except Exception as e:
        error_msg = f"Failed to get boards: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def get_board_labels(ctx: Context, board_id: str) -> List[TrelloLabel]:
    """Retrieves all labels for a specific board.

    Args:
        board_id (str): The ID of the board whose labels to retrieve.

    Returns:
        List[TrelloLabel]: A list of label objects for the board.
    """
    try:
        logger.info(f"Getting labels for board: {board_id}")
        result = await service.get_board_labels(board_id)
        logger.info(f"Successfully retrieved {len(result)} labels for board: {board_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get board labels: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def create_board_label(ctx: Context, board_id: str, payload: CreateLabelPayload) -> TrelloLabel:
    """Create label for a specific board.

    Args:
        board_id (str): The ID of the board whose to add label to.
        name (str): The name of the label.
        color (str): The color of the label.

    Returns:
        TrelloLabel: A label object for the board.
    """
    try:
        logger.info(f"Creating label {payload.name} label for board: {board_id}")
        result = await service.create_board_label(board_id, **payload.model_dump(exclude_unset=True))
        logger.info(f"Successfully created label {payload.name} labels for board: {board_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get board labels: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


```

--------------------------------------------------------------------------------
/server/utils/trello_api.py:
--------------------------------------------------------------------------------

```python
# trello_api.py
import logging

import httpx

# Configure logging
logger = logging.getLogger(__name__)

TRELLO_API_BASE = "https://api.trello.com/1"


class TrelloClient:
    """
    Client class for interacting with the Trello API over REST.
    """

    def __init__(self, api_key: str, token: str):
        self.api_key = api_key
        self.token = token
        self.base_url = TRELLO_API_BASE
        self.client = httpx.AsyncClient(base_url=self.base_url)

    async def close(self):
        await self.client.aclose()

    async def GET(self, endpoint: str, params: dict = None):
        all_params = {"key": self.api_key, "token": self.token}
        if params:
            all_params.update(params)
        try:
            response = await self.client.get(endpoint, params=all_params)
            response.raise_for_status()
            return response.json()
        except httpx.HTTPStatusError as e:
            logger.error(f"HTTP error: {e}")
            raise httpx.HTTPStatusError(
                f"Failed to get {endpoint}: {str(e)}",
                request=e.request,
                response=e.response,
            )
        except httpx.RequestError as e:
            logger.error(f"Request error: {e}")
            raise httpx.RequestError(f"Failed to get {endpoint}: {str(e)}")

    async def POST(self, endpoint: str, data: dict = None):
        all_params = {"key": self.api_key, "token": self.token}
        try:
            response = await self.client.post(endpoint, params=all_params, json=data)
            response.raise_for_status()
            return response.json()
        except httpx.HTTPStatusError as e:
            logger.error(f"HTTP error: {e}")
            raise httpx.HTTPStatusError(
                f"Failed to post to {endpoint}: {str(e)}",
                request=e.request,
                response=e.response,
            )
        except httpx.RequestError as e:
            logger.error(f"Request error: {e}")
            raise httpx.RequestError(f"Failed to post to {endpoint}: {str(e)}")

    async def PUT(self, endpoint: str, data: dict = None):
        all_params = {"key": self.api_key, "token": self.token}
        try:
            response = await self.client.put(endpoint, params=all_params, json=data)
            response.raise_for_status()
            return response.json()
        except httpx.HTTPStatusError as e:
            logger.error(f"HTTP error: {e}")
            raise httpx.HTTPStatusError(
                f"Failed to put to {endpoint}: {str(e)}",
                request=e.request,
                response=e.response,
            )
        except httpx.RequestError as e:
            logger.error(f"Request error: {e}")
            raise httpx.RequestError(f"Failed to put to {endpoint}: {str(e)}")

    async def DELETE(self, endpoint: str, params: dict = None):
        all_params = {"key": self.api_key, "token": self.token}
        if params:
            all_params.update(params)
        try:
            response = await self.client.delete(endpoint, params=all_params)
            response.raise_for_status()
            return response.json()
        except httpx.HTTPStatusError as e:
            logger.error(f"HTTP error: {e}")
            raise httpx.HTTPStatusError(
                f"Failed to delete {endpoint}: {str(e)}",
                request=e.request,
                response=e.response,
            )
        except httpx.RequestError as e:
            logger.error(f"Request error: {e}")
            raise httpx.RequestError(f"Failed to delete {endpoint}: {str(e)}")

```

--------------------------------------------------------------------------------
/server/tools/list.py:
--------------------------------------------------------------------------------

```python
"""
This module contains tools for managing Trello lists.
"""

import logging
from typing import List

from mcp.server.fastmcp import Context

from server.models import TrelloList
from server.services.list import ListService
from server.trello import client

logger = logging.getLogger(__name__)

service = ListService(client)


# List Tools
async def get_list(ctx: Context, list_id: str) -> TrelloList:
    """Retrieves a specific list by its ID.

    Args:
        list_id (str): The ID of the list to retrieve.

    Returns:
        TrelloList: The list object containing list details.
    """
    try:
        logger.info(f"Getting list with ID: {list_id}")
        result = await service.get_list(list_id)
        logger.info(f"Successfully retrieved list: {list_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get list: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def get_lists(ctx: Context, board_id: str) -> List[TrelloList]:
    """Retrieves all lists on a given board.

    Args:
        board_id (str): The ID of the board whose lists to retrieve.

    Returns:
        List[TrelloList]: A list of list objects.
    """
    try:
        logger.info(f"Getting lists for board: {board_id}")
        result = await service.get_lists(board_id)
        logger.info(f"Successfully retrieved {len(result)} lists for board: {board_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get lists: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def create_list(
    ctx: Context, board_id: str, name: str, pos: str = "bottom"
) -> TrelloList:
    """Creates a new list on a given board.

    Args:
        board_id (str): The ID of the board to create the list in.
        name (str): The name of the new list.
        pos (str, optional): The position of the new list. Can be "top" or "bottom". Defaults to "bottom".

    Returns:
        TrelloList: The newly created list object.
    """
    try:
        logger.info(f"Creating list '{name}' in board: {board_id}")
        result = await service.create_list(board_id, name, pos)
        logger.info(f"Successfully created list '{name}' in board: {board_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to create list: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def update_list(ctx: Context, list_id: str, name: str) -> TrelloList:
    """Updates the name of a list.

    Args:
        list_id (str): The ID of the list to update.
        name (str): The new name for the list.

    Returns:
        TrelloList: The updated list object.
    """
    try:
        logger.info(f"Updating list {list_id} with new name: {name}")
        result = await service.update_list(list_id, name)
        logger.info(f"Successfully updated list: {list_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to update list: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def delete_list(ctx: Context, list_id: str) -> TrelloList:
    """Archives a list.

    Args:
        list_id (str): The ID of the list to close.

    Returns:
        TrelloList: The archived list object.
    """
    try:
        logger.info(f"Archiving list: {list_id}")
        result = await service.delete_list(list_id)
        logger.info(f"Successfully archived list: {list_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to delete list: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise

```

--------------------------------------------------------------------------------
/server/tools/checklist.py:
--------------------------------------------------------------------------------

```python
"""
This module contains tools for managing Trello checklists.
"""

import logging
from typing import Dict, List

from server.services.checklist import ChecklistService
from server.trello import client

logger = logging.getLogger(__name__)
service = ChecklistService(client)


async def get_checklist(checklist_id: str) -> Dict:
    """
    Get a specific checklist by ID.

    Args:
        checklist_id (str): The ID of the checklist to retrieve

    Returns:
        Dict: The checklist data
    """
    return await service.get_checklist(checklist_id)


async def get_card_checklists(card_id: str) -> List[Dict]:
    """
    Get all checklists for a specific card.

    Args:
        card_id (str): The ID of the card to get checklists for

    Returns:
        List[Dict]: List of checklists on the card
    """
    return await service.get_card_checklists(card_id)


async def create_checklist(card_id: str, name: str, pos: str | None = None) -> Dict:
    """
    Create a new checklist on a card.

    Args:
        card_id (str): The ID of the card to create the checklist on
        name (str): The name of the checklist
        pos (Optional[str]): The position of the checklist (top, bottom, or a positive number)

    Returns:
        Dict: The created checklist data
    """
    return await service.create_checklist(card_id, name, pos)


async def update_checklist(
    checklist_id: str, name: str | None = None, pos: str | None = None
) -> Dict:
    """
    Update an existing checklist.

    Args:
        checklist_id (str): The ID of the checklist to update
        name (Optional[str]): New name for the checklist
        pos (Optional[str]): New position for the checklist

    Returns:
        Dict: The updated checklist data
    """
    return await service.update_checklist(checklist_id, name, pos)


async def delete_checklist(checklist_id: str) -> Dict:
    """
    Delete a checklist.

    Args:
        checklist_id (str): The ID of the checklist to delete

    Returns:
        Dict: The response from the delete operation
    """
    return await service.delete_checklist(checklist_id)


async def add_checkitem(
    checklist_id: str, name: str, checked: bool = False, pos: str | None = None
) -> Dict:
    """
    Add a new item to a checklist.

    Args:
        checklist_id (str): The ID of the checklist to add the item to
        name (str): The name of the checkitem
        checked (bool): Whether the item is checked
        pos (Optional[str]): The position of the item

    Returns:
        Dict: The created checkitem data
    """
    return await service.add_checkitem(checklist_id, name, checked, pos)


async def update_checkitem(
    checklist_id: str,
    checkitem_id: str,
    name: str | None = None,
    checked: bool | None = None,
    pos: str | None = None,
) -> Dict:
    """
    Update a checkitem in a checklist.

    Args:
        checklist_id (str): The ID of the checklist containing the item
        checkitem_id (str): The ID of the checkitem to update
        name (Optional[str]): New name for the checkitem
        checked (Optional[bool]): New checked state
        pos (Optional[str]): New position for the item

    Returns:
        Dict: The updated checkitem data
    """
    return await service.update_checkitem(
        checklist_id, checkitem_id, name, checked, pos
    )


async def delete_checkitem(checklist_id: str, checkitem_id: str) -> Dict:
    """
    Delete a checkitem from a checklist.

    Args:
        checklist_id (str): The ID of the checklist containing the item
        checkitem_id (str): The ID of the checkitem to delete

    Returns:
        Dict: The response from the delete operation
    """
    return await service.delete_checkitem(checklist_id, checkitem_id)

```

--------------------------------------------------------------------------------
/server/tools/card.py:
--------------------------------------------------------------------------------

```python
"""
This module contains tools for managing Trello cards.
"""

import logging
from typing import List

from mcp.server.fastmcp import Context

from server.models import TrelloCard
from server.services.card import CardService
from server.trello import client
from server.dtos.update_card import UpdateCardPayload
from server.dtos.create_card import CreateCardPayload

logger = logging.getLogger(__name__)

service = CardService(client)


async def get_card(ctx: Context, card_id: str) -> TrelloCard:
    """Retrieves a specific card by its ID.

    Args:
        card_id (str): The ID of the card to retrieve.

    Returns:
        TrelloCard: The card object containing card details.
    """
    try:
        logger.info(f"Getting card with ID: {card_id}")
        result = await service.get_card(card_id)
        logger.info(f"Successfully retrieved card: {card_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get card: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def get_cards(ctx: Context, list_id: str) -> List[TrelloCard]:
    """Retrieves all cards in a given list.

    Args:
        list_id (str): The ID of the list whose cards to retrieve.

    Returns:
        List[TrelloCard]: A list of card objects.
    """
    try:
        logger.info(f"Getting cards for list: {list_id}")
        result = await service.get_cards(list_id)
        logger.info(f"Successfully retrieved {len(result)} cards for list: {list_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to get cards: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def create_card(ctx: Context, payload: CreateCardPayload) -> TrelloCard:
    """Creates a new card in a given list.

    Args:
        list_id (str): The ID of the list to create the card in.
        name (str): The name of the new card.
        desc (str, optional): The description of the new card. Defaults to None.

    Returns:
        TrelloCard: The newly created card object.
    """
    try:
        logger.info(f"Creating card in list {payload.idList} with name: {payload.name}")
        result = await service.create_card(**payload.model_dump(exclude_unset=True))
        logger.info(f"Successfully created card in list: {payload.idList}")
        return result
    except Exception as e:
        error_msg = f"Failed to create card: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def update_card(
    ctx: Context, card_id: str, payload: UpdateCardPayload
) -> TrelloCard:
    """Updates a card's attributes.

    Args:
        card_id (str): The ID of the card to update.
        **kwargs: Keyword arguments representing the attributes to update on the card.

    Returns:
        TrelloCard: The updated card object.
    """
    try:
        logger.info(f"Updating card: {card_id} with payload: {payload}")
        result = await service.update_card(
            card_id, **payload.model_dump(exclude_unset=True)
        )
        logger.info(f"Successfully updated card: {card_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to update card: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise


async def delete_card(ctx: Context, card_id: str) -> dict:
    """Deletes a card.

    Args:
        card_id (str): The ID of the card to delete.

    Returns:
        dict: The response from the delete operation.
    """
    try:
        logger.info(f"Deleting card: {card_id}")
        result = await service.delete_card(card_id)
        logger.info(f"Successfully deleted card: {card_id}")
        return result
    except Exception as e:
        error_msg = f"Failed to delete card: {str(e)}"
        logger.error(error_msg)
        await ctx.error(error_msg)
        raise

```

--------------------------------------------------------------------------------
/server/services/checklist.py:
--------------------------------------------------------------------------------

```python
import logging
from typing import Dict, List

from server.utils.trello_api import TrelloClient

logger = logging.getLogger(__name__)


class ChecklistService:
    """
    Service class for handling Trello checklist operations.
    """

    def __init__(self, client: TrelloClient):
        self.client = client

    async def get_checklist(self, checklist_id: str) -> Dict:
        """
        Get a specific checklist by ID.

        Args:
            checklist_id (str): The ID of the checklist to retrieve

        Returns:
            Dict: The checklist data
        """
        return await self.client.GET(f"/checklists/{checklist_id}")

    async def get_card_checklists(self, card_id: str) -> List[Dict]:
        """
        Get all checklists for a specific card.

        Args:
            card_id (str): The ID of the card to get checklists for

        Returns:
            List[Dict]: List of checklists on the card
        """
        return await self.client.GET(f"/cards/{card_id}/checklists")

    async def create_checklist(
        self, card_id: str, name: str, pos: str | None = None
    ) -> Dict:
        """
        Create a new checklist on a card.

        Args:
            card_id (str): The ID of the card to create the checklist on
            name (str): The name of the checklist
            pos (Optional[str]): The position of the checklist (top, bottom, or a positive number)

        Returns:
            Dict: The created checklist data
        """
        data = {"name": name}
        if pos:
            data["pos"] = pos
        return await self.client.POST(f"/checklists", data={"idCard": card_id, **data})

    async def update_checklist(
        self, checklist_id: str, name: str | None = None, pos: str | None = None
    ) -> Dict:
        """
        Update an existing checklist.

        Args:
            checklist_id (str): The ID of the checklist to update
            name (Optional[str]): New name for the checklist
            pos (Optional[str]): New position for the checklist

        Returns:
            Dict: The updated checklist data
        """
        data = {}
        if name:
            data["name"] = name
        if pos:
            data["pos"] = pos
        return await self.client.PUT(f"/checklists/{checklist_id}", data=data)

    async def delete_checklist(self, checklist_id: str) -> Dict:
        """
        Delete a checklist.

        Args:
            checklist_id (str): The ID of the checklist to delete

        Returns:
            Dict: The response from the delete operation
        """
        return await self.client.DELETE(f"/checklists/{checklist_id}")

    async def add_checkitem(
        self,
        checklist_id: str,
        name: str,
        checked: bool = False,
        pos: str | None = None,
    ) -> Dict:
        """
        Add a new item to a checklist.

        Args:
            checklist_id (str): The ID of the checklist to add the item to
            name (str): The name of the checkitem
            checked (bool): Whether the item is checked
            pos (Optional[str]): The position of the item

        Returns:
            Dict: The created checkitem data
        """
        data = {"name": name, "checked": checked}
        if pos:
            data["pos"] = pos
        return await self.client.POST(
            f"/checklists/{checklist_id}/checkItems", data=data
        )

    async def update_checkitem(
        self,
        checklist_id: str,
        checkitem_id: str,
        name: str | None = None,
        checked: bool | None = None,
        pos: str | None = None,
    ) -> Dict:
        """
        Update a checkitem in a checklist.

        Args:
            checklist_id (str): The ID of the checklist containing the item
            checkitem_id (str): The ID of the checkitem to update
            name (Optional[str]): New name for the checkitem
            checked (Optional[bool]): New checked state
            pos (Optional[str]): New position for the item

        Returns:
            Dict: The updated checkitem data
        """
        data = {}
        if name:
            data["name"] = name
        if checked is not None:
            data["checked"] = checked
        if pos:
            data["pos"] = pos
        return await self.client.PUT(
            f"/checklists/{checklist_id}/checkItems/{checkitem_id}", data=data
        )

    async def delete_checkitem(self, checklist_id: str, checkitem_id: str) -> Dict:
        """
        Delete a checkitem from a checklist.

        Args:
            checklist_id (str): The ID of the checklist containing the item
            checkitem_id (str): The ID of the checkitem to delete

        Returns:
            Dict: The response from the delete operation
        """
        return await self.client.DELETE(
            f"/checklists/{checklist_id}/checkItems/{checkitem_id}"
        )

```