# 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}"
)
```