t.aiello commited on
Commit
7360460
·
1 Parent(s): 4d17015

add trello mcp server

Browse files
pmcp/trello_server/__init__.py ADDED
File without changes
pmcp/trello_server/dtos/update_card.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class UpdateCardPayload(BaseModel):
5
+ """
6
+ Payload for updating a card.
7
+
8
+ Attributes:
9
+ name (str): The name of the card.
10
+ desc (str): The description of the card.
11
+ pos (str | int): The position of the card.
12
+ closed (bool): Whether the card is closed or not.
13
+ due (str): The due date of the card in ISO 8601 format.
14
+ idLabels (str): Comma-separated list of label IDs for the card.
15
+ """
16
+
17
+ name: str | None = None
18
+ desc: str | None = None
19
+ pos: str | None = None
20
+ closed: bool | None = None
21
+ due: str | None = None
22
+ idLabels: str | None = None
pmcp/trello_server/models.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class TrelloBoard(BaseModel):
7
+ """Model representing a Trello board."""
8
+
9
+ id: str
10
+ name: str
11
+ desc: Optional[str] = None
12
+ closed: bool = False
13
+ idOrganization: Optional[str] = None
14
+ url: str
15
+
16
+
17
+ class TrelloList(BaseModel):
18
+ """Model representing a Trello list."""
19
+
20
+ id: str
21
+ name: str
22
+ closed: bool = False
23
+ idBoard: str
24
+ pos: float
25
+
26
+
27
+ class TrelloLabel(BaseModel):
28
+ """Model representing a Trello label."""
29
+
30
+ id: str
31
+ name: str
32
+ color: Optional[str] = None
33
+
34
+
35
+ class TrelloCard(BaseModel):
36
+ """Model representing a Trello card."""
37
+
38
+ id: str
39
+ name: str
40
+ desc: Optional[str] = None
41
+ closed: bool = False
42
+ idList: str
43
+ idBoard: str
44
+ url: str
45
+ pos: float
46
+ labels: List[TrelloLabel] = []
47
+ due: Optional[str] = None
pmcp/trello_server/services/__init__.py ADDED
File without changes
pmcp/trello_server/services/board.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Service for managing Trello boards in MCP server.
3
+ """
4
+
5
+ from typing import List
6
+
7
+ from pmcp.trello_server.models import TrelloBoard, TrelloLabel
8
+ from pmcp.trello_server.utils.trello_api import TrelloClient
9
+
10
+
11
+ class BoardService:
12
+ """
13
+ Service class for managing Trello boards
14
+ """
15
+
16
+ def __init__(self, client: TrelloClient):
17
+ self.client = client
18
+
19
+ async def get_board(self, board_id: str) -> TrelloBoard:
20
+ """Retrieves a specific board by its ID.
21
+
22
+ Args:
23
+ board_id (str): The ID of the board to retrieve.
24
+
25
+ Returns:
26
+ TrelloBoard: The board object containing board details.
27
+ """
28
+ response = await self.client.GET(f"/boards/{board_id}")
29
+ return TrelloBoard(**response)
30
+
31
+ async def get_boards(self, member_id: str = "me") -> List[TrelloBoard]:
32
+ """Retrieves all boards for a given member.
33
+
34
+ Args:
35
+ member_id (str): The ID of the member whose boards to retrieve. Defaults to "me" for the authenticated user.
36
+
37
+ Returns:
38
+ List[TrelloBoard]: A list of board objects.
39
+ """
40
+ response = await self.client.GET(f"/members/{member_id}/boards")
41
+ return [TrelloBoard(**board) for board in response]
42
+
43
+ async def get_board_labels(self, board_id: str) -> List[TrelloLabel]:
44
+ """Retrieves all labels for a specific board.
45
+
46
+ Args:
47
+ board_id (str): The ID of the board whose labels to retrieve.
48
+
49
+ Returns:
50
+ List[TrelloLabel]: A list of label objects for the board.
51
+ """
52
+ response = await self.client.GET(f"/boards/{board_id}/labels")
53
+ return [TrelloLabel(**label) for label in response]
pmcp/trello_server/services/card.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Service for managing Trello cards in MCP server.
3
+ """
4
+
5
+ from typing import Any, Dict, List
6
+
7
+ from pmcp.trello_server.models import TrelloCard
8
+ from pmcp.trello_server.utils.trello_api import TrelloClient
9
+
10
+
11
+ class CardService:
12
+ """
13
+ Service class for managing Trello cards.
14
+ """
15
+
16
+ def __init__(self, client: TrelloClient):
17
+ self.client = client
18
+
19
+ async def get_card(self, card_id: str) -> TrelloCard:
20
+ """Retrieves a specific card by its ID.
21
+
22
+ Args:
23
+ card_id (str): The ID of the card to retrieve.
24
+
25
+ Returns:
26
+ TrelloCard: The card object containing card details.
27
+ """
28
+ response = await self.client.GET(f"/cards/{card_id}")
29
+ return TrelloCard(**response)
30
+
31
+ async def get_cards(self, list_id: str) -> List[TrelloCard]:
32
+ """Retrieves all cards in a given list.
33
+
34
+ Args:
35
+ list_id (str): The ID of the list whose cards to retrieve.
36
+
37
+ Returns:
38
+ List[TrelloCard]: A list of card objects.
39
+ """
40
+ response = await self.client.GET(f"/lists/{list_id}/cards")
41
+ return [TrelloCard(**card) for card in response]
42
+
43
+ async def create_card(
44
+ self, list_id: str, name: str, desc: str | None = None
45
+ ) -> TrelloCard:
46
+ """Creates a new card in a given list.
47
+
48
+ Args:
49
+ list_id (str): The ID of the list to create the card in.
50
+ name (str): The name of the new card.
51
+ desc (str, optional): The description of the new card. Defaults to None.
52
+
53
+ Returns:
54
+ TrelloCard: The newly created card object.
55
+ """
56
+ data = {"name": name, "idList": list_id}
57
+ if desc:
58
+ data["desc"] = desc
59
+ response = await self.client.POST("/cards", data=data)
60
+ return TrelloCard(**response)
61
+
62
+ async def update_card(self, card_id: str, **kwargs) -> TrelloCard:
63
+ """Updates a card's attributes.
64
+
65
+ Args:
66
+ card_id (str): The ID of the card to update.
67
+ **kwargs: Keyword arguments representing the attributes to update on the card.
68
+
69
+ Returns:
70
+ TrelloCard: The updated card object.
71
+ """
72
+ response = await self.client.PUT(f"/cards/{card_id}", data=kwargs)
73
+ return TrelloCard(**response)
74
+
75
+ async def delete_card(self, card_id: str) -> Dict[str, Any]:
76
+ """Deletes a card.
77
+
78
+ Args:
79
+ card_id (str): The ID of the card to delete.
80
+
81
+ Returns:
82
+ Dict[str, Any]: The response from the delete operation.
83
+ """
84
+ return await self.client.DELETE(f"/cards/{card_id}")
pmcp/trello_server/services/checklist.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from typing import Dict, List, Optional
3
+
4
+ from pmcp.trello_server.utils.trello_api import TrelloClient
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class ChecklistService:
10
+ """
11
+ Service class for handling Trello checklist operations.
12
+ """
13
+
14
+ def __init__(self, client: TrelloClient):
15
+ self.client = client
16
+
17
+ async def get_checklist(self, checklist_id: str) -> Dict:
18
+ """
19
+ Get a specific checklist by ID.
20
+
21
+ Args:
22
+ checklist_id (str): The ID of the checklist to retrieve
23
+
24
+ Returns:
25
+ Dict: The checklist data
26
+ """
27
+ return await self.client.GET(f"/checklists/{checklist_id}")
28
+
29
+ async def get_card_checklists(self, card_id: str) -> List[Dict]:
30
+ """
31
+ Get all checklists for a specific card.
32
+
33
+ Args:
34
+ card_id (str): The ID of the card to get checklists for
35
+
36
+ Returns:
37
+ List[Dict]: List of checklists on the card
38
+ """
39
+ return await self.client.GET(f"/cards/{card_id}/checklists")
40
+
41
+ async def create_checklist(
42
+ self, card_id: str, name: str, pos: Optional[str] = None
43
+ ) -> Dict:
44
+ """
45
+ Create a new checklist on a card.
46
+
47
+ Args:
48
+ card_id (str): The ID of the card to create the checklist on
49
+ name (str): The name of the checklist
50
+ pos (Optional[str]): The position of the checklist (top, bottom, or a positive number)
51
+
52
+ Returns:
53
+ Dict: The created checklist data
54
+ """
55
+ data = {"name": name}
56
+ if pos:
57
+ data["pos"] = pos
58
+ return await self.client.POST(f"/checklists", data={"idCard": card_id, **data})
59
+
60
+ async def update_checklist(
61
+ self, checklist_id: str, name: Optional[str] = None, pos: Optional[str] = None
62
+ ) -> Dict:
63
+ """
64
+ Update an existing checklist.
65
+
66
+ Args:
67
+ checklist_id (str): The ID of the checklist to update
68
+ name (Optional[str]): New name for the checklist
69
+ pos (Optional[str]): New position for the checklist
70
+
71
+ Returns:
72
+ Dict: The updated checklist data
73
+ """
74
+ data = {}
75
+ if name:
76
+ data["name"] = name
77
+ if pos:
78
+ data["pos"] = pos
79
+ return await self.client.PUT(f"/checklists/{checklist_id}", data=data)
80
+
81
+ async def delete_checklist(self, checklist_id: str) -> Dict:
82
+ """
83
+ Delete a checklist.
84
+
85
+ Args:
86
+ checklist_id (str): The ID of the checklist to delete
87
+
88
+ Returns:
89
+ Dict: The response from the delete operation
90
+ """
91
+ return await self.client.DELETE(f"/checklists/{checklist_id}")
92
+
93
+ async def add_checkitem(
94
+ self,
95
+ checklist_id: str,
96
+ name: str,
97
+ checked: bool = False,
98
+ pos: Optional[str] = None,
99
+ ) -> Dict:
100
+ """
101
+ Add a new item to a checklist.
102
+
103
+ Args:
104
+ checklist_id (str): The ID of the checklist to add the item to
105
+ name (str): The name of the checkitem
106
+ checked (bool): Whether the item is checked
107
+ pos (Optional[str]): The position of the item
108
+
109
+ Returns:
110
+ Dict: The created checkitem data
111
+ """
112
+ data = {"name": name, "checked": checked}
113
+ if pos:
114
+ data["pos"] = pos
115
+ return await self.client.POST(
116
+ f"/checklists/{checklist_id}/checkItems", data=data
117
+ )
118
+
119
+ async def update_checkitem(
120
+ self,
121
+ checklist_id: str,
122
+ checkitem_id: str,
123
+ name: Optional[str] = None,
124
+ checked: Optional[bool] = None,
125
+ pos: Optional[str] = None,
126
+ ) -> Dict:
127
+ """
128
+ Update a checkitem in a checklist.
129
+
130
+ Args:
131
+ checklist_id (str): The ID of the checklist containing the item
132
+ checkitem_id (str): The ID of the checkitem to update
133
+ name (Optional[str]): New name for the checkitem
134
+ checked (Optional[bool]): New checked state
135
+ pos (Optional[str]): New position for the item
136
+
137
+ Returns:
138
+ Dict: The updated checkitem data
139
+ """
140
+ data = {}
141
+ if name:
142
+ data["name"] = name
143
+ if checked is not None:
144
+ data["checked"] = checked
145
+ if pos:
146
+ data["pos"] = pos
147
+ return await self.client.PUT(
148
+ f"/checklists/{checklist_id}/checkItems/{checkitem_id}", data=data
149
+ )
150
+
151
+ async def delete_checkitem(self, checklist_id: str, checkitem_id: str) -> Dict:
152
+ """
153
+ Delete a checkitem from a checklist.
154
+
155
+ Args:
156
+ checklist_id (str): The ID of the checklist containing the item
157
+ checkitem_id (str): The ID of the checkitem to delete
158
+
159
+ Returns:
160
+ Dict: The response from the delete operation
161
+ """
162
+ return await self.client.DELETE(
163
+ f"/checklists/{checklist_id}/checkItems/{checkitem_id}"
164
+ )
pmcp/trello_server/services/list.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+
3
+ from pmcp.trello_server.models import TrelloList
4
+ from pmcp.trello_server.utils.trello_api import TrelloClient
5
+
6
+
7
+ class ListService:
8
+ """
9
+ Service class for managing Trello lists.
10
+ """
11
+
12
+ def __init__(self, client: TrelloClient):
13
+ self.client = client
14
+
15
+ # Lists
16
+ async def get_list(self, list_id: str) -> TrelloList:
17
+ """Retrieves a specific list by its ID.
18
+
19
+ Args:
20
+ list_id (str): The ID of the list to retrieve.
21
+
22
+ Returns:
23
+ TrelloList: The list object containing list details.
24
+ """
25
+ response = await self.client.GET(f"/lists/{list_id}")
26
+ return TrelloList(**response)
27
+
28
+ async def get_lists(self, board_id: str) -> List[TrelloList]:
29
+ """Retrieves all lists on a given board.
30
+
31
+ Args:
32
+ board_id (str): The ID of the board whose lists to retrieve.
33
+
34
+ Returns:
35
+ List[TrelloList]: A list of list objects.
36
+ """
37
+ response = await self.client.GET(f"/boards/{board_id}/lists")
38
+ return [TrelloList(**list_data) for list_data in response]
39
+
40
+ async def create_list(
41
+ self, board_id: str, name: str, pos: str = "bottom"
42
+ ) -> TrelloList:
43
+ """Creates a new list on a given board.
44
+
45
+ Args:
46
+ board_id (str): The ID of the board to create the list in.
47
+ name (str): The name of the new list.
48
+ pos (str, optional): The position of the new list. Can be "top" or "bottom". Defaults to "bottom".
49
+
50
+ Returns:
51
+ TrelloList: The newly created list object.
52
+ """
53
+ data = {"name": name, "idBoard": board_id, "pos": pos}
54
+ response = await self.client.POST("/lists", data=data)
55
+ return TrelloList(**response)
56
+
57
+ async def update_list(self, list_id: str, name: str) -> TrelloList:
58
+ """Updates the name of a list.
59
+
60
+ Args:
61
+ list_id (str): The ID of the list to update.
62
+ name (str): The new name for the list.
63
+
64
+ Returns:
65
+ TrelloList: The updated list object.
66
+ """
67
+ response = await self.client.PUT(f"/lists/{list_id}", data={"name": name})
68
+ return TrelloList(**response)
69
+
70
+ async def delete_list(self, list_id: str) -> TrelloList:
71
+ """Archives a list.
72
+
73
+ Args:
74
+ list_id (str): The ID of the list to close.
75
+
76
+ Returns:
77
+ TrelloList: The archived list object.
78
+ """
79
+ response = await self.client.PUT(
80
+ f"/lists/{list_id}/closed", data={"value": "true"}
81
+ )
82
+ return TrelloList(**response)
pmcp/trello_server/tools/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+
pmcp/trello_server/tools/board.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains tools for managing Trello boards.
3
+ """
4
+
5
+ import logging
6
+ from typing import List
7
+
8
+ from mcp.server.fastmcp import Context
9
+
10
+ from pmcp.trello_server.models import TrelloBoard, TrelloLabel
11
+ from pmcp.trello_server.services.board import BoardService
12
+ from pmcp.trello_server.trello import client
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ service = BoardService(client)
17
+
18
+
19
+ async def get_board(ctx: Context, board_id: str) -> TrelloBoard:
20
+ """Retrieves a specific board by its ID.
21
+
22
+ Args:
23
+ board_id (str): The ID of the board to retrieve.
24
+
25
+ Returns:
26
+ TrelloBoard: The board object containing board details.
27
+ """
28
+ try:
29
+ logger.info(f"Getting board with ID: {board_id}")
30
+ result = await service.get_board(board_id)
31
+ logger.info(f"Successfully retrieved board: {board_id}")
32
+ return result
33
+ except Exception as e:
34
+ error_msg = f"Failed to get board: {str(e)}"
35
+ logger.error(error_msg)
36
+ await ctx.error(error_msg)
37
+ raise
38
+
39
+
40
+ async def get_boards(ctx: Context) -> List[TrelloBoard]:
41
+ """Retrieves all boards for the authenticated user.
42
+
43
+ Returns:
44
+ List[TrelloBoard]: A list of board objects.
45
+ """
46
+ try:
47
+ logger.info("Getting all boards")
48
+ result = await service.get_boards()
49
+ logger.info(f"Successfully retrieved {len(result)} boards")
50
+ return result
51
+ except Exception as e:
52
+ error_msg = f"Failed to get boards: {str(e)}"
53
+ logger.error(error_msg)
54
+ await ctx.error(error_msg)
55
+ raise
56
+
57
+
58
+ async def get_board_labels(ctx: Context, board_id: str) -> List[TrelloLabel]:
59
+ """Retrieves all labels for a specific board.
60
+
61
+ Args:
62
+ board_id (str): The ID of the board whose labels to retrieve.
63
+
64
+ Returns:
65
+ List[TrelloLabel]: A list of label objects for the board.
66
+ """
67
+ try:
68
+ logger.info(f"Getting labels for board: {board_id}")
69
+ result = await service.get_board_labels(board_id)
70
+ logger.info(f"Successfully retrieved {len(result)} labels for board: {board_id}")
71
+ return result
72
+ except Exception as e:
73
+ error_msg = f"Failed to get board labels: {str(e)}"
74
+ logger.error(error_msg)
75
+ await ctx.error(error_msg)
76
+ raise
pmcp/trello_server/tools/card.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains tools for managing Trello cards.
3
+ """
4
+
5
+ import logging
6
+ from typing import List
7
+
8
+ from mcp.server.fastmcp import Context
9
+
10
+ from pmcp.trello_server.models import TrelloCard
11
+ from pmcp.trello_server.services.card import CardService
12
+ from pmcp.trello_server.trello import client
13
+ from pmcp.trello_server.dtos.update_card import UpdateCardPayload
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ service = CardService(client)
18
+
19
+
20
+ async def get_card(ctx: Context, card_id: str) -> TrelloCard:
21
+ """Retrieves a specific card by its ID.
22
+
23
+ Args:
24
+ card_id (str): The ID of the card to retrieve.
25
+
26
+ Returns:
27
+ TrelloCard: The card object containing card details.
28
+ """
29
+ try:
30
+ logger.info(f"Getting card with ID: {card_id}")
31
+ result = await service.get_card(card_id)
32
+ logger.info(f"Successfully retrieved card: {card_id}")
33
+ return result
34
+ except Exception as e:
35
+ error_msg = f"Failed to get card: {str(e)}"
36
+ logger.error(error_msg)
37
+ await ctx.error(error_msg)
38
+ raise
39
+
40
+
41
+ async def get_cards(ctx: Context, list_id: str) -> List[TrelloCard]:
42
+ """Retrieves all cards in a given list.
43
+
44
+ Args:
45
+ list_id (str): The ID of the list whose cards to retrieve.
46
+
47
+ Returns:
48
+ List[TrelloCard]: A list of card objects.
49
+ """
50
+ try:
51
+ logger.info(f"Getting cards for list: {list_id}")
52
+ result = await service.get_cards(list_id)
53
+ logger.info(f"Successfully retrieved {len(result)} cards for list: {list_id}")
54
+ return result
55
+ except Exception as e:
56
+ error_msg = f"Failed to get cards: {str(e)}"
57
+ logger.error(error_msg)
58
+ await ctx.error(error_msg)
59
+ raise
60
+
61
+
62
+ async def create_card(
63
+ ctx: Context, list_id: str, name: str, desc: str | None = None
64
+ ) -> TrelloCard:
65
+ """Creates a new card in a given list.
66
+
67
+ Args:
68
+ list_id (str): The ID of the list to create the card in.
69
+ name (str): The name of the new card.
70
+ desc (str, optional): The description of the new card. Defaults to None.
71
+
72
+ Returns:
73
+ TrelloCard: The newly created card object.
74
+ """
75
+ try:
76
+ logger.info(f"Creating card in list {list_id} with name: {name}")
77
+ result = await service.create_card(list_id, name, desc)
78
+ logger.info(f"Successfully created card in list: {list_id}")
79
+ return result
80
+ except Exception as e:
81
+ error_msg = f"Failed to create card: {str(e)}"
82
+ logger.error(error_msg)
83
+ await ctx.error(error_msg)
84
+ raise
85
+
86
+
87
+ async def update_card(
88
+ ctx: Context, card_id: str, payload: UpdateCardPayload
89
+ ) -> TrelloCard:
90
+ """Updates a card's attributes.
91
+
92
+ Args:
93
+ card_id (str): The ID of the card to update.
94
+ **kwargs: Keyword arguments representing the attributes to update on the card.
95
+
96
+ Returns:
97
+ TrelloCard: The updated card object.
98
+ """
99
+ try:
100
+ logger.info(f"Updating card: {card_id} with payload: {payload}")
101
+ result = await service.update_card(
102
+ card_id, **payload.model_dump(exclude_unset=True)
103
+ )
104
+ logger.info(f"Successfully updated card: {card_id}")
105
+ return result
106
+ except Exception as e:
107
+ error_msg = f"Failed to update card: {str(e)}"
108
+ logger.error(error_msg)
109
+ await ctx.error(error_msg)
110
+ raise
111
+
112
+
113
+ async def delete_card(ctx: Context, card_id: str) -> dict:
114
+ """Deletes a card.
115
+
116
+ Args:
117
+ card_id (str): The ID of the card to delete.
118
+
119
+ Returns:
120
+ dict: The response from the delete operation.
121
+ """
122
+ try:
123
+ logger.info(f"Deleting card: {card_id}")
124
+ result = await service.delete_card(card_id)
125
+ logger.info(f"Successfully deleted card: {card_id}")
126
+ return result
127
+ except Exception as e:
128
+ error_msg = f"Failed to delete card: {str(e)}"
129
+ logger.error(error_msg)
130
+ await ctx.error(error_msg)
131
+ raise
pmcp/trello_server/tools/checklist.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains tools for managing Trello checklists.
3
+ """
4
+
5
+ import logging
6
+ from typing import Dict, List, Optional
7
+
8
+ from pmcp.trello_server.services.checklist import ChecklistService
9
+ from pmcp.trello_server.trello import client
10
+
11
+ logger = logging.getLogger(__name__)
12
+ service = ChecklistService(client)
13
+
14
+
15
+ async def get_checklist(checklist_id: str) -> Dict:
16
+ """
17
+ Get a specific checklist by ID.
18
+
19
+ Args:
20
+ checklist_id (str): The ID of the checklist to retrieve
21
+
22
+ Returns:
23
+ Dict: The checklist data
24
+ """
25
+ return await service.get_checklist(checklist_id)
26
+
27
+
28
+ async def get_card_checklists(card_id: str) -> List[Dict]:
29
+ """
30
+ Get all checklists for a specific card.
31
+
32
+ Args:
33
+ card_id (str): The ID of the card to get checklists for
34
+
35
+ Returns:
36
+ List[Dict]: List of checklists on the card
37
+ """
38
+ return await service.get_card_checklists(card_id)
39
+
40
+
41
+ async def create_checklist(card_id: str, name: str, pos: Optional[str] = None) -> Dict:
42
+ """
43
+ Create a new checklist on a card.
44
+
45
+ Args:
46
+ card_id (str): The ID of the card to create the checklist on
47
+ name (str): The name of the checklist
48
+ pos (Optional[str]): The position of the checklist (top, bottom, or a positive number)
49
+
50
+ Returns:
51
+ Dict: The created checklist data
52
+ """
53
+ return await service.create_checklist(card_id, name, pos)
54
+
55
+
56
+ async def update_checklist(
57
+ checklist_id: str, name: Optional[str] = None, pos: Optional[str] = None
58
+ ) -> Dict:
59
+ """
60
+ Update an existing checklist.
61
+
62
+ Args:
63
+ checklist_id (str): The ID of the checklist to update
64
+ name (Optional[str]): New name for the checklist
65
+ pos (Optional[str]): New position for the checklist
66
+
67
+ Returns:
68
+ Dict: The updated checklist data
69
+ """
70
+ return await service.update_checklist(checklist_id, name, pos)
71
+
72
+
73
+ async def delete_checklist(checklist_id: str) -> Dict:
74
+ """
75
+ Delete a checklist.
76
+
77
+ Args:
78
+ checklist_id (str): The ID of the checklist to delete
79
+
80
+ Returns:
81
+ Dict: The response from the delete operation
82
+ """
83
+ return await service.delete_checklist(checklist_id)
84
+
85
+
86
+ async def add_checkitem(
87
+ checklist_id: str, name: str, checked: bool = False, pos: Optional[str] = None
88
+ ) -> Dict:
89
+ """
90
+ Add a new item to a checklist.
91
+
92
+ Args:
93
+ checklist_id (str): The ID of the checklist to add the item to
94
+ name (str): The name of the checkitem
95
+ checked (bool): Whether the item is checked
96
+ pos (Optional[str]): The position of the item
97
+
98
+ Returns:
99
+ Dict: The created checkitem data
100
+ """
101
+ return await service.add_checkitem(checklist_id, name, checked, pos)
102
+
103
+
104
+ async def update_checkitem(
105
+ checklist_id: str,
106
+ checkitem_id: str,
107
+ name: Optional[str] = None,
108
+ checked: Optional[bool] = None,
109
+ pos: Optional[str] = None,
110
+ ) -> Dict:
111
+ """
112
+ Update a checkitem in a checklist.
113
+
114
+ Args:
115
+ checklist_id (str): The ID of the checklist containing the item
116
+ checkitem_id (str): The ID of the checkitem to update
117
+ name (Optional[str]): New name for the checkitem
118
+ checked (Optional[bool]): New checked state
119
+ pos (Optional[str]): New position for the item
120
+
121
+ Returns:
122
+ Dict: The updated checkitem data
123
+ """
124
+ return await service.update_checkitem(
125
+ checklist_id, checkitem_id, name, checked, pos
126
+ )
127
+
128
+
129
+ async def delete_checkitem(checklist_id: str, checkitem_id: str) -> Dict:
130
+ """
131
+ Delete a checkitem from a checklist.
132
+
133
+ Args:
134
+ checklist_id (str): The ID of the checklist containing the item
135
+ checkitem_id (str): The ID of the checkitem to delete
136
+
137
+ Returns:
138
+ Dict: The response from the delete operation
139
+ """
140
+ return await service.delete_checkitem(checklist_id, checkitem_id)
pmcp/trello_server/tools/list.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains tools for managing Trello lists.
3
+ """
4
+
5
+ import logging
6
+ from typing import List
7
+
8
+ from mcp.server.fastmcp import Context
9
+
10
+ from pmcp.trello_server.models import TrelloList
11
+ from pmcp.trello_server.services.list import ListService
12
+ from pmcp.trello_server.trello import client
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ service = ListService(client)
17
+
18
+
19
+ # List Tools
20
+ async def get_list(ctx: Context, list_id: str) -> TrelloList:
21
+ """Retrieves a specific list by its ID.
22
+
23
+ Args:
24
+ list_id (str): The ID of the list to retrieve.
25
+
26
+ Returns:
27
+ TrelloList: The list object containing list details.
28
+ """
29
+ try:
30
+ logger.info(f"Getting list with ID: {list_id}")
31
+ result = await service.get_list(list_id)
32
+ logger.info(f"Successfully retrieved list: {list_id}")
33
+ return result
34
+ except Exception as e:
35
+ error_msg = f"Failed to get list: {str(e)}"
36
+ logger.error(error_msg)
37
+ await ctx.error(error_msg)
38
+ raise
39
+
40
+
41
+ async def get_lists(ctx: Context, board_id: str) -> List[TrelloList]:
42
+ """Retrieves all lists on a given board.
43
+
44
+ Args:
45
+ board_id (str): The ID of the board whose lists to retrieve.
46
+
47
+ Returns:
48
+ List[TrelloList]: A list of list objects.
49
+ """
50
+ try:
51
+ logger.info(f"Getting lists for board: {board_id}")
52
+ result = await service.get_lists(board_id)
53
+ logger.info(f"Successfully retrieved {len(result)} lists for board: {board_id}")
54
+ return result
55
+ except Exception as e:
56
+ error_msg = f"Failed to get lists: {str(e)}"
57
+ logger.error(error_msg)
58
+ await ctx.error(error_msg)
59
+ raise
60
+
61
+
62
+ async def create_list(
63
+ ctx: Context, board_id: str, name: str, pos: str = "bottom"
64
+ ) -> TrelloList:
65
+ """Creates a new list on a given board.
66
+
67
+ Args:
68
+ board_id (str): The ID of the board to create the list in.
69
+ name (str): The name of the new list.
70
+ pos (str, optional): The position of the new list. Can be "top" or "bottom". Defaults to "bottom".
71
+
72
+ Returns:
73
+ TrelloList: The newly created list object.
74
+ """
75
+ try:
76
+ logger.info(f"Creating list '{name}' in board: {board_id}")
77
+ result = await service.create_list(board_id, name, pos)
78
+ logger.info(f"Successfully created list '{name}' in board: {board_id}")
79
+ return result
80
+ except Exception as e:
81
+ error_msg = f"Failed to create list: {str(e)}"
82
+ logger.error(error_msg)
83
+ await ctx.error(error_msg)
84
+ raise
85
+
86
+
87
+ async def update_list(ctx: Context, list_id: str, name: str) -> TrelloList:
88
+ """Updates the name of a list.
89
+
90
+ Args:
91
+ list_id (str): The ID of the list to update.
92
+ name (str): The new name for the list.
93
+
94
+ Returns:
95
+ TrelloList: The updated list object.
96
+ """
97
+ try:
98
+ logger.info(f"Updating list {list_id} with new name: {name}")
99
+ result = await service.update_list(list_id, name)
100
+ logger.info(f"Successfully updated list: {list_id}")
101
+ return result
102
+ except Exception as e:
103
+ error_msg = f"Failed to update list: {str(e)}"
104
+ logger.error(error_msg)
105
+ await ctx.error(error_msg)
106
+ raise
107
+
108
+
109
+ async def delete_list(ctx: Context, list_id: str) -> TrelloList:
110
+ """Archives a list.
111
+
112
+ Args:
113
+ list_id (str): The ID of the list to close.
114
+
115
+ Returns:
116
+ TrelloList: The archived list object.
117
+ """
118
+ try:
119
+ logger.info(f"Archiving list: {list_id}")
120
+ result = await service.delete_list(list_id)
121
+ logger.info(f"Successfully archived list: {list_id}")
122
+ return result
123
+ except Exception as e:
124
+ error_msg = f"Failed to delete list: {str(e)}"
125
+ logger.error(error_msg)
126
+ await ctx.error(error_msg)
127
+ raise
pmcp/trello_server/tools/tools.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module contains tools for managing Trello boards, lists, and cards.
3
+ """
4
+
5
+ from pmcp.trello_server.tools import board, card, checklist, list
6
+
7
+
8
+ def register_tools(mcp):
9
+ """Register tools with the MCP server."""
10
+ # Board Tools
11
+ mcp.add_tool(board.get_board)
12
+ mcp.add_tool(board.get_boards)
13
+ mcp.add_tool(board.get_board_labels)
14
+
15
+ # List Tools
16
+ mcp.add_tool(list.get_list)
17
+ mcp.add_tool(list.get_lists)
18
+ mcp.add_tool(list.create_list)
19
+ mcp.add_tool(list.update_list)
20
+ mcp.add_tool(list.delete_list)
21
+
22
+ # Card Tools
23
+ mcp.add_tool(card.get_card)
24
+ mcp.add_tool(card.get_cards)
25
+ mcp.add_tool(card.create_card)
26
+ mcp.add_tool(card.update_card)
27
+ mcp.add_tool(card.delete_card)
28
+
29
+ # Checklist Tools
30
+ mcp.add_tool(checklist.get_checklist)
31
+ mcp.add_tool(checklist.get_card_checklists)
32
+ mcp.add_tool(checklist.create_checklist)
33
+ mcp.add_tool(checklist.update_checklist)
34
+ mcp.add_tool(checklist.delete_checklist)
35
+ mcp.add_tool(checklist.add_checkitem)
36
+ mcp.add_tool(checklist.update_checkitem)
37
+ mcp.add_tool(checklist.delete_checkitem)
pmcp/trello_server/trello.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+
4
+ from dotenv import load_dotenv
5
+
6
+ from pmcp.trello_server.utils.trello_api import TrelloClient
7
+
8
+ # Configure logging
9
+ logging.basicConfig(
10
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
11
+ )
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Load environment variables
15
+ load_dotenv()
16
+
17
+
18
+ # Initialize Trello client and service
19
+ try:
20
+ api_key = os.getenv("TRELLO_API_KEY")
21
+ token = os.getenv("TRELLO_TOKEN")
22
+ if not api_key or not token:
23
+ raise ValueError(
24
+ "TRELLO_API_KEY and TRELLO_TOKEN must be set in environment variables"
25
+ )
26
+ client = TrelloClient(api_key=api_key, token=token)
27
+ logger.info("Trello client and service initialized successfully")
28
+ except Exception as e:
29
+ logger.error(f"Failed to initialize Trello client: {str(e)}")
30
+ raise
31
+
32
+
33
+ # Add a prompt for common Trello operations
34
+ def trello_help() -> str:
35
+ """Provides help information about available Trello operations."""
36
+ return """
37
+ Available Trello Operations:
38
+ 1. Board Operations:
39
+ - Get a specific board
40
+ - List all boards
41
+ 2. List Operations:
42
+ - Get a specific list
43
+ - List all lists in a board
44
+ - Create a new list
45
+ - Update a list's name
46
+ - Archive a list
47
+ 3. Card Operations:
48
+ - Get a specific card
49
+ - List all cards in a list
50
+ - Create a new card
51
+ - Update a card's attributes
52
+ - Delete a card
53
+ 4. Checklist Operations:
54
+ - Get a specific checklist
55
+ - List all checklists in a card
56
+ - Create a new checklist
57
+ - Update a checklist
58
+ - Delete a checklist
59
+ - Add checkitem to checklist
60
+ - Update checkitem
61
+ - Delete checkitem
62
+ """
pmcp/trello_server/utils/__init__.py ADDED
File without changes
pmcp/trello_server/utils/trello_api.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # trello_api.py
2
+ import logging
3
+
4
+ import httpx
5
+
6
+ # Configure logging
7
+ logger = logging.getLogger(__name__)
8
+
9
+ TRELLO_API_BASE = "https://api.trello.com/1"
10
+
11
+
12
+ class TrelloClient:
13
+ """
14
+ Client class for interacting with the Trello API over REST.
15
+ """
16
+
17
+ def __init__(self, api_key: str, token: str):
18
+ self.api_key = api_key
19
+ self.token = token
20
+ self.base_url = TRELLO_API_BASE
21
+ self.client = httpx.AsyncClient(base_url=self.base_url)
22
+
23
+ async def close(self):
24
+ await self.client.aclose()
25
+
26
+ async def GET(self, endpoint: str, params: dict = None):
27
+ all_params = {"key": self.api_key, "token": self.token}
28
+ if params:
29
+ all_params.update(params)
30
+ try:
31
+ response = await self.client.get(endpoint, params=all_params)
32
+ response.raise_for_status()
33
+ return response.json()
34
+ except httpx.HTTPStatusError as e:
35
+ logger.error(f"HTTP error: {e}")
36
+ raise httpx.HTTPStatusError(
37
+ f"Failed to get {endpoint}: {str(e)}",
38
+ request=e.request,
39
+ response=e.response,
40
+ )
41
+ except httpx.RequestError as e:
42
+ logger.error(f"Request error: {e}")
43
+ raise httpx.RequestError(f"Failed to get {endpoint}: {str(e)}")
44
+
45
+ async def POST(self, endpoint: str, data: dict = None):
46
+ all_params = {"key": self.api_key, "token": self.token}
47
+ try:
48
+ response = await self.client.post(endpoint, params=all_params, json=data)
49
+ response.raise_for_status()
50
+ return response.json()
51
+ except httpx.HTTPStatusError as e:
52
+ logger.error(f"HTTP error: {e}")
53
+ raise httpx.HTTPStatusError(
54
+ f"Failed to post to {endpoint}: {str(e)}",
55
+ request=e.request,
56
+ response=e.response,
57
+ )
58
+ except httpx.RequestError as e:
59
+ logger.error(f"Request error: {e}")
60
+ raise httpx.RequestError(f"Failed to post to {endpoint}: {str(e)}")
61
+
62
+ async def PUT(self, endpoint: str, data: dict = None):
63
+ all_params = {"key": self.api_key, "token": self.token}
64
+ try:
65
+ response = await self.client.put(endpoint, params=all_params, json=data)
66
+ response.raise_for_status()
67
+ return response.json()
68
+ except httpx.HTTPStatusError as e:
69
+ logger.error(f"HTTP error: {e}")
70
+ raise httpx.HTTPStatusError(
71
+ f"Failed to put to {endpoint}: {str(e)}",
72
+ request=e.request,
73
+ response=e.response,
74
+ )
75
+ except httpx.RequestError as e:
76
+ logger.error(f"Request error: {e}")
77
+ raise httpx.RequestError(f"Failed to put to {endpoint}: {str(e)}")
78
+
79
+ async def DELETE(self, endpoint: str, params: dict = None):
80
+ all_params = {"key": self.api_key, "token": self.token}
81
+ if params:
82
+ all_params.update(params)
83
+ try:
84
+ response = await self.client.delete(endpoint, params=all_params)
85
+ response.raise_for_status()
86
+ return response.json()
87
+ except httpx.HTTPStatusError as e:
88
+ logger.error(f"HTTP error: {e}")
89
+ raise httpx.HTTPStatusError(
90
+ f"Failed to delete {endpoint}: {str(e)}",
91
+ request=e.request,
92
+ response=e.response,
93
+ )
94
+ except httpx.RequestError as e:
95
+ logger.error(f"Request error: {e}")
96
+ raise httpx.RequestError(f"Failed to delete {endpoint}: {str(e)}")