t.aiello
commited on
Commit
·
7360460
1
Parent(s):
4d17015
add trello mcp server
Browse files- pmcp/trello_server/__init__.py +0 -0
- pmcp/trello_server/dtos/update_card.py +22 -0
- pmcp/trello_server/models.py +47 -0
- pmcp/trello_server/services/__init__.py +0 -0
- pmcp/trello_server/services/board.py +53 -0
- pmcp/trello_server/services/card.py +84 -0
- pmcp/trello_server/services/checklist.py +164 -0
- pmcp/trello_server/services/list.py +82 -0
- pmcp/trello_server/tools/__init__.py +1 -0
- pmcp/trello_server/tools/board.py +76 -0
- pmcp/trello_server/tools/card.py +131 -0
- pmcp/trello_server/tools/checklist.py +140 -0
- pmcp/trello_server/tools/list.py +127 -0
- pmcp/trello_server/tools/tools.py +37 -0
- pmcp/trello_server/trello.py +62 -0
- pmcp/trello_server/utils/__init__.py +0 -0
- pmcp/trello_server/utils/trello_api.py +96 -0
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)}")
|