fix(planka): robustly handle nested 'included' and 'item' keys in API responses
This commit is contained in:
59
server.py
59
server.py
@@ -22,12 +22,22 @@ class PlankaClient:
|
||||
def _get(self, endpoint: str, params: Optional[dict] = None) -> Any:
|
||||
response = requests.get(f"{self.url}/api/{endpoint}", headers=self.headers, params=params)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
if not response.text:
|
||||
return None
|
||||
try:
|
||||
return response.json()
|
||||
except requests.exceptions.JSONDecodeError:
|
||||
return None
|
||||
|
||||
def _post(self, endpoint: str, data: dict) -> Any:
|
||||
response = requests.post(f"{self.url}/api/{endpoint}", headers=self.headers, json=data)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
if not response.text:
|
||||
return None
|
||||
try:
|
||||
return response.json()
|
||||
except requests.exceptions.JSONDecodeError:
|
||||
return None
|
||||
|
||||
def get_projects(self) -> List[dict]:
|
||||
data = self._get("projects")
|
||||
@@ -37,12 +47,27 @@ class PlankaClient:
|
||||
|
||||
def get_boards(self, project_id: str) -> List[dict]:
|
||||
data = self._get(f"projects/{project_id}")
|
||||
boards = []
|
||||
if isinstance(data, dict):
|
||||
if "boards" in data:
|
||||
return data["boards"]
|
||||
boards.extend(data["boards"])
|
||||
if "item" in data and isinstance(data["item"], dict) and "boards" in data["item"]:
|
||||
boards.extend(data["item"]["boards"])
|
||||
if "included" in data and isinstance(data["included"], dict) and "boards" in data["included"]:
|
||||
return data["included"]["boards"]
|
||||
return []
|
||||
boards.extend(data["included"]["boards"])
|
||||
return boards
|
||||
|
||||
def get_board_lists(self, board_id: str) -> List[dict]:
|
||||
data = self._get(f"boards/{board_id}")
|
||||
lists = []
|
||||
if isinstance(data, dict):
|
||||
if "lists" in data:
|
||||
lists.extend(data["lists"])
|
||||
if "item" in data and isinstance(data["item"], dict) and "lists" in data["item"]:
|
||||
lists.extend(data["item"]["lists"])
|
||||
if "included" in data and isinstance(data["included"], dict) and "lists" in data["included"]:
|
||||
lists.extend(data["included"]["lists"])
|
||||
return lists
|
||||
|
||||
def get_cards(self, board_id: str) -> List[dict]:
|
||||
data = self._get(f"boards/{board_id}/cards")
|
||||
@@ -59,7 +84,6 @@ class PlankaClient:
|
||||
})
|
||||
|
||||
def get_actions(self, card_id: str) -> List[dict]:
|
||||
# Planka comments are in the 'actions' endpoint for a card
|
||||
data = self._get(f"cards/{card_id}/actions")
|
||||
if isinstance(data, dict) and "items" in data:
|
||||
return data["items"]
|
||||
@@ -100,6 +124,14 @@ def list_boards(project_id: str) -> str:
|
||||
return "No boards found."
|
||||
return "\n".join([f"- {b['name']} (ID: {b['id']})" for b in boards])
|
||||
|
||||
@mcp.tool()
|
||||
def list_board_columns(board_id: str) -> str:
|
||||
"""List all columns (lists) for a given board ID."""
|
||||
lists = get_client().get_board_lists(board_id)
|
||||
if not lists:
|
||||
return "No columns found."
|
||||
return "\n".join([f"- {l['name']} (ID: {l['id']})" for l in lists])
|
||||
|
||||
@mcp.tool()
|
||||
def list_cards(board_id: str) -> str:
|
||||
"""List all cards for a given board ID."""
|
||||
@@ -130,4 +162,17 @@ def add_comment(card_id: str, text: str) -> str:
|
||||
return f"Comment added to card {card_id}."
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
if len(sys.argv) > 1:
|
||||
# CLI debug mode
|
||||
cmd = sys.argv[1]
|
||||
c = get_client()
|
||||
if cmd == "list_projects":
|
||||
print(list_projects())
|
||||
elif cmd == "list_boards":
|
||||
print(list_boards(sys.argv[2]))
|
||||
elif cmd == "list_cards":
|
||||
print(list_cards(sys.argv[2]))
|
||||
elif cmd == "list_columns":
|
||||
print(list_board_columns(sys.argv[2]))
|
||||
else:
|
||||
mcp.run()
|
||||
|
||||
Reference in New Issue
Block a user